aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/oprofile/op_model_power4.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/oprofile/op_model_power4.c')
-rw-r--r--arch/powerpc/oprofile/op_model_power4.c116
1 files changed, 107 insertions, 9 deletions
diff --git a/arch/powerpc/oprofile/op_model_power4.c b/arch/powerpc/oprofile/op_model_power4.c
index 95ae77dec3f..315f9495e9b 100644
--- a/arch/powerpc/oprofile/op_model_power4.c
+++ b/arch/powerpc/oprofile/op_model_power4.c
@@ -21,6 +21,13 @@
#include <asm/reg.h>
#define dbg(args...)
+#define OPROFILE_PM_PMCSEL_MSK 0xffULL
+#define OPROFILE_PM_UNIT_SHIFT 60
+#define OPROFILE_PM_UNIT_MSK 0xfULL
+#define OPROFILE_MAX_PMC_NUM 3
+#define OPROFILE_PMSEL_FIELD_WIDTH 8
+#define OPROFILE_UNIT_FIELD_WIDTH 4
+#define MMCRA_SIAR_VALID_MASK 0x10000000ULL
static unsigned long reset_value[OP_MAX_COUNTER];
@@ -31,6 +38,61 @@ static int use_slot_nums;
static u32 mmcr0_val;
static u64 mmcr1_val;
static u64 mmcra_val;
+static u32 cntr_marked_events;
+
+static int power7_marked_instr_event(u64 mmcr1)
+{
+ u64 psel, unit;
+ int pmc, cntr_marked_events = 0;
+
+ /* Given the MMCR1 value, look at the field for each counter to
+ * determine if it is a marked event. Code based on the function
+ * power7_marked_instr_event() in file arch/powerpc/perf/power7-pmu.c.
+ */
+ for (pmc = 0; pmc < 4; pmc++) {
+ psel = mmcr1 & (OPROFILE_PM_PMCSEL_MSK
+ << (OPROFILE_MAX_PMC_NUM - pmc)
+ * OPROFILE_MAX_PMC_NUM);
+ psel = (psel >> ((OPROFILE_MAX_PMC_NUM - pmc)
+ * OPROFILE_PMSEL_FIELD_WIDTH)) & ~1ULL;
+ unit = mmcr1 & (OPROFILE_PM_UNIT_MSK
+ << (OPROFILE_PM_UNIT_SHIFT
+ - (pmc * OPROFILE_PMSEL_FIELD_WIDTH )));
+ unit = unit >> (OPROFILE_PM_UNIT_SHIFT
+ - (pmc * OPROFILE_PMSEL_FIELD_WIDTH));
+
+ switch (psel >> 4) {
+ case 2:
+ cntr_marked_events |= (pmc == 1 || pmc == 3) << pmc;
+ break;
+ case 3:
+ if (psel == 0x3c) {
+ cntr_marked_events |= (pmc == 0) << pmc;
+ break;
+ }
+
+ if (psel == 0x3e) {
+ cntr_marked_events |= (pmc != 1) << pmc;
+ break;
+ }
+
+ cntr_marked_events |= 1 << pmc;
+ break;
+ case 4:
+ case 5:
+ cntr_marked_events |= (unit == 0xd) << pmc;
+ break;
+ case 6:
+ if (psel == 0x64)
+ cntr_marked_events |= (pmc >= 2) << pmc;
+ break;
+ case 8:
+ cntr_marked_events |= (unit == 0xd) << pmc;
+ break;
+ }
+ }
+ return cntr_marked_events;
+}
static int power4_reg_setup(struct op_counter_config *ctr,
struct op_system_config *sys,
@@ -47,6 +109,23 @@ static int power4_reg_setup(struct op_counter_config *ctr,
mmcr1_val = sys->mmcr1;
mmcra_val = sys->mmcra;
+ /* Power 7+ and newer architectures:
+ * Determine which counter events in the group (the group of events is
+ * specified by the bit settings in the MMCR1 register) are marked
+ * events for use in the interrupt handler. Do the calculation once
+ * before OProfile starts. Information is used in the interrupt
+ * handler. Starting with Power 7+ we only record the sample for
+ * marked events if the SIAR valid bit is set. For non marked events
+ * the sample is always recorded.
+ */
+ if (pvr_version_is(PVR_POWER7p))
+ cntr_marked_events = power7_marked_instr_event(mmcr1_val);
+ else
+ cntr_marked_events = 0; /* For older processors, set the bit map
+ * to zero so the sample will always be
+ * be recorded.
+ */
+
for (i = 0; i < cur_cpu_spec->num_pmcs; ++i)
reset_value[i] = 0x80000000UL - ctr[i].count;
@@ -61,10 +140,10 @@ static int power4_reg_setup(struct op_counter_config *ctr,
else
mmcr0_val |= MMCR0_PROBLEM_DISABLE;
- if (__is_processor(PV_POWER4) || __is_processor(PV_POWER4p) ||
- __is_processor(PV_970) || __is_processor(PV_970FX) ||
- __is_processor(PV_970MP) || __is_processor(PV_970GX) ||
- __is_processor(PV_POWER5) || __is_processor(PV_POWER5p))
+ if (pvr_version_is(PVR_POWER4) || pvr_version_is(PVR_POWER4p) ||
+ pvr_version_is(PVR_970) || pvr_version_is(PVR_970FX) ||
+ pvr_version_is(PVR_970MP) || pvr_version_is(PVR_970GX) ||
+ pvr_version_is(PVR_POWER5) || pvr_version_is(PVR_POWER5p))
use_slot_nums = 1;
return 0;
@@ -84,9 +163,9 @@ extern void ppc_enable_pmcs(void);
*/
static inline int mmcra_must_set_sample(void)
{
- if (__is_processor(PV_POWER4) || __is_processor(PV_POWER4p) ||
- __is_processor(PV_970) || __is_processor(PV_970FX) ||
- __is_processor(PV_970MP) || __is_processor(PV_970GX))
+ if (pvr_version_is(PVR_POWER4) || pvr_version_is(PVR_POWER4p) ||
+ pvr_version_is(PVR_970) || pvr_version_is(PVR_970FX) ||
+ pvr_version_is(PVR_970MP) || pvr_version_is(PVR_970GX))
return 1;
return 0;
@@ -276,7 +355,7 @@ static bool pmc_overflow(unsigned long val)
* PMCs because a user might set a period of less than 256 and we
* don't want to mistakenly reset them.
*/
- if (__is_processor(PV_POWER7) && ((0x80000000 - val) <= 256))
+ if (pvr_version_is(PVR_POWER7) && ((0x80000000 - val) <= 256))
return true;
return false;
@@ -291,6 +370,7 @@ static void power4_handle_interrupt(struct pt_regs *regs,
int i;
unsigned int mmcr0;
unsigned long mmcra;
+ bool siar_valid = false;
mmcra = mfspr(SPRN_MMCRA);
@@ -300,11 +380,29 @@ static void power4_handle_interrupt(struct pt_regs *regs,
/* set the PMM bit (see comment below) */
mtmsrd(mfmsr() | MSR_PMM);
+ /* Check that the SIAR valid bit in MMCRA is set to 1. */
+ if ((mmcra & MMCRA_SIAR_VALID_MASK) == MMCRA_SIAR_VALID_MASK)
+ siar_valid = true;
+
for (i = 0; i < cur_cpu_spec->num_pmcs; ++i) {
val = classic_ctr_read(i);
if (pmc_overflow(val)) {
if (oprofile_running && ctr[i].enabled) {
- oprofile_add_ext_sample(pc, regs, i, is_kernel);
+ /* Power 7+ and newer architectures:
+ * If the event is a marked event, then only
+ * save the sample if the SIAR valid bit is
+ * set. If the event is not marked, then
+ * always save the sample.
+ * Note, the Sample enable bit in the MMCRA
+ * register must be set to 1 if the group
+ * contains a marked event.
+ */
+ if ((siar_valid &&
+ (cntr_marked_events & (1 << i)))
+ || !(cntr_marked_events & (1 << i)))
+ oprofile_add_ext_sample(pc, regs, i,
+ is_kernel);
+
classic_ctr_write(i, reset_value[i]);
} else {
classic_ctr_write(i, 0);