in arm-ccn.c [1220:1320]
static int arm_ccn_pmu_init(struct arm_ccn *ccn)
{
int i;
char *name;
int err;
/* Initialize DT subsystem */
ccn->dt.base = ccn->base + CCN_REGION_SIZE;
spin_lock_init(&ccn->dt.config_lock);
writel(CCN_DT_PMOVSR_CLR__MASK, ccn->dt.base + CCN_DT_PMOVSR_CLR);
writel(CCN_DT_CTL__DT_EN, ccn->dt.base + CCN_DT_CTL);
writel(CCN_DT_PMCR__OVFL_INTR_EN | CCN_DT_PMCR__PMU_EN,
ccn->dt.base + CCN_DT_PMCR);
writel(0x1, ccn->dt.base + CCN_DT_PMSR_CLR);
for (i = 0; i < ccn->num_xps; i++) {
writel(0, ccn->xp[i].base + CCN_XP_DT_CONFIG);
writel((CCN_XP_DT_CONTROL__WP_ARM_SEL__ALWAYS <<
CCN_XP_DT_CONTROL__WP_ARM_SEL__SHIFT(0)) |
(CCN_XP_DT_CONTROL__WP_ARM_SEL__ALWAYS <<
CCN_XP_DT_CONTROL__WP_ARM_SEL__SHIFT(1)) |
CCN_XP_DT_CONTROL__DT_ENABLE,
ccn->xp[i].base + CCN_XP_DT_CONTROL);
}
ccn->dt.cmp_mask[CCN_IDX_MASK_ANY].l = ~0;
ccn->dt.cmp_mask[CCN_IDX_MASK_ANY].h = ~0;
ccn->dt.cmp_mask[CCN_IDX_MASK_EXACT].l = 0;
ccn->dt.cmp_mask[CCN_IDX_MASK_EXACT].h = 0;
ccn->dt.cmp_mask[CCN_IDX_MASK_ORDER].l = ~0;
ccn->dt.cmp_mask[CCN_IDX_MASK_ORDER].h = ~(0x1 << 15);
ccn->dt.cmp_mask[CCN_IDX_MASK_OPCODE].l = ~0;
ccn->dt.cmp_mask[CCN_IDX_MASK_OPCODE].h = ~(0x1f << 9);
/* Get a convenient /sys/event_source/devices/ name */
ccn->dt.id = ida_simple_get(&arm_ccn_pmu_ida, 0, 0, GFP_KERNEL);
if (ccn->dt.id == 0) {
name = "ccn";
} else {
name = devm_kasprintf(ccn->dev, GFP_KERNEL, "ccn_%d",
ccn->dt.id);
if (!name) {
err = -ENOMEM;
goto error_choose_name;
}
}
/* Perf driver registration */
ccn->dt.pmu = (struct pmu) {
.module = THIS_MODULE,
.attr_groups = arm_ccn_pmu_attr_groups,
.task_ctx_nr = perf_invalid_context,
.event_init = arm_ccn_pmu_event_init,
.add = arm_ccn_pmu_event_add,
.del = arm_ccn_pmu_event_del,
.start = arm_ccn_pmu_event_start,
.stop = arm_ccn_pmu_event_stop,
.read = arm_ccn_pmu_event_read,
.pmu_enable = arm_ccn_pmu_enable,
.pmu_disable = arm_ccn_pmu_disable,
.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
};
/* No overflow interrupt? Have to use a timer instead. */
if (!ccn->irq) {
dev_info(ccn->dev, "No access to interrupts, using timer.\n");
hrtimer_init(&ccn->dt.hrtimer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
ccn->dt.hrtimer.function = arm_ccn_pmu_timer_handler;
}
/* Pick one CPU which we will use to collect data from CCN... */
ccn->dt.cpu = raw_smp_processor_id();
/* Also make sure that the overflow interrupt is handled by this CPU */
if (ccn->irq) {
err = irq_set_affinity(ccn->irq, cpumask_of(ccn->dt.cpu));
if (err) {
dev_err(ccn->dev, "Failed to set interrupt affinity!\n");
goto error_set_affinity;
}
}
cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
&ccn->dt.node);
err = perf_pmu_register(&ccn->dt.pmu, name, -1);
if (err)
goto error_pmu_register;
return 0;
error_pmu_register:
cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
&ccn->dt.node);
error_set_affinity:
error_choose_name:
ida_simple_remove(&arm_ccn_pmu_ida, ccn->dt.id);
for (i = 0; i < ccn->num_xps; i++)
writel(0, ccn->xp[i].base + CCN_XP_DT_CONTROL);
writel(0, ccn->dt.base + CCN_DT_PMCR);
return err;
}