18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Copyright (C) 2016-2020 Arm Limited
38c2ecf20Sopenharmony_ci// CMN-600 Coherent Mesh Network PMU driver
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/acpi.h>
68c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
78c2ecf20Sopenharmony_ci#include <linux/bitops.h>
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/list.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/perf_event.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/sort.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* Common register stuff */
208c2ecf20Sopenharmony_ci#define CMN_NODE_INFO			0x0000
218c2ecf20Sopenharmony_ci#define CMN_NI_NODE_TYPE		GENMASK_ULL(15, 0)
228c2ecf20Sopenharmony_ci#define CMN_NI_NODE_ID			GENMASK_ULL(31, 16)
238c2ecf20Sopenharmony_ci#define CMN_NI_LOGICAL_ID		GENMASK_ULL(47, 32)
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define CMN_NODEID_DEVID(reg)		((reg) & 3)
268c2ecf20Sopenharmony_ci#define CMN_NODEID_PID(reg)		(((reg) >> 2) & 1)
278c2ecf20Sopenharmony_ci#define CMN_NODEID_X(reg, bits)		((reg) >> (3 + (bits)))
288c2ecf20Sopenharmony_ci#define CMN_NODEID_Y(reg, bits)		(((reg) >> 3) & ((1U << (bits)) - 1))
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define CMN_CHILD_INFO			0x0080
318c2ecf20Sopenharmony_ci#define CMN_CI_CHILD_COUNT		GENMASK_ULL(15, 0)
328c2ecf20Sopenharmony_ci#define CMN_CI_CHILD_PTR_OFFSET		GENMASK_ULL(31, 16)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define CMN_CHILD_NODE_ADDR		GENMASK(27,0)
358c2ecf20Sopenharmony_ci#define CMN_CHILD_NODE_EXTERNAL		BIT(31)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define CMN_ADDR_NODE_PTR		GENMASK(27, 14)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define CMN_NODE_PTR_DEVID(ptr)		(((ptr) >> 2) & 3)
408c2ecf20Sopenharmony_ci#define CMN_NODE_PTR_PID(ptr)		((ptr) & 1)
418c2ecf20Sopenharmony_ci#define CMN_NODE_PTR_X(ptr, bits)	((ptr) >> (6 + (bits)))
428c2ecf20Sopenharmony_ci#define CMN_NODE_PTR_Y(ptr, bits)	(((ptr) >> 6) & ((1U << (bits)) - 1))
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define CMN_MAX_XPS			(8 * 8)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* The CFG node has one other useful purpose */
478c2ecf20Sopenharmony_ci#define CMN_CFGM_PERIPH_ID_2		0x0010
488c2ecf20Sopenharmony_ci#define CMN_CFGM_PID2_REVISION		GENMASK(7, 4)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* PMU registers occupy the 3rd 4KB page of each node's 16KB space */
518c2ecf20Sopenharmony_ci#define CMN_PMU_OFFSET			0x2000
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* For most nodes, this is all there is */
548c2ecf20Sopenharmony_ci#define CMN_PMU_EVENT_SEL		0x000
558c2ecf20Sopenharmony_ci#define CMN_PMU_EVENTn_ID_SHIFT(n)	((n) * 8)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* DTMs live in the PMU space of XP registers */
588c2ecf20Sopenharmony_ci#define CMN_DTM_WPn(n)			(0x1A0 + (n) * 0x18)
598c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG(n)		(CMN_DTM_WPn(n) + 0x00)
608c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG_WP_COMBINE	BIT(6)
618c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG_WP_EXCLUSIVE	BIT(5)
628c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG_WP_GRP	BIT(4)
638c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG_WP_CHN_SEL	GENMASK_ULL(3, 1)
648c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG_WP_DEV_SEL	BIT(0)
658c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_VAL(n)		(CMN_DTM_WPn(n) + 0x08)
668c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_MASK(n)		(CMN_DTM_WPn(n) + 0x10)
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define CMN_DTM_PMU_CONFIG		0x210
698c2ecf20Sopenharmony_ci#define CMN__PMEVCNT0_INPUT_SEL		GENMASK_ULL(37, 32)
708c2ecf20Sopenharmony_ci#define CMN__PMEVCNT0_INPUT_SEL_WP	0x00
718c2ecf20Sopenharmony_ci#define CMN__PMEVCNT0_INPUT_SEL_XP	0x04
728c2ecf20Sopenharmony_ci#define CMN__PMEVCNT0_INPUT_SEL_DEV	0x10
738c2ecf20Sopenharmony_ci#define CMN__PMEVCNT0_GLOBAL_NUM	GENMASK_ULL(18, 16)
748c2ecf20Sopenharmony_ci#define CMN__PMEVCNTn_GLOBAL_NUM_SHIFT(n)	((n) * 4)
758c2ecf20Sopenharmony_ci#define CMN__PMEVCNT_PAIRED(n)		BIT(4 + (n))
768c2ecf20Sopenharmony_ci#define CMN__PMEVCNT23_COMBINED		BIT(2)
778c2ecf20Sopenharmony_ci#define CMN__PMEVCNT01_COMBINED		BIT(1)
788c2ecf20Sopenharmony_ci#define CMN_DTM_PMU_CONFIG_PMU_EN	BIT(0)
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define CMN_DTM_PMEVCNT			0x220
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define CMN_DTM_PMEVCNTSR		0x240
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#define CMN_DTM_NUM_COUNTERS		4
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/* The DTC node is where the magic happens */
878c2ecf20Sopenharmony_ci#define CMN_DT_DTC_CTL			0x0a00
888c2ecf20Sopenharmony_ci#define CMN_DT_DTC_CTL_DT_EN		BIT(0)
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* DTC counters are paired in 64-bit registers on a 16-byte stride. Yuck */
918c2ecf20Sopenharmony_ci#define _CMN_DT_CNT_REG(n)		((((n) / 2) * 4 + (n) % 2) * 4)
928c2ecf20Sopenharmony_ci#define CMN_DT_PMEVCNT(n)		(CMN_PMU_OFFSET + _CMN_DT_CNT_REG(n))
938c2ecf20Sopenharmony_ci#define CMN_DT_PMCCNTR			(CMN_PMU_OFFSET + 0x40)
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define CMN_DT_PMEVCNTSR(n)		(CMN_PMU_OFFSET + 0x50 + _CMN_DT_CNT_REG(n))
968c2ecf20Sopenharmony_ci#define CMN_DT_PMCCNTRSR		(CMN_PMU_OFFSET + 0x90)
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define CMN_DT_PMCR			(CMN_PMU_OFFSET + 0x100)
998c2ecf20Sopenharmony_ci#define CMN_DT_PMCR_PMU_EN		BIT(0)
1008c2ecf20Sopenharmony_ci#define CMN_DT_PMCR_CNTR_RST		BIT(5)
1018c2ecf20Sopenharmony_ci#define CMN_DT_PMCR_OVFL_INTR_EN	BIT(6)
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define CMN_DT_PMOVSR			(CMN_PMU_OFFSET + 0x118)
1048c2ecf20Sopenharmony_ci#define CMN_DT_PMOVSR_CLR		(CMN_PMU_OFFSET + 0x120)
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci#define CMN_DT_PMSSR			(CMN_PMU_OFFSET + 0x128)
1078c2ecf20Sopenharmony_ci#define CMN_DT_PMSSR_SS_STATUS(n)	BIT(n)
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci#define CMN_DT_PMSRR			(CMN_PMU_OFFSET + 0x130)
1108c2ecf20Sopenharmony_ci#define CMN_DT_PMSRR_SS_REQ		BIT(0)
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci#define CMN_DT_NUM_COUNTERS		8
1138c2ecf20Sopenharmony_ci#define CMN_MAX_DTCS			4
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/*
1168c2ecf20Sopenharmony_ci * Even in the worst case a DTC counter can't wrap in fewer than 2^42 cycles,
1178c2ecf20Sopenharmony_ci * so throwing away one bit to make overflow handling easy is no big deal.
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_ci#define CMN_COUNTER_INIT		0x80000000
1208c2ecf20Sopenharmony_ci/* Similarly for the 40-bit cycle counter */
1218c2ecf20Sopenharmony_ci#define CMN_CC_INIT			0x8000000000ULL
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/* Event attributes */
1258c2ecf20Sopenharmony_ci#define CMN_CONFIG_TYPE			GENMASK(15, 0)
1268c2ecf20Sopenharmony_ci#define CMN_CONFIG_EVENTID		GENMASK(23, 16)
1278c2ecf20Sopenharmony_ci#define CMN_CONFIG_OCCUPID		GENMASK(27, 24)
1288c2ecf20Sopenharmony_ci#define CMN_CONFIG_BYNODEID		BIT(31)
1298c2ecf20Sopenharmony_ci#define CMN_CONFIG_NODEID		GENMASK(47, 32)
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci#define CMN_EVENT_TYPE(event)		FIELD_GET(CMN_CONFIG_TYPE, (event)->attr.config)
1328c2ecf20Sopenharmony_ci#define CMN_EVENT_EVENTID(event)	FIELD_GET(CMN_CONFIG_EVENTID, (event)->attr.config)
1338c2ecf20Sopenharmony_ci#define CMN_EVENT_OCCUPID(event)	FIELD_GET(CMN_CONFIG_OCCUPID, (event)->attr.config)
1348c2ecf20Sopenharmony_ci#define CMN_EVENT_BYNODEID(event)	FIELD_GET(CMN_CONFIG_BYNODEID, (event)->attr.config)
1358c2ecf20Sopenharmony_ci#define CMN_EVENT_NODEID(event)		FIELD_GET(CMN_CONFIG_NODEID, (event)->attr.config)
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci#define CMN_CONFIG_WP_COMBINE		GENMASK(27, 24)
1388c2ecf20Sopenharmony_ci#define CMN_CONFIG_WP_DEV_SEL		BIT(48)
1398c2ecf20Sopenharmony_ci#define CMN_CONFIG_WP_CHN_SEL		GENMASK(50, 49)
1408c2ecf20Sopenharmony_ci#define CMN_CONFIG_WP_GRP		BIT(52)
1418c2ecf20Sopenharmony_ci#define CMN_CONFIG_WP_EXCLUSIVE		BIT(53)
1428c2ecf20Sopenharmony_ci#define CMN_CONFIG1_WP_VAL		GENMASK(63, 0)
1438c2ecf20Sopenharmony_ci#define CMN_CONFIG2_WP_MASK		GENMASK(63, 0)
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_COMBINE(event)	FIELD_GET(CMN_CONFIG_WP_COMBINE, (event)->attr.config)
1468c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_DEV_SEL(event)	FIELD_GET(CMN_CONFIG_WP_DEV_SEL, (event)->attr.config)
1478c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_CHN_SEL(event)	FIELD_GET(CMN_CONFIG_WP_CHN_SEL, (event)->attr.config)
1488c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_GRP(event)		FIELD_GET(CMN_CONFIG_WP_GRP, (event)->attr.config)
1498c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_EXCLUSIVE(event)	FIELD_GET(CMN_CONFIG_WP_EXCLUSIVE, (event)->attr.config)
1508c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_VAL(event)		FIELD_GET(CMN_CONFIG1_WP_VAL, (event)->attr.config1)
1518c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_MASK(event)	FIELD_GET(CMN_CONFIG2_WP_MASK, (event)->attr.config2)
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/* Made-up event IDs for watchpoint direction */
1548c2ecf20Sopenharmony_ci#define CMN_WP_UP			0
1558c2ecf20Sopenharmony_ci#define CMN_WP_DOWN			2
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/* r0px probably don't exist in silicon, thankfully */
1598c2ecf20Sopenharmony_cienum cmn_revision {
1608c2ecf20Sopenharmony_ci	CMN600_R1P0,
1618c2ecf20Sopenharmony_ci	CMN600_R1P1,
1628c2ecf20Sopenharmony_ci	CMN600_R1P2,
1638c2ecf20Sopenharmony_ci	CMN600_R1P3,
1648c2ecf20Sopenharmony_ci	CMN600_R2P0,
1658c2ecf20Sopenharmony_ci	CMN600_R3P0,
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cienum cmn_node_type {
1698c2ecf20Sopenharmony_ci	CMN_TYPE_INVALID,
1708c2ecf20Sopenharmony_ci	CMN_TYPE_DVM,
1718c2ecf20Sopenharmony_ci	CMN_TYPE_CFG,
1728c2ecf20Sopenharmony_ci	CMN_TYPE_DTC,
1738c2ecf20Sopenharmony_ci	CMN_TYPE_HNI,
1748c2ecf20Sopenharmony_ci	CMN_TYPE_HNF,
1758c2ecf20Sopenharmony_ci	CMN_TYPE_XP,
1768c2ecf20Sopenharmony_ci	CMN_TYPE_SBSX,
1778c2ecf20Sopenharmony_ci	CMN_TYPE_RNI = 0xa,
1788c2ecf20Sopenharmony_ci	CMN_TYPE_RND = 0xd,
1798c2ecf20Sopenharmony_ci	CMN_TYPE_RNSAM = 0xf,
1808c2ecf20Sopenharmony_ci	CMN_TYPE_CXRA = 0x100,
1818c2ecf20Sopenharmony_ci	CMN_TYPE_CXHA = 0x101,
1828c2ecf20Sopenharmony_ci	CMN_TYPE_CXLA = 0x102,
1838c2ecf20Sopenharmony_ci	/* Not a real node type */
1848c2ecf20Sopenharmony_ci	CMN_TYPE_WP = 0x7770
1858c2ecf20Sopenharmony_ci};
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistruct arm_cmn_node {
1888c2ecf20Sopenharmony_ci	void __iomem *pmu_base;
1898c2ecf20Sopenharmony_ci	u16 id, logid;
1908c2ecf20Sopenharmony_ci	enum cmn_node_type type;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	union {
1938c2ecf20Sopenharmony_ci		/* Device node */
1948c2ecf20Sopenharmony_ci		struct {
1958c2ecf20Sopenharmony_ci			int to_xp;
1968c2ecf20Sopenharmony_ci			/* DN/HN-F/CXHA */
1978c2ecf20Sopenharmony_ci			unsigned int occupid_val;
1988c2ecf20Sopenharmony_ci			unsigned int occupid_count;
1998c2ecf20Sopenharmony_ci		};
2008c2ecf20Sopenharmony_ci		/* XP */
2018c2ecf20Sopenharmony_ci		struct {
2028c2ecf20Sopenharmony_ci			int dtc;
2038c2ecf20Sopenharmony_ci			u32 pmu_config_low;
2048c2ecf20Sopenharmony_ci			union {
2058c2ecf20Sopenharmony_ci				u8 input_sel[4];
2068c2ecf20Sopenharmony_ci				__le32 pmu_config_high;
2078c2ecf20Sopenharmony_ci			};
2088c2ecf20Sopenharmony_ci			s8 wp_event[4];
2098c2ecf20Sopenharmony_ci		};
2108c2ecf20Sopenharmony_ci	};
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	union {
2138c2ecf20Sopenharmony_ci		u8 event[4];
2148c2ecf20Sopenharmony_ci		__le32 event_sel;
2158c2ecf20Sopenharmony_ci	};
2168c2ecf20Sopenharmony_ci};
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistruct arm_cmn_dtc {
2198c2ecf20Sopenharmony_ci	void __iomem *base;
2208c2ecf20Sopenharmony_ci	int irq;
2218c2ecf20Sopenharmony_ci	int irq_friend;
2228c2ecf20Sopenharmony_ci	bool cc_active;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	struct perf_event *counters[CMN_DT_NUM_COUNTERS];
2258c2ecf20Sopenharmony_ci	struct perf_event *cycles;
2268c2ecf20Sopenharmony_ci};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci#define CMN_STATE_DISABLED	BIT(0)
2298c2ecf20Sopenharmony_ci#define CMN_STATE_TXN		BIT(1)
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistruct arm_cmn {
2328c2ecf20Sopenharmony_ci	struct device *dev;
2338c2ecf20Sopenharmony_ci	void __iomem *base;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	enum cmn_revision rev;
2368c2ecf20Sopenharmony_ci	u8 mesh_x;
2378c2ecf20Sopenharmony_ci	u8 mesh_y;
2388c2ecf20Sopenharmony_ci	u16 num_xps;
2398c2ecf20Sopenharmony_ci	u16 num_dns;
2408c2ecf20Sopenharmony_ci	struct arm_cmn_node *xps;
2418c2ecf20Sopenharmony_ci	struct arm_cmn_node *dns;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	struct arm_cmn_dtc *dtc;
2448c2ecf20Sopenharmony_ci	unsigned int num_dtcs;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	int cpu;
2478c2ecf20Sopenharmony_ci	struct hlist_node cpuhp_node;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	unsigned int state;
2508c2ecf20Sopenharmony_ci	struct pmu pmu;
2518c2ecf20Sopenharmony_ci};
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci#define to_cmn(p)	container_of(p, struct arm_cmn, pmu)
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int arm_cmn_hp_state;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistruct arm_cmn_hw_event {
2588c2ecf20Sopenharmony_ci	struct arm_cmn_node *dn;
2598c2ecf20Sopenharmony_ci	u64 dtm_idx[2];
2608c2ecf20Sopenharmony_ci	unsigned int dtc_idx;
2618c2ecf20Sopenharmony_ci	u8 dtcs_used;
2628c2ecf20Sopenharmony_ci	u8 num_dns;
2638c2ecf20Sopenharmony_ci};
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci#define for_each_hw_dn(hw, dn, i) \
2668c2ecf20Sopenharmony_ci	for (i = 0, dn = hw->dn; i < hw->num_dns; i++, dn++)
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic struct arm_cmn_hw_event *to_cmn_hw(struct perf_event *event)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct arm_cmn_hw_event) > offsetof(struct hw_perf_event, target));
2718c2ecf20Sopenharmony_ci	return (struct arm_cmn_hw_event *)&event->hw;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void arm_cmn_set_index(u64 x[], unsigned int pos, unsigned int val)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	x[pos / 32] |= (u64)val << ((pos % 32) * 2);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic unsigned int arm_cmn_get_index(u64 x[], unsigned int pos)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	return (x[pos / 32] >> ((pos % 32) * 2)) & 3;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistruct arm_cmn_event_attr {
2858c2ecf20Sopenharmony_ci	struct device_attribute attr;
2868c2ecf20Sopenharmony_ci	enum cmn_node_type type;
2878c2ecf20Sopenharmony_ci	u8 eventid;
2888c2ecf20Sopenharmony_ci	u8 occupid;
2898c2ecf20Sopenharmony_ci};
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistruct arm_cmn_format_attr {
2928c2ecf20Sopenharmony_ci	struct device_attribute attr;
2938c2ecf20Sopenharmony_ci	u64 field;
2948c2ecf20Sopenharmony_ci	int config;
2958c2ecf20Sopenharmony_ci};
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int arm_cmn_xyidbits(const struct arm_cmn *cmn)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	return cmn->mesh_x > 4 || cmn->mesh_y > 4 ? 3 : 2;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic void arm_cmn_init_node_to_xp(const struct arm_cmn *cmn,
3038c2ecf20Sopenharmony_ci				    struct arm_cmn_node *dn)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	int bits = arm_cmn_xyidbits(cmn);
3068c2ecf20Sopenharmony_ci	int x = CMN_NODEID_X(dn->id, bits);
3078c2ecf20Sopenharmony_ci	int y = CMN_NODEID_Y(dn->id, bits);
3088c2ecf20Sopenharmony_ci	int xp_idx = cmn->mesh_x * y + x;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	dn->to_xp = (cmn->xps + xp_idx) - dn;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic struct arm_cmn_node *arm_cmn_node_to_xp(struct arm_cmn_node *dn)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	return dn->type == CMN_TYPE_XP ? dn : dn + dn->to_xp;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic struct arm_cmn_node *arm_cmn_node(const struct arm_cmn *cmn,
3198c2ecf20Sopenharmony_ci					 enum cmn_node_type type)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	int i;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	for (i = 0; i < cmn->num_dns; i++)
3248c2ecf20Sopenharmony_ci		if (cmn->dns[i].type == type)
3258c2ecf20Sopenharmony_ci			return &cmn->dns[i];
3268c2ecf20Sopenharmony_ci	return NULL;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci#define CMN_EVENT_ATTR(_name, _type, _eventid, _occupid)		\
3308c2ecf20Sopenharmony_ci	(&((struct arm_cmn_event_attr[]) {{				\
3318c2ecf20Sopenharmony_ci		.attr = __ATTR(_name, 0444, arm_cmn_event_show, NULL),	\
3328c2ecf20Sopenharmony_ci		.type = _type,						\
3338c2ecf20Sopenharmony_ci		.eventid = _eventid,					\
3348c2ecf20Sopenharmony_ci		.occupid = _occupid,					\
3358c2ecf20Sopenharmony_ci	}})[0].attr.attr)
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic bool arm_cmn_is_occup_event(enum cmn_node_type type, unsigned int id)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	return (type == CMN_TYPE_DVM && id == 0x05) ||
3408c2ecf20Sopenharmony_ci	       (type == CMN_TYPE_HNF && id == 0x0f);
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic ssize_t arm_cmn_event_show(struct device *dev,
3448c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct arm_cmn_event_attr *eattr;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	eattr = container_of(attr, typeof(*eattr), attr);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (eattr->type == CMN_TYPE_DTC)
3518c2ecf20Sopenharmony_ci		return snprintf(buf, PAGE_SIZE, "type=0x%x\n", eattr->type);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (eattr->type == CMN_TYPE_WP)
3548c2ecf20Sopenharmony_ci		return snprintf(buf, PAGE_SIZE,
3558c2ecf20Sopenharmony_ci				"type=0x%x,eventid=0x%x,wp_dev_sel=?,wp_chn_sel=?,wp_grp=?,wp_val=?,wp_mask=?\n",
3568c2ecf20Sopenharmony_ci				eattr->type, eattr->eventid);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (arm_cmn_is_occup_event(eattr->type, eattr->eventid))
3598c2ecf20Sopenharmony_ci		return snprintf(buf, PAGE_SIZE, "type=0x%x,eventid=0x%x,occupid=0x%x\n",
3608c2ecf20Sopenharmony_ci				eattr->type, eattr->eventid, eattr->occupid);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "type=0x%x,eventid=0x%x\n",
3638c2ecf20Sopenharmony_ci			eattr->type, eattr->eventid);
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
3678c2ecf20Sopenharmony_ci					     struct attribute *attr,
3688c2ecf20Sopenharmony_ci					     int unused)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
3718c2ecf20Sopenharmony_ci	struct arm_cmn *cmn = to_cmn(dev_get_drvdata(dev));
3728c2ecf20Sopenharmony_ci	struct arm_cmn_event_attr *eattr;
3738c2ecf20Sopenharmony_ci	enum cmn_node_type type;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	eattr = container_of(attr, typeof(*eattr), attr.attr);
3768c2ecf20Sopenharmony_ci	type = eattr->type;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* Watchpoints aren't nodes */
3798c2ecf20Sopenharmony_ci	if (type == CMN_TYPE_WP)
3808c2ecf20Sopenharmony_ci		type = CMN_TYPE_XP;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/* Revision-specific differences */
3838c2ecf20Sopenharmony_ci	if (cmn->rev < CMN600_R1P2) {
3848c2ecf20Sopenharmony_ci		if (type == CMN_TYPE_HNF && eattr->eventid == 0x1b)
3858c2ecf20Sopenharmony_ci			return 0;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (!arm_cmn_node(cmn, type))
3898c2ecf20Sopenharmony_ci		return 0;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return attr->mode;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci#define _CMN_EVENT_DVM(_name, _event, _occup)			\
3958c2ecf20Sopenharmony_ci	CMN_EVENT_ATTR(dn_##_name, CMN_TYPE_DVM, _event, _occup)
3968c2ecf20Sopenharmony_ci#define CMN_EVENT_DTC(_name)					\
3978c2ecf20Sopenharmony_ci	CMN_EVENT_ATTR(dtc_##_name, CMN_TYPE_DTC, 0, 0)
3988c2ecf20Sopenharmony_ci#define _CMN_EVENT_HNF(_name, _event, _occup)			\
3998c2ecf20Sopenharmony_ci	CMN_EVENT_ATTR(hnf_##_name, CMN_TYPE_HNF, _event, _occup)
4008c2ecf20Sopenharmony_ci#define CMN_EVENT_HNI(_name, _event)				\
4018c2ecf20Sopenharmony_ci	CMN_EVENT_ATTR(hni_##_name, CMN_TYPE_HNI, _event, 0)
4028c2ecf20Sopenharmony_ci#define __CMN_EVENT_XP(_name, _event)				\
4038c2ecf20Sopenharmony_ci	CMN_EVENT_ATTR(mxp_##_name, CMN_TYPE_XP, _event, 0)
4048c2ecf20Sopenharmony_ci#define CMN_EVENT_SBSX(_name, _event)				\
4058c2ecf20Sopenharmony_ci	CMN_EVENT_ATTR(sbsx_##_name, CMN_TYPE_SBSX, _event, 0)
4068c2ecf20Sopenharmony_ci#define CMN_EVENT_RNID(_name, _event)				\
4078c2ecf20Sopenharmony_ci	CMN_EVENT_ATTR(rnid_##_name, CMN_TYPE_RNI, _event, 0)
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci#define CMN_EVENT_DVM(_name, _event)				\
4108c2ecf20Sopenharmony_ci	_CMN_EVENT_DVM(_name, _event, 0)
4118c2ecf20Sopenharmony_ci#define CMN_EVENT_HNF(_name, _event)				\
4128c2ecf20Sopenharmony_ci	_CMN_EVENT_HNF(_name, _event, 0)
4138c2ecf20Sopenharmony_ci#define _CMN_EVENT_XP(_name, _event)				\
4148c2ecf20Sopenharmony_ci	__CMN_EVENT_XP(e_##_name, (_event) | (0 << 2)),		\
4158c2ecf20Sopenharmony_ci	__CMN_EVENT_XP(w_##_name, (_event) | (1 << 2)),		\
4168c2ecf20Sopenharmony_ci	__CMN_EVENT_XP(n_##_name, (_event) | (2 << 2)),		\
4178c2ecf20Sopenharmony_ci	__CMN_EVENT_XP(s_##_name, (_event) | (3 << 2)),		\
4188c2ecf20Sopenharmony_ci	__CMN_EVENT_XP(p0_##_name, (_event) | (4 << 2)),	\
4198c2ecf20Sopenharmony_ci	__CMN_EVENT_XP(p1_##_name, (_event) | (5 << 2))
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci/* Good thing there are only 3 fundamental XP events... */
4228c2ecf20Sopenharmony_ci#define CMN_EVENT_XP(_name, _event)				\
4238c2ecf20Sopenharmony_ci	_CMN_EVENT_XP(req_##_name, (_event) | (0 << 5)),	\
4248c2ecf20Sopenharmony_ci	_CMN_EVENT_XP(rsp_##_name, (_event) | (1 << 5)),	\
4258c2ecf20Sopenharmony_ci	_CMN_EVENT_XP(snp_##_name, (_event) | (2 << 5)),	\
4268c2ecf20Sopenharmony_ci	_CMN_EVENT_XP(dat_##_name, (_event) | (3 << 5))
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic struct attribute *arm_cmn_event_attrs[] = {
4308c2ecf20Sopenharmony_ci	CMN_EVENT_DTC(cycles),
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/*
4338c2ecf20Sopenharmony_ci	 * DVM node events conflict with HN-I events in the equivalent PMU
4348c2ecf20Sopenharmony_ci	 * slot, but our lazy short-cut of using the DTM counter index for
4358c2ecf20Sopenharmony_ci	 * the PMU index as well happens to avoid that by construction.
4368c2ecf20Sopenharmony_ci	 */
4378c2ecf20Sopenharmony_ci	CMN_EVENT_DVM(rxreq_dvmop,	0x01),
4388c2ecf20Sopenharmony_ci	CMN_EVENT_DVM(rxreq_dvmsync,	0x02),
4398c2ecf20Sopenharmony_ci	CMN_EVENT_DVM(rxreq_dvmop_vmid_filtered, 0x03),
4408c2ecf20Sopenharmony_ci	CMN_EVENT_DVM(rxreq_retried,	0x04),
4418c2ecf20Sopenharmony_ci	_CMN_EVENT_DVM(rxreq_trk_occupancy_all, 0x05, 0),
4428c2ecf20Sopenharmony_ci	_CMN_EVENT_DVM(rxreq_trk_occupancy_dvmop, 0x05, 1),
4438c2ecf20Sopenharmony_ci	_CMN_EVENT_DVM(rxreq_trk_occupancy_dvmsync, 0x05, 2),
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(cache_miss,	0x01),
4468c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(slc_sf_cache_access, 0x02),
4478c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(cache_fill,	0x03),
4488c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(pocq_retry,	0x04),
4498c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(pocq_reqs_recvd,	0x05),
4508c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(sf_hit,		0x06),
4518c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(sf_evictions,	0x07),
4528c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(dir_snoops_sent,	0x08),
4538c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(brd_snoops_sent,	0x09),
4548c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(slc_eviction,	0x0a),
4558c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(slc_fill_invalid_way, 0x0b),
4568c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(mc_retries,	0x0c),
4578c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(mc_reqs,		0x0d),
4588c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(qos_hh_retry,	0x0e),
4598c2ecf20Sopenharmony_ci	_CMN_EVENT_HNF(qos_pocq_occupancy_all, 0x0f, 0),
4608c2ecf20Sopenharmony_ci	_CMN_EVENT_HNF(qos_pocq_occupancy_read, 0x0f, 1),
4618c2ecf20Sopenharmony_ci	_CMN_EVENT_HNF(qos_pocq_occupancy_write, 0x0f, 2),
4628c2ecf20Sopenharmony_ci	_CMN_EVENT_HNF(qos_pocq_occupancy_atomic, 0x0f, 3),
4638c2ecf20Sopenharmony_ci	_CMN_EVENT_HNF(qos_pocq_occupancy_stash, 0x0f, 4),
4648c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(pocq_addrhaz,	0x10),
4658c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(pocq_atomic_addrhaz, 0x11),
4668c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(ld_st_swp_adq_full, 0x12),
4678c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(cmp_adq_full,	0x13),
4688c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(txdat_stall,	0x14),
4698c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(txrsp_stall,	0x15),
4708c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(seq_full,		0x16),
4718c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(seq_hit,		0x17),
4728c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(snp_sent,		0x18),
4738c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(sfbi_dir_snp_sent, 0x19),
4748c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(sfbi_brd_snp_sent, 0x1a),
4758c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(snp_sent_untrk,	0x1b),
4768c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(intv_dirty,	0x1c),
4778c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(stash_snp_sent,	0x1d),
4788c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(stash_data_pull,	0x1e),
4798c2ecf20Sopenharmony_ci	CMN_EVENT_HNF(snp_fwded,	0x1f),
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(rrt_rd_occ_cnt_ovfl, 0x20),
4828c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(rrt_wr_occ_cnt_ovfl, 0x21),
4838c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(rdt_rd_occ_cnt_ovfl, 0x22),
4848c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(rdt_wr_occ_cnt_ovfl, 0x23),
4858c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(wdb_occ_cnt_ovfl,	0x24),
4868c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(rrt_rd_alloc,	0x25),
4878c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(rrt_wr_alloc,	0x26),
4888c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(rdt_rd_alloc,	0x27),
4898c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(rdt_wr_alloc,	0x28),
4908c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(wdb_alloc,	0x29),
4918c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(txrsp_retryack,	0x2a),
4928c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(arvalid_no_arready, 0x2b),
4938c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(arready_no_arvalid, 0x2c),
4948c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(awvalid_no_awready, 0x2d),
4958c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(awready_no_awvalid, 0x2e),
4968c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(wvalid_no_wready,	0x2f),
4978c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(txdat_stall,	0x30),
4988c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(nonpcie_serialization, 0x31),
4998c2ecf20Sopenharmony_ci	CMN_EVENT_HNI(pcie_serialization, 0x32),
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	CMN_EVENT_XP(txflit_valid,	0x01),
5028c2ecf20Sopenharmony_ci	CMN_EVENT_XP(txflit_stall,	0x02),
5038c2ecf20Sopenharmony_ci	CMN_EVENT_XP(partial_dat_flit,	0x03),
5048c2ecf20Sopenharmony_ci	/* We treat watchpoints as a special made-up class of XP events */
5058c2ecf20Sopenharmony_ci	CMN_EVENT_ATTR(watchpoint_up, CMN_TYPE_WP, 0, 0),
5068c2ecf20Sopenharmony_ci	CMN_EVENT_ATTR(watchpoint_down, CMN_TYPE_WP, 2, 0),
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(rd_req,		0x01),
5098c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(wr_req,		0x02),
5108c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(cmo_req,		0x03),
5118c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(txrsp_retryack,	0x04),
5128c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(txdat_flitv,	0x05),
5138c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(txrsp_flitv,	0x06),
5148c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(rd_req_trkr_occ_cnt_ovfl, 0x11),
5158c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(wr_req_trkr_occ_cnt_ovfl, 0x12),
5168c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(cmo_req_trkr_occ_cnt_ovfl, 0x13),
5178c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(wdb_occ_cnt_ovfl, 0x14),
5188c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(rd_axi_trkr_occ_cnt_ovfl, 0x15),
5198c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(cmo_axi_trkr_occ_cnt_ovfl, 0x16),
5208c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(arvalid_no_arready, 0x21),
5218c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(awvalid_no_awready, 0x22),
5228c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(wvalid_no_wready, 0x23),
5238c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(txdat_stall,	0x24),
5248c2ecf20Sopenharmony_ci	CMN_EVENT_SBSX(txrsp_stall,	0x25),
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(s0_rdata_beats,	0x01),
5278c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(s1_rdata_beats,	0x02),
5288c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(s2_rdata_beats,	0x03),
5298c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(rxdat_flits,	0x04),
5308c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(txdat_flits,	0x05),
5318c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(txreq_flits_total, 0x06),
5328c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(txreq_flits_retried, 0x07),
5338c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(rrt_occ_ovfl,	0x08),
5348c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(wrt_occ_ovfl,	0x09),
5358c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(txreq_flits_replayed, 0x0a),
5368c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(wrcancel_sent,	0x0b),
5378c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(s0_wdata_beats,	0x0c),
5388c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(s1_wdata_beats,	0x0d),
5398c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(s2_wdata_beats,	0x0e),
5408c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(rrt_alloc,	0x0f),
5418c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(wrt_alloc,	0x10),
5428c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(rdb_unord,	0x11),
5438c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(rdb_replay,	0x12),
5448c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(rdb_hybrid,	0x13),
5458c2ecf20Sopenharmony_ci	CMN_EVENT_RNID(rdb_ord,		0x14),
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	NULL
5488c2ecf20Sopenharmony_ci};
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic const struct attribute_group arm_cmn_event_attrs_group = {
5518c2ecf20Sopenharmony_ci	.name = "events",
5528c2ecf20Sopenharmony_ci	.attrs = arm_cmn_event_attrs,
5538c2ecf20Sopenharmony_ci	.is_visible = arm_cmn_event_attr_is_visible,
5548c2ecf20Sopenharmony_ci};
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic ssize_t arm_cmn_format_show(struct device *dev,
5578c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	struct arm_cmn_format_attr *fmt = container_of(attr, typeof(*fmt), attr);
5608c2ecf20Sopenharmony_ci	int lo = __ffs(fmt->field), hi = __fls(fmt->field);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (lo == hi)
5638c2ecf20Sopenharmony_ci		return snprintf(buf, PAGE_SIZE, "config:%d\n", lo);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if (!fmt->config)
5668c2ecf20Sopenharmony_ci		return snprintf(buf, PAGE_SIZE, "config:%d-%d\n", lo, hi);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "config%d:%d-%d\n", fmt->config, lo, hi);
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci#define _CMN_FORMAT_ATTR(_name, _cfg, _fld)				\
5728c2ecf20Sopenharmony_ci	(&((struct arm_cmn_format_attr[]) {{				\
5738c2ecf20Sopenharmony_ci		.attr = __ATTR(_name, 0444, arm_cmn_format_show, NULL),	\
5748c2ecf20Sopenharmony_ci		.config = _cfg,						\
5758c2ecf20Sopenharmony_ci		.field = _fld,						\
5768c2ecf20Sopenharmony_ci	}})[0].attr.attr)
5778c2ecf20Sopenharmony_ci#define CMN_FORMAT_ATTR(_name, _fld)	_CMN_FORMAT_ATTR(_name, 0, _fld)
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic struct attribute *arm_cmn_format_attrs[] = {
5808c2ecf20Sopenharmony_ci	CMN_FORMAT_ATTR(type, CMN_CONFIG_TYPE),
5818c2ecf20Sopenharmony_ci	CMN_FORMAT_ATTR(eventid, CMN_CONFIG_EVENTID),
5828c2ecf20Sopenharmony_ci	CMN_FORMAT_ATTR(occupid, CMN_CONFIG_OCCUPID),
5838c2ecf20Sopenharmony_ci	CMN_FORMAT_ATTR(bynodeid, CMN_CONFIG_BYNODEID),
5848c2ecf20Sopenharmony_ci	CMN_FORMAT_ATTR(nodeid, CMN_CONFIG_NODEID),
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	CMN_FORMAT_ATTR(wp_dev_sel, CMN_CONFIG_WP_DEV_SEL),
5878c2ecf20Sopenharmony_ci	CMN_FORMAT_ATTR(wp_chn_sel, CMN_CONFIG_WP_CHN_SEL),
5888c2ecf20Sopenharmony_ci	CMN_FORMAT_ATTR(wp_grp, CMN_CONFIG_WP_GRP),
5898c2ecf20Sopenharmony_ci	CMN_FORMAT_ATTR(wp_exclusive, CMN_CONFIG_WP_EXCLUSIVE),
5908c2ecf20Sopenharmony_ci	CMN_FORMAT_ATTR(wp_combine, CMN_CONFIG_WP_COMBINE),
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	_CMN_FORMAT_ATTR(wp_val, 1, CMN_CONFIG1_WP_VAL),
5938c2ecf20Sopenharmony_ci	_CMN_FORMAT_ATTR(wp_mask, 2, CMN_CONFIG2_WP_MASK),
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	NULL
5968c2ecf20Sopenharmony_ci};
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic const struct attribute_group arm_cmn_format_attrs_group = {
5998c2ecf20Sopenharmony_ci	.name = "format",
6008c2ecf20Sopenharmony_ci	.attrs = arm_cmn_format_attrs,
6018c2ecf20Sopenharmony_ci};
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic ssize_t arm_cmn_cpumask_show(struct device *dev,
6048c2ecf20Sopenharmony_ci				    struct device_attribute *attr, char *buf)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct arm_cmn *cmn = to_cmn(dev_get_drvdata(dev));
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	return cpumap_print_to_pagebuf(true, buf, cpumask_of(cmn->cpu));
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_cistatic struct device_attribute arm_cmn_cpumask_attr =
6128c2ecf20Sopenharmony_ci		__ATTR(cpumask, 0444, arm_cmn_cpumask_show, NULL);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic struct attribute *arm_cmn_cpumask_attrs[] = {
6158c2ecf20Sopenharmony_ci	&arm_cmn_cpumask_attr.attr,
6168c2ecf20Sopenharmony_ci	NULL,
6178c2ecf20Sopenharmony_ci};
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic struct attribute_group arm_cmn_cpumask_attr_group = {
6208c2ecf20Sopenharmony_ci	.attrs = arm_cmn_cpumask_attrs,
6218c2ecf20Sopenharmony_ci};
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic const struct attribute_group *arm_cmn_attr_groups[] = {
6248c2ecf20Sopenharmony_ci	&arm_cmn_event_attrs_group,
6258c2ecf20Sopenharmony_ci	&arm_cmn_format_attrs_group,
6268c2ecf20Sopenharmony_ci	&arm_cmn_cpumask_attr_group,
6278c2ecf20Sopenharmony_ci	NULL
6288c2ecf20Sopenharmony_ci};
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic int arm_cmn_wp_idx(struct perf_event *event)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	return CMN_EVENT_EVENTID(event) + CMN_EVENT_WP_GRP(event);
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cistatic u32 arm_cmn_wp_config(struct perf_event *event)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	u32 config;
6388c2ecf20Sopenharmony_ci	u32 dev = CMN_EVENT_WP_DEV_SEL(event);
6398c2ecf20Sopenharmony_ci	u32 chn = CMN_EVENT_WP_CHN_SEL(event);
6408c2ecf20Sopenharmony_ci	u32 grp = CMN_EVENT_WP_GRP(event);
6418c2ecf20Sopenharmony_ci	u32 exc = CMN_EVENT_WP_EXCLUSIVE(event);
6428c2ecf20Sopenharmony_ci	u32 combine = CMN_EVENT_WP_COMBINE(event);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	config = FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_DEV_SEL, dev) |
6458c2ecf20Sopenharmony_ci		 FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_CHN_SEL, chn) |
6468c2ecf20Sopenharmony_ci		 FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_GRP, grp) |
6478c2ecf20Sopenharmony_ci		 FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_EXCLUSIVE, exc);
6488c2ecf20Sopenharmony_ci	if (combine && !grp)
6498c2ecf20Sopenharmony_ci		config |= CMN_DTM_WPn_CONFIG_WP_COMBINE;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	return config;
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_cistatic void arm_cmn_set_state(struct arm_cmn *cmn, u32 state)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	if (!cmn->state)
6578c2ecf20Sopenharmony_ci		writel_relaxed(0, cmn->dtc[0].base + CMN_DT_PMCR);
6588c2ecf20Sopenharmony_ci	cmn->state |= state;
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_cistatic void arm_cmn_clear_state(struct arm_cmn *cmn, u32 state)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	cmn->state &= ~state;
6648c2ecf20Sopenharmony_ci	if (!cmn->state)
6658c2ecf20Sopenharmony_ci		writel_relaxed(CMN_DT_PMCR_PMU_EN | CMN_DT_PMCR_OVFL_INTR_EN,
6668c2ecf20Sopenharmony_ci			       cmn->dtc[0].base + CMN_DT_PMCR);
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic void arm_cmn_pmu_enable(struct pmu *pmu)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	arm_cmn_clear_state(to_cmn(pmu), CMN_STATE_DISABLED);
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic void arm_cmn_pmu_disable(struct pmu *pmu)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	arm_cmn_set_state(to_cmn(pmu), CMN_STATE_DISABLED);
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic u64 arm_cmn_read_dtm(struct arm_cmn *cmn, struct arm_cmn_hw_event *hw,
6808c2ecf20Sopenharmony_ci			    bool snapshot)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	struct arm_cmn_node *dn;
6838c2ecf20Sopenharmony_ci	unsigned int i, offset;
6848c2ecf20Sopenharmony_ci	u64 count = 0;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	offset = snapshot ? CMN_DTM_PMEVCNTSR : CMN_DTM_PMEVCNT;
6878c2ecf20Sopenharmony_ci	for_each_hw_dn(hw, dn, i) {
6888c2ecf20Sopenharmony_ci		struct arm_cmn_node *xp = arm_cmn_node_to_xp(dn);
6898c2ecf20Sopenharmony_ci		int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
6908c2ecf20Sopenharmony_ci		u64 reg = readq_relaxed(xp->pmu_base + offset);
6918c2ecf20Sopenharmony_ci		u16 dtm_count = reg >> (dtm_idx * 16);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci		count += dtm_count;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci	return count;
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic u64 arm_cmn_read_cc(struct arm_cmn_dtc *dtc)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	u64 val = readq_relaxed(dtc->base + CMN_DT_PMCCNTR);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	writeq_relaxed(CMN_CC_INIT, dtc->base + CMN_DT_PMCCNTR);
7038c2ecf20Sopenharmony_ci	return (val - CMN_CC_INIT) & ((CMN_CC_INIT << 1) - 1);
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic u32 arm_cmn_read_counter(struct arm_cmn_dtc *dtc, int idx)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	u32 val, pmevcnt = CMN_DT_PMEVCNT(idx);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	val = readl_relaxed(dtc->base + pmevcnt);
7118c2ecf20Sopenharmony_ci	writel_relaxed(CMN_COUNTER_INIT, dtc->base + pmevcnt);
7128c2ecf20Sopenharmony_ci	return val - CMN_COUNTER_INIT;
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic void arm_cmn_init_counter(struct perf_event *event)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	struct arm_cmn *cmn = to_cmn(event->pmu);
7188c2ecf20Sopenharmony_ci	struct arm_cmn_hw_event *hw = to_cmn_hw(event);
7198c2ecf20Sopenharmony_ci	unsigned int i, pmevcnt = CMN_DT_PMEVCNT(hw->dtc_idx);
7208c2ecf20Sopenharmony_ci	u64 count;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	for (i = 0; hw->dtcs_used & (1U << i); i++) {
7238c2ecf20Sopenharmony_ci		writel_relaxed(CMN_COUNTER_INIT, cmn->dtc[i].base + pmevcnt);
7248c2ecf20Sopenharmony_ci		cmn->dtc[i].counters[hw->dtc_idx] = event;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	count = arm_cmn_read_dtm(cmn, hw, false);
7288c2ecf20Sopenharmony_ci	local64_set(&event->hw.prev_count, count);
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_cistatic void arm_cmn_event_read(struct perf_event *event)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	struct arm_cmn *cmn = to_cmn(event->pmu);
7348c2ecf20Sopenharmony_ci	struct arm_cmn_hw_event *hw = to_cmn_hw(event);
7358c2ecf20Sopenharmony_ci	u64 delta, new, prev;
7368c2ecf20Sopenharmony_ci	unsigned long flags;
7378c2ecf20Sopenharmony_ci	unsigned int i;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	if (hw->dtc_idx == CMN_DT_NUM_COUNTERS) {
7408c2ecf20Sopenharmony_ci		i = __ffs(hw->dtcs_used);
7418c2ecf20Sopenharmony_ci		delta = arm_cmn_read_cc(cmn->dtc + i);
7428c2ecf20Sopenharmony_ci		local64_add(delta, &event->count);
7438c2ecf20Sopenharmony_ci		return;
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci	new = arm_cmn_read_dtm(cmn, hw, false);
7468c2ecf20Sopenharmony_ci	prev = local64_xchg(&event->hw.prev_count, new);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	delta = new - prev;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	local_irq_save(flags);
7518c2ecf20Sopenharmony_ci	for (i = 0; hw->dtcs_used & (1U << i); i++) {
7528c2ecf20Sopenharmony_ci		new = arm_cmn_read_counter(cmn->dtc + i, hw->dtc_idx);
7538c2ecf20Sopenharmony_ci		delta += new << 16;
7548c2ecf20Sopenharmony_ci	}
7558c2ecf20Sopenharmony_ci	local_irq_restore(flags);
7568c2ecf20Sopenharmony_ci	local64_add(delta, &event->count);
7578c2ecf20Sopenharmony_ci}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_cistatic void arm_cmn_event_start(struct perf_event *event, int flags)
7608c2ecf20Sopenharmony_ci{
7618c2ecf20Sopenharmony_ci	struct arm_cmn *cmn = to_cmn(event->pmu);
7628c2ecf20Sopenharmony_ci	struct arm_cmn_hw_event *hw = to_cmn_hw(event);
7638c2ecf20Sopenharmony_ci	struct arm_cmn_node *dn;
7648c2ecf20Sopenharmony_ci	enum cmn_node_type type = CMN_EVENT_TYPE(event);
7658c2ecf20Sopenharmony_ci	int i;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	if (type == CMN_TYPE_DTC) {
7688c2ecf20Sopenharmony_ci		i = __ffs(hw->dtcs_used);
7698c2ecf20Sopenharmony_ci		writeq_relaxed(CMN_CC_INIT, cmn->dtc[i].base + CMN_DT_PMCCNTR);
7708c2ecf20Sopenharmony_ci		cmn->dtc[i].cc_active = true;
7718c2ecf20Sopenharmony_ci	} else if (type == CMN_TYPE_WP) {
7728c2ecf20Sopenharmony_ci		int wp_idx = arm_cmn_wp_idx(event);
7738c2ecf20Sopenharmony_ci		u64 val = CMN_EVENT_WP_VAL(event);
7748c2ecf20Sopenharmony_ci		u64 mask = CMN_EVENT_WP_MASK(event);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci		for_each_hw_dn(hw, dn, i) {
7778c2ecf20Sopenharmony_ci			writeq_relaxed(val, dn->pmu_base + CMN_DTM_WPn_VAL(wp_idx));
7788c2ecf20Sopenharmony_ci			writeq_relaxed(mask, dn->pmu_base + CMN_DTM_WPn_MASK(wp_idx));
7798c2ecf20Sopenharmony_ci		}
7808c2ecf20Sopenharmony_ci	} else for_each_hw_dn(hw, dn, i) {
7818c2ecf20Sopenharmony_ci		int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci		dn->event[dtm_idx] = CMN_EVENT_EVENTID(event);
7848c2ecf20Sopenharmony_ci		writel_relaxed(le32_to_cpu(dn->event_sel), dn->pmu_base + CMN_PMU_EVENT_SEL);
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic void arm_cmn_event_stop(struct perf_event *event, int flags)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	struct arm_cmn *cmn = to_cmn(event->pmu);
7918c2ecf20Sopenharmony_ci	struct arm_cmn_hw_event *hw = to_cmn_hw(event);
7928c2ecf20Sopenharmony_ci	struct arm_cmn_node *dn;
7938c2ecf20Sopenharmony_ci	enum cmn_node_type type = CMN_EVENT_TYPE(event);
7948c2ecf20Sopenharmony_ci	int i;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	if (type == CMN_TYPE_DTC) {
7978c2ecf20Sopenharmony_ci		i = __ffs(hw->dtcs_used);
7988c2ecf20Sopenharmony_ci		cmn->dtc[i].cc_active = false;
7998c2ecf20Sopenharmony_ci	} else if (type == CMN_TYPE_WP) {
8008c2ecf20Sopenharmony_ci		int wp_idx = arm_cmn_wp_idx(event);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci		for_each_hw_dn(hw, dn, i) {
8038c2ecf20Sopenharmony_ci			writeq_relaxed(0, dn->pmu_base + CMN_DTM_WPn_MASK(wp_idx));
8048c2ecf20Sopenharmony_ci			writeq_relaxed(~0ULL, dn->pmu_base + CMN_DTM_WPn_VAL(wp_idx));
8058c2ecf20Sopenharmony_ci		}
8068c2ecf20Sopenharmony_ci	} else for_each_hw_dn(hw, dn, i) {
8078c2ecf20Sopenharmony_ci		int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci		dn->event[dtm_idx] = 0;
8108c2ecf20Sopenharmony_ci		writel_relaxed(le32_to_cpu(dn->event_sel), dn->pmu_base + CMN_PMU_EVENT_SEL);
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	arm_cmn_event_read(event);
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_cistruct arm_cmn_val {
8178c2ecf20Sopenharmony_ci	u8 dtm_count[CMN_MAX_XPS];
8188c2ecf20Sopenharmony_ci	u8 occupid[CMN_MAX_XPS];
8198c2ecf20Sopenharmony_ci	u8 wp[CMN_MAX_XPS][4];
8208c2ecf20Sopenharmony_ci	int dtc_count;
8218c2ecf20Sopenharmony_ci	bool cycles;
8228c2ecf20Sopenharmony_ci};
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic void arm_cmn_val_add_event(struct arm_cmn_val *val, struct perf_event *event)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	struct arm_cmn_hw_event *hw = to_cmn_hw(event);
8278c2ecf20Sopenharmony_ci	struct arm_cmn_node *dn;
8288c2ecf20Sopenharmony_ci	enum cmn_node_type type;
8298c2ecf20Sopenharmony_ci	int i;
8308c2ecf20Sopenharmony_ci	u8 occupid;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	if (is_software_event(event))
8338c2ecf20Sopenharmony_ci		return;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	type = CMN_EVENT_TYPE(event);
8368c2ecf20Sopenharmony_ci	if (type == CMN_TYPE_DTC) {
8378c2ecf20Sopenharmony_ci		val->cycles = true;
8388c2ecf20Sopenharmony_ci		return;
8398c2ecf20Sopenharmony_ci	}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	val->dtc_count++;
8428c2ecf20Sopenharmony_ci	if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event)))
8438c2ecf20Sopenharmony_ci		occupid = CMN_EVENT_OCCUPID(event) + 1;
8448c2ecf20Sopenharmony_ci	else
8458c2ecf20Sopenharmony_ci		occupid = 0;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	for_each_hw_dn(hw, dn, i) {
8488c2ecf20Sopenharmony_ci		int wp_idx, xp = arm_cmn_node_to_xp(dn)->logid;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci		val->dtm_count[xp]++;
8518c2ecf20Sopenharmony_ci		val->occupid[xp] = occupid;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci		if (type != CMN_TYPE_WP)
8548c2ecf20Sopenharmony_ci			continue;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		wp_idx = arm_cmn_wp_idx(event);
8578c2ecf20Sopenharmony_ci		val->wp[xp][wp_idx] = CMN_EVENT_WP_COMBINE(event) + 1;
8588c2ecf20Sopenharmony_ci	}
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_cistatic int arm_cmn_validate_group(struct perf_event *event)
8628c2ecf20Sopenharmony_ci{
8638c2ecf20Sopenharmony_ci	struct arm_cmn_hw_event *hw = to_cmn_hw(event);
8648c2ecf20Sopenharmony_ci	struct arm_cmn_node *dn;
8658c2ecf20Sopenharmony_ci	struct perf_event *sibling, *leader = event->group_leader;
8668c2ecf20Sopenharmony_ci	enum cmn_node_type type;
8678c2ecf20Sopenharmony_ci	struct arm_cmn_val val;
8688c2ecf20Sopenharmony_ci	int i;
8698c2ecf20Sopenharmony_ci	u8 occupid;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	if (leader == event)
8728c2ecf20Sopenharmony_ci		return 0;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if (event->pmu != leader->pmu && !is_software_event(leader))
8758c2ecf20Sopenharmony_ci		return -EINVAL;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	memset(&val, 0, sizeof(val));
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	arm_cmn_val_add_event(&val, leader);
8808c2ecf20Sopenharmony_ci	for_each_sibling_event(sibling, leader)
8818c2ecf20Sopenharmony_ci		arm_cmn_val_add_event(&val, sibling);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	type = CMN_EVENT_TYPE(event);
8848c2ecf20Sopenharmony_ci	if (type == CMN_TYPE_DTC)
8858c2ecf20Sopenharmony_ci		return val.cycles ? -EINVAL : 0;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	if (val.dtc_count == CMN_DT_NUM_COUNTERS)
8888c2ecf20Sopenharmony_ci		return -EINVAL;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event)))
8918c2ecf20Sopenharmony_ci		occupid = CMN_EVENT_OCCUPID(event) + 1;
8928c2ecf20Sopenharmony_ci	else
8938c2ecf20Sopenharmony_ci		occupid = 0;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	for_each_hw_dn(hw, dn, i) {
8968c2ecf20Sopenharmony_ci		int wp_idx, wp_cmb, xp = arm_cmn_node_to_xp(dn)->logid;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci		if (val.dtm_count[xp] == CMN_DTM_NUM_COUNTERS)
8998c2ecf20Sopenharmony_ci			return -EINVAL;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci		if (occupid && val.occupid[xp] && occupid != val.occupid[xp])
9028c2ecf20Sopenharmony_ci			return -EINVAL;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		if (type != CMN_TYPE_WP)
9058c2ecf20Sopenharmony_ci			continue;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci		wp_idx = arm_cmn_wp_idx(event);
9088c2ecf20Sopenharmony_ci		if (val.wp[xp][wp_idx])
9098c2ecf20Sopenharmony_ci			return -EINVAL;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci		wp_cmb = val.wp[xp][wp_idx ^ 1];
9128c2ecf20Sopenharmony_ci		if (wp_cmb && wp_cmb != CMN_EVENT_WP_COMBINE(event) + 1)
9138c2ecf20Sopenharmony_ci			return -EINVAL;
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	return 0;
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_cistatic int arm_cmn_event_init(struct perf_event *event)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	struct arm_cmn *cmn = to_cmn(event->pmu);
9228c2ecf20Sopenharmony_ci	struct arm_cmn_hw_event *hw = to_cmn_hw(event);
9238c2ecf20Sopenharmony_ci	enum cmn_node_type type;
9248c2ecf20Sopenharmony_ci	unsigned int i;
9258c2ecf20Sopenharmony_ci	bool bynodeid;
9268c2ecf20Sopenharmony_ci	u16 nodeid, eventid;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	if (event->attr.type != event->pmu->type)
9298c2ecf20Sopenharmony_ci		return -ENOENT;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
9328c2ecf20Sopenharmony_ci		return -EINVAL;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	event->cpu = cmn->cpu;
9358c2ecf20Sopenharmony_ci	if (event->cpu < 0)
9368c2ecf20Sopenharmony_ci		return -EINVAL;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	type = CMN_EVENT_TYPE(event);
9398c2ecf20Sopenharmony_ci	/* DTC events (i.e. cycles) already have everything they need */
9408c2ecf20Sopenharmony_ci	if (type == CMN_TYPE_DTC)
9418c2ecf20Sopenharmony_ci		return 0;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/* For watchpoints we need the actual XP node here */
9448c2ecf20Sopenharmony_ci	if (type == CMN_TYPE_WP) {
9458c2ecf20Sopenharmony_ci		type = CMN_TYPE_XP;
9468c2ecf20Sopenharmony_ci		/* ...and we need a "real" direction */
9478c2ecf20Sopenharmony_ci		eventid = CMN_EVENT_EVENTID(event);
9488c2ecf20Sopenharmony_ci		if (eventid != CMN_WP_UP && eventid != CMN_WP_DOWN)
9498c2ecf20Sopenharmony_ci			return -EINVAL;
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	bynodeid = CMN_EVENT_BYNODEID(event);
9538c2ecf20Sopenharmony_ci	nodeid = CMN_EVENT_NODEID(event);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	hw->dn = arm_cmn_node(cmn, type);
9568c2ecf20Sopenharmony_ci	for (i = hw->dn - cmn->dns; i < cmn->num_dns && cmn->dns[i].type == type; i++) {
9578c2ecf20Sopenharmony_ci		if (!bynodeid) {
9588c2ecf20Sopenharmony_ci			hw->num_dns++;
9598c2ecf20Sopenharmony_ci		} else if (cmn->dns[i].id != nodeid) {
9608c2ecf20Sopenharmony_ci			hw->dn++;
9618c2ecf20Sopenharmony_ci		} else {
9628c2ecf20Sopenharmony_ci			hw->num_dns = 1;
9638c2ecf20Sopenharmony_ci			break;
9648c2ecf20Sopenharmony_ci		}
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	if (!hw->num_dns) {
9688c2ecf20Sopenharmony_ci		int bits = arm_cmn_xyidbits(cmn);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci		dev_dbg(cmn->dev, "invalid node 0x%x (%d,%d,%d,%d) type 0x%x\n",
9718c2ecf20Sopenharmony_ci			nodeid, CMN_NODEID_X(nodeid, bits), CMN_NODEID_Y(nodeid, bits),
9728c2ecf20Sopenharmony_ci			CMN_NODEID_PID(nodeid), CMN_NODEID_DEVID(nodeid), type);
9738c2ecf20Sopenharmony_ci		return -EINVAL;
9748c2ecf20Sopenharmony_ci	}
9758c2ecf20Sopenharmony_ci	/*
9768c2ecf20Sopenharmony_ci	 * By assuming events count in all DTC domains, we cunningly avoid
9778c2ecf20Sopenharmony_ci	 * needing to know anything about how XPs are assigned to domains.
9788c2ecf20Sopenharmony_ci	 */
9798c2ecf20Sopenharmony_ci	hw->dtcs_used = (1U << cmn->num_dtcs) - 1;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	return arm_cmn_validate_group(event);
9828c2ecf20Sopenharmony_ci}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_cistatic void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
9858c2ecf20Sopenharmony_ci				int i)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	struct arm_cmn_hw_event *hw = to_cmn_hw(event);
9888c2ecf20Sopenharmony_ci	enum cmn_node_type type = CMN_EVENT_TYPE(event);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	while (i--) {
9918c2ecf20Sopenharmony_ci		struct arm_cmn_node *xp = arm_cmn_node_to_xp(hw->dn + i);
9928c2ecf20Sopenharmony_ci		unsigned int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci		if (type == CMN_TYPE_WP)
9958c2ecf20Sopenharmony_ci			hw->dn[i].wp_event[arm_cmn_wp_idx(event)] = -1;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci		if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event)))
9988c2ecf20Sopenharmony_ci			hw->dn[i].occupid_count--;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci		xp->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx);
10018c2ecf20Sopenharmony_ci		writel_relaxed(xp->pmu_config_low, xp->pmu_base + CMN_DTM_PMU_CONFIG);
10028c2ecf20Sopenharmony_ci	}
10038c2ecf20Sopenharmony_ci	memset(hw->dtm_idx, 0, sizeof(hw->dtm_idx));
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	for (i = 0; hw->dtcs_used & (1U << i); i++)
10068c2ecf20Sopenharmony_ci		cmn->dtc[i].counters[hw->dtc_idx] = NULL;
10078c2ecf20Sopenharmony_ci}
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_cistatic int arm_cmn_event_add(struct perf_event *event, int flags)
10108c2ecf20Sopenharmony_ci{
10118c2ecf20Sopenharmony_ci	struct arm_cmn *cmn = to_cmn(event->pmu);
10128c2ecf20Sopenharmony_ci	struct arm_cmn_hw_event *hw = to_cmn_hw(event);
10138c2ecf20Sopenharmony_ci	struct arm_cmn_dtc *dtc = &cmn->dtc[0];
10148c2ecf20Sopenharmony_ci	struct arm_cmn_node *dn;
10158c2ecf20Sopenharmony_ci	enum cmn_node_type type = CMN_EVENT_TYPE(event);
10168c2ecf20Sopenharmony_ci	unsigned int i, dtc_idx, input_sel;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	if (type == CMN_TYPE_DTC) {
10198c2ecf20Sopenharmony_ci		i = 0;
10208c2ecf20Sopenharmony_ci		while (cmn->dtc[i].cycles)
10218c2ecf20Sopenharmony_ci			if (++i == cmn->num_dtcs)
10228c2ecf20Sopenharmony_ci				return -ENOSPC;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci		cmn->dtc[i].cycles = event;
10258c2ecf20Sopenharmony_ci		hw->dtc_idx = CMN_DT_NUM_COUNTERS;
10268c2ecf20Sopenharmony_ci		hw->dtcs_used = 1U << i;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci		if (flags & PERF_EF_START)
10298c2ecf20Sopenharmony_ci			arm_cmn_event_start(event, 0);
10308c2ecf20Sopenharmony_ci		return 0;
10318c2ecf20Sopenharmony_ci	}
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	/* Grab a free global counter first... */
10348c2ecf20Sopenharmony_ci	dtc_idx = 0;
10358c2ecf20Sopenharmony_ci	while (dtc->counters[dtc_idx])
10368c2ecf20Sopenharmony_ci		if (++dtc_idx == CMN_DT_NUM_COUNTERS)
10378c2ecf20Sopenharmony_ci			return -ENOSPC;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	hw->dtc_idx = dtc_idx;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	/* ...then the local counters to feed it. */
10428c2ecf20Sopenharmony_ci	for_each_hw_dn(hw, dn, i) {
10438c2ecf20Sopenharmony_ci		struct arm_cmn_node *xp = arm_cmn_node_to_xp(dn);
10448c2ecf20Sopenharmony_ci		unsigned int dtm_idx, shift;
10458c2ecf20Sopenharmony_ci		u64 reg;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci		dtm_idx = 0;
10488c2ecf20Sopenharmony_ci		while (xp->pmu_config_low & CMN__PMEVCNT_PAIRED(dtm_idx))
10498c2ecf20Sopenharmony_ci			if (++dtm_idx == CMN_DTM_NUM_COUNTERS)
10508c2ecf20Sopenharmony_ci				goto free_dtms;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci		if (type == CMN_TYPE_XP) {
10538c2ecf20Sopenharmony_ci			input_sel = CMN__PMEVCNT0_INPUT_SEL_XP + dtm_idx;
10548c2ecf20Sopenharmony_ci		} else if (type == CMN_TYPE_WP) {
10558c2ecf20Sopenharmony_ci			int tmp, wp_idx = arm_cmn_wp_idx(event);
10568c2ecf20Sopenharmony_ci			u32 cfg = arm_cmn_wp_config(event);
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci			if (dn->wp_event[wp_idx] >= 0)
10598c2ecf20Sopenharmony_ci				goto free_dtms;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci			tmp = dn->wp_event[wp_idx ^ 1];
10628c2ecf20Sopenharmony_ci			if (tmp >= 0 && CMN_EVENT_WP_COMBINE(event) !=
10638c2ecf20Sopenharmony_ci					CMN_EVENT_WP_COMBINE(dtc->counters[tmp]))
10648c2ecf20Sopenharmony_ci				goto free_dtms;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci			input_sel = CMN__PMEVCNT0_INPUT_SEL_WP + wp_idx;
10678c2ecf20Sopenharmony_ci			dn->wp_event[wp_idx] = dtc_idx;
10688c2ecf20Sopenharmony_ci			writel_relaxed(cfg, dn->pmu_base + CMN_DTM_WPn_CONFIG(wp_idx));
10698c2ecf20Sopenharmony_ci		} else {
10708c2ecf20Sopenharmony_ci			unsigned int port = CMN_NODEID_PID(dn->id);
10718c2ecf20Sopenharmony_ci			unsigned int dev = CMN_NODEID_DEVID(dn->id);
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci			input_sel = CMN__PMEVCNT0_INPUT_SEL_DEV + dtm_idx +
10748c2ecf20Sopenharmony_ci				    (port << 4) + (dev << 2);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci			if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event))) {
10778c2ecf20Sopenharmony_ci				int occupid = CMN_EVENT_OCCUPID(event);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci				if (dn->occupid_count == 0) {
10808c2ecf20Sopenharmony_ci					dn->occupid_val = occupid;
10818c2ecf20Sopenharmony_ci					writel_relaxed(occupid,
10828c2ecf20Sopenharmony_ci						       dn->pmu_base + CMN_PMU_EVENT_SEL + 4);
10838c2ecf20Sopenharmony_ci				} else if (dn->occupid_val != occupid) {
10848c2ecf20Sopenharmony_ci					goto free_dtms;
10858c2ecf20Sopenharmony_ci				}
10868c2ecf20Sopenharmony_ci				dn->occupid_count++;
10878c2ecf20Sopenharmony_ci			}
10888c2ecf20Sopenharmony_ci		}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci		arm_cmn_set_index(hw->dtm_idx, i, dtm_idx);
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci		xp->input_sel[dtm_idx] = input_sel;
10938c2ecf20Sopenharmony_ci		shift = CMN__PMEVCNTn_GLOBAL_NUM_SHIFT(dtm_idx);
10948c2ecf20Sopenharmony_ci		xp->pmu_config_low &= ~(CMN__PMEVCNT0_GLOBAL_NUM << shift);
10958c2ecf20Sopenharmony_ci		xp->pmu_config_low |= FIELD_PREP(CMN__PMEVCNT0_GLOBAL_NUM, dtc_idx) << shift;
10968c2ecf20Sopenharmony_ci		xp->pmu_config_low |= CMN__PMEVCNT_PAIRED(dtm_idx);
10978c2ecf20Sopenharmony_ci		reg = (u64)le32_to_cpu(xp->pmu_config_high) << 32 | xp->pmu_config_low;
10988c2ecf20Sopenharmony_ci		writeq_relaxed(reg, xp->pmu_base + CMN_DTM_PMU_CONFIG);
10998c2ecf20Sopenharmony_ci	}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	/* Go go go! */
11028c2ecf20Sopenharmony_ci	arm_cmn_init_counter(event);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	if (flags & PERF_EF_START)
11058c2ecf20Sopenharmony_ci		arm_cmn_event_start(event, 0);
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	return 0;
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cifree_dtms:
11108c2ecf20Sopenharmony_ci	arm_cmn_event_clear(cmn, event, i);
11118c2ecf20Sopenharmony_ci	return -ENOSPC;
11128c2ecf20Sopenharmony_ci}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_cistatic void arm_cmn_event_del(struct perf_event *event, int flags)
11158c2ecf20Sopenharmony_ci{
11168c2ecf20Sopenharmony_ci	struct arm_cmn *cmn = to_cmn(event->pmu);
11178c2ecf20Sopenharmony_ci	struct arm_cmn_hw_event *hw = to_cmn_hw(event);
11188c2ecf20Sopenharmony_ci	enum cmn_node_type type = CMN_EVENT_TYPE(event);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	arm_cmn_event_stop(event, PERF_EF_UPDATE);
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	if (type == CMN_TYPE_DTC)
11238c2ecf20Sopenharmony_ci		cmn->dtc[__ffs(hw->dtcs_used)].cycles = NULL;
11248c2ecf20Sopenharmony_ci	else
11258c2ecf20Sopenharmony_ci		arm_cmn_event_clear(cmn, event, hw->num_dns);
11268c2ecf20Sopenharmony_ci}
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci/*
11298c2ecf20Sopenharmony_ci * We stop the PMU for both add and read, to avoid skew across DTM counters.
11308c2ecf20Sopenharmony_ci * In theory we could use snapshots to read without stopping, but then it
11318c2ecf20Sopenharmony_ci * becomes a lot trickier to deal with overlow and racing against interrupts,
11328c2ecf20Sopenharmony_ci * plus it seems they don't work properly on some hardware anyway :(
11338c2ecf20Sopenharmony_ci */
11348c2ecf20Sopenharmony_cistatic void arm_cmn_start_txn(struct pmu *pmu, unsigned int flags)
11358c2ecf20Sopenharmony_ci{
11368c2ecf20Sopenharmony_ci	arm_cmn_set_state(to_cmn(pmu), CMN_STATE_TXN);
11378c2ecf20Sopenharmony_ci}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_cistatic void arm_cmn_end_txn(struct pmu *pmu)
11408c2ecf20Sopenharmony_ci{
11418c2ecf20Sopenharmony_ci	arm_cmn_clear_state(to_cmn(pmu), CMN_STATE_TXN);
11428c2ecf20Sopenharmony_ci}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_cistatic int arm_cmn_commit_txn(struct pmu *pmu)
11458c2ecf20Sopenharmony_ci{
11468c2ecf20Sopenharmony_ci	arm_cmn_end_txn(pmu);
11478c2ecf20Sopenharmony_ci	return 0;
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic int arm_cmn_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
11518c2ecf20Sopenharmony_ci{
11528c2ecf20Sopenharmony_ci	struct arm_cmn *cmn;
11538c2ecf20Sopenharmony_ci	unsigned int i, target;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	cmn = hlist_entry_safe(node, struct arm_cmn, cpuhp_node);
11568c2ecf20Sopenharmony_ci	if (cpu != cmn->cpu)
11578c2ecf20Sopenharmony_ci		return 0;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	target = cpumask_any_but(cpu_online_mask, cpu);
11608c2ecf20Sopenharmony_ci	if (target >= nr_cpu_ids)
11618c2ecf20Sopenharmony_ci		return 0;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	perf_pmu_migrate_context(&cmn->pmu, cpu, target);
11648c2ecf20Sopenharmony_ci	for (i = 0; i < cmn->num_dtcs; i++)
11658c2ecf20Sopenharmony_ci		irq_set_affinity_hint(cmn->dtc[i].irq, cpumask_of(target));
11668c2ecf20Sopenharmony_ci	cmn->cpu = target;
11678c2ecf20Sopenharmony_ci	return 0;
11688c2ecf20Sopenharmony_ci}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_cistatic irqreturn_t arm_cmn_handle_irq(int irq, void *dev_id)
11718c2ecf20Sopenharmony_ci{
11728c2ecf20Sopenharmony_ci	struct arm_cmn_dtc *dtc = dev_id;
11738c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	for (;;) {
11768c2ecf20Sopenharmony_ci		u32 status = readl_relaxed(dtc->base + CMN_DT_PMOVSR);
11778c2ecf20Sopenharmony_ci		u64 delta;
11788c2ecf20Sopenharmony_ci		int i;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci		for (i = 0; i < CMN_DT_NUM_COUNTERS; i++) {
11818c2ecf20Sopenharmony_ci			if (status & (1U << i)) {
11828c2ecf20Sopenharmony_ci				ret = IRQ_HANDLED;
11838c2ecf20Sopenharmony_ci				if (WARN_ON(!dtc->counters[i]))
11848c2ecf20Sopenharmony_ci					continue;
11858c2ecf20Sopenharmony_ci				delta = (u64)arm_cmn_read_counter(dtc, i) << 16;
11868c2ecf20Sopenharmony_ci				local64_add(delta, &dtc->counters[i]->count);
11878c2ecf20Sopenharmony_ci			}
11888c2ecf20Sopenharmony_ci		}
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci		if (status & (1U << CMN_DT_NUM_COUNTERS)) {
11918c2ecf20Sopenharmony_ci			ret = IRQ_HANDLED;
11928c2ecf20Sopenharmony_ci			if (dtc->cc_active && !WARN_ON(!dtc->cycles)) {
11938c2ecf20Sopenharmony_ci				delta = arm_cmn_read_cc(dtc);
11948c2ecf20Sopenharmony_ci				local64_add(delta, &dtc->cycles->count);
11958c2ecf20Sopenharmony_ci			}
11968c2ecf20Sopenharmony_ci		}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci		writel_relaxed(status, dtc->base + CMN_DT_PMOVSR_CLR);
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci		if (!dtc->irq_friend)
12018c2ecf20Sopenharmony_ci			return ret;
12028c2ecf20Sopenharmony_ci		dtc += dtc->irq_friend;
12038c2ecf20Sopenharmony_ci	}
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci/* We can reasonably accommodate DTCs of the same CMN sharing IRQs */
12078c2ecf20Sopenharmony_cistatic int arm_cmn_init_irqs(struct arm_cmn *cmn)
12088c2ecf20Sopenharmony_ci{
12098c2ecf20Sopenharmony_ci	int i, j, irq, err;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	for (i = 0; i < cmn->num_dtcs; i++) {
12128c2ecf20Sopenharmony_ci		irq = cmn->dtc[i].irq;
12138c2ecf20Sopenharmony_ci		for (j = i; j--; ) {
12148c2ecf20Sopenharmony_ci			if (cmn->dtc[j].irq == irq) {
12158c2ecf20Sopenharmony_ci				cmn->dtc[j].irq_friend = i - j;
12168c2ecf20Sopenharmony_ci				goto next;
12178c2ecf20Sopenharmony_ci			}
12188c2ecf20Sopenharmony_ci		}
12198c2ecf20Sopenharmony_ci		err = devm_request_irq(cmn->dev, irq, arm_cmn_handle_irq,
12208c2ecf20Sopenharmony_ci				       IRQF_NOBALANCING | IRQF_NO_THREAD,
12218c2ecf20Sopenharmony_ci				       dev_name(cmn->dev), &cmn->dtc[i]);
12228c2ecf20Sopenharmony_ci		if (err)
12238c2ecf20Sopenharmony_ci			return err;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci		err = irq_set_affinity_hint(irq, cpumask_of(cmn->cpu));
12268c2ecf20Sopenharmony_ci		if (err)
12278c2ecf20Sopenharmony_ci			return err;
12288c2ecf20Sopenharmony_ci	next:
12298c2ecf20Sopenharmony_ci		; /* isn't C great? */
12308c2ecf20Sopenharmony_ci	}
12318c2ecf20Sopenharmony_ci	return 0;
12328c2ecf20Sopenharmony_ci}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_cistatic void arm_cmn_init_dtm(struct arm_cmn_node *xp)
12358c2ecf20Sopenharmony_ci{
12368c2ecf20Sopenharmony_ci	int i;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
12398c2ecf20Sopenharmony_ci		xp->wp_event[i] = -1;
12408c2ecf20Sopenharmony_ci		writeq_relaxed(0, xp->pmu_base + CMN_DTM_WPn_MASK(i));
12418c2ecf20Sopenharmony_ci		writeq_relaxed(~0ULL, xp->pmu_base + CMN_DTM_WPn_VAL(i));
12428c2ecf20Sopenharmony_ci	}
12438c2ecf20Sopenharmony_ci	xp->pmu_config_low = CMN_DTM_PMU_CONFIG_PMU_EN;
12448c2ecf20Sopenharmony_ci	xp->dtc = -1;
12458c2ecf20Sopenharmony_ci}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_cistatic int arm_cmn_init_dtc(struct arm_cmn *cmn, struct arm_cmn_node *dn, int idx)
12488c2ecf20Sopenharmony_ci{
12498c2ecf20Sopenharmony_ci	struct arm_cmn_dtc *dtc = cmn->dtc + idx;
12508c2ecf20Sopenharmony_ci	struct arm_cmn_node *xp;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	dtc->base = dn->pmu_base - CMN_PMU_OFFSET;
12538c2ecf20Sopenharmony_ci	dtc->irq = platform_get_irq(to_platform_device(cmn->dev), idx);
12548c2ecf20Sopenharmony_ci	if (dtc->irq < 0)
12558c2ecf20Sopenharmony_ci		return dtc->irq;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	writel_relaxed(CMN_DT_DTC_CTL_DT_EN, dtc->base + CMN_DT_DTC_CTL);
12588c2ecf20Sopenharmony_ci	writel_relaxed(CMN_DT_PMCR_PMU_EN | CMN_DT_PMCR_OVFL_INTR_EN, dtc->base + CMN_DT_PMCR);
12598c2ecf20Sopenharmony_ci	writeq_relaxed(0, dtc->base + CMN_DT_PMCCNTR);
12608c2ecf20Sopenharmony_ci	writel_relaxed(0x1ff, dtc->base + CMN_DT_PMOVSR_CLR);
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	/* We do at least know that a DTC's XP must be in that DTC's domain */
12638c2ecf20Sopenharmony_ci	xp = arm_cmn_node_to_xp(dn);
12648c2ecf20Sopenharmony_ci	xp->dtc = idx;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	return 0;
12678c2ecf20Sopenharmony_ci}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_cistatic int arm_cmn_node_cmp(const void *a, const void *b)
12708c2ecf20Sopenharmony_ci{
12718c2ecf20Sopenharmony_ci	const struct arm_cmn_node *dna = a, *dnb = b;
12728c2ecf20Sopenharmony_ci	int cmp;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	cmp = dna->type - dnb->type;
12758c2ecf20Sopenharmony_ci	if (!cmp)
12768c2ecf20Sopenharmony_ci		cmp = dna->logid - dnb->logid;
12778c2ecf20Sopenharmony_ci	return cmp;
12788c2ecf20Sopenharmony_ci}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_cistatic int arm_cmn_init_dtcs(struct arm_cmn *cmn)
12818c2ecf20Sopenharmony_ci{
12828c2ecf20Sopenharmony_ci	struct arm_cmn_node *dn;
12838c2ecf20Sopenharmony_ci	int dtc_idx = 0;
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	cmn->dtc = devm_kcalloc(cmn->dev, cmn->num_dtcs, sizeof(cmn->dtc[0]), GFP_KERNEL);
12868c2ecf20Sopenharmony_ci	if (!cmn->dtc)
12878c2ecf20Sopenharmony_ci		return -ENOMEM;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	sort(cmn->dns, cmn->num_dns, sizeof(cmn->dns[0]), arm_cmn_node_cmp, NULL);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	cmn->xps = arm_cmn_node(cmn, CMN_TYPE_XP);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	for (dn = cmn->dns; dn < cmn->dns + cmn->num_dns; dn++) {
12948c2ecf20Sopenharmony_ci		if (dn->type != CMN_TYPE_XP)
12958c2ecf20Sopenharmony_ci			arm_cmn_init_node_to_xp(cmn, dn);
12968c2ecf20Sopenharmony_ci		else if (cmn->num_dtcs == 1)
12978c2ecf20Sopenharmony_ci			dn->dtc = 0;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci		if (dn->type == CMN_TYPE_DTC)
13008c2ecf20Sopenharmony_ci			arm_cmn_init_dtc(cmn, dn, dtc_idx++);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci		/* To the PMU, RN-Ds don't add anything over RN-Is, so smoosh them together */
13038c2ecf20Sopenharmony_ci		if (dn->type == CMN_TYPE_RND)
13048c2ecf20Sopenharmony_ci			dn->type = CMN_TYPE_RNI;
13058c2ecf20Sopenharmony_ci	}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	arm_cmn_set_state(cmn, CMN_STATE_DISABLED);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	return 0;
13108c2ecf20Sopenharmony_ci}
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_cistatic void arm_cmn_init_node_info(struct arm_cmn *cmn, u32 offset, struct arm_cmn_node *node)
13138c2ecf20Sopenharmony_ci{
13148c2ecf20Sopenharmony_ci	int level;
13158c2ecf20Sopenharmony_ci	u64 reg = readq_relaxed(cmn->base + offset + CMN_NODE_INFO);
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	node->type = FIELD_GET(CMN_NI_NODE_TYPE, reg);
13188c2ecf20Sopenharmony_ci	node->id = FIELD_GET(CMN_NI_NODE_ID, reg);
13198c2ecf20Sopenharmony_ci	node->logid = FIELD_GET(CMN_NI_LOGICAL_ID, reg);
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	node->pmu_base = cmn->base + offset + CMN_PMU_OFFSET;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	if (node->type == CMN_TYPE_CFG)
13248c2ecf20Sopenharmony_ci		level = 0;
13258c2ecf20Sopenharmony_ci	else if (node->type == CMN_TYPE_XP)
13268c2ecf20Sopenharmony_ci		level = 1;
13278c2ecf20Sopenharmony_ci	else
13288c2ecf20Sopenharmony_ci		level = 2;
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	dev_dbg(cmn->dev, "node%*c%#06hx%*ctype:%-#6x id:%-4hd off:%#x\n",
13318c2ecf20Sopenharmony_ci			(level * 2) + 1, ' ', node->id, 5 - (level * 2), ' ',
13328c2ecf20Sopenharmony_ci			node->type, node->logid, offset);
13338c2ecf20Sopenharmony_ci}
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_cistatic int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
13368c2ecf20Sopenharmony_ci{
13378c2ecf20Sopenharmony_ci	void __iomem *cfg_region;
13388c2ecf20Sopenharmony_ci	struct arm_cmn_node cfg, *dn;
13398c2ecf20Sopenharmony_ci	u16 child_count, child_poff;
13408c2ecf20Sopenharmony_ci	u32 xp_offset[CMN_MAX_XPS];
13418c2ecf20Sopenharmony_ci	u64 reg;
13428c2ecf20Sopenharmony_ci	int i, j;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	cfg_region = cmn->base + rgn_offset;
13458c2ecf20Sopenharmony_ci	reg = readl_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_2);
13468c2ecf20Sopenharmony_ci	cmn->rev = FIELD_GET(CMN_CFGM_PID2_REVISION, reg);
13478c2ecf20Sopenharmony_ci	dev_dbg(cmn->dev, "periph_id_2 revision: %d\n", cmn->rev);
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	arm_cmn_init_node_info(cmn, rgn_offset, &cfg);
13508c2ecf20Sopenharmony_ci	if (cfg.type != CMN_TYPE_CFG)
13518c2ecf20Sopenharmony_ci		return -ENODEV;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	reg = readq_relaxed(cfg_region + CMN_CHILD_INFO);
13548c2ecf20Sopenharmony_ci	child_count = FIELD_GET(CMN_CI_CHILD_COUNT, reg);
13558c2ecf20Sopenharmony_ci	child_poff = FIELD_GET(CMN_CI_CHILD_PTR_OFFSET, reg);
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	cmn->num_xps = child_count;
13588c2ecf20Sopenharmony_ci	cmn->num_dns = cmn->num_xps;
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	/* Pass 1: visit the XPs, enumerate their children */
13618c2ecf20Sopenharmony_ci	for (i = 0; i < cmn->num_xps; i++) {
13628c2ecf20Sopenharmony_ci		reg = readq_relaxed(cfg_region + child_poff + i * 8);
13638c2ecf20Sopenharmony_ci		xp_offset[i] = reg & CMN_CHILD_NODE_ADDR;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci		reg = readq_relaxed(cmn->base + xp_offset[i] + CMN_CHILD_INFO);
13668c2ecf20Sopenharmony_ci		cmn->num_dns += FIELD_GET(CMN_CI_CHILD_COUNT, reg);
13678c2ecf20Sopenharmony_ci	}
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	/* Cheeky +1 to help terminate pointer-based iteration */
13708c2ecf20Sopenharmony_ci	cmn->dns = devm_kcalloc(cmn->dev, cmn->num_dns + 1,
13718c2ecf20Sopenharmony_ci				sizeof(*cmn->dns), GFP_KERNEL);
13728c2ecf20Sopenharmony_ci	if (!cmn->dns)
13738c2ecf20Sopenharmony_ci		return -ENOMEM;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	/* Pass 2: now we can actually populate the nodes */
13768c2ecf20Sopenharmony_ci	dn = cmn->dns;
13778c2ecf20Sopenharmony_ci	for (i = 0; i < cmn->num_xps; i++) {
13788c2ecf20Sopenharmony_ci		void __iomem *xp_region = cmn->base + xp_offset[i];
13798c2ecf20Sopenharmony_ci		struct arm_cmn_node *xp = dn++;
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci		arm_cmn_init_node_info(cmn, xp_offset[i], xp);
13828c2ecf20Sopenharmony_ci		arm_cmn_init_dtm(xp);
13838c2ecf20Sopenharmony_ci		/*
13848c2ecf20Sopenharmony_ci		 * Thanks to the order in which XP logical IDs seem to be
13858c2ecf20Sopenharmony_ci		 * assigned, we can handily infer the mesh X dimension by
13868c2ecf20Sopenharmony_ci		 * looking out for the XP at (0,1) without needing to know
13878c2ecf20Sopenharmony_ci		 * the exact node ID format, which we can later derive.
13888c2ecf20Sopenharmony_ci		 */
13898c2ecf20Sopenharmony_ci		if (xp->id == (1 << 3))
13908c2ecf20Sopenharmony_ci			cmn->mesh_x = xp->logid;
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci		reg = readq_relaxed(xp_region + CMN_CHILD_INFO);
13938c2ecf20Sopenharmony_ci		child_count = FIELD_GET(CMN_CI_CHILD_COUNT, reg);
13948c2ecf20Sopenharmony_ci		child_poff = FIELD_GET(CMN_CI_CHILD_PTR_OFFSET, reg);
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci		for (j = 0; j < child_count; j++) {
13978c2ecf20Sopenharmony_ci			reg = readq_relaxed(xp_region + child_poff + j * 8);
13988c2ecf20Sopenharmony_ci			/*
13998c2ecf20Sopenharmony_ci			 * Don't even try to touch anything external, since in general
14008c2ecf20Sopenharmony_ci			 * we haven't a clue how to power up arbitrary CHI requesters.
14018c2ecf20Sopenharmony_ci			 * As of CMN-600r1 these could only be RN-SAMs or CXLAs,
14028c2ecf20Sopenharmony_ci			 * neither of which have any PMU events anyway.
14038c2ecf20Sopenharmony_ci			 * (Actually, CXLAs do seem to have grown some events in r1p2,
14048c2ecf20Sopenharmony_ci			 * but they don't go to regular XP DTMs, and they depend on
14058c2ecf20Sopenharmony_ci			 * secure configuration which we can't easily deal with)
14068c2ecf20Sopenharmony_ci			 */
14078c2ecf20Sopenharmony_ci			if (reg & CMN_CHILD_NODE_EXTERNAL) {
14088c2ecf20Sopenharmony_ci				dev_dbg(cmn->dev, "ignoring external node %llx\n", reg);
14098c2ecf20Sopenharmony_ci				continue;
14108c2ecf20Sopenharmony_ci			}
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci			arm_cmn_init_node_info(cmn, reg & CMN_CHILD_NODE_ADDR, dn);
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci			switch (dn->type) {
14158c2ecf20Sopenharmony_ci			case CMN_TYPE_DTC:
14168c2ecf20Sopenharmony_ci				cmn->num_dtcs++;
14178c2ecf20Sopenharmony_ci				dn++;
14188c2ecf20Sopenharmony_ci				break;
14198c2ecf20Sopenharmony_ci			/* These guys have PMU events */
14208c2ecf20Sopenharmony_ci			case CMN_TYPE_DVM:
14218c2ecf20Sopenharmony_ci			case CMN_TYPE_HNI:
14228c2ecf20Sopenharmony_ci			case CMN_TYPE_HNF:
14238c2ecf20Sopenharmony_ci			case CMN_TYPE_SBSX:
14248c2ecf20Sopenharmony_ci			case CMN_TYPE_RNI:
14258c2ecf20Sopenharmony_ci			case CMN_TYPE_RND:
14268c2ecf20Sopenharmony_ci			case CMN_TYPE_CXRA:
14278c2ecf20Sopenharmony_ci			case CMN_TYPE_CXHA:
14288c2ecf20Sopenharmony_ci				dn++;
14298c2ecf20Sopenharmony_ci				break;
14308c2ecf20Sopenharmony_ci			/* Nothing to see here */
14318c2ecf20Sopenharmony_ci			case CMN_TYPE_RNSAM:
14328c2ecf20Sopenharmony_ci			case CMN_TYPE_CXLA:
14338c2ecf20Sopenharmony_ci				break;
14348c2ecf20Sopenharmony_ci			/* Something has gone horribly wrong */
14358c2ecf20Sopenharmony_ci			default:
14368c2ecf20Sopenharmony_ci				dev_err(cmn->dev, "invalid device node type: 0x%x\n", dn->type);
14378c2ecf20Sopenharmony_ci				return -ENODEV;
14388c2ecf20Sopenharmony_ci			}
14398c2ecf20Sopenharmony_ci		}
14408c2ecf20Sopenharmony_ci	}
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	/* Correct for any nodes we skipped */
14438c2ecf20Sopenharmony_ci	cmn->num_dns = dn - cmn->dns;
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	/*
14468c2ecf20Sopenharmony_ci	 * If mesh_x wasn't set during discovery then we never saw
14478c2ecf20Sopenharmony_ci	 * an XP at (0,1), thus we must have an Nx1 configuration.
14488c2ecf20Sopenharmony_ci	 */
14498c2ecf20Sopenharmony_ci	if (!cmn->mesh_x)
14508c2ecf20Sopenharmony_ci		cmn->mesh_x = cmn->num_xps;
14518c2ecf20Sopenharmony_ci	cmn->mesh_y = cmn->num_xps / cmn->mesh_x;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	dev_dbg(cmn->dev, "mesh %dx%d, ID width %d\n",
14548c2ecf20Sopenharmony_ci		cmn->mesh_x, cmn->mesh_y, arm_cmn_xyidbits(cmn));
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	return 0;
14578c2ecf20Sopenharmony_ci}
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_cistatic int arm_cmn_acpi_probe(struct platform_device *pdev, struct arm_cmn *cmn)
14608c2ecf20Sopenharmony_ci{
14618c2ecf20Sopenharmony_ci	struct resource *cfg, *root;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	cfg = platform_get_resource(pdev, IORESOURCE_MEM, 0);
14648c2ecf20Sopenharmony_ci	if (!cfg)
14658c2ecf20Sopenharmony_ci		return -EINVAL;
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	root = platform_get_resource(pdev, IORESOURCE_MEM, 1);
14688c2ecf20Sopenharmony_ci	if (!root)
14698c2ecf20Sopenharmony_ci		return -EINVAL;
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	if (!resource_contains(cfg, root))
14728c2ecf20Sopenharmony_ci		swap(cfg, root);
14738c2ecf20Sopenharmony_ci	/*
14748c2ecf20Sopenharmony_ci	 * Note that devm_ioremap_resource() is dumb and won't let the platform
14758c2ecf20Sopenharmony_ci	 * device claim cfg when the ACPI companion device has already claimed
14768c2ecf20Sopenharmony_ci	 * root within it. But since they *are* already both claimed in the
14778c2ecf20Sopenharmony_ci	 * appropriate name, we don't really need to do it again here anyway.
14788c2ecf20Sopenharmony_ci	 */
14798c2ecf20Sopenharmony_ci	cmn->base = devm_ioremap(cmn->dev, cfg->start, resource_size(cfg));
14808c2ecf20Sopenharmony_ci	if (!cmn->base)
14818c2ecf20Sopenharmony_ci		return -ENOMEM;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	return root->start - cfg->start;
14848c2ecf20Sopenharmony_ci}
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_cistatic int arm_cmn_of_probe(struct platform_device *pdev, struct arm_cmn *cmn)
14878c2ecf20Sopenharmony_ci{
14888c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
14898c2ecf20Sopenharmony_ci	u32 rootnode;
14908c2ecf20Sopenharmony_ci	int ret;
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	cmn->base = devm_platform_ioremap_resource(pdev, 0);
14938c2ecf20Sopenharmony_ci	if (IS_ERR(cmn->base))
14948c2ecf20Sopenharmony_ci		return PTR_ERR(cmn->base);
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "arm,root-node", &rootnode);
14978c2ecf20Sopenharmony_ci	if (ret)
14988c2ecf20Sopenharmony_ci		return ret;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	return rootnode;
15018c2ecf20Sopenharmony_ci}
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_cistatic int arm_cmn_probe(struct platform_device *pdev)
15048c2ecf20Sopenharmony_ci{
15058c2ecf20Sopenharmony_ci	struct arm_cmn *cmn;
15068c2ecf20Sopenharmony_ci	const char *name;
15078c2ecf20Sopenharmony_ci	static atomic_t id;
15088c2ecf20Sopenharmony_ci	int err, rootnode;
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	cmn = devm_kzalloc(&pdev->dev, sizeof(*cmn), GFP_KERNEL);
15118c2ecf20Sopenharmony_ci	if (!cmn)
15128c2ecf20Sopenharmony_ci		return -ENOMEM;
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	cmn->dev = &pdev->dev;
15158c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, cmn);
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	if (has_acpi_companion(cmn->dev))
15188c2ecf20Sopenharmony_ci		rootnode = arm_cmn_acpi_probe(pdev, cmn);
15198c2ecf20Sopenharmony_ci	else
15208c2ecf20Sopenharmony_ci		rootnode = arm_cmn_of_probe(pdev, cmn);
15218c2ecf20Sopenharmony_ci	if (rootnode < 0)
15228c2ecf20Sopenharmony_ci		return rootnode;
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	err = arm_cmn_discover(cmn, rootnode);
15258c2ecf20Sopenharmony_ci	if (err)
15268c2ecf20Sopenharmony_ci		return err;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	err = arm_cmn_init_dtcs(cmn);
15298c2ecf20Sopenharmony_ci	if (err)
15308c2ecf20Sopenharmony_ci		return err;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	err = arm_cmn_init_irqs(cmn);
15338c2ecf20Sopenharmony_ci	if (err)
15348c2ecf20Sopenharmony_ci		return err;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	cmn->cpu = raw_smp_processor_id();
15378c2ecf20Sopenharmony_ci	cmn->pmu = (struct pmu) {
15388c2ecf20Sopenharmony_ci		.module = THIS_MODULE,
15398c2ecf20Sopenharmony_ci		.attr_groups = arm_cmn_attr_groups,
15408c2ecf20Sopenharmony_ci		.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
15418c2ecf20Sopenharmony_ci		.task_ctx_nr = perf_invalid_context,
15428c2ecf20Sopenharmony_ci		.pmu_enable = arm_cmn_pmu_enable,
15438c2ecf20Sopenharmony_ci		.pmu_disable = arm_cmn_pmu_disable,
15448c2ecf20Sopenharmony_ci		.event_init = arm_cmn_event_init,
15458c2ecf20Sopenharmony_ci		.add = arm_cmn_event_add,
15468c2ecf20Sopenharmony_ci		.del = arm_cmn_event_del,
15478c2ecf20Sopenharmony_ci		.start = arm_cmn_event_start,
15488c2ecf20Sopenharmony_ci		.stop = arm_cmn_event_stop,
15498c2ecf20Sopenharmony_ci		.read = arm_cmn_event_read,
15508c2ecf20Sopenharmony_ci		.start_txn = arm_cmn_start_txn,
15518c2ecf20Sopenharmony_ci		.commit_txn = arm_cmn_commit_txn,
15528c2ecf20Sopenharmony_ci		.cancel_txn = arm_cmn_end_txn,
15538c2ecf20Sopenharmony_ci	};
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	name = devm_kasprintf(cmn->dev, GFP_KERNEL, "arm_cmn_%d", atomic_fetch_inc(&id));
15568c2ecf20Sopenharmony_ci	if (!name)
15578c2ecf20Sopenharmony_ci		return -ENOMEM;
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	err = cpuhp_state_add_instance(arm_cmn_hp_state, &cmn->cpuhp_node);
15608c2ecf20Sopenharmony_ci	if (err)
15618c2ecf20Sopenharmony_ci		return err;
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	err = perf_pmu_register(&cmn->pmu, name, -1);
15648c2ecf20Sopenharmony_ci	if (err)
15658c2ecf20Sopenharmony_ci		cpuhp_state_remove_instance(arm_cmn_hp_state, &cmn->cpuhp_node);
15668c2ecf20Sopenharmony_ci	return err;
15678c2ecf20Sopenharmony_ci}
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_cistatic int arm_cmn_remove(struct platform_device *pdev)
15708c2ecf20Sopenharmony_ci{
15718c2ecf20Sopenharmony_ci	struct arm_cmn *cmn = platform_get_drvdata(pdev);
15728c2ecf20Sopenharmony_ci	int i;
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	writel_relaxed(0, cmn->dtc[0].base + CMN_DT_DTC_CTL);
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	perf_pmu_unregister(&cmn->pmu);
15778c2ecf20Sopenharmony_ci	cpuhp_state_remove_instance(arm_cmn_hp_state, &cmn->cpuhp_node);
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	for (i = 0; i < cmn->num_dtcs; i++)
15808c2ecf20Sopenharmony_ci		irq_set_affinity_hint(cmn->dtc[i].irq, NULL);
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	return 0;
15838c2ecf20Sopenharmony_ci}
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
15868c2ecf20Sopenharmony_cistatic const struct of_device_id arm_cmn_of_match[] = {
15878c2ecf20Sopenharmony_ci	{ .compatible = "arm,cmn-600", },
15888c2ecf20Sopenharmony_ci	{}
15898c2ecf20Sopenharmony_ci};
15908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, arm_cmn_of_match);
15918c2ecf20Sopenharmony_ci#endif
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
15948c2ecf20Sopenharmony_cistatic const struct acpi_device_id arm_cmn_acpi_match[] = {
15958c2ecf20Sopenharmony_ci	{ "ARMHC600", },
15968c2ecf20Sopenharmony_ci	{}
15978c2ecf20Sopenharmony_ci};
15988c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, arm_cmn_acpi_match);
15998c2ecf20Sopenharmony_ci#endif
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_cistatic struct platform_driver arm_cmn_driver = {
16028c2ecf20Sopenharmony_ci	.driver = {
16038c2ecf20Sopenharmony_ci		.name = "arm-cmn",
16048c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(arm_cmn_of_match),
16058c2ecf20Sopenharmony_ci		.acpi_match_table = ACPI_PTR(arm_cmn_acpi_match),
16068c2ecf20Sopenharmony_ci	},
16078c2ecf20Sopenharmony_ci	.probe = arm_cmn_probe,
16088c2ecf20Sopenharmony_ci	.remove = arm_cmn_remove,
16098c2ecf20Sopenharmony_ci};
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_cistatic int __init arm_cmn_init(void)
16128c2ecf20Sopenharmony_ci{
16138c2ecf20Sopenharmony_ci	int ret;
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
16168c2ecf20Sopenharmony_ci				      "perf/arm/cmn:online", NULL,
16178c2ecf20Sopenharmony_ci				      arm_cmn_pmu_offline_cpu);
16188c2ecf20Sopenharmony_ci	if (ret < 0)
16198c2ecf20Sopenharmony_ci		return ret;
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	arm_cmn_hp_state = ret;
16228c2ecf20Sopenharmony_ci	ret = platform_driver_register(&arm_cmn_driver);
16238c2ecf20Sopenharmony_ci	if (ret)
16248c2ecf20Sopenharmony_ci		cpuhp_remove_multi_state(arm_cmn_hp_state);
16258c2ecf20Sopenharmony_ci	return ret;
16268c2ecf20Sopenharmony_ci}
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_cistatic void __exit arm_cmn_exit(void)
16298c2ecf20Sopenharmony_ci{
16308c2ecf20Sopenharmony_ci	platform_driver_unregister(&arm_cmn_driver);
16318c2ecf20Sopenharmony_ci	cpuhp_remove_multi_state(arm_cmn_hp_state);
16328c2ecf20Sopenharmony_ci}
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_cimodule_init(arm_cmn_init);
16358c2ecf20Sopenharmony_cimodule_exit(arm_cmn_exit);
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robin Murphy <robin.murphy@arm.com>");
16388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Arm CMN-600 PMU driver");
16398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1640