diff options
author | Ben Widawsky <ben@bwidawsk.net> | 2012-05-25 16:56:25 -0700 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-05-31 12:29:21 +0200 |
commit | 84bc758124127a5b16b30f9a8bf5f1a981affc6b (patch) | |
tree | 8be6d37bdce83a12088c321efb21b6c228f09d33 /drivers/gpu/drm | |
parent | b9524a1e1c48cf461768914345ec94be6a15e710 (diff) | |
download | linux-84bc758124127a5b16b30f9a8bf5f1a981affc6b.tar.gz linux-84bc758124127a5b16b30f9a8bf5f1a981affc6b.tar.bz2 linux-84bc758124127a5b16b30f9a8bf5f1a981affc6b.zip |
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')
-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 @@ #include <linux/module.h> #include <linux/stat.h> #include <linux/sysfs.h> +#include "intel_drv.h" #include "i915_drv.h" static u32 calc_residency(struct drm_device *dev, const u32 reg) @@ -92,20 +93,136 @@ static struct attribute_group rc6_attr_group = { .attrs = rc6_attrs }; +static int l3_access_valid(struct drm_device *dev, loff_t offset) +{ + if (!IS_IVYBRIDGE(dev)) + return -EPERM; + + if (offset % 4 != 0) + return -EINVAL; + + if (offset >= GEN7_L3LOG_SIZE) + return -ENXIO; + + return 0; +} + +static ssize_t +i915_l3_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t offset, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev); + struct drm_device *drm_dev = dminor->dev; + struct drm_i915_private *dev_priv = drm_dev->dev_private; + uint32_t misccpctl; + int i, ret; + + ret = l3_access_valid(drm_dev, offset); + if (ret) + return ret; + + ret = i915_mutex_lock_interruptible(drm_dev); + if (ret) + return ret; + + misccpctl = I915_READ(GEN7_MISCCPCTL); + I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE); + + for (i = offset; count >= 4 && i < GEN7_L3LOG_SIZE; i += 4, count -= 4) + *((uint32_t *)(&buf[i])) = I915_READ(GEN7_L3LOG_BASE + i); + + I915_WRITE(GEN7_MISCCPCTL, misccpctl); + + mutex_unlock(&drm_dev->struct_mutex); + + return i - offset; +} + +static ssize_t +i915_l3_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t offset, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev); + struct drm_device *drm_dev = dminor->dev; + struct drm_i915_private *dev_priv = drm_dev->dev_private; + u32 *temp = NULL; /* Just here to make handling failures easy */ + int ret; + + ret = l3_access_valid(drm_dev, offset); + if (ret) + return ret; + + ret = i915_mutex_lock_interruptible(drm_dev); + if (ret) + return ret; + + if (!dev_priv->mm.l3_remap_info) { + temp = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL); + if (!temp) { + mutex_unlock(&drm_dev->struct_mutex); + return -ENOMEM; + } + } + + ret = i915_gpu_idle(drm_dev); + if (ret) { + kfree(temp); + mutex_unlock(&drm_dev->struct_mutex); + return ret; + } + + /* TODO: Ideally we really want a GPU reset here to make sure errors + * aren't propagated. Since I cannot find a stable way to reset the GPU + * at this point it is left as a TODO. + */ + if (temp) + dev_priv->mm.l3_remap_info = temp; + + memcpy(dev_priv->mm.l3_remap_info + (offset/4), + buf + (offset/4), + count); + + i915_gem_l3_remap(drm_dev); + + mutex_unlock(&drm_dev->struct_mutex); + + return count; +} + +static struct bin_attribute dpf_attrs = { + .attr = {.name = "l3_parity", .mode = (S_IRUSR | S_IWUSR)}, + .size = GEN7_L3LOG_SIZE, + .read = i915_l3_read, + .write = i915_l3_write, + .mmap = NULL +}; + void i915_setup_sysfs(struct drm_device *dev) { int ret; - /* ILK doesn't have any residency information */ + /* ILK and below don't yet have relevant sysfs files */ if (INTEL_INFO(dev)->gen < 6) return; ret = sysfs_merge_group(&dev->primary->kdev.kobj, &rc6_attr_group); if (ret) - DRM_ERROR("sysfs setup failed\n"); + DRM_ERROR("RC6 residency sysfs setup failed\n"); + + if (!IS_IVYBRIDGE(dev)) + return; + + ret = device_create_bin_file(&dev->primary->kdev, &dpf_attrs); + if (ret) + DRM_ERROR("l3 parity sysfs setup failed\n"); } void i915_teardown_sysfs(struct drm_device *dev) { + device_remove_bin_file(&dev->primary->kdev, &dpf_attrs); sysfs_unmerge_group(&dev->primary->kdev.kobj, &rc6_attr_group); } |