diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
| -rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 239 | 
1 files changed, 162 insertions, 77 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 7ba23a69a0c..7ebc84c2881 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -190,7 +190,7 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)  	low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;  	if (!i915_pipe_enabled(dev, pipe)) { -		DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe); +		DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);  		return 0;  	} @@ -219,7 +219,7 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)  	int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45;  	if (!i915_pipe_enabled(dev, pipe)) { -		DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe); +		DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);  		return 0;  	} @@ -290,6 +290,35 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev)  	return ret;  } +/** + * i915_error_work_func - do process context error handling work + * @work: work struct + * + * Fire an error uevent so userspace can see that a hang or error + * was detected. + */ +static void i915_error_work_func(struct work_struct *work) +{ +	drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, +						    error_work); +	struct drm_device *dev = dev_priv->dev; +	char *event_string = "ERROR=1"; +	char *envp[] = { event_string, NULL }; + +	DRM_DEBUG("generating error event\n"); + +	kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); +} + +/** + * i915_capture_error_state - capture an error record for later analysis + * @dev: drm device + * + * Should be called when an error is detected (either a hang or an error + * interrupt) to capture error state from the time of the error.  Fills + * out a structure which becomes available in debugfs for user level tools + * to pick up. + */  static void i915_capture_error_state(struct drm_device *dev)  {  	struct drm_i915_private *dev_priv = dev->dev_private; @@ -325,12 +354,137 @@ static void i915_capture_error_state(struct drm_device *dev)  		error->acthd = I915_READ(ACTHD_I965);  	} +	do_gettimeofday(&error->time); +  	dev_priv->first_error = error;  out:  	spin_unlock_irqrestore(&dev_priv->error_lock, flags);  } +/** + * i915_handle_error - handle an error interrupt + * @dev: drm device + * + * Do some basic checking of regsiter state at error interrupt time and + * dump it to the syslog.  Also call i915_capture_error_state() to make + * sure we get a record and make it available in debugfs.  Fire a uevent + * so userspace knows something bad happened (should trigger collection + * of a ring dump etc.). + */ +static void i915_handle_error(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32 eir = I915_READ(EIR); +	u32 pipea_stats = I915_READ(PIPEASTAT); +	u32 pipeb_stats = I915_READ(PIPEBSTAT); + +	i915_capture_error_state(dev); + +	printk(KERN_ERR "render error detected, EIR: 0x%08x\n", +	       eir); + +	if (IS_G4X(dev)) { +		if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) { +			u32 ipeir = I915_READ(IPEIR_I965); + +			printk(KERN_ERR "  IPEIR: 0x%08x\n", +			       I915_READ(IPEIR_I965)); +			printk(KERN_ERR "  IPEHR: 0x%08x\n", +			       I915_READ(IPEHR_I965)); +			printk(KERN_ERR "  INSTDONE: 0x%08x\n", +			       I915_READ(INSTDONE_I965)); +			printk(KERN_ERR "  INSTPS: 0x%08x\n", +			       I915_READ(INSTPS)); +			printk(KERN_ERR "  INSTDONE1: 0x%08x\n", +			       I915_READ(INSTDONE1)); +			printk(KERN_ERR "  ACTHD: 0x%08x\n", +			       I915_READ(ACTHD_I965)); +			I915_WRITE(IPEIR_I965, ipeir); +			(void)I915_READ(IPEIR_I965); +		} +		if (eir & GM45_ERROR_PAGE_TABLE) { +			u32 pgtbl_err = I915_READ(PGTBL_ER); +			printk(KERN_ERR "page table error\n"); +			printk(KERN_ERR "  PGTBL_ER: 0x%08x\n", +			       pgtbl_err); +			I915_WRITE(PGTBL_ER, pgtbl_err); +			(void)I915_READ(PGTBL_ER); +		} +	} + +	if (IS_I9XX(dev)) { +		if (eir & I915_ERROR_PAGE_TABLE) { +			u32 pgtbl_err = I915_READ(PGTBL_ER); +			printk(KERN_ERR "page table error\n"); +			printk(KERN_ERR "  PGTBL_ER: 0x%08x\n", +			       pgtbl_err); +			I915_WRITE(PGTBL_ER, pgtbl_err); +			(void)I915_READ(PGTBL_ER); +		} +	} + +	if (eir & I915_ERROR_MEMORY_REFRESH) { +		printk(KERN_ERR "memory refresh error\n"); +		printk(KERN_ERR "PIPEASTAT: 0x%08x\n", +		       pipea_stats); +		printk(KERN_ERR "PIPEBSTAT: 0x%08x\n", +		       pipeb_stats); +		/* pipestat has already been acked */ +	} +	if (eir & I915_ERROR_INSTRUCTION) { +		printk(KERN_ERR "instruction error\n"); +		printk(KERN_ERR "  INSTPM: 0x%08x\n", +		       I915_READ(INSTPM)); +		if (!IS_I965G(dev)) { +			u32 ipeir = I915_READ(IPEIR); + +			printk(KERN_ERR "  IPEIR: 0x%08x\n", +			       I915_READ(IPEIR)); +			printk(KERN_ERR "  IPEHR: 0x%08x\n", +			       I915_READ(IPEHR)); +			printk(KERN_ERR "  INSTDONE: 0x%08x\n", +			       I915_READ(INSTDONE)); +			printk(KERN_ERR "  ACTHD: 0x%08x\n", +			       I915_READ(ACTHD)); +			I915_WRITE(IPEIR, ipeir); +			(void)I915_READ(IPEIR); +		} else { +			u32 ipeir = I915_READ(IPEIR_I965); + +			printk(KERN_ERR "  IPEIR: 0x%08x\n", +			       I915_READ(IPEIR_I965)); +			printk(KERN_ERR "  IPEHR: 0x%08x\n", +			       I915_READ(IPEHR_I965)); +			printk(KERN_ERR "  INSTDONE: 0x%08x\n", +			       I915_READ(INSTDONE_I965)); +			printk(KERN_ERR "  INSTPS: 0x%08x\n", +			       I915_READ(INSTPS)); +			printk(KERN_ERR "  INSTDONE1: 0x%08x\n", +			       I915_READ(INSTDONE1)); +			printk(KERN_ERR "  ACTHD: 0x%08x\n", +			       I915_READ(ACTHD_I965)); +			I915_WRITE(IPEIR_I965, ipeir); +			(void)I915_READ(IPEIR_I965); +		} +	} + +	I915_WRITE(EIR, eir); +	(void)I915_READ(EIR); +	eir = I915_READ(EIR); +	if (eir) { +		/* +		 * some errors might have become stuck, +		 * mask them. +		 */ +		DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir); +		I915_WRITE(EMR, I915_READ(EMR) | eir); +		I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); +	} + +	queue_work(dev_priv->wq, &dev_priv->error_work); +} +  irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)  {  	struct drm_device *dev = (struct drm_device *) arg; @@ -372,6 +526,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)  		pipea_stats = I915_READ(PIPEASTAT);  		pipeb_stats = I915_READ(PIPEBSTAT); +		if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) +			i915_handle_error(dev); +  		/*  		 * Clear the PIPE(A|B)STAT regs before the IIR  		 */ @@ -403,86 +560,13 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)  			DRM_DEBUG("hotplug event received, stat 0x%08x\n",  				  hotplug_status);  			if (hotplug_status & dev_priv->hotplug_supported_mask) -				schedule_work(&dev_priv->hotplug_work); +				queue_work(dev_priv->wq, +					   &dev_priv->hotplug_work);  			I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);  			I915_READ(PORT_HOTPLUG_STAT);  		} -		if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) { -			u32 eir = I915_READ(EIR); - -			i915_capture_error_state(dev); - -			printk(KERN_ERR "render error detected, EIR: 0x%08x\n", -			       eir); -			if (eir & I915_ERROR_PAGE_TABLE) { -				u32 pgtbl_err = I915_READ(PGTBL_ER); -				printk(KERN_ERR "page table error\n"); -				printk(KERN_ERR "  PGTBL_ER: 0x%08x\n", -				       pgtbl_err); -				I915_WRITE(PGTBL_ER, pgtbl_err); -				(void)I915_READ(PGTBL_ER); -			} -			if (eir & I915_ERROR_MEMORY_REFRESH) { -				printk(KERN_ERR "memory refresh error\n"); -				printk(KERN_ERR "PIPEASTAT: 0x%08x\n", -				       pipea_stats); -				printk(KERN_ERR "PIPEBSTAT: 0x%08x\n", -				       pipeb_stats); -				/* pipestat has already been acked */ -			} -			if (eir & I915_ERROR_INSTRUCTION) { -				printk(KERN_ERR "instruction error\n"); -				printk(KERN_ERR "  INSTPM: 0x%08x\n", -				       I915_READ(INSTPM)); -				if (!IS_I965G(dev)) { -					u32 ipeir = I915_READ(IPEIR); - -					printk(KERN_ERR "  IPEIR: 0x%08x\n", -					       I915_READ(IPEIR)); -					printk(KERN_ERR "  IPEHR: 0x%08x\n", -						   I915_READ(IPEHR)); -					printk(KERN_ERR "  INSTDONE: 0x%08x\n", -						   I915_READ(INSTDONE)); -					printk(KERN_ERR "  ACTHD: 0x%08x\n", -						   I915_READ(ACTHD)); -					I915_WRITE(IPEIR, ipeir); -					(void)I915_READ(IPEIR); -				} else { -					u32 ipeir = I915_READ(IPEIR_I965); - -					printk(KERN_ERR "  IPEIR: 0x%08x\n", -					       I915_READ(IPEIR_I965)); -					printk(KERN_ERR "  IPEHR: 0x%08x\n", -					       I915_READ(IPEHR_I965)); -					printk(KERN_ERR "  INSTDONE: 0x%08x\n", -					       I915_READ(INSTDONE_I965)); -					printk(KERN_ERR "  INSTPS: 0x%08x\n", -					       I915_READ(INSTPS)); -					printk(KERN_ERR "  INSTDONE1: 0x%08x\n", -					       I915_READ(INSTDONE1)); -					printk(KERN_ERR "  ACTHD: 0x%08x\n", -					       I915_READ(ACTHD_I965)); -					I915_WRITE(IPEIR_I965, ipeir); -					(void)I915_READ(IPEIR_I965); -				} -			} - -			I915_WRITE(EIR, eir); -			(void)I915_READ(EIR); -			eir = I915_READ(EIR); -			if (eir) { -				/* -				 * some errors might have become stuck, -				 * mask them. -				 */ -				DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir); -				I915_WRITE(EMR, I915_READ(EMR) | eir); -				I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); -			} -		} -  		I915_WRITE(IIR, iir);  		new_iir = I915_READ(IIR); /* Flush posted writes */ @@ -830,6 +914,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev)  	atomic_set(&dev_priv->irq_received, 0);  	INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); +	INIT_WORK(&dev_priv->error_work, i915_error_work_func);  	if (IS_IGDNG(dev)) {  		igdng_irq_preinstall(dev);  |