Commit 823437ed authored by Will Deacon's avatar Will Deacon
Browse files

Merge branch 'perf/m1-guest-events' of...

Merge branch 'perf/m1-guest-events' of git://git.kernel.org/pub/scm/linux/kernel/git/oupton/linux into for-next/perf

Pull Apple-M1 PMU driver changes from Oliver Upton, which form a prefix
of the series in the KVM/Arm tree that allows the PMU to be virtualised.
Sort of, anyway.

* 'perf/m1-guest-events' of git://git.kernel.org/pub/scm/linux/kernel/git/oupton/linux:
  drivers/perf: apple_m1: Support host/guest event filtering
  drivers/perf: apple_m1: Refactor event select/filter configuration
parents 7f35b429 46573d94
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@
#define PMCR0_PMI_ENABLE_8_9	GENMASK(45, 44)

#define SYS_IMP_APL_PMCR1_EL1	sys_reg(3, 1, 15, 1, 0)
#define SYS_IMP_APL_PMCR1_EL12	sys_reg(3, 1, 15, 7, 2)
#define PMCR1_COUNT_A64_EL0_0_7	GENMASK(15, 8)
#define PMCR1_COUNT_A64_EL1_0_7	GENMASK(23, 16)
#define PMCR1_COUNT_A64_EL0_8_9	GENMASK(41, 40)
+45 −21
Original line number Diff line number Diff line
@@ -120,6 +120,8 @@ enum m1_pmu_events {
	 */
	M1_PMU_CFG_COUNT_USER					= BIT(8),
	M1_PMU_CFG_COUNT_KERNEL					= BIT(9),
	M1_PMU_CFG_COUNT_HOST					= BIT(10),
	M1_PMU_CFG_COUNT_GUEST					= BIT(11),
};

/*
@@ -327,11 +329,10 @@ static void m1_pmu_disable_counter_interrupt(unsigned int index)
	__m1_pmu_enable_counter_interrupt(index, false);
}

static void m1_pmu_configure_counter(unsigned int index, u8 event,
				     bool user, bool kernel)
static void __m1_pmu_configure_event_filter(unsigned int index, bool user,
					    bool kernel, bool host)
{
	u64 val, user_bit, kernel_bit;
	int shift;
	u64 clear, set, user_bit, kernel_bit;

	switch (index) {
	case 0 ... 7:
@@ -346,19 +347,27 @@ static void m1_pmu_configure_counter(unsigned int index, u8 event,
		BUG();
	}

	val = read_sysreg_s(SYS_IMP_APL_PMCR1_EL1);

	clear = set = 0;
	if (user)
		val |= user_bit;
		set |= user_bit;
	else
		val &= ~user_bit;
		clear |= user_bit;

	if (kernel)
		val |= kernel_bit;
		set |= kernel_bit;
	else
		val &= ~kernel_bit;
		clear |= kernel_bit;

	write_sysreg_s(val, SYS_IMP_APL_PMCR1_EL1);
	if (host)
		sysreg_clear_set_s(SYS_IMP_APL_PMCR1_EL1, clear, set);
	else if (is_kernel_in_hyp_mode())
		sysreg_clear_set_s(SYS_IMP_APL_PMCR1_EL12, clear, set);
}

static void __m1_pmu_configure_eventsel(unsigned int index, u8 event)
{
	u64 clear = 0, set = 0;
	int shift;

	/*
	 * Counters 0 and 1 have fixed events. For anything else,
@@ -371,21 +380,32 @@ static void m1_pmu_configure_counter(unsigned int index, u8 event,
		break;
	case 2 ... 5:
		shift = (index - 2) * 8;
		val = read_sysreg_s(SYS_IMP_APL_PMESR0_EL1);
		val &= ~((u64)0xff << shift);
		val |= (u64)event << shift;
		write_sysreg_s(val, SYS_IMP_APL_PMESR0_EL1);
		clear |= (u64)0xff << shift;
		set |= (u64)event << shift;
		sysreg_clear_set_s(SYS_IMP_APL_PMESR0_EL1, clear, set);
		break;
	case 6 ... 9:
		shift = (index - 6) * 8;
		val = read_sysreg_s(SYS_IMP_APL_PMESR1_EL1);
		val &= ~((u64)0xff << shift);
		val |= (u64)event << shift;
		write_sysreg_s(val, SYS_IMP_APL_PMESR1_EL1);
		clear |= (u64)0xff << shift;
		set |= (u64)event << shift;
		sysreg_clear_set_s(SYS_IMP_APL_PMESR1_EL1, clear, set);
		break;
	}
}

static void m1_pmu_configure_counter(unsigned int index, unsigned long config_base)
{
	bool kernel = config_base & M1_PMU_CFG_COUNT_KERNEL;
	bool guest = config_base & M1_PMU_CFG_COUNT_GUEST;
	bool host = config_base & M1_PMU_CFG_COUNT_HOST;
	bool user = config_base & M1_PMU_CFG_COUNT_USER;
	u8 evt = config_base & M1_PMU_CFG_EVENT;

	__m1_pmu_configure_event_filter(index, user && host, kernel && host, true);
	__m1_pmu_configure_event_filter(index, user && guest, kernel && guest, false);
	__m1_pmu_configure_eventsel(index, evt);
}

/* arm_pmu backend */
static void m1_pmu_enable_event(struct perf_event *event)
{
@@ -396,7 +416,7 @@ static void m1_pmu_enable_event(struct perf_event *event)
	user = event->hw.config_base & M1_PMU_CFG_COUNT_USER;
	kernel = event->hw.config_base & M1_PMU_CFG_COUNT_KERNEL;

	m1_pmu_configure_counter(event->hw.idx, evt, user, kernel);
	m1_pmu_configure_counter(event->hw.idx, event->hw.config_base);
	m1_pmu_enable_counter(event->hw.idx);
	m1_pmu_enable_counter_interrupt(event->hw.idx);
	isb();
@@ -554,7 +574,7 @@ static int m1_pmu_set_event_filter(struct hw_perf_event *event,
{
	unsigned long config_base = 0;

	if (!attr->exclude_guest) {
	if (!attr->exclude_guest && !is_kernel_in_hyp_mode()) {
		pr_debug("ARM performance counters do not support mode exclusion\n");
		return -EOPNOTSUPP;
	}
@@ -562,6 +582,10 @@ static int m1_pmu_set_event_filter(struct hw_perf_event *event,
		config_base |= M1_PMU_CFG_COUNT_KERNEL;
	if (!attr->exclude_user)
		config_base |= M1_PMU_CFG_COUNT_USER;
	if (!attr->exclude_host)
		config_base |= M1_PMU_CFG_COUNT_HOST;
	if (!attr->exclude_guest)
		config_base |= M1_PMU_CFG_COUNT_GUEST;

	event->config_base = config_base;