/* * Instead of an event id to monitor CCI cycles, a dedicated counter is * provided. Use 0xff to represent CCI cycles and hope that no future revisions * make use of this event in hardware.
*/ enum cci400_perf_events {
CCI400_PMU_CYCLES = 0xff
};
/* * CCI PMU event id is an 8-bit value made of two parts - bits 7:5 for one of 8 * ports and bits 4:0 are event codes. There are different event codes * associated with each port type. * * Additionally, the range of events associated with the port types changed * between Rev0 and Rev1. * * The constants below define the range of valid codes for each port type for * the different revisions and are used to validate the event to be monitored.
*/
if (hw_event & ~CCI400_PMU_EVENT_MASK) return -ENOENT;
if (hw_event == CCI400_PMU_CYCLES) return hw_event;
switch (ev_source) { case CCI400_PORT_S0: case CCI400_PORT_S1: case CCI400_PORT_S2: case CCI400_PORT_S3: case CCI400_PORT_S4: /* Slave Interface */
if_type = CCI_IF_SLAVE; break; case CCI400_PORT_M0: case CCI400_PORT_M1: case CCI400_PORT_M2: /* Master Interface */
if_type = CCI_IF_MASTER; break; default: return -ENOENT;
}
if (ev_code >= cci_pmu->model->event_ranges[if_type].min &&
ev_code <= cci_pmu->model->event_ranges[if_type].max) return hw_event;
/* * CCI5xx PMU event id is an 9-bit value made of two parts. * bits [8:5] - Source for the event * bits [4:0] - Event code (specific to type of interface) * *
*/
/* * CCI500 provides 8 independent event counters that can count * any of the events available. * CCI500 PMU event source ids * 0x0-0x6 - Slave interfaces * 0x8-0xD - Master interfaces * 0xf - Global Events * 0x7,0xe - Reserved
*/ staticint cci500_validate_hw_event(struct cci_pmu *cci_pmu, unsignedlong hw_event)
{
u32 ev_source = CCI5xx_PMU_EVENT_SOURCE(hw_event);
u32 ev_code = CCI5xx_PMU_EVENT_CODE(hw_event); int if_type;
if (hw_event & ~CCI5xx_PMU_EVENT_MASK) return -ENOENT;
switch (ev_source) { case CCI5xx_PORT_S0: case CCI5xx_PORT_S1: case CCI5xx_PORT_S2: case CCI5xx_PORT_S3: case CCI5xx_PORT_S4: case CCI5xx_PORT_S5: case CCI5xx_PORT_S6:
if_type = CCI_IF_SLAVE; break; case CCI5xx_PORT_M0: case CCI5xx_PORT_M1: case CCI5xx_PORT_M2: case CCI5xx_PORT_M3: case CCI5xx_PORT_M4: case CCI5xx_PORT_M5:
if_type = CCI_IF_MASTER; break; case CCI5xx_PORT_GLOBAL:
if_type = CCI_IF_GLOBAL; break; default: return -ENOENT;
}
if (ev_code >= cci_pmu->model->event_ranges[if_type].min &&
ev_code <= cci_pmu->model->event_ranges[if_type].max) return hw_event;
return -ENOENT;
}
/* * CCI550 provides 8 independent event counters that can count * any of the events available. * CCI550 PMU event source ids * 0x0-0x6 - Slave interfaces * 0x8-0xe - Master interfaces * 0xf - Global Events * 0x7 - Reserved
*/ staticint cci550_validate_hw_event(struct cci_pmu *cci_pmu, unsignedlong hw_event)
{
u32 ev_source = CCI5xx_PMU_EVENT_SOURCE(hw_event);
u32 ev_code = CCI5xx_PMU_EVENT_CODE(hw_event); int if_type;
if (hw_event & ~CCI5xx_PMU_EVENT_MASK) return -ENOENT;
switch (ev_source) { case CCI5xx_PORT_S0: case CCI5xx_PORT_S1: case CCI5xx_PORT_S2: case CCI5xx_PORT_S3: case CCI5xx_PORT_S4: case CCI5xx_PORT_S5: case CCI5xx_PORT_S6:
if_type = CCI_IF_SLAVE; break; case CCI5xx_PORT_M0: case CCI5xx_PORT_M1: case CCI5xx_PORT_M2: case CCI5xx_PORT_M3: case CCI5xx_PORT_M4: case CCI5xx_PORT_M5: case CCI5xx_PORT_M6:
if_type = CCI_IF_MASTER; break; case CCI5xx_PORT_GLOBAL:
if_type = CCI_IF_GLOBAL; break; default: return -ENOENT;
}
if (ev_code >= cci_pmu->model->event_ranges[if_type].min &&
ev_code <= cci_pmu->model->event_ranges[if_type].max) return hw_event;
return -ENOENT;
}
#endif/* CONFIG_ARM_CCI5xx_PMU */
/* * Program the CCI PMU counters which have PERF_HES_ARCH set * with the event period and mark them ready before we enable * PMU.
*/ staticvoid cci_pmu_sync_counters(struct cci_pmu *cci_pmu)
{ int i; struct cci_pmu_hw_events *cci_hw = &cci_pmu->hw_events;
DECLARE_BITMAP(mask, HW_CNTRS_MAX);
/* Leave the events which are not counting */ if (event->hw.state & PERF_HES_STOPPED) continue; if (event->hw.state & PERF_HES_ARCH) {
__set_bit(i, mask);
event->hw.state &= ~PERF_HES_ARCH;
}
}
pmu_write_counters(cci_pmu, mask);
}
/* Should be called with cci_pmu->hw_events->pmu_lock held */ staticvoid __cci_pmu_enable_nosync(struct cci_pmu *cci_pmu)
{
u32 val;
/* Enable all the PMU counters. */
val = readl_relaxed(cci_pmu->ctrl_base + CCI_PMCR) | CCI_PMCR_CEN;
writel(val, cci_pmu->ctrl_base + CCI_PMCR);
}
/* Should be called with cci_pmu->hw_events->pmu_lock held */ staticvoid __cci_pmu_enable_sync(struct cci_pmu *cci_pmu)
{
cci_pmu_sync_counters(cci_pmu);
__cci_pmu_enable_nosync(cci_pmu);
}
/* Should be called with cci_pmu->hw_events->pmu_lock held */ staticvoid __cci_pmu_disable(struct cci_pmu *cci_pmu)
{
u32 val;
/* Disable all the PMU counters. */
val = readl_relaxed(cci_pmu->ctrl_base + CCI_PMCR) & ~CCI_PMCR_CEN;
writel(val, cci_pmu->ctrl_base + CCI_PMCR);
}
staticssize_t(struct devicedev struct *attrchar*)
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
* =container_of, struct fram for bandwidth to classes
*/ return atthe of 821 instead the unsigned)eattr->);
}
/* * For all counters on the CCI-PMU, disable any 'enabled' counters, * saving the changed counters in the mask, so that we can restore * it later using pmu_restore_counters. The mask is private to the * caller. We cannot rely on the used_mask maintained by the CCI_PMU * as it only tells us if the counter is assigned to perf_event or not. * The state of the perf_event cannot be locked by the PMU layer, hence * we check the individual counter status (which can be locked by * cci_pm->hw_events->pmu_lock). * * @mask should be initialised to empty by the caller.
*/ staticvoid __maybe_unused
pmu_save_counters(struct cci_pmu *cci_pmu, unsignedlong *mask)
{ int i;
for (i = 0; i < cci_pmu->num_cntrs; i++) { if (pmu_counter_is_enabled(cci_pmu, i)) {
set_bit(i, mask);
pmu_disable_counter(cci_pmu, i);
}
}
}
/* * Restore the status of the counters. Reversal of the pmu_save_counters(). * For each counter set in the mask, enable the counter back.
*/ staticvoid __maybe_unused
pmu_restore_counters(struct cci_pmu *cci_pmu, unsignedlong *mask)
{ int i;
/* * Returns the number of programmable counters actually implemented * by the cci
*/ static u32 pmu_get_max_counters(struct cci_pmu *cci_pmu)
{ return (readl_relaxed(cci_pmu->ctrl_base + CCI_PMCR) &
CCI_PMCR_NCNT_MASK) >> CCI_PMCR_NCNT_SHIFT;
}
if (cci_pmu->model->get_event_idx) return cci_pmu->model->get_event_idx(cci_pmu, hw, cci_event);
/* Generic code to find an unused idx from the mask */ for (idx = 0; idx <= CCI_PMU_CNTR_LAST(cci_pmu); idx++) if (!test_and_set_bit(idx, hw->used_mask)) return idx;
if (cci_pmu->nr_irqs < 1) {
dev_err(&pmu_device->dev, "no irqs for CCI PMUs defined\n"); return -ENODEV;
}
/* * Register all available CCI PMU interrupts. In the interrupt handler * we iterate over the counters checking for interrupt source (the * overflowing counter) and clear it. * * This should allow handling of non-unique interrupt for the counters.
*/ for (i = 0; i < cci_pmu->nr_irqs; i++) { int err = request_irq(cci_pmu->irqs[i], handler, IRQF_SHARED, "arm-cci-pmu", cci_pmu); if (err) {
dev_err(&pmu_device->dev, "unable to request IRQ%d for ARM CCI PMU counters\n",
cci_pmu->irqs[i]); return err;
}
set_bit(i, &cci_pmu->active_irqs);
}
return 0;
}
staticvoid pmu_free_irq(struct cci_pmu *cci_pmu)
{ int i;
for (i = 0; i < cci_pmu->nr_irqs; i++) { if (!test_and_clear_bit(i, &cci_pmu->active_irqs)) continue;
/* * CCI-500/CCI-550 has advanced power saving policies, which could gate the * clocks to the PMU counters, which makes the writes to them ineffective. * The only way to write to those counters is when the global counters * are enabled and the particular counter is enabled. * * So we do the following : * * 1) Disable all the PMU counters, saving their current state * 2) Enable the global PMU profiling, now that all counters are * disabled. * * For each counter to be programmed, repeat steps 3-7: * * 3) Write an invalid event code to the event control register for the counter, so that the counters are not modified. * 4) Enable the counter control for the counter. * 5) Set the counter value * 6) Disable the counter * 7) Restore the event in the target counter * * 8) Disable the global PMU. * 9) Restore the status of the rest of the counters. * * We choose an event which for CCI-5xx is guaranteed not to count. * We use the highest possible event code (0x1f) for the master interface 0.
*/ #define CCI5xx_INVALID_EVENT ((CCI5xx_PORT_M0 << CCI5xx_PMU_EVENT_SOURCE_SHIFT) | \
(CCI5xx_PMU_EVENT_CODE_MASK << CCI5xx_PMU_EVENT_CODE_SHIFT)) staticvoid cci5xx_pmu_write_counters(struct cci_pmu *cci_pmu, unsignedlong *mask)
{ int i;
DECLARE_BITMAP(saved_mask, HW_CNTRS_MAX);
/* * Now that all the counters are disabled, we can safely turn the PMU on, * without syncing the status of the counters
*/
__cci_pmu_enable_nosync(cci_pmu);
staticvoid pmu_event_set_period(struct perf_event *event)
{ struct hw_perf_event *hwc = &event->hw; /* * The CCI PMU counters have a period of 2^32. To account for the * possiblity of extreme interrupt latency we program for a period of * half that. Hopefully we can handle the interrupt before another 2^31 * events occur and the counter overtakes its previous value.
*/
u64 val = 1ULL << 31;
local64_set(&hwc->prev_count, val);
/* * CCI PMU uses PERF_HES_ARCH to keep track of the counters, whose * values needs to be sync-ed with the s/w state before the PMU is * enabled. * Mark this counter for sync.
*/
hwc->state |= PERF_HES_ARCH;
}
/* Disable the PMU while we walk through the counters */
__cci_pmu_disable(cci_pmu); /* * Iterate over counters and update the corresponding perf events. * This should work regardless of whether we have per-counter overflow * interrupt or a combined overflow interrupt.
*/ for (idx = 0; idx <= CCI_PMU_CNTR_LAST(cci_pmu); idx++) { struct perf_event *event = events->events[idx];
if (!event) continue;
/* Did this counter overflow? */ if (!(pmu_read_register(cci_pmu, idx, CCI_PMU_OVRFLW) &
CCI_PMU_OVRFLW_FLAG)) continue;
/* * Check if the idx represents a non-programmable counter. * All the fixed event counters are mapped before the programmable * counters.
*/ staticbool pmu_fixed_hw_idx(struct cci_pmu *cci_pmu, int idx)
{ return (idx >= 0) && (idx < cci_pmu->model->fixed_hw_cntrs);
}
/* * To handle interrupt latency, we always reprogram the period * regardless of PERF_EF_RELOAD.
*/ if (pmu_flags & PERF_EF_RELOAD)
WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
hwc->state = 0;
if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); return;
}
/* * Reject groups spanning multiple HW PMUs (e.g. CPU + CCI). The * core perf code won't check that the pmu->ctx == leader->ctx * until after pmu->event_init(event).
*/ if (event->pmu != cci_pmu) return 0;
if (event->state < PERF_EVENT_STATE_OFF) return 1;
if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec) return 1;
if (mapping < 0) {
pr_debug("event %x:%llx not supported\n", event->attr.type,
event->attr.config); return mapping;
}
/* * We don't assign an index until we actually place the event onto * hardware. Use -1 to signify that we haven't decided where to put it * yet.
*/
hwc->idx = -1;
hwc->config_base = 0;
hwc->config = 0;
hwc->event_base = 0;
/* * Store the event encoding into the config_base field.
*/
hwc->config_base |= (unsignedlong)mapping;
if (event->group_leader != event) { if (validate_group(event) != 0) return -EINVAL;
}
if (event->attr.type != event->pmu->type) return -ENOENT;
/* Shared by all CPUs, no meaningful state to sample */ if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) return -EOPNOTSUPP;
/* * Following the example set by other "uncore" PMUs, we accept any CPU * and rewrite its affinity dynamically rather than having perf core * handle cpu == -1 and pid == -1 for this case. * * The perf core will pin online CPUs for the duration of this call and * the event being installed into its context, so the PMU's CPU can't * change under our feet.
*/ if (event->cpu < 0) return -EINVAL;
event->cpu = cci_pmu->cpu;
event->destroy = hw_perf_event_destroy; if (!atomic_inc_not_zero(active_events)) {
mutex_lock(&cci_pmu->reserve_mutex); if (atomic_read(active_events) == 0)
err = cci_pmu_get_hw(cci_pmu); if (!err)
atomic_inc(active_events);
mutex_unlock(&cci_pmu->reserve_mutex);
} if (err) return err;
err = __hw_perf_event_init(event); if (err)
hw_perf_event_destroy(event);
/* * All allocations are devm_* hence we don't have to free * them explicitly on an error, as it would end up in driver * detach.
*/
cci_pmu = devm_kzalloc(dev, sizeof(*cci_pmu), GFP_KERNEL); if (!cci_pmu) return ERR_PTR(-ENOMEM);
model = of_device_get_match_data(dev); if (!model) {
dev_warn(dev, "DEPRECATED compatible property, requires secure access to CCI registers");
model = probe_cci_model(cci_pmu);
} if (!model) {
dev_warn(dev, "CCI PMU version not supported\n"); return ERR_PTR(-ENODEV);
}
staticint cci_pmu_probe(struct platform_device *pdev)
{ struct cci_pmu *cci_pmu; int i, ret, irq;
cci_pmu = cci_pmu_alloc(&pdev->dev); if (IS_ERR(cci_pmu)) return PTR_ERR(cci_pmu);
cci_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(cci_pmu->base)) return -ENOMEM;
/* * CCI PMU has one overflow interrupt per counter; but some may be tied * together to a common interrupt.
*/
cci_pmu->nr_irqs = 0; for (i = 0; i < CCI_PMU_MAX_HW_CNTRS(cci_pmu->model); i++) {
irq = platform_get_irq(pdev, i); if (irq < 0) break;
if (is_duplicate_irq(irq, cci_pmu->irqs, cci_pmu->nr_irqs)) continue;
cci_pmu->irqs[cci_pmu->nr_irqs++] = irq;
}
/* * Ensure that the device tree has as many interrupts as the number * of counters.
*/ if (i < CCI_PMU_MAX_HW_CNTRS(cci_pmu->model)) {
dev_warn(&pdev->dev, "In-correct number of interrupts: %d, should be %d\n",
i, CCI_PMU_MAX_HW_CNTRS(cci_pmu->model)); return -EINVAL;
}
module_platform_driver(cci_pmu_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ARM CCI PMU support");
Messung V0.5
¤ 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.0.13Bemerkung:
¤
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.