18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * L220/L310 cache controller support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 ARM Limited
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/errno.h>
88c2ecf20Sopenharmony_ci#include <linux/hrtimer.h>
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/list.h>
118c2ecf20Sopenharmony_ci#include <linux/perf_event.h>
128c2ecf20Sopenharmony_ci#include <linux/printk.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/types.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <asm/hardware/cache-l2x0.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define PMU_NR_COUNTERS 2
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic void __iomem *l2x0_base;
218c2ecf20Sopenharmony_cistatic struct pmu *l2x0_pmu;
228c2ecf20Sopenharmony_cistatic cpumask_t pmu_cpu;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic const char *l2x0_name;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic ktime_t l2x0_pmu_poll_period;
278c2ecf20Sopenharmony_cistatic struct hrtimer l2x0_pmu_hrtimer;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * The L220/PL310 PMU has two equivalent counters, Counter1 and Counter0.
318c2ecf20Sopenharmony_ci * Registers controlling these are laid out in pairs, in descending order, i.e.
328c2ecf20Sopenharmony_ci * the register for Counter1 comes first, followed by the register for
338c2ecf20Sopenharmony_ci * Counter0.
348c2ecf20Sopenharmony_ci * We ensure that idx 0 -> Counter0, and idx1 -> Counter1.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_cistatic struct perf_event *events[PMU_NR_COUNTERS];
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Find an unused counter */
398c2ecf20Sopenharmony_cistatic int l2x0_pmu_find_idx(void)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	int i;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	for (i = 0; i < PMU_NR_COUNTERS; i++) {
448c2ecf20Sopenharmony_ci		if (!events[i])
458c2ecf20Sopenharmony_ci			return i;
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return -1;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* How many counters are allocated? */
528c2ecf20Sopenharmony_cistatic int l2x0_pmu_num_active_counters(void)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	int i, cnt = 0;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	for (i = 0; i < PMU_NR_COUNTERS; i++) {
578c2ecf20Sopenharmony_ci		if (events[i])
588c2ecf20Sopenharmony_ci			cnt++;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return cnt;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void l2x0_pmu_counter_config_write(int idx, u32 val)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	writel_relaxed(val, l2x0_base + L2X0_EVENT_CNT0_CFG - 4 * idx);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic u32 l2x0_pmu_counter_read(int idx)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	return readl_relaxed(l2x0_base + L2X0_EVENT_CNT0_VAL - 4 * idx);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void l2x0_pmu_counter_write(int idx, u32 val)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	writel_relaxed(val, l2x0_base + L2X0_EVENT_CNT0_VAL - 4 * idx);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void __l2x0_pmu_enable(void)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	u32 val = readl_relaxed(l2x0_base + L2X0_EVENT_CNT_CTRL);
828c2ecf20Sopenharmony_ci	val |= L2X0_EVENT_CNT_CTRL_ENABLE;
838c2ecf20Sopenharmony_ci	writel_relaxed(val, l2x0_base + L2X0_EVENT_CNT_CTRL);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic void __l2x0_pmu_disable(void)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	u32 val = readl_relaxed(l2x0_base + L2X0_EVENT_CNT_CTRL);
898c2ecf20Sopenharmony_ci	val &= ~L2X0_EVENT_CNT_CTRL_ENABLE;
908c2ecf20Sopenharmony_ci	writel_relaxed(val, l2x0_base + L2X0_EVENT_CNT_CTRL);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void l2x0_pmu_enable(struct pmu *pmu)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	if (l2x0_pmu_num_active_counters() == 0)
968c2ecf20Sopenharmony_ci		return;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	__l2x0_pmu_enable();
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void l2x0_pmu_disable(struct pmu *pmu)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	if (l2x0_pmu_num_active_counters() == 0)
1048c2ecf20Sopenharmony_ci		return;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	__l2x0_pmu_disable();
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic void warn_if_saturated(u32 count)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	if (count != 0xffffffff)
1128c2ecf20Sopenharmony_ci		return;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	pr_warn_ratelimited("L2X0 counter saturated. Poll period too long\n");
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic void l2x0_pmu_event_read(struct perf_event *event)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct hw_perf_event *hw = &event->hw;
1208c2ecf20Sopenharmony_ci	u64 prev_count, new_count, mask;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	do {
1238c2ecf20Sopenharmony_ci		 prev_count = local64_read(&hw->prev_count);
1248c2ecf20Sopenharmony_ci		 new_count = l2x0_pmu_counter_read(hw->idx);
1258c2ecf20Sopenharmony_ci	} while (local64_xchg(&hw->prev_count, new_count) != prev_count);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	mask = GENMASK_ULL(31, 0);
1288c2ecf20Sopenharmony_ci	local64_add((new_count - prev_count) & mask, &event->count);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	warn_if_saturated(new_count);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic void l2x0_pmu_event_configure(struct perf_event *event)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct hw_perf_event *hw = &event->hw;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/*
1388c2ecf20Sopenharmony_ci	 * The L2X0 counters saturate at 0xffffffff rather than wrapping, so we
1398c2ecf20Sopenharmony_ci	 * will *always* lose some number of events when a counter saturates,
1408c2ecf20Sopenharmony_ci	 * and have no way of detecting how many were lost.
1418c2ecf20Sopenharmony_ci	 *
1428c2ecf20Sopenharmony_ci	 * To minimize the impact of this, we try to maximize the period by
1438c2ecf20Sopenharmony_ci	 * always starting counters at zero. To ensure that group ratios are
1448c2ecf20Sopenharmony_ci	 * representative, we poll periodically to avoid counters saturating.
1458c2ecf20Sopenharmony_ci	 * See l2x0_pmu_poll().
1468c2ecf20Sopenharmony_ci	 */
1478c2ecf20Sopenharmony_ci	local64_set(&hw->prev_count, 0);
1488c2ecf20Sopenharmony_ci	l2x0_pmu_counter_write(hw->idx, 0);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic enum hrtimer_restart l2x0_pmu_poll(struct hrtimer *hrtimer)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	unsigned long flags;
1548c2ecf20Sopenharmony_ci	int i;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	local_irq_save(flags);
1578c2ecf20Sopenharmony_ci	__l2x0_pmu_disable();
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	for (i = 0; i < PMU_NR_COUNTERS; i++) {
1608c2ecf20Sopenharmony_ci		struct perf_event *event = events[i];
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci		if (!event)
1638c2ecf20Sopenharmony_ci			continue;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		l2x0_pmu_event_read(event);
1668c2ecf20Sopenharmony_ci		l2x0_pmu_event_configure(event);
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	__l2x0_pmu_enable();
1708c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	hrtimer_forward_now(hrtimer, l2x0_pmu_poll_period);
1738c2ecf20Sopenharmony_ci	return HRTIMER_RESTART;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic void __l2x0_pmu_event_enable(int idx, u32 event)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	u32 val;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	val = event << L2X0_EVENT_CNT_CFG_SRC_SHIFT;
1828c2ecf20Sopenharmony_ci	val |= L2X0_EVENT_CNT_CFG_INT_DISABLED;
1838c2ecf20Sopenharmony_ci	l2x0_pmu_counter_config_write(idx, val);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic void l2x0_pmu_event_start(struct perf_event *event, int flags)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct hw_perf_event *hw = &event->hw;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
1918c2ecf20Sopenharmony_ci		return;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (flags & PERF_EF_RELOAD) {
1948c2ecf20Sopenharmony_ci		WARN_ON_ONCE(!(hw->state & PERF_HES_UPTODATE));
1958c2ecf20Sopenharmony_ci		l2x0_pmu_event_configure(event);
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	hw->state = 0;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	__l2x0_pmu_event_enable(hw->idx, hw->config_base);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic void __l2x0_pmu_event_disable(int idx)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	u32 val;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	val = L2X0_EVENT_CNT_CFG_SRC_DISABLED << L2X0_EVENT_CNT_CFG_SRC_SHIFT;
2088c2ecf20Sopenharmony_ci	val |= L2X0_EVENT_CNT_CFG_INT_DISABLED;
2098c2ecf20Sopenharmony_ci	l2x0_pmu_counter_config_write(idx, val);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic void l2x0_pmu_event_stop(struct perf_event *event, int flags)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct hw_perf_event *hw = &event->hw;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(event->hw.state & PERF_HES_STOPPED))
2178c2ecf20Sopenharmony_ci		return;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	__l2x0_pmu_event_disable(hw->idx);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	hw->state |= PERF_HES_STOPPED;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (flags & PERF_EF_UPDATE) {
2248c2ecf20Sopenharmony_ci		l2x0_pmu_event_read(event);
2258c2ecf20Sopenharmony_ci		hw->state |= PERF_HES_UPTODATE;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int l2x0_pmu_event_add(struct perf_event *event, int flags)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct hw_perf_event *hw = &event->hw;
2328c2ecf20Sopenharmony_ci	int idx = l2x0_pmu_find_idx();
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (idx == -1)
2358c2ecf20Sopenharmony_ci		return -EAGAIN;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/*
2388c2ecf20Sopenharmony_ci	 * Pin the timer, so that the overflows are handled by the chosen
2398c2ecf20Sopenharmony_ci	 * event->cpu (this is the same one as presented in "cpumask"
2408c2ecf20Sopenharmony_ci	 * attribute).
2418c2ecf20Sopenharmony_ci	 */
2428c2ecf20Sopenharmony_ci	if (l2x0_pmu_num_active_counters() == 0)
2438c2ecf20Sopenharmony_ci		hrtimer_start(&l2x0_pmu_hrtimer, l2x0_pmu_poll_period,
2448c2ecf20Sopenharmony_ci			      HRTIMER_MODE_REL_PINNED);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	events[idx] = event;
2478c2ecf20Sopenharmony_ci	hw->idx = idx;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	l2x0_pmu_event_configure(event);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	hw->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (flags & PERF_EF_START)
2548c2ecf20Sopenharmony_ci		l2x0_pmu_event_start(event, 0);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return 0;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic void l2x0_pmu_event_del(struct perf_event *event, int flags)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct hw_perf_event *hw = &event->hw;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	l2x0_pmu_event_stop(event, PERF_EF_UPDATE);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	events[hw->idx] = NULL;
2668c2ecf20Sopenharmony_ci	hw->idx = -1;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (l2x0_pmu_num_active_counters() == 0)
2698c2ecf20Sopenharmony_ci		hrtimer_cancel(&l2x0_pmu_hrtimer);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic bool l2x0_pmu_group_is_valid(struct perf_event *event)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct pmu *pmu = event->pmu;
2758c2ecf20Sopenharmony_ci	struct perf_event *leader = event->group_leader;
2768c2ecf20Sopenharmony_ci	struct perf_event *sibling;
2778c2ecf20Sopenharmony_ci	int num_hw = 0;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (leader->pmu == pmu)
2808c2ecf20Sopenharmony_ci		num_hw++;
2818c2ecf20Sopenharmony_ci	else if (!is_software_event(leader))
2828c2ecf20Sopenharmony_ci		return false;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	for_each_sibling_event(sibling, leader) {
2858c2ecf20Sopenharmony_ci		if (sibling->pmu == pmu)
2868c2ecf20Sopenharmony_ci			num_hw++;
2878c2ecf20Sopenharmony_ci		else if (!is_software_event(sibling))
2888c2ecf20Sopenharmony_ci			return false;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return num_hw <= PMU_NR_COUNTERS;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int l2x0_pmu_event_init(struct perf_event *event)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct hw_perf_event *hw = &event->hw;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (event->attr.type != l2x0_pmu->type)
2998c2ecf20Sopenharmony_ci		return -ENOENT;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (is_sampling_event(event) ||
3028c2ecf20Sopenharmony_ci	    event->attach_state & PERF_ATTACH_TASK)
3038c2ecf20Sopenharmony_ci		return -EINVAL;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (event->cpu < 0)
3068c2ecf20Sopenharmony_ci		return -EINVAL;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	if (event->attr.config & ~L2X0_EVENT_CNT_CFG_SRC_MASK)
3098c2ecf20Sopenharmony_ci		return -EINVAL;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	hw->config_base = event->attr.config;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (!l2x0_pmu_group_is_valid(event))
3148c2ecf20Sopenharmony_ci		return -EINVAL;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	event->cpu = cpumask_first(&pmu_cpu);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistruct l2x0_event_attribute {
3228c2ecf20Sopenharmony_ci	struct device_attribute attr;
3238c2ecf20Sopenharmony_ci	unsigned int config;
3248c2ecf20Sopenharmony_ci	bool pl310_only;
3258c2ecf20Sopenharmony_ci};
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci#define L2X0_EVENT_ATTR(_name, _config, _pl310_only)				\
3288c2ecf20Sopenharmony_ci	(&((struct l2x0_event_attribute[]) {{					\
3298c2ecf20Sopenharmony_ci		.attr = __ATTR(_name, S_IRUGO, l2x0_pmu_event_show, NULL),	\
3308c2ecf20Sopenharmony_ci		.config = _config,						\
3318c2ecf20Sopenharmony_ci		.pl310_only = _pl310_only,					\
3328c2ecf20Sopenharmony_ci	}})[0].attr.attr)
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci#define L220_PLUS_EVENT_ATTR(_name, _config)					\
3358c2ecf20Sopenharmony_ci	L2X0_EVENT_ATTR(_name, _config, false)
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci#define PL310_EVENT_ATTR(_name, _config)					\
3388c2ecf20Sopenharmony_ci	L2X0_EVENT_ATTR(_name, _config, true)
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic ssize_t l2x0_pmu_event_show(struct device *dev,
3418c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct l2x0_event_attribute *lattr;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	lattr = container_of(attr, typeof(*lattr), attr);
3468c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "config=0x%x\n", lattr->config);
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic umode_t l2x0_pmu_event_attr_is_visible(struct kobject *kobj,
3508c2ecf20Sopenharmony_ci					      struct attribute *attr,
3518c2ecf20Sopenharmony_ci					      int unused)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
3548c2ecf20Sopenharmony_ci	struct pmu *pmu = dev_get_drvdata(dev);
3558c2ecf20Sopenharmony_ci	struct l2x0_event_attribute *lattr;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	lattr = container_of(attr, typeof(*lattr), attr.attr);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (!lattr->pl310_only || strcmp("l2c_310", pmu->name) == 0)
3608c2ecf20Sopenharmony_ci		return attr->mode;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	return 0;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic struct attribute *l2x0_pmu_event_attrs[] = {
3668c2ecf20Sopenharmony_ci	L220_PLUS_EVENT_ATTR(co,	0x1),
3678c2ecf20Sopenharmony_ci	L220_PLUS_EVENT_ATTR(drhit,	0x2),
3688c2ecf20Sopenharmony_ci	L220_PLUS_EVENT_ATTR(drreq,	0x3),
3698c2ecf20Sopenharmony_ci	L220_PLUS_EVENT_ATTR(dwhit,	0x4),
3708c2ecf20Sopenharmony_ci	L220_PLUS_EVENT_ATTR(dwreq,	0x5),
3718c2ecf20Sopenharmony_ci	L220_PLUS_EVENT_ATTR(dwtreq,	0x6),
3728c2ecf20Sopenharmony_ci	L220_PLUS_EVENT_ATTR(irhit,	0x7),
3738c2ecf20Sopenharmony_ci	L220_PLUS_EVENT_ATTR(irreq,	0x8),
3748c2ecf20Sopenharmony_ci	L220_PLUS_EVENT_ATTR(wa,	0x9),
3758c2ecf20Sopenharmony_ci	PL310_EVENT_ATTR(ipfalloc,	0xa),
3768c2ecf20Sopenharmony_ci	PL310_EVENT_ATTR(epfhit,	0xb),
3778c2ecf20Sopenharmony_ci	PL310_EVENT_ATTR(epfalloc,	0xc),
3788c2ecf20Sopenharmony_ci	PL310_EVENT_ATTR(srrcvd,	0xd),
3798c2ecf20Sopenharmony_ci	PL310_EVENT_ATTR(srconf,	0xe),
3808c2ecf20Sopenharmony_ci	PL310_EVENT_ATTR(epfrcvd,	0xf),
3818c2ecf20Sopenharmony_ci	NULL
3828c2ecf20Sopenharmony_ci};
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic struct attribute_group l2x0_pmu_event_attrs_group = {
3858c2ecf20Sopenharmony_ci	.name = "events",
3868c2ecf20Sopenharmony_ci	.attrs = l2x0_pmu_event_attrs,
3878c2ecf20Sopenharmony_ci	.is_visible = l2x0_pmu_event_attr_is_visible,
3888c2ecf20Sopenharmony_ci};
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic ssize_t l2x0_pmu_cpumask_show(struct device *dev,
3918c2ecf20Sopenharmony_ci				     struct device_attribute *attr, char *buf)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	return cpumap_print_to_pagebuf(true, buf, &pmu_cpu);
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic struct device_attribute l2x0_pmu_cpumask_attr =
3978c2ecf20Sopenharmony_ci		__ATTR(cpumask, S_IRUGO, l2x0_pmu_cpumask_show, NULL);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic struct attribute *l2x0_pmu_cpumask_attrs[] = {
4008c2ecf20Sopenharmony_ci	&l2x0_pmu_cpumask_attr.attr,
4018c2ecf20Sopenharmony_ci	NULL,
4028c2ecf20Sopenharmony_ci};
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic struct attribute_group l2x0_pmu_cpumask_attr_group = {
4058c2ecf20Sopenharmony_ci	.attrs = l2x0_pmu_cpumask_attrs,
4068c2ecf20Sopenharmony_ci};
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic const struct attribute_group *l2x0_pmu_attr_groups[] = {
4098c2ecf20Sopenharmony_ci	&l2x0_pmu_event_attrs_group,
4108c2ecf20Sopenharmony_ci	&l2x0_pmu_cpumask_attr_group,
4118c2ecf20Sopenharmony_ci	NULL,
4128c2ecf20Sopenharmony_ci};
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic void l2x0_pmu_reset(void)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	int i;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	__l2x0_pmu_disable();
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	for (i = 0; i < PMU_NR_COUNTERS; i++)
4218c2ecf20Sopenharmony_ci		__l2x0_pmu_event_disable(i);
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic int l2x0_pmu_offline_cpu(unsigned int cpu)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	unsigned int target;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (!cpumask_test_and_clear_cpu(cpu, &pmu_cpu))
4298c2ecf20Sopenharmony_ci		return 0;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	target = cpumask_any_but(cpu_online_mask, cpu);
4328c2ecf20Sopenharmony_ci	if (target >= nr_cpu_ids)
4338c2ecf20Sopenharmony_ci		return 0;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	perf_pmu_migrate_context(l2x0_pmu, cpu, target);
4368c2ecf20Sopenharmony_ci	cpumask_set_cpu(target, &pmu_cpu);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return 0;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_civoid l2x0_pmu_suspend(void)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	int i;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (!l2x0_pmu)
4468c2ecf20Sopenharmony_ci		return;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	l2x0_pmu_disable(l2x0_pmu);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	for (i = 0; i < PMU_NR_COUNTERS; i++) {
4518c2ecf20Sopenharmony_ci		if (events[i])
4528c2ecf20Sopenharmony_ci			l2x0_pmu_event_stop(events[i], PERF_EF_UPDATE);
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_civoid l2x0_pmu_resume(void)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	int i;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (!l2x0_pmu)
4628c2ecf20Sopenharmony_ci		return;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	l2x0_pmu_reset();
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	for (i = 0; i < PMU_NR_COUNTERS; i++) {
4678c2ecf20Sopenharmony_ci		if (events[i])
4688c2ecf20Sopenharmony_ci			l2x0_pmu_event_start(events[i], PERF_EF_RELOAD);
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	l2x0_pmu_enable(l2x0_pmu);
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_civoid __init l2x0_pmu_register(void __iomem *base, u32 part)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	/*
4778c2ecf20Sopenharmony_ci	 * Determine whether we support the PMU, and choose the name for sysfs.
4788c2ecf20Sopenharmony_ci	 * This is also used by l2x0_pmu_event_attr_is_visible to determine
4798c2ecf20Sopenharmony_ci	 * which events to display, as the PL310 PMU supports a superset of
4808c2ecf20Sopenharmony_ci	 * L220 events.
4818c2ecf20Sopenharmony_ci	 *
4828c2ecf20Sopenharmony_ci	 * The L210 PMU has a different programmer's interface, and is not
4838c2ecf20Sopenharmony_ci	 * supported by this driver.
4848c2ecf20Sopenharmony_ci	 *
4858c2ecf20Sopenharmony_ci	 * We must defer registering the PMU until the perf subsystem is up and
4868c2ecf20Sopenharmony_ci	 * running, so just stash the name and base, and leave that to another
4878c2ecf20Sopenharmony_ci	 * initcall.
4888c2ecf20Sopenharmony_ci	 */
4898c2ecf20Sopenharmony_ci	switch (part & L2X0_CACHE_ID_PART_MASK) {
4908c2ecf20Sopenharmony_ci	case L2X0_CACHE_ID_PART_L220:
4918c2ecf20Sopenharmony_ci		l2x0_name = "l2c_220";
4928c2ecf20Sopenharmony_ci		break;
4938c2ecf20Sopenharmony_ci	case L2X0_CACHE_ID_PART_L310:
4948c2ecf20Sopenharmony_ci		l2x0_name = "l2c_310";
4958c2ecf20Sopenharmony_ci		break;
4968c2ecf20Sopenharmony_ci	default:
4978c2ecf20Sopenharmony_ci		return;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	l2x0_base = base;
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistatic __init int l2x0_pmu_init(void)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	int ret;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (!l2x0_base)
5088c2ecf20Sopenharmony_ci		return 0;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	l2x0_pmu = kzalloc(sizeof(*l2x0_pmu), GFP_KERNEL);
5118c2ecf20Sopenharmony_ci	if (!l2x0_pmu) {
5128c2ecf20Sopenharmony_ci		pr_warn("Unable to allocate L2x0 PMU\n");
5138c2ecf20Sopenharmony_ci		return -ENOMEM;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	*l2x0_pmu = (struct pmu) {
5178c2ecf20Sopenharmony_ci		.task_ctx_nr = perf_invalid_context,
5188c2ecf20Sopenharmony_ci		.pmu_enable = l2x0_pmu_enable,
5198c2ecf20Sopenharmony_ci		.pmu_disable = l2x0_pmu_disable,
5208c2ecf20Sopenharmony_ci		.read = l2x0_pmu_event_read,
5218c2ecf20Sopenharmony_ci		.start = l2x0_pmu_event_start,
5228c2ecf20Sopenharmony_ci		.stop = l2x0_pmu_event_stop,
5238c2ecf20Sopenharmony_ci		.add = l2x0_pmu_event_add,
5248c2ecf20Sopenharmony_ci		.del = l2x0_pmu_event_del,
5258c2ecf20Sopenharmony_ci		.event_init = l2x0_pmu_event_init,
5268c2ecf20Sopenharmony_ci		.attr_groups = l2x0_pmu_attr_groups,
5278c2ecf20Sopenharmony_ci		.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
5288c2ecf20Sopenharmony_ci	};
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	l2x0_pmu_reset();
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	/*
5338c2ecf20Sopenharmony_ci	 * We always use a hrtimer rather than an interrupt.
5348c2ecf20Sopenharmony_ci	 * See comments in l2x0_pmu_event_configure and l2x0_pmu_poll.
5358c2ecf20Sopenharmony_ci	 *
5368c2ecf20Sopenharmony_ci	 * Polling once a second allows the counters to fill up to 1/128th on a
5378c2ecf20Sopenharmony_ci	 * quad-core test chip with cores clocked at 400MHz. Hopefully this
5388c2ecf20Sopenharmony_ci	 * leaves sufficient headroom to avoid overflow on production silicon
5398c2ecf20Sopenharmony_ci	 * at higher frequencies.
5408c2ecf20Sopenharmony_ci	 */
5418c2ecf20Sopenharmony_ci	l2x0_pmu_poll_period = ms_to_ktime(1000);
5428c2ecf20Sopenharmony_ci	hrtimer_init(&l2x0_pmu_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
5438c2ecf20Sopenharmony_ci	l2x0_pmu_hrtimer.function = l2x0_pmu_poll;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	cpumask_set_cpu(0, &pmu_cpu);
5468c2ecf20Sopenharmony_ci	ret = cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_L2X0_ONLINE,
5478c2ecf20Sopenharmony_ci					"perf/arm/l2x0:online", NULL,
5488c2ecf20Sopenharmony_ci					l2x0_pmu_offline_cpu);
5498c2ecf20Sopenharmony_ci	if (ret)
5508c2ecf20Sopenharmony_ci		goto out_pmu;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	ret = perf_pmu_register(l2x0_pmu, l2x0_name, -1);
5538c2ecf20Sopenharmony_ci	if (ret)
5548c2ecf20Sopenharmony_ci		goto out_cpuhp;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	return 0;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ciout_cpuhp:
5598c2ecf20Sopenharmony_ci	cpuhp_remove_state_nocalls(CPUHP_AP_PERF_ARM_L2X0_ONLINE);
5608c2ecf20Sopenharmony_ciout_pmu:
5618c2ecf20Sopenharmony_ci	kfree(l2x0_pmu);
5628c2ecf20Sopenharmony_ci	l2x0_pmu = NULL;
5638c2ecf20Sopenharmony_ci	return ret;
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_cidevice_initcall(l2x0_pmu_init);
566