perf/x86/amd/ibs: Support 27-bit extended Op/cycle counter
[linux-2.6-microblaze.git] / arch / x86 / events / amd / ibs.c
index cfbd020..ea323dc 100644 (file)
@@ -339,10 +339,13 @@ static u64 get_ibs_op_count(u64 config)
         * and the lower 7 bits of CurCnt are randomized.
         * Otherwise CurCnt has the full 27-bit current counter value.
         */
-       if (config & IBS_OP_VAL)
+       if (config & IBS_OP_VAL) {
                count = (config & IBS_OP_MAX_CNT) << 4;
-       else if (ibs_caps & IBS_CAPS_RDWROPCNT)
+               if (ibs_caps & IBS_CAPS_OPCNTEXT)
+                       count += config & IBS_OP_MAX_CNT_EXT_MASK;
+       } else if (ibs_caps & IBS_CAPS_RDWROPCNT) {
                count = (config & IBS_OP_CUR_CNT) >> 32;
+       }
 
        return count;
 }
@@ -398,7 +401,7 @@ static void perf_ibs_start(struct perf_event *event, int flags)
        struct hw_perf_event *hwc = &event->hw;
        struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
        struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
-       u64 period;
+       u64 period, config = 0;
 
        if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
                return;
@@ -407,13 +410,19 @@ static void perf_ibs_start(struct perf_event *event, int flags)
        hwc->state = 0;
 
        perf_ibs_set_period(perf_ibs, hwc, &period);
+       if (perf_ibs == &perf_ibs_op && (ibs_caps & IBS_CAPS_OPCNTEXT)) {
+               config |= period & IBS_OP_MAX_CNT_EXT_MASK;
+               period &= ~IBS_OP_MAX_CNT_EXT_MASK;
+       }
+       config |= period >> 4;
+
        /*
         * Set STARTED before enabling the hardware, such that a subsequent NMI
         * must observe it.
         */
        set_bit(IBS_STARTED,    pcpu->state);
        clear_bit(IBS_STOPPING, pcpu->state);
-       perf_ibs_enable_event(perf_ibs, hwc, period >> 4);
+       perf_ibs_enable_event(perf_ibs, hwc, config);
 
        perf_event_update_userpage(event);
 }
@@ -581,7 +590,7 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
        struct perf_ibs_data ibs_data;
        int offset, size, check_rip, offset_max, throttle = 0;
        unsigned int msr;
-       u64 *buf, *config, period;
+       u64 *buf, *config, period, new_config = 0;
 
        if (!test_bit(IBS_STARTED, pcpu->state)) {
 fail:
@@ -676,13 +685,17 @@ out:
        if (throttle) {
                perf_ibs_stop(event, 0);
        } else {
-               period >>= 4;
-
-               if ((ibs_caps & IBS_CAPS_RDWROPCNT) &&
-                   (*config & IBS_OP_CNT_CTL))
-                       period |= *config & IBS_OP_CUR_CNT_RAND;
+               if (perf_ibs == &perf_ibs_op) {
+                       if (ibs_caps & IBS_CAPS_OPCNTEXT) {
+                               new_config = period & IBS_OP_MAX_CNT_EXT_MASK;
+                               period &= ~IBS_OP_MAX_CNT_EXT_MASK;
+                       }
+                       if ((ibs_caps & IBS_CAPS_RDWROPCNT) && (*config & IBS_OP_CNT_CTL))
+                               new_config |= *config & IBS_OP_CUR_CNT_RAND;
+               }
+               new_config |= period >> 4;
 
-               perf_ibs_enable_event(perf_ibs, hwc, period);
+               perf_ibs_enable_event(perf_ibs, hwc, new_config);
        }
 
        perf_event_update_userpage(event);
@@ -749,6 +762,13 @@ static __init void perf_event_ibs_init(void)
                perf_ibs_op.config_mask |= IBS_OP_CNT_CTL;
                *attr++ = &format_attr_cnt_ctl.attr;
        }
+
+       if (ibs_caps & IBS_CAPS_OPCNTEXT) {
+               perf_ibs_op.max_period  |= IBS_OP_MAX_CNT_EXT_MASK;
+               perf_ibs_op.config_mask |= IBS_OP_MAX_CNT_EXT_MASK;
+               perf_ibs_op.cnt_mask    |= IBS_OP_MAX_CNT_EXT_MASK;
+       }
+
        perf_ibs_pmu_init(&perf_ibs_op, "ibs_op");
 
        register_nmi_handler(NMI_LOCAL, perf_ibs_nmi_handler, 0, "perf_ibs");