diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 235 |
1 files changed, 160 insertions, 75 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 7ba23a69a0c0..83aee80e77a6 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c | |||
@@ -290,6 +290,35 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev) | |||
290 | return ret; | 290 | return ret; |
291 | } | 291 | } |
292 | 292 | ||
293 | /** | ||
294 | * i915_error_work_func - do process context error handling work | ||
295 | * @work: work struct | ||
296 | * | ||
297 | * Fire an error uevent so userspace can see that a hang or error | ||
298 | * was detected. | ||
299 | */ | ||
300 | static void i915_error_work_func(struct work_struct *work) | ||
301 | { | ||
302 | drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, | ||
303 | error_work); | ||
304 | struct drm_device *dev = dev_priv->dev; | ||
305 | char *event_string = "ERROR=1"; | ||
306 | char *envp[] = { event_string, NULL }; | ||
307 | |||
308 | DRM_DEBUG("generating error event\n"); | ||
309 | |||
310 | kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); | ||
311 | } | ||
312 | |||
313 | /** | ||
314 | * i915_capture_error_state - capture an error record for later analysis | ||
315 | * @dev: drm device | ||
316 | * | ||
317 | * Should be called when an error is detected (either a hang or an error | ||
318 | * interrupt) to capture error state from the time of the error. Fills | ||
319 | * out a structure which becomes available in debugfs for user level tools | ||
320 | * to pick up. | ||
321 | */ | ||
293 | static void i915_capture_error_state(struct drm_device *dev) | 322 | static void i915_capture_error_state(struct drm_device *dev) |
294 | { | 323 | { |
295 | struct drm_i915_private *dev_priv = dev->dev_private; | 324 | struct drm_i915_private *dev_priv = dev->dev_private; |
@@ -325,12 +354,137 @@ static void i915_capture_error_state(struct drm_device *dev) | |||
325 | error->acthd = I915_READ(ACTHD_I965); | 354 | error->acthd = I915_READ(ACTHD_I965); |
326 | } | 355 | } |
327 | 356 | ||
357 | do_gettimeofday(&error->time); | ||
358 | |||
328 | dev_priv->first_error = error; | 359 | dev_priv->first_error = error; |
329 | 360 | ||
330 | out: | 361 | out: |
331 | spin_unlock_irqrestore(&dev_priv->error_lock, flags); | 362 | spin_unlock_irqrestore(&dev_priv->error_lock, flags); |
332 | } | 363 | } |
333 | 364 | ||
365 | /** | ||
366 | * i915_handle_error - handle an error interrupt | ||
367 | * @dev: drm device | ||
368 | * | ||
369 | * Do some basic checking of regsiter state at error interrupt time and | ||
370 | * dump it to the syslog. Also call i915_capture_error_state() to make | ||
371 | * sure we get a record and make it available in debugfs. Fire a uevent | ||
372 | * so userspace knows something bad happened (should trigger collection | ||
373 | * of a ring dump etc.). | ||
374 | */ | ||
375 | static void i915_handle_error(struct drm_device *dev) | ||
376 | { | ||
377 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
378 | u32 eir = I915_READ(EIR); | ||
379 | u32 pipea_stats = I915_READ(PIPEASTAT); | ||
380 | u32 pipeb_stats = I915_READ(PIPEBSTAT); | ||
381 | |||
382 | i915_capture_error_state(dev); | ||
383 | |||
384 | printk(KERN_ERR "render error detected, EIR: 0x%08x\n", | ||
385 | eir); | ||
386 | |||
387 | if (IS_G4X(dev)) { | ||
388 | if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) { | ||
389 | u32 ipeir = I915_READ(IPEIR_I965); | ||
390 | |||
391 | printk(KERN_ERR " IPEIR: 0x%08x\n", | ||
392 | I915_READ(IPEIR_I965)); | ||
393 | printk(KERN_ERR " IPEHR: 0x%08x\n", | ||
394 | I915_READ(IPEHR_I965)); | ||
395 | printk(KERN_ERR " INSTDONE: 0x%08x\n", | ||
396 | I915_READ(INSTDONE_I965)); | ||
397 | printk(KERN_ERR " INSTPS: 0x%08x\n", | ||
398 | I915_READ(INSTPS)); | ||
399 | printk(KERN_ERR " INSTDONE1: 0x%08x\n", | ||
400 | I915_READ(INSTDONE1)); | ||
401 | printk(KERN_ERR " ACTHD: 0x%08x\n", | ||
402 | I915_READ(ACTHD_I965)); | ||
403 | I915_WRITE(IPEIR_I965, ipeir); | ||
404 | (void)I915_READ(IPEIR_I965); | ||
405 | } | ||
406 | if (eir & GM45_ERROR_PAGE_TABLE) { | ||
407 | u32 pgtbl_err = I915_READ(PGTBL_ER); | ||
408 | printk(KERN_ERR "page table error\n"); | ||
409 | printk(KERN_ERR " PGTBL_ER: 0x%08x\n", | ||
410 | pgtbl_err); | ||
411 | I915_WRITE(PGTBL_ER, pgtbl_err); | ||
412 | (void)I915_READ(PGTBL_ER); | ||
413 | } | ||
414 | } | ||
415 | |||
416 | if (IS_I9XX(dev)) { | ||
417 | if (eir & I915_ERROR_PAGE_TABLE) { | ||
418 | u32 pgtbl_err = I915_READ(PGTBL_ER); | ||
419 | printk(KERN_ERR "page table error\n"); | ||
420 | printk(KERN_ERR " PGTBL_ER: 0x%08x\n", | ||
421 | pgtbl_err); | ||
422 | I915_WRITE(PGTBL_ER, pgtbl_err); | ||
423 | (void)I915_READ(PGTBL_ER); | ||
424 | } | ||
425 | } | ||
426 | |||
427 | if (eir & I915_ERROR_MEMORY_REFRESH) { | ||
428 | printk(KERN_ERR "memory refresh error\n"); | ||
429 | printk(KERN_ERR "PIPEASTAT: 0x%08x\n", | ||
430 | pipea_stats); | ||
431 | printk(KERN_ERR "PIPEBSTAT: 0x%08x\n", | ||
432 | pipeb_stats); | ||
433 | /* pipestat has already been acked */ | ||
434 | } | ||
435 | if (eir & I915_ERROR_INSTRUCTION) { | ||
436 | printk(KERN_ERR "instruction error\n"); | ||
437 | printk(KERN_ERR " INSTPM: 0x%08x\n", | ||
438 | I915_READ(INSTPM)); | ||
439 | if (!IS_I965G(dev)) { | ||
440 | u32 ipeir = I915_READ(IPEIR); | ||
441 | |||
442 | printk(KERN_ERR " IPEIR: 0x%08x\n", | ||
443 | I915_READ(IPEIR)); | ||
444 | printk(KERN_ERR " IPEHR: 0x%08x\n", | ||
445 | I915_READ(IPEHR)); | ||
446 | printk(KERN_ERR " INSTDONE: 0x%08x\n", | ||
447 | I915_READ(INSTDONE)); | ||
448 | printk(KERN_ERR " ACTHD: 0x%08x\n", | ||
449 | I915_READ(ACTHD)); | ||
450 | I915_WRITE(IPEIR, ipeir); | ||
451 | (void)I915_READ(IPEIR); | ||
452 | } else { | ||
453 | u32 ipeir = I915_READ(IPEIR_I965); | ||
454 | |||
455 | printk(KERN_ERR " IPEIR: 0x%08x\n", | ||
456 | I915_READ(IPEIR_I965)); | ||
457 | printk(KERN_ERR " IPEHR: 0x%08x\n", | ||
458 | I915_READ(IPEHR_I965)); | ||
459 | printk(KERN_ERR " INSTDONE: 0x%08x\n", | ||
460 | I915_READ(INSTDONE_I965)); | ||
461 | printk(KERN_ERR " INSTPS: 0x%08x\n", | ||
462 | I915_READ(INSTPS)); | ||
463 | printk(KERN_ERR " INSTDONE1: 0x%08x\n", | ||
464 | I915_READ(INSTDONE1)); | ||
465 | printk(KERN_ERR " ACTHD: 0x%08x\n", | ||
466 | I915_READ(ACTHD_I965)); | ||
467 | I915_WRITE(IPEIR_I965, ipeir); | ||
468 | (void)I915_READ(IPEIR_I965); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | I915_WRITE(EIR, eir); | ||
473 | (void)I915_READ(EIR); | ||
474 | eir = I915_READ(EIR); | ||
475 | if (eir) { | ||
476 | /* | ||
477 | * some errors might have become stuck, | ||
478 | * mask them. | ||
479 | */ | ||
480 | DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir); | ||
481 | I915_WRITE(EMR, I915_READ(EMR) | eir); | ||
482 | I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); | ||
483 | } | ||
484 | |||
485 | queue_work(dev_priv->wq, &dev_priv->error_work); | ||
486 | } | ||
487 | |||
334 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | 488 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) |
335 | { | 489 | { |
336 | struct drm_device *dev = (struct drm_device *) arg; | 490 | struct drm_device *dev = (struct drm_device *) arg; |
@@ -372,6 +526,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | |||
372 | pipea_stats = I915_READ(PIPEASTAT); | 526 | pipea_stats = I915_READ(PIPEASTAT); |
373 | pipeb_stats = I915_READ(PIPEBSTAT); | 527 | pipeb_stats = I915_READ(PIPEBSTAT); |
374 | 528 | ||
529 | if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) | ||
530 | i915_handle_error(dev); | ||
531 | |||
375 | /* | 532 | /* |
376 | * Clear the PIPE(A|B)STAT regs before the IIR | 533 | * Clear the PIPE(A|B)STAT regs before the IIR |
377 | */ | 534 | */ |
@@ -403,86 +560,13 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | |||
403 | DRM_DEBUG("hotplug event received, stat 0x%08x\n", | 560 | DRM_DEBUG("hotplug event received, stat 0x%08x\n", |
404 | hotplug_status); | 561 | hotplug_status); |
405 | if (hotplug_status & dev_priv->hotplug_supported_mask) | 562 | if (hotplug_status & dev_priv->hotplug_supported_mask) |
406 | schedule_work(&dev_priv->hotplug_work); | 563 | queue_work(dev_priv->wq, |
564 | &dev_priv->hotplug_work); | ||
407 | 565 | ||
408 | I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); | 566 | I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); |
409 | I915_READ(PORT_HOTPLUG_STAT); | 567 | I915_READ(PORT_HOTPLUG_STAT); |
410 | } | 568 | } |
411 | 569 | ||
412 | if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) { | ||
413 | u32 eir = I915_READ(EIR); | ||
414 | |||
415 | i915_capture_error_state(dev); | ||
416 | |||
417 | printk(KERN_ERR "render error detected, EIR: 0x%08x\n", | ||
418 | eir); | ||
419 | if (eir & I915_ERROR_PAGE_TABLE) { | ||
420 | u32 pgtbl_err = I915_READ(PGTBL_ER); | ||
421 | printk(KERN_ERR "page table error\n"); | ||
422 | printk(KERN_ERR " PGTBL_ER: 0x%08x\n", | ||
423 | pgtbl_err); | ||
424 | I915_WRITE(PGTBL_ER, pgtbl_err); | ||
425 | (void)I915_READ(PGTBL_ER); | ||
426 | } | ||
427 | if (eir & I915_ERROR_MEMORY_REFRESH) { | ||
428 | printk(KERN_ERR "memory refresh error\n"); | ||
429 | printk(KERN_ERR "PIPEASTAT: 0x%08x\n", | ||
430 | pipea_stats); | ||
431 | printk(KERN_ERR "PIPEBSTAT: 0x%08x\n", | ||
432 | pipeb_stats); | ||
433 | /* pipestat has already been acked */ | ||
434 | } | ||
435 | if (eir & I915_ERROR_INSTRUCTION) { | ||
436 | printk(KERN_ERR "instruction error\n"); | ||
437 | printk(KERN_ERR " INSTPM: 0x%08x\n", | ||
438 | I915_READ(INSTPM)); | ||
439 | if (!IS_I965G(dev)) { | ||
440 | u32 ipeir = I915_READ(IPEIR); | ||
441 | |||
442 | printk(KERN_ERR " IPEIR: 0x%08x\n", | ||
443 | I915_READ(IPEIR)); | ||
444 | printk(KERN_ERR " IPEHR: 0x%08x\n", | ||
445 | I915_READ(IPEHR)); | ||
446 | printk(KERN_ERR " INSTDONE: 0x%08x\n", | ||
447 | I915_READ(INSTDONE)); | ||
448 | printk(KERN_ERR " ACTHD: 0x%08x\n", | ||
449 | I915_READ(ACTHD)); | ||
450 | I915_WRITE(IPEIR, ipeir); | ||
451 | (void)I915_READ(IPEIR); | ||
452 | } else { | ||
453 | u32 ipeir = I915_READ(IPEIR_I965); | ||
454 | |||
455 | printk(KERN_ERR " IPEIR: 0x%08x\n", | ||
456 | I915_READ(IPEIR_I965)); | ||
457 | printk(KERN_ERR " IPEHR: 0x%08x\n", | ||
458 | I915_READ(IPEHR_I965)); | ||
459 | printk(KERN_ERR " INSTDONE: 0x%08x\n", | ||
460 | I915_READ(INSTDONE_I965)); | ||
461 | printk(KERN_ERR " INSTPS: 0x%08x\n", | ||
462 | I915_READ(INSTPS)); | ||
463 | printk(KERN_ERR " INSTDONE1: 0x%08x\n", | ||
464 | I915_READ(INSTDONE1)); | ||
465 | printk(KERN_ERR " ACTHD: 0x%08x\n", | ||
466 | I915_READ(ACTHD_I965)); | ||
467 | I915_WRITE(IPEIR_I965, ipeir); | ||
468 | (void)I915_READ(IPEIR_I965); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | I915_WRITE(EIR, eir); | ||
473 | (void)I915_READ(EIR); | ||
474 | eir = I915_READ(EIR); | ||
475 | if (eir) { | ||
476 | /* | ||
477 | * some errors might have become stuck, | ||
478 | * mask them. | ||
479 | */ | ||
480 | DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir); | ||
481 | I915_WRITE(EMR, I915_READ(EMR) | eir); | ||
482 | I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); | ||
483 | } | ||
484 | } | ||
485 | |||
486 | I915_WRITE(IIR, iir); | 570 | I915_WRITE(IIR, iir); |
487 | new_iir = I915_READ(IIR); /* Flush posted writes */ | 571 | new_iir = I915_READ(IIR); /* Flush posted writes */ |
488 | 572 | ||
@@ -830,6 +914,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev) | |||
830 | atomic_set(&dev_priv->irq_received, 0); | 914 | atomic_set(&dev_priv->irq_received, 0); |
831 | 915 | ||
832 | INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); | 916 | INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); |
917 | INIT_WORK(&dev_priv->error_work, i915_error_work_func); | ||
833 | 918 | ||
834 | if (IS_IGDNG(dev)) { | 919 | if (IS_IGDNG(dev)) { |
835 | igdng_irq_preinstall(dev); | 920 | igdng_irq_preinstall(dev); |