aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorBen Gamari <bgamari.foss@gmail.com>2009-09-14 17:48:45 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-09-17 17:36:22 -0400
commit11ed50ec2a316928c2bacc1149bded86c6a96068 (patch)
treeb363e00c943a34a04c1d48ec747d0a41f920e567 /drivers/gpu
parentf65d94211e2bcba17faf05a6a3809af0e4217767 (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.c8
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c124
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h1
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c8
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h4
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 */
146int 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
130static int __devinit 254static int __devinit
131i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 255i915_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,
624extern int i915_emit_box(struct drm_device *dev, 624extern 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);
627extern int i965_reset(struct drm_device *dev, u8 flags);
627 628
628/* i915_irq.c */ 629/* i915_irq.c */
629void i915_hangcheck_elapsed(unsigned long data); 630void 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