drm/i915/ehl: Add support for DPLL4 (v10)
[linux-2.6-block.git] / drivers / gpu / drm / i915 / intel_uncore.c
index 8e5716bc53e20305fc7d947f0da56827ef53580e..2042c94c9cc94d2224bc1007fa6e81c591726e49 100644 (file)
@@ -322,7 +322,7 @@ static void __gen6_gt_wait_for_fifo(struct intel_uncore *uncore)
 
        /* On VLV, FIFO will be shared by both SW and HW.
         * So, we need to read the FREE_ENTRIES everytime */
-       if (IS_VALLEYVIEW(uncore_to_i915(uncore)))
+       if (IS_VALLEYVIEW(uncore->i915))
                n = fifo_free_entries(uncore);
        else
                n = uncore->fifo_count;
@@ -344,7 +344,7 @@ intel_uncore_fw_release_timer(struct hrtimer *timer)
 {
        struct intel_uncore_forcewake_domain *domain =
               container_of(timer, struct intel_uncore_forcewake_domain, timer);
-       struct intel_uncore *uncore = forcewake_domain_to_uncore(domain);
+       struct intel_uncore *uncore = domain->uncore;
        unsigned long irqflags;
 
        assert_rpm_device_not_suspended(uncore->rpm);
@@ -485,15 +485,13 @@ check_for_unclaimed_mmio(struct intel_uncore *uncore)
        return ret;
 }
 
-static void __intel_uncore_early_sanitize(struct intel_uncore *uncore,
-                                         unsigned int restore_forcewake)
+static void forcewake_early_sanitize(struct intel_uncore *uncore,
+                                    unsigned int restore_forcewake)
 {
-       /* clear out unclaimed reg detection bit */
-       if (check_for_unclaimed_mmio(uncore))
-               DRM_DEBUG("unclaimed mmio detected on uncore init, clearing\n");
+       GEM_BUG_ON(!intel_uncore_has_forcewake(uncore));
 
        /* WaDisableShadowRegForCpd:chv */
-       if (IS_CHERRYVIEW(uncore_to_i915(uncore))) {
+       if (IS_CHERRYVIEW(uncore->i915)) {
                __raw_uncore_write32(uncore, GTFIFOCTL,
                                     __raw_uncore_read32(uncore, GTFIFOCTL) |
                                     GT_FIFO_CTL_BLOCK_ALL_POLICY_STALL |
@@ -515,6 +513,9 @@ static void __intel_uncore_early_sanitize(struct intel_uncore *uncore,
 
 void intel_uncore_suspend(struct intel_uncore *uncore)
 {
+       if (!intel_uncore_has_forcewake(uncore))
+               return;
+
        iosf_mbi_punit_acquire();
        iosf_mbi_unregister_pmic_bus_access_notifier_unlocked(
                &uncore->pmic_bus_access_nb);
@@ -526,21 +527,24 @@ void intel_uncore_resume_early(struct intel_uncore *uncore)
 {
        unsigned int restore_forcewake;
 
+       if (intel_uncore_unclaimed_mmio(uncore))
+               DRM_DEBUG("unclaimed mmio detected on resume, clearing\n");
+
+       if (!intel_uncore_has_forcewake(uncore))
+               return;
+
        restore_forcewake = fetch_and_zero(&uncore->fw_domains_saved);
-       __intel_uncore_early_sanitize(uncore, restore_forcewake);
+       forcewake_early_sanitize(uncore, restore_forcewake);
 
        iosf_mbi_register_pmic_bus_access_notifier(&uncore->pmic_bus_access_nb);
 }
 
 void intel_uncore_runtime_resume(struct intel_uncore *uncore)
 {
-       iosf_mbi_register_pmic_bus_access_notifier(&uncore->pmic_bus_access_nb);
-}
+       if (!intel_uncore_has_forcewake(uncore))
+               return;
 
-void intel_uncore_sanitize(struct drm_i915_private *dev_priv)
-{
-       /* BIOS often leaves RC6 enabled, but disable it for hw init */
-       intel_sanitize_gt_powersave(dev_priv);
+       iosf_mbi_register_pmic_bus_access_notifier(&uncore->pmic_bus_access_nb);
 }
 
 static void __intel_uncore_forcewake_get(struct intel_uncore *uncore,
@@ -628,7 +632,7 @@ void intel_uncore_forcewake_user_put(struct intel_uncore *uncore)
        spin_lock_irq(&uncore->lock);
        if (!--uncore->user_forcewake.count) {
                if (intel_uncore_unclaimed_mmio(uncore))
-                       dev_info(uncore_to_i915(uncore)->drm.dev,
+                       dev_info(uncore->i915->drm.dev,
                                 "Invalid mmio detected during user access\n");
 
                uncore->unclaimed_mmio_check =
@@ -734,6 +738,12 @@ void assert_forcewakes_inactive(struct intel_uncore *uncore)
 void assert_forcewakes_active(struct intel_uncore *uncore,
                              enum forcewake_domains fw_domains)
 {
+       struct intel_uncore_forcewake_domain *domain;
+       unsigned int tmp;
+
+       if (!IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM))
+               return;
+
        if (!uncore->funcs.force_wake_get)
                return;
 
@@ -743,6 +753,24 @@ void assert_forcewakes_active(struct intel_uncore *uncore,
        WARN(fw_domains & ~uncore->fw_domains_active,
             "Expected %08x fw_domains to be active, but %08x are off\n",
             fw_domains, fw_domains & ~uncore->fw_domains_active);
+
+       /*
+        * Check that the caller has an explicit wakeref and we don't mistake
+        * it for the auto wakeref.
+        */
+       local_irq_disable();
+       for_each_fw_domain_masked(domain, fw_domains, uncore, tmp) {
+               unsigned int expect = 1;
+
+               if (hrtimer_active(&domain->timer) && READ_ONCE(domain->active))
+                       expect++; /* pending automatic release */
+
+               if (WARN(domain->wake_count < expect,
+                        "Expected domain %d to be held awake by caller, count=%d\n",
+                        domain->id, domain->wake_count))
+                       break;
+       }
+       local_irq_enable();
 }
 
 /* We give fast paths for the really cool registers */
@@ -1289,23 +1317,27 @@ do { \
        (uncore)->funcs.read_fw_domains = x##_reg_read_fw_domains; \
 } while (0)
 
-static void fw_domain_init(struct intel_uncore *uncore,
-                          enum forcewake_domain_id domain_id,
-                          i915_reg_t reg_set,
-                          i915_reg_t reg_ack)
+static int __fw_domain_init(struct intel_uncore *uncore,
+                           enum forcewake_domain_id domain_id,
+                           i915_reg_t reg_set,
+                           i915_reg_t reg_ack)
 {
        struct intel_uncore_forcewake_domain *d;
 
-       if (WARN_ON(domain_id >= FW_DOMAIN_ID_COUNT))
-               return;
+       GEM_BUG_ON(domain_id >= FW_DOMAIN_ID_COUNT);
+       GEM_BUG_ON(uncore->fw_domain[domain_id]);
 
-       d = &uncore->fw_domain[domain_id];
+       if (i915_inject_load_failure())
+               return -ENOMEM;
 
-       WARN_ON(d->wake_count);
+       d = kzalloc(sizeof(*d), GFP_KERNEL);
+       if (!d)
+               return -ENOMEM;
 
        WARN_ON(!i915_mmio_reg_valid(reg_set));
        WARN_ON(!i915_mmio_reg_valid(reg_ack));
 
+       d->uncore = uncore;
        d->wake_count = 0;
        d->reg_set = uncore->regs + i915_mmio_reg_offset(reg_set);
        d->reg_ack = uncore->regs + i915_mmio_reg_offset(reg_ack);
@@ -1322,7 +1354,6 @@ static void fw_domain_init(struct intel_uncore *uncore,
        BUILD_BUG_ON(FORCEWAKE_MEDIA_VEBOX0 != (1 << FW_DOMAIN_ID_MEDIA_VEBOX0));
        BUILD_BUG_ON(FORCEWAKE_MEDIA_VEBOX1 != (1 << FW_DOMAIN_ID_MEDIA_VEBOX1));
 
-
        d->mask = BIT(domain_id);
 
        hrtimer_init(&d->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
@@ -1331,6 +1362,10 @@ static void fw_domain_init(struct intel_uncore *uncore,
        uncore->fw_domains |= BIT(domain_id);
 
        fw_domain_reset(d);
+
+       uncore->fw_domain[domain_id] = d;
+
+       return 0;
 }
 
 static void fw_domain_fini(struct intel_uncore *uncore,
@@ -1338,30 +1373,41 @@ static void fw_domain_fini(struct intel_uncore *uncore,
 {
        struct intel_uncore_forcewake_domain *d;
 
-       if (WARN_ON(domain_id >= FW_DOMAIN_ID_COUNT))
-               return;
+       GEM_BUG_ON(domain_id >= FW_DOMAIN_ID_COUNT);
 
-       d = &uncore->fw_domain[domain_id];
+       d = fetch_and_zero(&uncore->fw_domain[domain_id]);
+       if (!d)
+               return;
 
+       uncore->fw_domains &= ~BIT(domain_id);
        WARN_ON(d->wake_count);
        WARN_ON(hrtimer_cancel(&d->timer));
-       memset(d, 0, sizeof(*d));
+       kfree(d);
+}
 
-       uncore->fw_domains &= ~BIT(domain_id);
+static void intel_uncore_fw_domains_fini(struct intel_uncore *uncore)
+{
+       struct intel_uncore_forcewake_domain *d;
+       int tmp;
+
+       for_each_fw_domain(d, uncore, tmp)
+               fw_domain_fini(uncore, d->id);
 }
 
-static void intel_uncore_fw_domains_init(struct intel_uncore *uncore)
+static int intel_uncore_fw_domains_init(struct intel_uncore *uncore)
 {
-       struct drm_i915_private *i915 = uncore_to_i915(uncore);
+       struct drm_i915_private *i915 = uncore->i915;
+       int ret = 0;
 
-       if (!intel_uncore_has_forcewake(uncore))
-               return;
+       GEM_BUG_ON(!intel_uncore_has_forcewake(uncore));
+
+#define fw_domain_init(uncore__, id__, set__, ack__) \
+       (ret ?: (ret = __fw_domain_init((uncore__), (id__), (set__), (ack__))))
 
        if (INTEL_GEN(i915) >= 11) {
                int i;
 
-               uncore->funcs.force_wake_get =
-                       fw_domains_get_with_fallback;
+               uncore->funcs.force_wake_get = fw_domains_get_with_fallback;
                uncore->funcs.force_wake_put = fw_domains_put;
                fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
                               FORCEWAKE_RENDER_GEN9,
@@ -1369,6 +1415,7 @@ static void intel_uncore_fw_domains_init(struct intel_uncore *uncore)
                fw_domain_init(uncore, FW_DOMAIN_ID_BLITTER,
                               FORCEWAKE_BLITTER_GEN9,
                               FORCEWAKE_ACK_BLITTER_GEN9);
+
                for (i = 0; i < I915_MAX_VCS; i++) {
                        if (!HAS_ENGINE(i915, _VCS(i)))
                                continue;
@@ -1386,8 +1433,7 @@ static void intel_uncore_fw_domains_init(struct intel_uncore *uncore)
                                       FORCEWAKE_ACK_MEDIA_VEBOX_GEN11(i));
                }
        } else if (IS_GEN_RANGE(i915, 9, 10)) {
-               uncore->funcs.force_wake_get =
-                       fw_domains_get_with_fallback;
+               uncore->funcs.force_wake_get = fw_domains_get_with_fallback;
                uncore->funcs.force_wake_put = fw_domains_put;
                fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
                               FORCEWAKE_RENDER_GEN9,
@@ -1436,8 +1482,10 @@ static void intel_uncore_fw_domains_init(struct intel_uncore *uncore)
                __raw_uncore_write32(uncore, FORCEWAKE, 0);
                __raw_posting_read(uncore, ECOBUS);
 
-               fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
-                              FORCEWAKE_MT, FORCEWAKE_MT_ACK);
+               ret = __fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
+                                      FORCEWAKE_MT, FORCEWAKE_MT_ACK);
+               if (ret)
+                       goto out;
 
                spin_lock_irq(&uncore->lock);
                fw_domains_get_with_thread_status(uncore, FORCEWAKE_RENDER);
@@ -1448,6 +1496,7 @@ static void intel_uncore_fw_domains_init(struct intel_uncore *uncore)
                if (!(ecobus & FORCEWAKE_MT_ENABLE)) {
                        DRM_INFO("No MT forcewake available on Ivybridge, this can result in issues\n");
                        DRM_INFO("when using vblank-synced partial screen updates.\n");
+                       fw_domain_fini(uncore, FW_DOMAIN_ID_RENDER);
                        fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
                                       FORCEWAKE, FORCEWAKE_ACK);
                }
@@ -1459,8 +1508,16 @@ static void intel_uncore_fw_domains_init(struct intel_uncore *uncore)
                               FORCEWAKE, FORCEWAKE_ACK);
        }
 
+#undef fw_domain_init
+
        /* All future platforms are expected to require complex power gating */
-       WARN_ON(uncore->fw_domains == 0);
+       WARN_ON(!ret && uncore->fw_domains == 0);
+
+out:
+       if (ret)
+               intel_uncore_fw_domains_fini(uncore);
+
+       return ret;
 }
 
 #define ASSIGN_FW_DOMAINS_TABLE(uncore, d) \
@@ -1505,7 +1562,7 @@ static int i915_pmic_bus_access_notifier(struct notifier_block *nb,
 
 static int uncore_mmio_setup(struct intel_uncore *uncore)
 {
-       struct drm_i915_private *i915 = uncore_to_i915(uncore);
+       struct drm_i915_private *i915 = uncore->i915;
        struct pci_dev *pdev = i915->drm.pdev;
        int mmio_bar;
        int mmio_size;
@@ -1535,49 +1592,46 @@ static int uncore_mmio_setup(struct intel_uncore *uncore)
 
 static void uncore_mmio_cleanup(struct intel_uncore *uncore)
 {
-       struct drm_i915_private *i915 = uncore_to_i915(uncore);
-       struct pci_dev *pdev = i915->drm.pdev;
+       struct pci_dev *pdev = uncore->i915->drm.pdev;
 
        pci_iounmap(pdev, uncore->regs);
 }
 
-void intel_uncore_init_early(struct intel_uncore *uncore)
+void intel_uncore_init_early(struct intel_uncore *uncore,
+                            struct drm_i915_private *i915)
 {
        spin_lock_init(&uncore->lock);
+       uncore->i915 = i915;
+       uncore->rpm = &i915->runtime_pm;
 }
 
-int intel_uncore_init_mmio(struct intel_uncore *uncore)
+static void uncore_raw_init(struct intel_uncore *uncore)
 {
-       struct drm_i915_private *i915 = uncore_to_i915(uncore);
-       int ret;
-
-       ret = uncore_mmio_setup(uncore);
-       if (ret)
-               return ret;
+       GEM_BUG_ON(intel_uncore_has_forcewake(uncore));
 
-       i915_check_vgpu(i915);
+       if (IS_GEN(uncore->i915, 5)) {
+               ASSIGN_RAW_WRITE_MMIO_VFUNCS(uncore, gen5);
+               ASSIGN_RAW_READ_MMIO_VFUNCS(uncore, gen5);
+       } else {
+               ASSIGN_RAW_WRITE_MMIO_VFUNCS(uncore, gen2);
+               ASSIGN_RAW_READ_MMIO_VFUNCS(uncore, gen2);
+       }
+}
 
-       if (INTEL_GEN(i915) > 5 && !intel_vgpu_active(i915))
-               uncore->flags |= UNCORE_HAS_FORCEWAKE;
+static int uncore_forcewake_init(struct intel_uncore *uncore)
+{
+       struct drm_i915_private *i915 = uncore->i915;
+       int ret;
 
-       intel_uncore_fw_domains_init(uncore);
-       __intel_uncore_early_sanitize(uncore, 0);
+       GEM_BUG_ON(!intel_uncore_has_forcewake(uncore));
 
-       uncore->unclaimed_mmio_check = 1;
-       uncore->pmic_bus_access_nb.notifier_call =
-               i915_pmic_bus_access_notifier;
+       ret = intel_uncore_fw_domains_init(uncore);
+       if (ret)
+               return ret;
 
-       uncore->rpm = &i915->runtime_pm;
+       forcewake_early_sanitize(uncore, 0);
 
-       if (!intel_uncore_has_forcewake(uncore)) {
-               if (IS_GEN(i915, 5)) {
-                       ASSIGN_RAW_WRITE_MMIO_VFUNCS(uncore, gen5);
-                       ASSIGN_RAW_READ_MMIO_VFUNCS(uncore, gen5);
-               } else {
-                       ASSIGN_RAW_WRITE_MMIO_VFUNCS(uncore, gen2);
-                       ASSIGN_RAW_READ_MMIO_VFUNCS(uncore, gen2);
-               }
-       } else if (IS_GEN_RANGE(i915, 6, 7)) {
+       if (IS_GEN_RANGE(i915, 6, 7)) {
                ASSIGN_WRITE_MMIO_VFUNCS(uncore, gen6);
 
                if (IS_VALLEYVIEW(i915)) {
@@ -1591,7 +1645,6 @@ int intel_uncore_init_mmio(struct intel_uncore *uncore)
                        ASSIGN_FW_DOMAINS_TABLE(uncore, __chv_fw_ranges);
                        ASSIGN_WRITE_MMIO_VFUNCS(uncore, fwtable);
                        ASSIGN_READ_MMIO_VFUNCS(uncore, fwtable);
-
                } else {
                        ASSIGN_WRITE_MMIO_VFUNCS(uncore, gen8);
                        ASSIGN_READ_MMIO_VFUNCS(uncore, gen6);
@@ -1606,6 +1659,34 @@ int intel_uncore_init_mmio(struct intel_uncore *uncore)
                ASSIGN_READ_MMIO_VFUNCS(uncore, gen11_fwtable);
        }
 
+       uncore->pmic_bus_access_nb.notifier_call = i915_pmic_bus_access_notifier;
+       iosf_mbi_register_pmic_bus_access_notifier(&uncore->pmic_bus_access_nb);
+
+       return 0;
+}
+
+int intel_uncore_init_mmio(struct intel_uncore *uncore)
+{
+       struct drm_i915_private *i915 = uncore->i915;
+       int ret;
+
+       ret = uncore_mmio_setup(uncore);
+       if (ret)
+               return ret;
+
+       if (INTEL_GEN(i915) > 5 && !intel_vgpu_active(i915))
+               uncore->flags |= UNCORE_HAS_FORCEWAKE;
+
+       uncore->unclaimed_mmio_check = 1;
+
+       if (!intel_uncore_has_forcewake(uncore)) {
+               uncore_raw_init(uncore);
+       } else {
+               ret = uncore_forcewake_init(uncore);
+               if (ret)
+                       goto out_mmio_cleanup;
+       }
+
        /* make sure fw funcs are set if and only if we have fw*/
        GEM_BUG_ON(intel_uncore_has_forcewake(uncore) != !!uncore->funcs.force_wake_get);
        GEM_BUG_ON(intel_uncore_has_forcewake(uncore) != !!uncore->funcs.force_wake_put);
@@ -1621,9 +1702,16 @@ int intel_uncore_init_mmio(struct intel_uncore *uncore)
        if (IS_GEN_RANGE(i915, 6, 7))
                uncore->flags |= UNCORE_HAS_FIFO;
 
-       iosf_mbi_register_pmic_bus_access_notifier(&uncore->pmic_bus_access_nb);
+       /* clear out unclaimed reg detection bit */
+       if (check_for_unclaimed_mmio(uncore))
+               DRM_DEBUG("unclaimed mmio detected on uncore init, clearing\n");
 
        return 0;
+
+out_mmio_cleanup:
+       uncore_mmio_cleanup(uncore);
+
+       return ret;
 }
 
 /*
@@ -1633,45 +1721,46 @@ int intel_uncore_init_mmio(struct intel_uncore *uncore)
  */
 void intel_uncore_prune_mmio_domains(struct intel_uncore *uncore)
 {
-       struct drm_i915_private *i915 = uncore_to_i915(uncore);
+       struct drm_i915_private *i915 = uncore->i915;
+       enum forcewake_domains fw_domains = uncore->fw_domains;
+       enum forcewake_domain_id domain_id;
+       int i;
 
-       if (INTEL_GEN(i915) >= 11) {
-               enum forcewake_domains fw_domains = uncore->fw_domains;
-               enum forcewake_domain_id domain_id;
-               int i;
+       if (!intel_uncore_has_forcewake(uncore) || INTEL_GEN(i915) < 11)
+               return;
 
-               for (i = 0; i < I915_MAX_VCS; i++) {
-                       domain_id = FW_DOMAIN_ID_MEDIA_VDBOX0 + i;
+       for (i = 0; i < I915_MAX_VCS; i++) {
+               domain_id = FW_DOMAIN_ID_MEDIA_VDBOX0 + i;
 
-                       if (HAS_ENGINE(i915, _VCS(i)))
-                               continue;
+               if (HAS_ENGINE(i915, _VCS(i)))
+                       continue;
 
-                       if (fw_domains & BIT(domain_id))
-                               fw_domain_fini(uncore, domain_id);
-               }
+               if (fw_domains & BIT(domain_id))
+                       fw_domain_fini(uncore, domain_id);
+       }
 
-               for (i = 0; i < I915_MAX_VECS; i++) {
-                       domain_id = FW_DOMAIN_ID_MEDIA_VEBOX0 + i;
+       for (i = 0; i < I915_MAX_VECS; i++) {
+               domain_id = FW_DOMAIN_ID_MEDIA_VEBOX0 + i;
 
-                       if (HAS_ENGINE(i915, _VECS(i)))
-                               continue;
+               if (HAS_ENGINE(i915, _VECS(i)))
+                       continue;
 
-                       if (fw_domains & BIT(domain_id))
-                               fw_domain_fini(uncore, domain_id);
-               }
+               if (fw_domains & BIT(domain_id))
+                       fw_domain_fini(uncore, domain_id);
        }
 }
 
 void intel_uncore_fini_mmio(struct intel_uncore *uncore)
 {
-       /* Paranoia: make sure we have disabled everything before we exit. */
-       intel_uncore_sanitize(uncore_to_i915(uncore));
+       if (intel_uncore_has_forcewake(uncore)) {
+               iosf_mbi_punit_acquire();
+               iosf_mbi_unregister_pmic_bus_access_notifier_unlocked(
+                       &uncore->pmic_bus_access_nb);
+               intel_uncore_forcewake_reset(uncore);
+               intel_uncore_fw_domains_fini(uncore);
+               iosf_mbi_punit_release();
+       }
 
-       iosf_mbi_punit_acquire();
-       iosf_mbi_unregister_pmic_bus_access_notifier_unlocked(
-               &uncore->pmic_bus_access_nb);
-       intel_uncore_forcewake_reset(uncore);
-       iosf_mbi_punit_release();
        uncore_mmio_cleanup(uncore);
 }