diff options
author | Ben Widawsky <ben@bwidawsk.net> | 2012-05-25 19:56:25 -0400 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-05-31 06:29:21 -0400 |
commit | 84bc758124127a5b16b30f9a8bf5f1a981affc6b (patch) | |
tree | 8be6d37bdce83a12088c321efb21b6c228f09d33 /drivers/gpu/drm/i915/i915_sysfs.c | |
parent | b9524a1e1c48cf461768914345ec94be6a15e710 (diff) |
drm/i915: l3 parity sysfs interface
Dumb binary interfaces which allow root-only updates of the cache
remapping registers. As mentioned in a previous patch, software using
this interface needs to know about HW limits, and other programming
considerations as the kernel interface does no checking for these things
on the root-only interface.
v1: Drop extra posting reads (Chris)
Return negative values in the sysfs interfaces on errors (Chris)
v2: Return -EINVAL for offset % 4 (Jesse)
Move schizo userspace check out (Jesse)
Cleaner sysfs item initializers (Daniel)
Signed-off-by: Ben Widawsky <ben@bwidawsk.net>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/i915/i915_sysfs.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_sysfs.c | 121 |
1 files changed, 119 insertions, 2 deletions
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index 79f83445afa0..c2013273a4c6 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,136 @@ static struct attribute_group rc6_attr_group = { | |||
92 | .attrs = rc6_attrs | 93 | .attrs = rc6_attrs |
93 | }; | 94 | }; |
94 | 95 | ||
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) | ||
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 */ | ||
153 | int ret; | ||
154 | |||
155 | ret = l3_access_valid(drm_dev, offset); | ||
156 | if (ret) | ||
157 | return ret; | ||
158 | |||
159 | ret = i915_mutex_lock_interruptible(drm_dev); | ||
160 | if (ret) | ||
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 | |||
95 | void i915_setup_sysfs(struct drm_device *dev) | 204 | void i915_setup_sysfs(struct drm_device *dev) |
96 | { | 205 | { |
97 | int ret; | 206 | int ret; |
98 | 207 | ||
99 | /* ILK doesn't have any residency information */ | 208 | /* ILK and below don't yet have relevant sysfs files */ |
100 | if (INTEL_INFO(dev)->gen < 6) | 209 | if (INTEL_INFO(dev)->gen < 6) |
101 | return; | 210 | return; |
102 | 211 | ||
103 | ret = sysfs_merge_group(&dev->primary->kdev.kobj, &rc6_attr_group); | 212 | ret = sysfs_merge_group(&dev->primary->kdev.kobj, &rc6_attr_group); |
104 | if (ret) | 213 | if (ret) |
105 | DRM_ERROR("sysfs setup failed\n"); | 214 | DRM_ERROR("RC6 residency sysfs setup failed\n"); |
215 | |||
216 | if (!IS_IVYBRIDGE(dev)) | ||
217 | return; | ||
218 | |||
219 | ret = device_create_bin_file(&dev->primary->kdev, &dpf_attrs); | ||
220 | if (ret) | ||
221 | DRM_ERROR("l3 parity sysfs setup failed\n"); | ||
106 | } | 222 | } |
107 | 223 | ||
108 | void i915_teardown_sysfs(struct drm_device *dev) | 224 | void i915_teardown_sysfs(struct drm_device *dev) |
109 | { | 225 | { |
226 | device_remove_bin_file(&dev->primary->kdev, &dpf_attrs); | ||
110 | sysfs_unmerge_group(&dev->primary->kdev.kobj, &rc6_attr_group); | 227 | sysfs_unmerge_group(&dev->primary->kdev.kobj, &rc6_attr_group); |
111 | } | 228 | } |