drm/i915/gt: Reset twice
authorjsg <jsg@openbsd.org>
Wed, 18 Jan 2023 23:55:06 +0000 (23:55 +0000)
committerjsg <jsg@openbsd.org>
Wed, 18 Jan 2023 23:55:06 +0000 (23:55 +0000)
From Chris Wilson
4009502c091c1543ae8708a12d1a97583ae411ac in linux-6.1.y/6.1.7
d3de5616d36462a646f5b360ba82d3b09ff668eb in mainline linux

sys/dev/pci/drm/i915/gt/intel_reset.c

index 8862e89..843b00b 100644 (file)
@@ -278,6 +278,7 @@ out:
 static int gen6_hw_domain_reset(struct intel_gt *gt, u32 hw_domain_mask)
 {
        struct intel_uncore *uncore = gt->uncore;
+       int loops = 2;
        int err;
 
        /*
@@ -285,18 +286,39 @@ static int gen6_hw_domain_reset(struct intel_gt *gt, u32 hw_domain_mask)
         * for fifo space for the write or forcewake the chip for
         * the read
         */
-       intel_uncore_write_fw(uncore, GEN6_GDRST, hw_domain_mask);
+       do {
+               intel_uncore_write_fw(uncore, GEN6_GDRST, hw_domain_mask);
 
-       /* Wait for the device to ack the reset requests */
-       err = __intel_wait_for_register_fw(uncore,
-                                          GEN6_GDRST, hw_domain_mask, 0,
-                                          500, 0,
-                                          NULL);
+               /*
+                * Wait for the device to ack the reset requests.
+                *
+                * On some platforms, e.g. Jasperlake, we see that the
+                * engine register state is not cleared until shortly after
+                * GDRST reports completion, causing a failure as we try
+                * to immediately resume while the internal state is still
+                * in flux. If we immediately repeat the reset, the second
+                * reset appears to serialise with the first, and since
+                * it is a no-op, the registers should retain their reset
+                * value. However, there is still a concern that upon
+                * leaving the second reset, the internal engine state
+                * is still in flux and not ready for resuming.
+                */
+               err = __intel_wait_for_register_fw(uncore, GEN6_GDRST,
+                                                  hw_domain_mask, 0,
+                                                  2000, 0,
+                                                  NULL);
+       } while (err == 0 && --loops);
        if (err)
                GT_TRACE(gt,
                         "Wait for 0x%08x engines reset failed\n",
                         hw_domain_mask);
 
+       /*
+        * As we have observed that the engine state is still volatile
+        * after GDRST is acked, impose a small delay to let everything settle.
+        */
+       udelay(50);
+
        return err;
 }