drm/i915: Implement GPU reset on i965
This patch puts in place the machinery to attempt to reset the GPU. This will be used when attempting to recover from a GPU hang. Signed-off-by: Owain G. Ainsworth <oga@openbsd.org> Signed-off-by: Ben Gamari <bgamari.foss@gmail.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
@@ -1173,6 +1173,9 @@ static int i915_load_modeset_init(struct drm_device *dev,
|
|||||||
drm_mm_init(&dev_priv->vram, 0, prealloc_size);
|
drm_mm_init(&dev_priv->vram, 0, prealloc_size);
|
||||||
DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024));
|
DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024));
|
||||||
|
|
||||||
|
/* We're off and running w/KMS */
|
||||||
|
dev_priv->mm.suspended = 0;
|
||||||
|
|
||||||
/* Let GEM Manage from end of prealloc space to end of aperture.
|
/* Let GEM Manage from end of prealloc space to end of aperture.
|
||||||
*
|
*
|
||||||
* However, leave one page at the end still bound to the scratch page.
|
* However, leave one page at the end still bound to the scratch page.
|
||||||
@@ -1184,7 +1187,9 @@ static int i915_load_modeset_init(struct drm_device *dev,
|
|||||||
*/
|
*/
|
||||||
i915_gem_do_init(dev, prealloc_size, agp_size - 4096);
|
i915_gem_do_init(dev, prealloc_size, agp_size - 4096);
|
||||||
|
|
||||||
|
mutex_lock(&dev->struct_mutex);
|
||||||
ret = i915_gem_init_ringbuffer(dev);
|
ret = i915_gem_init_ringbuffer(dev);
|
||||||
|
mutex_unlock(&dev->struct_mutex);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@@ -1433,6 +1438,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Start out suspended */
|
||||||
|
dev_priv->mm.suspended = 1;
|
||||||
|
|
||||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||||
ret = i915_load_modeset_init(dev, prealloc_start,
|
ret = i915_load_modeset_init(dev, prealloc_start,
|
||||||
prealloc_size, agp_size);
|
prealloc_size, agp_size);
|
||||||
|
@@ -127,6 +127,130 @@ static int i915_resume(struct drm_device *dev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i965_reset - reset chip after a hang
|
||||||
|
* @dev: drm device to reset
|
||||||
|
* @flags: reset domains
|
||||||
|
*
|
||||||
|
* Reset the chip. Useful if a hang is detected. Returns zero on successful
|
||||||
|
* reset or otherwise an error code.
|
||||||
|
*
|
||||||
|
* Procedure is fairly simple:
|
||||||
|
* - reset the chip using the reset reg
|
||||||
|
* - re-init context state
|
||||||
|
* - re-init hardware status page
|
||||||
|
* - re-init ring buffer
|
||||||
|
* - re-init interrupt state
|
||||||
|
* - re-init display
|
||||||
|
*/
|
||||||
|
int i965_reset(struct drm_device *dev, u8 flags)
|
||||||
|
{
|
||||||
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||||
|
unsigned long timeout;
|
||||||
|
u8 gdrst;
|
||||||
|
/*
|
||||||
|
* We really should only reset the display subsystem if we actually
|
||||||
|
* need to
|
||||||
|
*/
|
||||||
|
bool need_display = true;
|
||||||
|
|
||||||
|
mutex_lock(&dev->struct_mutex);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear request list
|
||||||
|
*/
|
||||||
|
i915_gem_retire_requests(dev);
|
||||||
|
|
||||||
|
if (need_display)
|
||||||
|
i915_save_display(dev);
|
||||||
|
|
||||||
|
if (IS_I965G(dev) || IS_G4X(dev)) {
|
||||||
|
/*
|
||||||
|
* Set the domains we want to reset, then the reset bit (bit 0).
|
||||||
|
* Clear the reset bit after a while and wait for hardware status
|
||||||
|
* bit (bit 1) to be set
|
||||||
|
*/
|
||||||
|
pci_read_config_byte(dev->pdev, GDRST, &gdrst);
|
||||||
|
pci_write_config_byte(dev->pdev, GDRST, gdrst | flags | ((flags == GDRST_FULL) ? 0x1 : 0x0));
|
||||||
|
udelay(50);
|
||||||
|
pci_write_config_byte(dev->pdev, GDRST, gdrst & 0xfe);
|
||||||
|
|
||||||
|
/* ...we don't want to loop forever though, 500ms should be plenty */
|
||||||
|
timeout = jiffies + msecs_to_jiffies(500);
|
||||||
|
do {
|
||||||
|
udelay(100);
|
||||||
|
pci_read_config_byte(dev->pdev, GDRST, &gdrst);
|
||||||
|
} while ((gdrst & 0x1) && time_after(timeout, jiffies));
|
||||||
|
|
||||||
|
if (gdrst & 0x1) {
|
||||||
|
WARN(true, "i915: Failed to reset chip\n");
|
||||||
|
mutex_unlock(&dev->struct_mutex);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DRM_ERROR("Error occurred. Don't know how to reset this chip.\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ok, now get things going again... */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Everything depends on having the GTT running, so we need to start
|
||||||
|
* there. Fortunately we don't need to do this unless we reset the
|
||||||
|
* chip at a PCI level.
|
||||||
|
*
|
||||||
|
* Next we need to restore the context, but we don't use those
|
||||||
|
* yet either...
|
||||||
|
*
|
||||||
|
* Ring buffer needs to be re-initialized in the KMS case, or if X
|
||||||
|
* was running at the time of the reset (i.e. we weren't VT
|
||||||
|
* switched away).
|
||||||
|
*/
|
||||||
|
if (drm_core_check_feature(dev, DRIVER_MODESET) ||
|
||||||
|
!dev_priv->mm.suspended) {
|
||||||
|
drm_i915_ring_buffer_t *ring = &dev_priv->ring;
|
||||||
|
struct drm_gem_object *obj = ring->ring_obj;
|
||||||
|
struct drm_i915_gem_object *obj_priv = obj->driver_private;
|
||||||
|
dev_priv->mm.suspended = 0;
|
||||||
|
|
||||||
|
/* Stop the ring if it's running. */
|
||||||
|
I915_WRITE(PRB0_CTL, 0);
|
||||||
|
I915_WRITE(PRB0_TAIL, 0);
|
||||||
|
I915_WRITE(PRB0_HEAD, 0);
|
||||||
|
|
||||||
|
/* Initialize the ring. */
|
||||||
|
I915_WRITE(PRB0_START, obj_priv->gtt_offset);
|
||||||
|
I915_WRITE(PRB0_CTL,
|
||||||
|
((obj->size - 4096) & RING_NR_PAGES) |
|
||||||
|
RING_NO_REPORT |
|
||||||
|
RING_VALID);
|
||||||
|
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||||
|
i915_kernel_lost_context(dev);
|
||||||
|
else {
|
||||||
|
ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
|
||||||
|
ring->tail = I915_READ(PRB0_TAIL) & TAIL_ADDR;
|
||||||
|
ring->space = ring->head - (ring->tail + 8);
|
||||||
|
if (ring->space < 0)
|
||||||
|
ring->space += ring->Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&dev->struct_mutex);
|
||||||
|
drm_irq_uninstall(dev);
|
||||||
|
drm_irq_install(dev);
|
||||||
|
mutex_lock(&dev->struct_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Display needs restore too...
|
||||||
|
*/
|
||||||
|
if (need_display)
|
||||||
|
i915_restore_display(dev);
|
||||||
|
|
||||||
|
mutex_unlock(&dev->struct_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int __devinit
|
static int __devinit
|
||||||
i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||||
{
|
{
|
||||||
|
@@ -624,6 +624,7 @@ extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
|
|||||||
extern int i915_emit_box(struct drm_device *dev,
|
extern int i915_emit_box(struct drm_device *dev,
|
||||||
struct drm_clip_rect *boxes,
|
struct drm_clip_rect *boxes,
|
||||||
int i, int DR1, int DR4);
|
int i, int DR1, int DR4);
|
||||||
|
extern int i965_reset(struct drm_device *dev, u8 flags);
|
||||||
|
|
||||||
/* i915_irq.c */
|
/* i915_irq.c */
|
||||||
void i915_hangcheck_elapsed(unsigned long data);
|
void i915_hangcheck_elapsed(unsigned long data);
|
||||||
|
@@ -482,6 +482,14 @@ static void i915_handle_error(struct drm_device *dev)
|
|||||||
I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
|
I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dev_priv->mm.wedged) {
|
||||||
|
/*
|
||||||
|
* Wakeup waiting processes so they don't hang
|
||||||
|
*/
|
||||||
|
printk("i915: Waking up sleeping processes\n");
|
||||||
|
DRM_WAKEUP(&dev_priv->irq_queue);
|
||||||
|
}
|
||||||
|
|
||||||
queue_work(dev_priv->wq, &dev_priv->error_work);
|
queue_work(dev_priv->wq, &dev_priv->error_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -85,6 +85,10 @@
|
|||||||
#define I915_GC_RENDER_CLOCK_200_MHZ (1 << 0)
|
#define I915_GC_RENDER_CLOCK_200_MHZ (1 << 0)
|
||||||
#define I915_GC_RENDER_CLOCK_333_MHZ (4 << 0)
|
#define I915_GC_RENDER_CLOCK_333_MHZ (4 << 0)
|
||||||
#define LBB 0xf4
|
#define LBB 0xf4
|
||||||
|
#define GDRST 0xc0
|
||||||
|
#define GDRST_FULL (0<<2)
|
||||||
|
#define GDRST_RENDER (1<<2)
|
||||||
|
#define GDRST_MEDIA (3<<2)
|
||||||
|
|
||||||
/* VGA stuff */
|
/* VGA stuff */
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user