// SPDX-License-Identifier: GPL-2.0 /* * ARMv6 Performance counter handling code. * * Copyright (C) 2009 picoChip Designs, Ltd., Jamie Iles * * ARMv6 has 2 configurable performance counters and a single cycle counter. * They all share a single reset bit but can be written to zero so we can use * that for a reset. * * The counters can't be individually enabled or disabled so when we remove * one event and replace it with another we could get spurious counts from the * wrong event. However, we can take advantage of the fact that the * performance counters can export events to the event bus, and the event bus * itself can be monitored. This requires that we *don't* export the events to * the event bus. The procedure for disabling a configurable counter is: * - change the counter to count the ETMEXTOUT[0] signal (0x20). This * effectively stops the counter from counting. * - disable the counter's interrupt generation (each counter has it's * own interrupt enable bit). * Once stopped, the counter value can be written as 0 to reset. * * To enable a counter: * - enable the counter's interrupt generation. * - set the new event type. * * Note: the dedicated cycle counter only counts cycles and can't be * enabled/disabled independently of the others. When we want to disable the * cycle counter, we have to just disable the interrupt reporting and start * ignoring that counter. When re-enabling, we have to reset the value and * enable the interrupt.
*/
/* * 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 armv6_perf_map[PERF_COUNT_HW_MAX] = {
PERF_MAP_ALL_UNSUPPORTED,
[PERF_COUNT_HW_CPU_CYCLES] = ARMV6_PERFCTR_CPU_CYCLES,
[PERF_COUNT_HW_INSTRUCTIONS] = ARMV6_PERFCTR_INSTR_EXEC,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV6_PERFCTR_BR_EXEC,
[PERF_COUNT_HW_BRANCH_MISSES] = ARMV6_PERFCTR_BR_MISPREDICT,
[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = ARMV6_PERFCTR_IBUF_STALL,
[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = ARMV6_PERFCTR_LSU_FULL_STALL,
};
/* * 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)] = ARMV6_PERFCTR_DCACHE_ACCESS,
[C(L1D)][C(OP_READ)][C(RESULT_MISS)] = ARMV6_PERFCTR_DCACHE_MISS,
[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV6_PERFCTR_DCACHE_ACCESS,
[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV6_PERFCTR_DCACHE_MISS,
/* * The ARM performance counters can count micro DTLB misses, micro ITLB * misses and main TLB misses. There isn't an event for TLB misses, so * use the micro misses here and if users want the main TLB misses they * can use a raw counter.
*/
[C(DTLB)][C(OP_READ)][C(RESULT_MISS)] = ARMV6_PERFCTR_DTLB_MISS,
[C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV6_PERFCTR_DTLB_MISS,
/* * Mask out the current event and set the counter to count the event * that we're interested in.
*/
val = armv6_pmcr_read();
val &= ~mask;
val |= evt;
armv6_pmcr_write(val);
}
if (!armv6_pmcr_has_overflowed(pmcr)) return IRQ_NONE;
regs = get_irq_regs();
/* * The interrupts are cleared by writing the overflow flags back to * the control register. All of the other bits don't have any effect * if they are rewritten, so write the whole value back.
*/
armv6_pmcr_write(pmcr);
/* 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 (!armv6_pmcr_counter_has_overflowed(pmcr, 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();
val = armv6_pmcr_read();
val &= ~ARMV6_PMCR_ENABLE;
armv6_pmcr_write(val);
}
staticint
armv6pmu_get_event_idx(struct pmu_hw_events *cpuc, struct perf_event *event)
{ struct hw_perf_event *hwc = &event->hw; /* Always place a cycle counter into the cycle counter. */ if (ARMV6_PERFCTR_CPU_CYCLES == hwc->config_base) { if (test_and_set_bit(ARMV6_CYCLE_COUNTER, cpuc->used_mask)) return -EAGAIN;
return ARMV6_CYCLE_COUNTER;
} else { /* * For anything other than a cycle counter, try and use * counter0 and counter1.
*/ if (!test_and_set_bit(ARMV6_COUNTER1, cpuc->used_mask)) return ARMV6_COUNTER1;
if (!test_and_set_bit(ARMV6_COUNTER0, cpuc->used_mask)) return ARMV6_COUNTER0;
/* The counters are all in use. */ return -EAGAIN;
}
}
/* * Mask out the current event and set the counter to count the number * of ETM bus signal assertion cycles. The external reporting should * be disabled and so this should never increment.
*/
val = armv6_pmcr_read();
val &= ~mask;
val |= evt;
armv6_pmcr_write(val);
}
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.