drm/i915: fix how we mask PMIMR when adding work to the queue
[deliverable/linux.git] / drivers / gpu / drm / i915 / i915_irq.c
index 06659a72d0cf8d47d962233843648ad57c55ed6e..e0c6f7d6189d7c5b21f344d33dc919eb88acbf4e 100644 (file)
@@ -104,6 +104,69 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
        }
 }
 
+/**
+ * ilk_update_gt_irq - update GTIMR
+ * @dev_priv: driver private
+ * @interrupt_mask: mask of interrupt bits to update
+ * @enabled_irq_mask: mask of interrupt bits to enable
+ */
+static void ilk_update_gt_irq(struct drm_i915_private *dev_priv,
+                             uint32_t interrupt_mask,
+                             uint32_t enabled_irq_mask)
+{
+       assert_spin_locked(&dev_priv->irq_lock);
+
+       dev_priv->gt_irq_mask &= ~interrupt_mask;
+       dev_priv->gt_irq_mask |= (~enabled_irq_mask & interrupt_mask);
+       I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
+       POSTING_READ(GTIMR);
+}
+
+void ilk_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+{
+       ilk_update_gt_irq(dev_priv, mask, mask);
+}
+
+void ilk_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+{
+       ilk_update_gt_irq(dev_priv, mask, 0);
+}
+
+/**
+  * snb_update_pm_irq - update GEN6_PMIMR
+  * @dev_priv: driver private
+  * @interrupt_mask: mask of interrupt bits to update
+  * @enabled_irq_mask: mask of interrupt bits to enable
+  */
+static void snb_update_pm_irq(struct drm_i915_private *dev_priv,
+                             uint32_t interrupt_mask,
+                             uint32_t enabled_irq_mask)
+{
+       uint32_t new_val;
+
+       assert_spin_locked(&dev_priv->irq_lock);
+
+       new_val = dev_priv->pm_irq_mask;
+       new_val &= ~interrupt_mask;
+       new_val |= (~enabled_irq_mask & interrupt_mask);
+
+       if (new_val != dev_priv->pm_irq_mask) {
+               dev_priv->pm_irq_mask = new_val;
+               I915_WRITE(GEN6_PMIMR, dev_priv->pm_irq_mask);
+               POSTING_READ(GEN6_PMIMR);
+       }
+}
+
+void snb_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+{
+       snb_update_pm_irq(dev_priv, mask, mask);
+}
+
+void snb_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+{
+       snb_update_pm_irq(dev_priv, mask, 0);
+}
+
 static bool ivb_can_enable_err_int(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -711,17 +774,19 @@ static void gen6_pm_rps_work(struct work_struct *work)
 {
        drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
                                                    rps.work);
-       u32 pm_iir, pm_imr;
+       u32 pm_iir;
        u8 new_delay;
 
        spin_lock_irq(&dev_priv->irq_lock);
        pm_iir = dev_priv->rps.pm_iir;
        dev_priv->rps.pm_iir = 0;
-       pm_imr = I915_READ(GEN6_PMIMR);
        /* Make sure not to corrupt PMIMR state used by ringbuffer code */
-       I915_WRITE(GEN6_PMIMR, pm_imr & ~GEN6_PM_RPS_EVENTS);
+       snb_enable_pm_irq(dev_priv, GEN6_PM_RPS_EVENTS);
        spin_unlock_irq(&dev_priv->irq_lock);
 
+       /* Make sure we didn't queue anything we're not going to process. */
+       WARN_ON(pm_iir & ~GEN6_PM_RPS_EVENTS);
+
        if ((pm_iir & GEN6_PM_RPS_EVENTS) == 0)
                return;
 
@@ -806,8 +871,7 @@ static void ivybridge_parity_work(struct work_struct *work)
        I915_WRITE(GEN7_MISCCPCTL, misccpctl);
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       dev_priv->gt_irq_mask &= ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
-       I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
+       ilk_enable_gt_irq(dev_priv, GT_RENDER_L3_PARITY_ERROR_INTERRUPT);
        spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
 
        mutex_unlock(&dev_priv->dev->struct_mutex);
@@ -837,8 +901,7 @@ static void ivybridge_parity_error_irq_handler(struct drm_device *dev)
                return;
 
        spin_lock(&dev_priv->irq_lock);
-       dev_priv->gt_irq_mask |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
-       I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
+       ilk_disable_gt_irq(dev_priv, GT_RENDER_L3_PARITY_ERROR_INTERRUPT);
        spin_unlock(&dev_priv->irq_lock);
 
        queue_work(dev_priv->wq, &dev_priv->l3_parity.error_work);
@@ -894,9 +957,8 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv,
         */
 
        spin_lock(&dev_priv->irq_lock);
-       dev_priv->rps.pm_iir |= pm_iir;
-       I915_WRITE(GEN6_PMIMR, dev_priv->rps.pm_iir);
-       POSTING_READ(GEN6_PMIMR);
+       dev_priv->rps.pm_iir |= pm_iir & GEN6_PM_RPS_EVENTS;
+       snb_disable_pm_irq(dev_priv, pm_iir & GEN6_PM_RPS_EVENTS);
        spin_unlock(&dev_priv->irq_lock);
 
        queue_work(dev_priv->wq, &dev_priv->rps.work);
@@ -979,9 +1041,7 @@ static void hsw_pm_irq_handler(struct drm_i915_private *dev_priv,
        if (pm_iir & GEN6_PM_RPS_EVENTS) {
                spin_lock(&dev_priv->irq_lock);
                dev_priv->rps.pm_iir |= pm_iir & GEN6_PM_RPS_EVENTS;
-               I915_WRITE(GEN6_PMIMR, dev_priv->rps.pm_iir);
-               /* never want to mask useful interrupts. (also posting read) */
-               WARN_ON(I915_READ_NOTRACE(GEN6_PMIMR) & ~GEN6_PM_RPS_EVENTS);
+               snb_disable_pm_irq(dev_priv, pm_iir & GEN6_PM_RPS_EVENTS);
                spin_unlock(&dev_priv->irq_lock);
 
                queue_work(dev_priv->wq, &dev_priv->rps.work);
@@ -1064,7 +1124,7 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
                if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
                        gmbus_irq_handler(dev);
 
-               if (pm_iir & GEN6_PM_RPS_EVENTS)
+               if (pm_iir)
                        gen6_rps_irq_handler(dev_priv, pm_iir);
 
                I915_WRITE(GTIIR, gt_iir);
@@ -1309,6 +1369,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 de_iir, gt_iir, de_ier, sde_ier = 0;
        irqreturn_t ret = IRQ_NONE;
+       bool err_int_reenable = false;
 
        atomic_inc(&dev_priv->irq_received);
 
@@ -1337,7 +1398,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
         * handler. */
        if (IS_HASWELL(dev)) {
                spin_lock(&dev_priv->irq_lock);
-               ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
+               err_int_reenable = ~dev_priv->irq_mask & DE_ERR_INT_IVB;
+               if (err_int_reenable)
+                       ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
                spin_unlock(&dev_priv->irq_lock);
        }
 
@@ -1366,14 +1429,14 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
                if (pm_iir) {
                        if (IS_HASWELL(dev))
                                hsw_pm_irq_handler(dev_priv, pm_iir);
-                       else if (pm_iir & GEN6_PM_RPS_EVENTS)
+                       else
                                gen6_rps_irq_handler(dev_priv, pm_iir);
                        I915_WRITE(GEN6_PMIIR, pm_iir);
                        ret = IRQ_HANDLED;
                }
        }
 
-       if (IS_HASWELL(dev)) {
+       if (err_int_reenable) {
                spin_lock(&dev_priv->irq_lock);
                if (ivb_can_enable_err_int(dev))
                        ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
@@ -1905,8 +1968,6 @@ static void i915_hangcheck_elapsed(unsigned long data)
                                } else
                                        busy = false;
                        } else {
-                               int score;
-
                                /* We always increment the hangcheck score
                                 * if the ring is busy and still processing
                                 * the same request, so that no single request
@@ -1927,20 +1988,18 @@ static void i915_hangcheck_elapsed(unsigned long data)
 
                                switch (ring->hangcheck.action) {
                                case HANGCHECK_WAIT:
-                                       score = 0;
                                        break;
                                case HANGCHECK_ACTIVE:
-                                       score = BUSY;
+                                       ring->hangcheck.score += BUSY;
                                        break;
                                case HANGCHECK_KICK:
-                                       score = KICK;
+                                       ring->hangcheck.score += KICK;
                                        break;
                                case HANGCHECK_HUNG:
-                                       score = HUNG;
+                                       ring->hangcheck.score += HUNG;
                                        stuck[i] = true;
                                        break;
                                }
-                               ring->hangcheck.score += score;
                        }
                } else {
                        /* Gradually reduce the count so that we catch DoS
@@ -2158,8 +2217,9 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
                if (HAS_VEBOX(dev))
                        pm_irqs |= PM_VEBOX_USER_INTERRUPT;
 
+               dev_priv->pm_irq_mask = 0xffffffff;
                I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
-               I915_WRITE(GEN6_PMIMR, 0xffffffff);
+               I915_WRITE(GEN6_PMIMR, dev_priv->pm_irq_mask);
                I915_WRITE(GEN6_PMIER, pm_irqs);
                POSTING_READ(GEN6_PMIER);
        }
This page took 0.031267 seconds and 5 git commands to generate.