diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_sysfs.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_sysfs.c | 127 |
1 files changed, 121 insertions, 6 deletions
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index 79f83445afa0..2f5388af8df9 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/module.h> | 29 | #include <linux/module.h> |
30 | #include <linux/stat.h> | 30 | #include <linux/stat.h> |
31 | #include <linux/sysfs.h> | 31 | #include <linux/sysfs.h> |
32 | #include "intel_drv.h" | ||
32 | #include "i915_drv.h" | 33 | #include "i915_drv.h" |
33 | 34 | ||
34 | static u32 calc_residency(struct drm_device *dev, const u32 reg) | 35 | static u32 calc_residency(struct drm_device *dev, const u32 reg) |
@@ -92,20 +93,134 @@ static struct attribute_group rc6_attr_group = { | |||
92 | .attrs = rc6_attrs | 93 | .attrs = rc6_attrs |
93 | }; | 94 | }; |
94 | 95 | ||
95 | void i915_setup_sysfs(struct drm_device *dev) | 96 | static int l3_access_valid(struct drm_device *dev, loff_t offset) |
97 | { | ||
98 | if (!IS_IVYBRIDGE(dev)) | ||
99 | return -EPERM; | ||
100 | |||
101 | if (offset % 4 != 0) | ||
102 | return -EINVAL; | ||
103 | |||
104 | if (offset >= GEN7_L3LOG_SIZE) | ||
105 | return -ENXIO; | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static ssize_t | ||
111 | i915_l3_read(struct file *filp, struct kobject *kobj, | ||
112 | struct bin_attribute *attr, char *buf, | ||
113 | loff_t offset, size_t count) | ||
114 | { | ||
115 | struct device *dev = container_of(kobj, struct device, kobj); | ||
116 | struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev); | ||
117 | struct drm_device *drm_dev = dminor->dev; | ||
118 | struct drm_i915_private *dev_priv = drm_dev->dev_private; | ||
119 | uint32_t misccpctl; | ||
120 | int i, ret; | ||
121 | |||
122 | ret = l3_access_valid(drm_dev, offset); | ||
123 | if (ret) | ||
124 | return ret; | ||
125 | |||
126 | ret = i915_mutex_lock_interruptible(drm_dev); | ||
127 | if (ret) | ||
128 | return ret; | ||
129 | |||
130 | misccpctl = I915_READ(GEN7_MISCCPCTL); | ||
131 | I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE); | ||
132 | |||
133 | for (i = offset; count >= 4 && i < GEN7_L3LOG_SIZE; i += 4, count -= 4) | ||
134 | *((uint32_t *)(&buf[i])) = I915_READ(GEN7_L3LOG_BASE + i); | ||
135 | |||
136 | I915_WRITE(GEN7_MISCCPCTL, misccpctl); | ||
137 | |||
138 | mutex_unlock(&drm_dev->struct_mutex); | ||
139 | |||
140 | return i - offset; | ||
141 | } | ||
142 | |||
143 | static ssize_t | ||
144 | i915_l3_write(struct file *filp, struct kobject *kobj, | ||
145 | struct bin_attribute *attr, char *buf, | ||
146 | loff_t offset, size_t count) | ||
96 | { | 147 | { |
148 | struct device *dev = container_of(kobj, struct device, kobj); | ||
149 | struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev); | ||
150 | struct drm_device *drm_dev = dminor->dev; | ||
151 | struct drm_i915_private *dev_priv = drm_dev->dev_private; | ||
152 | u32 *temp = NULL; /* Just here to make handling failures easy */ | ||
97 | int ret; | 153 | int ret; |
98 | 154 | ||
99 | /* ILK doesn't have any residency information */ | 155 | ret = l3_access_valid(drm_dev, offset); |
100 | if (INTEL_INFO(dev)->gen < 6) | 156 | if (ret) |
101 | return; | 157 | return ret; |
102 | 158 | ||
103 | ret = sysfs_merge_group(&dev->primary->kdev.kobj, &rc6_attr_group); | 159 | ret = i915_mutex_lock_interruptible(drm_dev); |
104 | if (ret) | 160 | if (ret) |
105 | DRM_ERROR("sysfs setup failed\n"); | 161 | return ret; |
162 | |||
163 | if (!dev_priv->mm.l3_remap_info) { | ||
164 | temp = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL); | ||
165 | if (!temp) { | ||
166 | mutex_unlock(&drm_dev->struct_mutex); | ||
167 | return -ENOMEM; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | ret = i915_gpu_idle(drm_dev); | ||
172 | if (ret) { | ||
173 | kfree(temp); | ||
174 | mutex_unlock(&drm_dev->struct_mutex); | ||
175 | return ret; | ||
176 | } | ||
177 | |||
178 | /* TODO: Ideally we really want a GPU reset here to make sure errors | ||
179 | * aren't propagated. Since I cannot find a stable way to reset the GPU | ||
180 | * at this point it is left as a TODO. | ||
181 | */ | ||
182 | if (temp) | ||
183 | dev_priv->mm.l3_remap_info = temp; | ||
184 | |||
185 | memcpy(dev_priv->mm.l3_remap_info + (offset/4), | ||
186 | buf + (offset/4), | ||
187 | count); | ||
188 | |||
189 | i915_gem_l3_remap(drm_dev); | ||
190 | |||
191 | mutex_unlock(&drm_dev->struct_mutex); | ||
192 | |||
193 | return count; | ||
194 | } | ||
195 | |||
196 | static struct bin_attribute dpf_attrs = { | ||
197 | .attr = {.name = "l3_parity", .mode = (S_IRUSR | S_IWUSR)}, | ||
198 | .size = GEN7_L3LOG_SIZE, | ||
199 | .read = i915_l3_read, | ||
200 | .write = i915_l3_write, | ||
201 | .mmap = NULL | ||
202 | }; | ||
203 | |||
204 | void i915_setup_sysfs(struct drm_device *dev) | ||
205 | { | ||
206 | int ret; | ||
207 | |||
208 | if (INTEL_INFO(dev)->gen >= 6) { | ||
209 | ret = sysfs_merge_group(&dev->primary->kdev.kobj, | ||
210 | &rc6_attr_group); | ||
211 | if (ret) | ||
212 | DRM_ERROR("RC6 residency sysfs setup failed\n"); | ||
213 | } | ||
214 | |||
215 | if (IS_IVYBRIDGE(dev)) { | ||
216 | ret = device_create_bin_file(&dev->primary->kdev, &dpf_attrs); | ||
217 | if (ret) | ||
218 | DRM_ERROR("l3 parity sysfs setup failed\n"); | ||
219 | } | ||
106 | } | 220 | } |
107 | 221 | ||
108 | void i915_teardown_sysfs(struct drm_device *dev) | 222 | void i915_teardown_sysfs(struct drm_device *dev) |
109 | { | 223 | { |
224 | device_remove_bin_file(&dev->primary->kdev, &dpf_attrs); | ||
110 | sysfs_unmerge_group(&dev->primary->kdev.kobj, &rc6_attr_group); | 225 | sysfs_unmerge_group(&dev->primary->kdev.kobj, &rc6_attr_group); |
111 | } | 226 | } |