drivers/perf: hisi: Enable HiSilicon Erratum 162700402 quirk for HIP09
[linux-2.6-microblaze.git] / drivers / perf / hisilicon / hisi_uncore_uc_pmu.c
index 636fb79..481dcc9 100644 (file)
@@ -287,12 +287,52 @@ static u64 hisi_uc_pmu_read_counter(struct hisi_pmu *uc_pmu,
        return readq(uc_pmu->base + HISI_UC_CNTR_REGn(hwc->idx));
 }
 
-static void hisi_uc_pmu_write_counter(struct hisi_pmu *uc_pmu,
+static bool hisi_uc_pmu_get_glb_en_state(struct hisi_pmu *uc_pmu)
+{
+       u32 val;
+
+       val = readl(uc_pmu->base + HISI_UC_EVENT_CTRL_REG);
+       return !!FIELD_GET(HISI_UC_EVENT_GLB_EN, val);
+}
+
+static void hisi_uc_pmu_write_counter_normal(struct hisi_pmu *uc_pmu,
                                      struct hw_perf_event *hwc, u64 val)
 {
        writeq(val, uc_pmu->base + HISI_UC_CNTR_REGn(hwc->idx));
 }
 
+static void hisi_uc_pmu_write_counter_quirk_v2(struct hisi_pmu *uc_pmu,
+                                     struct hw_perf_event *hwc, u64 val)
+{
+       hisi_uc_pmu_start_counters(uc_pmu);
+       hisi_uc_pmu_write_counter_normal(uc_pmu, hwc, val);
+       hisi_uc_pmu_stop_counters(uc_pmu);
+}
+
+static void hisi_uc_pmu_write_counter(struct hisi_pmu *uc_pmu,
+                                     struct hw_perf_event *hwc, u64 val)
+{
+       bool enable = hisi_uc_pmu_get_glb_en_state(uc_pmu);
+       bool erratum = uc_pmu->identifier == HISI_PMU_V2;
+
+       /*
+        * HiSilicon UC PMU v2 suffers the erratum 162700402 that the
+        * PMU counter cannot be set due to the lack of clock under power
+        * saving mode. This will lead to error or inaccurate counts.
+        * The clock can be enabled by the PMU global enabling control.
+        * The irq handler and pmu_start() will call the function to set
+        * period. If the function under irq context, the PMU has been
+        * enabled therefore we set counter directly. Other situations
+        * the PMU is disabled, we need to enable it to turn on the
+        * counter clock to set period, and then restore PMU enable
+        * status, the counter can hold its value without a clock.
+        */
+       if (enable || !erratum)
+               hisi_uc_pmu_write_counter_normal(uc_pmu, hwc, val);
+       else
+               hisi_uc_pmu_write_counter_quirk_v2(uc_pmu, hwc, val);
+}
+
 static void hisi_uc_pmu_enable_counter_int(struct hisi_pmu *uc_pmu,
                                           struct hw_perf_event *hwc)
 {