static int arm_ccn_pmu_init()

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;
}