arch/x86/amd/ibs: Fix re-arming IBS Fetch
[linux-2.6-microblaze.git] / arch / x86 / events / amd / ibs.c
index ea323dc..40669ea 100644 (file)
@@ -89,6 +89,7 @@ struct perf_ibs {
        u64                             max_period;
        unsigned long                   offset_mask[1];
        int                             offset_max;
+       unsigned int                    fetch_count_reset_broken : 1;
        struct cpu_perf_ibs __percpu    *pcpu;
 
        struct attribute                **format_attrs;
@@ -370,7 +371,12 @@ perf_ibs_event_update(struct perf_ibs *perf_ibs, struct perf_event *event,
 static inline void perf_ibs_enable_event(struct perf_ibs *perf_ibs,
                                         struct hw_perf_event *hwc, u64 config)
 {
-       wrmsrl(hwc->config_base, hwc->config | config | perf_ibs->enable_mask);
+       u64 tmp = hwc->config | config;
+
+       if (perf_ibs->fetch_count_reset_broken)
+               wrmsrl(hwc->config_base, tmp & ~perf_ibs->enable_mask);
+
+       wrmsrl(hwc->config_base, tmp | perf_ibs->enable_mask);
 }
 
 /*
@@ -756,6 +762,13 @@ static __init void perf_event_ibs_init(void)
 {
        struct attribute **attr = ibs_op_format_attrs;
 
+       /*
+        * Some chips fail to reset the fetch count when it is written; instead
+        * they need a 0-1 transition of IbsFetchEn.
+        */
+       if (boot_cpu_data.x86 >= 0x16 && boot_cpu_data.x86 <= 0x18)
+               perf_ibs_fetch.fetch_count_reset_broken = 1;
+
        perf_ibs_pmu_init(&perf_ibs_fetch, "ibs_fetch");
 
        if (ibs_caps & IBS_CAPS_OPCNT) {