diff options
author | Ben Gamari <bgamari.foss@gmail.com> | 2009-09-14 17:48:45 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-09-17 17:36:22 -0400 |
commit | 11ed50ec2a316928c2bacc1149bded86c6a96068 (patch) | |
tree | b363e00c943a34a04c1d48ec747d0a41f920e567 /drivers/gpu | |
parent | f65d94211e2bcba17faf05a6a3809af0e4217767 (diff) |
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>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/i915/i915_dma.c | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.c | 124 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_reg.h | 4 |
5 files changed, 145 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 08a5048335e1..f47adb4aa59a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c | |||
@@ -1173,6 +1173,9 @@ static int i915_load_modeset_init(struct drm_device *dev, | |||
1173 | drm_mm_init(&dev_priv->vram, 0, prealloc_size); | 1173 | drm_mm_init(&dev_priv->vram, 0, prealloc_size); |
1174 | DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024)); | 1174 | DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024)); |
1175 | 1175 | ||
1176 | /* We're off and running w/KMS */ | ||
1177 | dev_priv->mm.suspended = 0; | ||
1178 | |||
1176 | /* Let GEM Manage from end of prealloc space to end of aperture. | 1179 | /* Let GEM Manage from end of prealloc space to end of aperture. |
1177 | * | 1180 | * |
1178 | * However, leave one page at the end still bound to the scratch page. | 1181 | * 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, | |||
1184 | */ | 1187 | */ |
1185 | i915_gem_do_init(dev, prealloc_size, agp_size - 4096); | 1188 | i915_gem_do_init(dev, prealloc_size, agp_size - 4096); |
1186 | 1189 | ||
1190 | mutex_lock(&dev->struct_mutex); | ||
1187 | ret = i915_gem_init_ringbuffer(dev); | 1191 | ret = i915_gem_init_ringbuffer(dev); |
1192 | mutex_unlock(&dev->struct_mutex); | ||
1188 | if (ret) | 1193 | if (ret) |
1189 | goto out; | 1194 | goto out; |
1190 | 1195 | ||
@@ -1433,6 +1438,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) | |||
1433 | return ret; | 1438 | return ret; |
1434 | } | 1439 | } |
1435 | 1440 | ||
1441 | /* Start out suspended */ | ||
1442 | dev_priv->mm.suspended = 1; | ||
1443 | |||
1436 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | 1444 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { |
1437 | ret = i915_load_modeset_init(dev, prealloc_start, | 1445 | ret = i915_load_modeset_init(dev, prealloc_start, |
1438 | prealloc_size, agp_size); | 1446 | prealloc_size, agp_size); |
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index dbe568c9327b..435082e40736 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c | |||
@@ -127,6 +127,130 @@ static int i915_resume(struct drm_device *dev) | |||
127 | return ret; | 127 | return ret; |
128 | } | 128 | } |
129 | 129 | ||
130 | /** | ||
131 | * i965_reset - reset chip after a hang | ||
132 | * @dev: drm device to reset | ||
133 | * @flags: reset domains | ||
134 | * | ||
135 | * Reset the chip. Useful if a hang is detected. Returns zero on successful | ||
136 | * reset or otherwise an error code. | ||
137 | * | ||
138 | * Procedure is fairly simple: | ||
139 | * - reset the chip using the reset reg | ||
140 | * - re-init context state | ||
141 | * - re-init hardware status page | ||
142 | * - re-init ring buffer | ||
143 | * - re-init interrupt state | ||
144 | * - re-init display | ||
145 | */ | ||
146 | int i965_reset(struct drm_device *dev, u8 flags) | ||
147 | { | ||
148 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
149 | unsigned long timeout; | ||
150 | u8 gdrst; | ||
151 | /* | ||
152 | * We really should only reset the display subsystem if we actually | ||
153 | * need to | ||
154 | */ | ||
155 | bool need_display = true; | ||
156 | |||
157 | mutex_lock(&dev->struct_mutex); | ||
158 | |||
159 | /* | ||
160 | * Clear request list | ||
161 | */ | ||
162 | i915_gem_retire_requests(dev); | ||
163 | |||
164 | if (need_display) | ||
165 | i915_save_display(dev); | ||
166 | |||
167 | if (IS_I965G(dev) || IS_G4X(dev)) { | ||
168 | /* | ||
169 | * Set the domains we want to reset, then the reset bit (bit 0). | ||
170 | * Clear the reset bit after a while and wait for hardware status | ||
171 | * bit (bit 1) to be set | ||
172 | */ | ||
173 | pci_read_config_byte(dev->pdev, GDRST, &gdrst); | ||
174 | pci_write_config_byte(dev->pdev, GDRST, gdrst | flags | ((flags == GDRST_FULL) ? 0x1 : 0x0)); | ||
175 | udelay(50); | ||
176 | pci_write_config_byte(dev->pdev, GDRST, gdrst & 0xfe); | ||
177 | |||
178 | /* ...we don't want to loop forever though, 500ms should be plenty */ | ||
179 | timeout = jiffies + msecs_to_jiffies(500); | ||
180 | do { | ||
181 | udelay(100); | ||
182 | pci_read_config_byte(dev->pdev, GDRST, &gdrst); | ||
183 | } while ((gdrst & 0x1) && time_after(timeout, jiffies)); | ||
184 | |||
185 | if (gdrst & 0x1) { | ||
186 | WARN(true, "i915: Failed to reset chip\n"); | ||
187 | mutex_unlock(&dev->struct_mutex); | ||
188 | return -EIO; | ||
189 | } | ||
190 | } else { | ||
191 | DRM_ERROR("Error occurred. Don't know how to reset this chip.\n"); | ||
192 | return -ENODEV; | ||
193 | } | ||
194 | |||
195 | /* Ok, now get things going again... */ | ||
196 | |||
197 | /* | ||
198 | * Everything depends on having the GTT running, so we need to start | ||
199 | * there. Fortunately we don't need to do this unless we reset the | ||
200 | * chip at a PCI level. | ||
201 | * | ||
202 | * Next we need to restore the context, but we don't use those | ||
203 | * yet either... | ||
204 | * | ||
205 | * Ring buffer needs to be re-initialized in the KMS case, or if X | ||
206 | * was running at the time of the reset (i.e. we weren't VT | ||
207 | * switched away). | ||
208 | */ | ||
209 | if (drm_core_check_feature(dev, DRIVER_MODESET) || | ||
210 | !dev_priv->mm.suspended) { | ||
211 | drm_i915_ring_buffer_t *ring = &dev_priv->ring; | ||
212 | struct drm_gem_object *obj = ring->ring_obj; | ||
213 | struct drm_i915_gem_object *obj_priv = obj->driver_private; | ||
214 | dev_priv->mm.suspended = 0; | ||
215 | |||
216 | /* Stop the ring if it's running. */ | ||
217 | I915_WRITE(PRB0_CTL, 0); | ||
218 | I915_WRITE(PRB0_TAIL, 0); | ||
219 | I915_WRITE(PRB0_HEAD, 0); | ||
220 | |||
221 | /* Initialize the ring. */ | ||
222 | I915_WRITE(PRB0_START, obj_priv->gtt_offset); | ||
223 | I915_WRITE(PRB0_CTL, | ||
224 | ((obj->size - 4096) & RING_NR_PAGES) | | ||
225 | RING_NO_REPORT | | ||
226 | RING_VALID); | ||
227 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | ||
228 | i915_kernel_lost_context(dev); | ||
229 | else { | ||
230 | ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR; | ||
231 | ring->tail = I915_READ(PRB0_TAIL) & TAIL_ADDR; | ||
232 | ring->space = ring->head - (ring->tail + 8); | ||
233 | if (ring->space < 0) | ||
234 | ring->space += ring->Size; | ||
235 | } | ||
236 | |||
237 | mutex_unlock(&dev->struct_mutex); | ||
238 | drm_irq_uninstall(dev); | ||
239 | drm_irq_install(dev); | ||
240 | mutex_lock(&dev->struct_mutex); | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * Display needs restore too... | ||
245 | */ | ||
246 | if (need_display) | ||
247 | i915_restore_display(dev); | ||
248 | |||
249 | mutex_unlock(&dev->struct_mutex); | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | |||
130 | static int __devinit | 254 | static int __devinit |
131 | i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | 255 | i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
132 | { | 256 | { |
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index afbcaa9866f4..42142f269765 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h | |||
@@ -624,6 +624,7 @@ extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, | |||
624 | extern int i915_emit_box(struct drm_device *dev, | 624 | extern int i915_emit_box(struct drm_device *dev, |
625 | struct drm_clip_rect *boxes, | 625 | struct drm_clip_rect *boxes, |
626 | int i, int DR1, int DR4); | 626 | int i, int DR1, int DR4); |
627 | extern int i965_reset(struct drm_device *dev, u8 flags); | ||
627 | 628 | ||
628 | /* i915_irq.c */ | 629 | /* i915_irq.c */ |
629 | void i915_hangcheck_elapsed(unsigned long data); | 630 | void i915_hangcheck_elapsed(unsigned long data); |
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 77e42e719d7d..2a042bc173fe 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c | |||
@@ -482,6 +482,14 @@ static void i915_handle_error(struct drm_device *dev) | |||
482 | I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); | 482 | I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); |
483 | } | 483 | } |
484 | 484 | ||
485 | if (dev_priv->mm.wedged) { | ||
486 | /* | ||
487 | * Wakeup waiting processes so they don't hang | ||
488 | */ | ||
489 | printk("i915: Waking up sleeping processes\n"); | ||
490 | DRM_WAKEUP(&dev_priv->irq_queue); | ||
491 | } | ||
492 | |||
485 | queue_work(dev_priv->wq, &dev_priv->error_work); | 493 | queue_work(dev_priv->wq, &dev_priv->error_work); |
486 | } | 494 | } |
487 | 495 | ||
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 6345bf20db03..f3d41397ce74 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h | |||
@@ -85,6 +85,10 @@ | |||
85 | #define I915_GC_RENDER_CLOCK_200_MHZ (1 << 0) | 85 | #define I915_GC_RENDER_CLOCK_200_MHZ (1 << 0) |
86 | #define I915_GC_RENDER_CLOCK_333_MHZ (4 << 0) | 86 | #define I915_GC_RENDER_CLOCK_333_MHZ (4 << 0) |
87 | #define LBB 0xf4 | 87 | #define LBB 0xf4 |
88 | #define GDRST 0xc0 | ||
89 | #define GDRST_FULL (0<<2) | ||
90 | #define GDRST_RENDER (1<<2) | ||
91 | #define GDRST_MEDIA (3<<2) | ||
88 | 92 | ||
89 | /* VGA stuff */ | 93 | /* VGA stuff */ |
90 | 94 | ||