// SPDX-License-Identifier: GPL-2.0 /* * ARMv7 Cortex-A8 and Cortex-A9 Performance Events handling code. * * ARMv7 support: Jean Pihet <jpihet@mvista.com> * 2010 (c) MontaVista Software, LLC. * * Copied from ARMv6 code, with the low level code inspired * by the ARMv7 Oprofile code. * * Cortex-A8 has up to 4 configurable performance counters and * a single cycle counter. * Cortex-A9 has up to 31 configurable performance counters and * a single cycle counter. * * All counters can be enabled/disabled and IRQ masked separately. The cycle * counter and all 4 performance counters together can be reset separately.
*/
/* * Common ARMv7 event types * * Note: An implementation may not be able to count all of these events * but the encodings are considered to be `reserved' in the case that * they are not available.
*/ #define ARMV7_PERFCTR_PMNC_SW_INCR 0x00 #define ARMV7_PERFCTR_L1_ICACHE_REFILL 0x01 #define ARMV7_PERFCTR_ITLB_REFILL 0x02 #define ARMV7_PERFCTR_L1_DCACHE_REFILL 0x03 #define ARMV7_PERFCTR_L1_DCACHE_ACCESS 0x04 #define ARMV7_PERFCTR_DTLB_REFILL 0x05 #define ARMV7_PERFCTR_MEM_READ 0x06 #define ARMV7_PERFCTR_MEM_WRITE 0x07 #define ARMV7_PERFCTR_INSTR_EXECUTED 0x08 #define ARMV7_PERFCTR_EXC_TAKEN 0x09 #define ARMV7_PERFCTR_EXC_EXECUTED 0x0A #define ARMV7_PERFCTR_CID_WRITE 0x0B
/* * ARMV7_PERFCTR_PC_WRITE is equivalent to HW_BRANCH_INSTRUCTIONS. * It counts: * - all (taken) branch instructions, * - instructions that explicitly write the PC, * - exception generating instructions.
*/ #define ARMV7_PERFCTR_PC_WRITE 0x0C #define ARMV7_PERFCTR_PC_IMM_BRANCH 0x0D #define ARMV7_PERFCTR_PC_PROC_RETURN 0x0E #define ARMV7_PERFCTR_MEM_UNALIGNED_ACCESS 0x0F #define ARMV7_PERFCTR_PC_BRANCH_MIS_PRED 0x10 #define ARMV7_PERFCTR_CLOCK_CYCLES 0x11 #define ARMV7_PERFCTR_PC_BRANCH_PRED 0x12
/* These events are defined by the PMUv2 supplement (ARM DDI 0457A). */ #define ARMV7_PERFCTR_MEM_ACCESS 0x13 #define ARMV7_PERFCTR_L1_ICACHE_ACCESS 0x14 #define ARMV7_PERFCTR_L1_DCACHE_WB 0x15 #define ARMV7_PERFCTR_L2_CACHE_ACCESS 0x16 #define ARMV7_PERFCTR_L2_CACHE_REFILL 0x17 #define ARMV7_PERFCTR_L2_CACHE_WB 0x18 #define ARMV7_PERFCTR_BUS_ACCESS 0x19 #define ARMV7_PERFCTR_MEM_ERROR 0x1A #define ARMV7_PERFCTR_INSTR_SPEC 0x1B #define ARMV7_PERFCTR_TTBR_WRITE 0x1C #define ARMV7_PERFCTR_BUS_CYCLES 0x1D
/* * Cortex-A8 HW events mapping * * The hardware events that we support. We do support cache operations but * we have harvard caches and no way to combine instruction and data * accesses/misses in hardware.
*/ staticconstunsigned armv7_a8_perf_map[PERF_COUNT_HW_MAX] = {
PERF_MAP_ALL_UNSUPPORTED,
[PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES,
[PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERFCTR_INSTR_EXECUTED,
[PERF_COUNT_HW_CACHE_REFERENCES] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
[PERF_COUNT_HW_CACHE_MISSES] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE,
[PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = ARMV7_A8_PERFCTR_STALL_ISIDE,
};
/* * The performance counters don't differentiate between read and write * accesses/misses so this isn't strictly correct, but it's the best we * can do. Writes and reads get combined.
*/
[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
[C(L1D)][C(OP_READ)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
/* * The performance counters don't differentiate between read and write * accesses/misses so this isn't strictly correct, but it's the best we * can do. Writes and reads get combined.
*/
[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
[C(L1D)][C(OP_READ)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
[C(L1I)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_ICACHE_ACCESS,
[C(L1I)][C(OP_READ)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_ICACHE_REFILL, /* * The prefetch counters don't differentiate between the I side and the * D side.
*/
[C(L1I)][C(OP_PREFETCH)][C(RESULT_ACCESS)] = ARMV7_A5_PERFCTR_PREFETCH_LINEFILL,
[C(L1I)][C(OP_PREFETCH)][C(RESULT_MISS)] = ARMV7_A5_PERFCTR_PREFETCH_LINEFILL_DROP,
/* * Not all performance counters differentiate between read and write * accesses/misses so we're not always strictly correct, but it's the * best we can do. Writes and reads get combined in these cases.
*/
[C(L1I)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_ICACHE_ACCESS,
[C(L1I)][C(OP_READ)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_ICACHE_REFILL,
/* * The performance counters don't differentiate between read and write * accesses/misses so this isn't strictly correct, but it's the best we * can do. Writes and reads get combined.
*/
[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
[C(L1D)][C(OP_READ)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
/* * Not all performance counters differentiate between read and write * accesses/misses so we're not always strictly correct, but it's the * best we can do. Writes and reads get combined in these cases.
*/
[C(L1I)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_ICACHE_ACCESS,
[C(L1I)][C(OP_READ)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_ICACHE_REFILL,
/* * The performance counters don't differentiate between read and write * accesses/misses so this isn't strictly correct, but it's the best we * can do. Writes and reads get combined.
*/
[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
[C(L1D)][C(OP_READ)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
/* * Set event (if destined for PMNx counters) * We only need to set the event for the cycle counter if we * have the ability to perform event filtering.
*/ if (cpu_pmu->set_event_filter || idx != ARMV7_IDX_CYCLE_COUNTER)
armv7_pmnc_write_evtsel(idx, hwc->config_base);
/* Ignore if we don't have an event. */ if (!event) continue;
/* * We have a single interrupt for all counters. Check that * each counter has overflowed before we process it.
*/ if (!armv7_pmnc_counter_has_overflowed(pmnc, idx)) continue;
hwc = &event->hw;
armpmu_event_update(event);
perf_sample_data_init(&data, 0, hwc->last_period); if (!armpmu_event_set_period(event)) continue;
perf_event_overflow(event, &data, regs);
}
/* * Handle the pending perf events. * * Note: this call *must* be run with interrupts disabled. For * platforms that can have the PMU interrupts raised as an NMI, this * will not work.
*/
irq_work_run();
/* Always place a cycle counter into the cycle counter. */ if (evtype == ARMV7_PERFCTR_CPU_CYCLES) { if (test_and_set_bit(ARMV7_IDX_CYCLE_COUNTER, cpuc->used_mask)) return -EAGAIN;
return ARMV7_IDX_CYCLE_COUNTER;
}
/* * For anything other than a cycle counter, try and use * the events counters
*/
for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMV7_IDX_COUNTER_MAX) { if (!test_and_set_bit(idx, cpuc->used_mask)) return idx;
}
/* The counters are all in use. */ return -EAGAIN;
}
/* * Add an event filter to a given event. This will only work for PMUv2 PMUs.
*/ staticint armv7pmu_set_event_filter(struct hw_perf_event *event, struct perf_event_attr *attr)
{ unsignedlong config_base = 0;
if (attr->exclude_idle) {
pr_debug("ARM performance counters do not support mode exclusion\n"); return -EOPNOTSUPP;
} if (attr->exclude_user)
config_base |= ARMV7_EXCLUDE_USER; if (attr->exclude_kernel)
config_base |= ARMV7_EXCLUDE_PL1; if (!attr->exclude_hv)
config_base |= ARMV7_INCLUDE_HYP;
/* * Install the filter into config_base as this is used to * construct the event type.
*/
event->config_base = config_base;
/* The counter and interrupt enable registers are unknown at reset. */
for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMPMU_MAX_HWEVENTS) {
armv7_pmnc_disable_counter(idx);
armv7_pmnc_disable_intens(idx);
}
/* Initialize & Reset PMNC: C and P bits */
armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C);
}
/* * Krait Performance Monitor Region Event Selection Register (PMRESRn) * * 31 30 24 16 8 0 * +--------------------------------+ * PMRESR0 | EN | CC | CC | CC | CC | N = 1, R = 0 * +--------------------------------+ * PMRESR1 | EN | CC | CC | CC | CC | N = 1, R = 1 * +--------------------------------+ * PMRESR2 | EN | CC | CC | CC | CC | N = 1, R = 2 * +--------------------------------+ * VPMRESR0 | EN | CC | CC | CC | CC | N = 2, R = ? * +--------------------------------+ * EN | G=3 | G=2 | G=1 | G=0 * * Event Encoding: * * hwc->config_base = 0xNRCCG * * N = prefix, 1 for Krait CPU (PMRESRn), 2 for Venum VFP (VPMRESR) * R = region register * CC = class of events the group G is choosing from * G = group or particular event * * Example: 0x12021 is a Krait CPU event in PMRESR2's group 1 with code 2 * * A region (R) corresponds to a piece of the CPU (execution unit, instruction * unit, etc.) while the event code (CC) corresponds to a particular class of * events (interrupts for example). An event code is broken down into * groups (G) that can be mapped into the PMU (irq, fiqs, and irq+fiqs for * example).
*/
group_shift = group * 8;
mask = 0xff << group_shift;
/* Configure evtsel for the region and group */ if (venum_event)
val = KRAIT_VPMRESR0_GROUP0; else
val = krait_get_pmresrn_event(region);
val += group; /* Mix in mode-exclusion bits */
val |= config_base & (ARMV7_EXCLUDE_USER | ARMV7_EXCLUDE_PL1);
armv7_pmnc_write_evtsel(idx, val);
if (venum_event) {
venum_pre_pmresr(&vval, &fval);
val = venum_read_pmresr();
val &= ~mask;
val |= code << group_shift;
val |= PMRESRn_EN;
venum_write_pmresr(val);
venum_post_pmresr(vval, fval);
} else {
val = krait_read_pmresrn(region);
val &= ~mask;
val |= code << group_shift;
val |= PMRESRn_EN;
krait_write_pmresrn(region, val);
}
}
static u32 clear_pmresrn_group(u32 val, int group)
{
u32 mask; int group_shift;
group_shift = group * 8;
mask = 0xff << group_shift;
val &= ~mask;
/* Don't clear enable bit if entire region isn't disabled */ if (val & ~PMRESRn_EN) return val |= PMRESRn_EN;
/* * Set event (if destined for PMNx counters) * We set the event for the cycle counter because we * have the ability to perform event filtering.
*/ if (hwc->config_base & KRAIT_EVENT_MASK)
krait_evt_setup(idx, hwc->config_base); else
armv7_pmnc_write_evtsel(idx, hwc->config_base);
if (hwc->config_base & VENUM_EVENT)
bit = KRAIT_VPMRESR0_GROUP0; else
bit = krait_get_pmresrn_event(region);
bit -= krait_get_pmresrn_event(0);
bit += group; /* * Lower bits are reserved for use by the counters (see * armv7pmu_get_event_idx() for more info)
*/
bit += bitmap_weight(cpu_pmu->cntr_mask, ARMV7_IDX_COUNTER_MAX);
return bit;
}
/* * We check for column exclusion constraints here. * Two events cant use the same group within a pmresr register.
*/ staticint krait_pmu_get_event_idx(struct pmu_hw_events *cpuc, struct perf_event *event)
{ int idx; int bit = -1; struct hw_perf_event *hwc = &event->hw; unsignedint region = EVENT_REGION(hwc->config_base); unsignedint code = EVENT_CODE(hwc->config_base); unsignedint group = EVENT_GROUP(hwc->config_base); bool venum_event = EVENT_VENUM(hwc->config_base); bool krait_event = EVENT_CPU(hwc->config_base);
if (venum_event || krait_event) { /* Ignore invalid events */ if (group > 3 || region > 2) return -EINVAL; if (venum_event && (code & 0xe0)) return -EINVAL;
bit = krait_event_to_bit(event, region, group); if (test_and_set_bit(bit, cpuc->used_mask)) return -EAGAIN;
}
idx = armv7pmu_get_event_idx(cpuc, event); if (idx < 0 && bit >= 0)
clear_bit(bit, cpuc->used_mask);
armv7pmu_clear_event_idx(cpuc, event); if (venum_event || krait_event) {
bit = krait_event_to_bit(event, region, group);
clear_bit(bit, cpuc->used_mask);
}
}
staticint krait_pmu_init(struct arm_pmu *cpu_pmu)
{
armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_krait"; /* Some early versions of Krait don't support PC write events */ if (of_property_read_bool(cpu_pmu->plat_device->dev.of_node, "qcom,no-pc-write"))
cpu_pmu->map_event = krait_map_event_no_branch; else
cpu_pmu->map_event = krait_map_event;
cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
cpu_pmu->reset = krait_pmu_reset;
cpu_pmu->enable = krait_pmu_enable_event;
cpu_pmu->disable = krait_pmu_disable_event;
cpu_pmu->get_event_idx = krait_pmu_get_event_idx;
cpu_pmu->clear_event_idx = krait_pmu_clear_event_idx; return armv7_probe_num_events(cpu_pmu);
}
/* * Scorpion Local Performance Monitor Register (LPMn) * * 31 30 24 16 8 0 * +--------------------------------+ * LPM0 | EN | CC | CC | CC | CC | N = 1, R = 0 * +--------------------------------+ * LPM1 | EN | CC | CC | CC | CC | N = 1, R = 1 * +--------------------------------+ * LPM2 | EN | CC | CC | CC | CC | N = 1, R = 2 * +--------------------------------+ * L2LPM | EN | CC | CC | CC | CC | N = 1, R = 3 * +--------------------------------+ * VLPM | EN | CC | CC | CC | CC | N = 2, R = ? * +--------------------------------+ * EN | G=3 | G=2 | G=1 | G=0 * * * Event Encoding: * * hwc->config_base = 0xNRCCG * * N = prefix, 1 for Scorpion CPU (LPMn/L2LPM), 2 for Venum VFP (VLPM) * R = region register * CC = class of events the group G is choosing from * G = group or particular event * * Example: 0x12021 is a Scorpion CPU event in LPM2's group 1 with code 2 * * A region (R) corresponds to a piece of the CPU (execution unit, instruction * unit, etc.) while the event code (CC) corresponds to a particular class of * events (interrupts for example). An event code is broken down into * groups (G) that can be mapped into the PMU (irq, fiqs, and irq+fiqs for * example).
*/
group_shift = group * 8;
mask = 0xff << group_shift;
/* Configure evtsel for the region and group */ if (venum_event)
val = SCORPION_VLPM_GROUP0; else
val = scorpion_get_pmresrn_event(region);
val += group; /* Mix in mode-exclusion bits */
val |= config_base & (ARMV7_EXCLUDE_USER | ARMV7_EXCLUDE_PL1);
armv7_pmnc_write_evtsel(idx, val);
if (venum_event) {
venum_pre_pmresr(&vval, &fval);
val = venum_read_pmresr();
val &= ~mask;
val |= code << group_shift;
val |= PMRESRn_EN;
venum_write_pmresr(val);
venum_post_pmresr(vval, fval);
} else {
val = scorpion_read_pmresrn(region);
val &= ~mask;
val |= code << group_shift;
val |= PMRESRn_EN;
scorpion_write_pmresrn(region, val);
}
}
/* * Set event (if destined for PMNx counters) * We don't set the event for the cycle counter because we * don't have the ability to perform event filtering.
*/ if (hwc->config_base & KRAIT_EVENT_MASK)
scorpion_evt_setup(idx, hwc->config_base); elseif (idx != ARMV7_IDX_CYCLE_COUNTER)
armv7_pmnc_write_evtsel(idx, hwc->config_base);
if (hwc->config_base & VENUM_EVENT)
bit = SCORPION_VLPM_GROUP0; else
bit = scorpion_get_pmresrn_event(region);
bit -= scorpion_get_pmresrn_event(0);
bit += group; /* * Lower bits are reserved for use by the counters (see * armv7pmu_get_event_idx() for more info)
*/
bit += bitmap_weight(cpu_pmu->cntr_mask, ARMV7_IDX_COUNTER_MAX);
return bit;
}
/* * We check for column exclusion constraints here. * Two events cant use the same group within a pmresr register.
*/ staticint scorpion_pmu_get_event_idx(struct pmu_hw_events *cpuc, struct perf_event *event)
{ int idx; int bit = -1; struct hw_perf_event *hwc = &event->hw; unsignedint region = EVENT_REGION(hwc->config_base); unsignedint group = EVENT_GROUP(hwc->config_base); bool venum_event = EVENT_VENUM(hwc->config_base); bool scorpion_event = EVENT_CPU(hwc->config_base);
if (venum_event || scorpion_event) { /* Ignore invalid events */ if (group > 3 || region > 3) return -EINVAL;
bit = scorpion_event_to_bit(event, region, group); if (test_and_set_bit(bit, cpuc->used_mask)) return -EAGAIN;
}
idx = armv7pmu_get_event_idx(cpuc, event); if (idx < 0 && bit >= 0)
clear_bit(bit, cpuc->used_mask);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.