xref: /linux/drivers/perf/arm-ni.c (revision 114143a595895c03fbefccfd8346fc51fb4908ed)
14d5a7680SRobin Murphy // SPDX-License-Identifier: GPL-2.0
24d5a7680SRobin Murphy // Copyright (C) 2022-2024 Arm Limited
34d5a7680SRobin Murphy // NI-700 Network-on-Chip PMU driver
44d5a7680SRobin Murphy 
54d5a7680SRobin Murphy #include <linux/acpi.h>
64d5a7680SRobin Murphy #include <linux/bitfield.h>
74d5a7680SRobin Murphy #include <linux/interrupt.h>
84d5a7680SRobin Murphy #include <linux/io.h>
94d5a7680SRobin Murphy #include <linux/io-64-nonatomic-lo-hi.h>
104d5a7680SRobin Murphy #include <linux/kernel.h>
114d5a7680SRobin Murphy #include <linux/module.h>
124d5a7680SRobin Murphy #include <linux/of.h>
134d5a7680SRobin Murphy #include <linux/perf_event.h>
144d5a7680SRobin Murphy #include <linux/platform_device.h>
154d5a7680SRobin Murphy #include <linux/slab.h>
164d5a7680SRobin Murphy 
174d5a7680SRobin Murphy /* Common registers */
184d5a7680SRobin Murphy #define NI_NODE_TYPE		0x000
194d5a7680SRobin Murphy #define NI_NODE_TYPE_NODE_ID	GENMASK(31, 16)
204d5a7680SRobin Murphy #define NI_NODE_TYPE_NODE_TYPE	GENMASK(15, 0)
214d5a7680SRobin Murphy 
224d5a7680SRobin Murphy #define NI_CHILD_NODE_INFO	0x004
234d5a7680SRobin Murphy #define NI_CHILD_PTR(n)		(0x008 + (n) * 4)
244d5a7680SRobin Murphy 
254d5a7680SRobin Murphy #define NI700_PMUSELA		0x00c
264d5a7680SRobin Murphy 
274d5a7680SRobin Murphy /* Config node */
284d5a7680SRobin Murphy #define NI_PERIPHERAL_ID0	0xfe0
294d5a7680SRobin Murphy #define NI_PIDR0_PART_7_0	GENMASK(7, 0)
304d5a7680SRobin Murphy #define NI_PERIPHERAL_ID1	0xfe4
314d5a7680SRobin Murphy #define NI_PIDR1_PART_11_8	GENMASK(3, 0)
324d5a7680SRobin Murphy #define NI_PERIPHERAL_ID2	0xfe8
334d5a7680SRobin Murphy #define NI_PIDR2_VERSION	GENMASK(7, 4)
344d5a7680SRobin Murphy 
354d5a7680SRobin Murphy /* PMU node */
364d5a7680SRobin Murphy #define NI_PMEVCNTR(n)		(0x008 + (n) * 8)
374d5a7680SRobin Murphy #define NI_PMCCNTR_L		0x0f8
384d5a7680SRobin Murphy #define NI_PMCCNTR_U		0x0fc
394d5a7680SRobin Murphy #define NI_PMEVTYPER(n)		(0x400 + (n) * 4)
404d5a7680SRobin Murphy #define NI_PMEVTYPER_NODE_TYPE	GENMASK(12, 9)
414d5a7680SRobin Murphy #define NI_PMEVTYPER_NODE_ID	GENMASK(8, 0)
424d5a7680SRobin Murphy #define NI_PMCNTENSET		0xc00
434d5a7680SRobin Murphy #define NI_PMCNTENCLR		0xc20
444d5a7680SRobin Murphy #define NI_PMINTENSET		0xc40
454d5a7680SRobin Murphy #define NI_PMINTENCLR		0xc60
464d5a7680SRobin Murphy #define NI_PMOVSCLR		0xc80
474d5a7680SRobin Murphy #define NI_PMOVSSET		0xcc0
484d5a7680SRobin Murphy #define NI_PMCFGR		0xe00
494d5a7680SRobin Murphy #define NI_PMCR			0xe04
504d5a7680SRobin Murphy #define NI_PMCR_RESET_CCNT	BIT(2)
514d5a7680SRobin Murphy #define NI_PMCR_RESET_EVCNT	BIT(1)
524d5a7680SRobin Murphy #define NI_PMCR_ENABLE		BIT(0)
534d5a7680SRobin Murphy 
544d5a7680SRobin Murphy #define NI_NUM_COUNTERS		8
554d5a7680SRobin Murphy #define NI_CCNT_IDX		31
564d5a7680SRobin Murphy 
574d5a7680SRobin Murphy /* Event attributes */
584d5a7680SRobin Murphy #define NI_CONFIG_TYPE		GENMASK_ULL(15, 0)
594d5a7680SRobin Murphy #define NI_CONFIG_NODEID	GENMASK_ULL(31, 16)
604d5a7680SRobin Murphy #define NI_CONFIG_EVENTID	GENMASK_ULL(47, 32)
614d5a7680SRobin Murphy 
624d5a7680SRobin Murphy #define NI_EVENT_TYPE(event)	FIELD_GET(NI_CONFIG_TYPE, (event)->attr.config)
634d5a7680SRobin Murphy #define NI_EVENT_NODEID(event)	FIELD_GET(NI_CONFIG_NODEID, (event)->attr.config)
644d5a7680SRobin Murphy #define NI_EVENT_EVENTID(event)	FIELD_GET(NI_CONFIG_EVENTID, (event)->attr.config)
654d5a7680SRobin Murphy 
664d5a7680SRobin Murphy enum ni_part {
674d5a7680SRobin Murphy 	PART_NI_700 = 0x43b,
684d5a7680SRobin Murphy 	PART_NI_710AE = 0x43d,
694d5a7680SRobin Murphy };
704d5a7680SRobin Murphy 
714d5a7680SRobin Murphy enum ni_node_type {
724d5a7680SRobin Murphy 	NI_GLOBAL,
734d5a7680SRobin Murphy 	NI_VOLTAGE,
744d5a7680SRobin Murphy 	NI_POWER,
754d5a7680SRobin Murphy 	NI_CLOCK,
764d5a7680SRobin Murphy 	NI_ASNI,
774d5a7680SRobin Murphy 	NI_AMNI,
784d5a7680SRobin Murphy 	NI_PMU,
794d5a7680SRobin Murphy 	NI_HSNI,
804d5a7680SRobin Murphy 	NI_HMNI,
814d5a7680SRobin Murphy 	NI_PMNI,
824d5a7680SRobin Murphy };
834d5a7680SRobin Murphy 
844d5a7680SRobin Murphy struct arm_ni_node {
854d5a7680SRobin Murphy 	void __iomem *base;
864d5a7680SRobin Murphy 	enum ni_node_type type;
874d5a7680SRobin Murphy 	u16 id;
884d5a7680SRobin Murphy 	u32 num_components;
894d5a7680SRobin Murphy };
904d5a7680SRobin Murphy 
914d5a7680SRobin Murphy struct arm_ni_unit {
924d5a7680SRobin Murphy 	void __iomem *pmusela;
934d5a7680SRobin Murphy 	enum ni_node_type type;
944d5a7680SRobin Murphy 	u16 id;
954d5a7680SRobin Murphy 	bool ns;
964d5a7680SRobin Murphy 	union {
974d5a7680SRobin Murphy 		__le64 pmusel;
984d5a7680SRobin Murphy 		u8 event[8];
994d5a7680SRobin Murphy 	};
1004d5a7680SRobin Murphy };
1014d5a7680SRobin Murphy 
1024d5a7680SRobin Murphy struct arm_ni_cd {
1034d5a7680SRobin Murphy 	void __iomem *pmu_base;
1044d5a7680SRobin Murphy 	u16 id;
1054d5a7680SRobin Murphy 	int num_units;
1064d5a7680SRobin Murphy 	int irq;
1074d5a7680SRobin Murphy 	int cpu;
1084d5a7680SRobin Murphy 	struct hlist_node cpuhp_node;
1094d5a7680SRobin Murphy 	struct pmu pmu;
1104d5a7680SRobin Murphy 	struct arm_ni_unit *units;
1114d5a7680SRobin Murphy 	struct perf_event *evcnt[NI_NUM_COUNTERS];
1124d5a7680SRobin Murphy 	struct perf_event *ccnt;
1134d5a7680SRobin Murphy };
1144d5a7680SRobin Murphy 
1154d5a7680SRobin Murphy struct arm_ni {
1164d5a7680SRobin Murphy 	struct device *dev;
1174d5a7680SRobin Murphy 	void __iomem *base;
1184d5a7680SRobin Murphy 	enum ni_part part;
1194d5a7680SRobin Murphy 	int id;
1204d5a7680SRobin Murphy 	int num_cds;
1214d5a7680SRobin Murphy 	struct arm_ni_cd cds[] __counted_by(num_cds);
1224d5a7680SRobin Murphy };
1234d5a7680SRobin Murphy 
1244d5a7680SRobin Murphy #define cd_to_ni(cd) container_of((cd), struct arm_ni, cds[(cd)->id])
1254d5a7680SRobin Murphy #define pmu_to_cd(p) container_of((p), struct arm_ni_cd, pmu)
1264d5a7680SRobin Murphy 
1274d5a7680SRobin Murphy #define cd_for_each_unit(cd, u) \
1284d5a7680SRobin Murphy 	for (struct arm_ni_unit *u = cd->units; u < cd->units + cd->num_units; u++)
1294d5a7680SRobin Murphy 
1304d5a7680SRobin Murphy static int arm_ni_hp_state;
1314d5a7680SRobin Murphy 
1324d5a7680SRobin Murphy struct arm_ni_event_attr {
1334d5a7680SRobin Murphy 	struct device_attribute attr;
1344d5a7680SRobin Murphy 	enum ni_node_type type;
1354d5a7680SRobin Murphy };
1364d5a7680SRobin Murphy 
1374d5a7680SRobin Murphy #define NI_EVENT_ATTR(_name, _type)					\
1384d5a7680SRobin Murphy 	(&((struct arm_ni_event_attr[]) {{				\
1394d5a7680SRobin Murphy 		.attr = __ATTR(_name, 0444, arm_ni_event_show, NULL),	\
1404d5a7680SRobin Murphy 		.type = _type,						\
1414d5a7680SRobin Murphy 	}})[0].attr.attr)
1424d5a7680SRobin Murphy 
arm_ni_event_show(struct device * dev,struct device_attribute * attr,char * buf)1434d5a7680SRobin Murphy static ssize_t arm_ni_event_show(struct device *dev,
1444d5a7680SRobin Murphy 				 struct device_attribute *attr, char *buf)
1454d5a7680SRobin Murphy {
1464d5a7680SRobin Murphy 	struct arm_ni_event_attr *eattr = container_of(attr, typeof(*eattr), attr);
1474d5a7680SRobin Murphy 
1484d5a7680SRobin Murphy 	if (eattr->type == NI_PMU)
1494d5a7680SRobin Murphy 		return sysfs_emit(buf, "type=0x%x\n", eattr->type);
1504d5a7680SRobin Murphy 
1514d5a7680SRobin Murphy 	return sysfs_emit(buf, "type=0x%x,eventid=?,nodeid=?\n", eattr->type);
1524d5a7680SRobin Murphy }
1534d5a7680SRobin Murphy 
arm_ni_event_attr_is_visible(struct kobject * kobj,struct attribute * attr,int unused)1544d5a7680SRobin Murphy static umode_t arm_ni_event_attr_is_visible(struct kobject *kobj,
1554d5a7680SRobin Murphy 					    struct attribute *attr, int unused)
1564d5a7680SRobin Murphy {
1574d5a7680SRobin Murphy 	struct device *dev = kobj_to_dev(kobj);
1584d5a7680SRobin Murphy 	struct arm_ni_cd *cd = pmu_to_cd(dev_get_drvdata(dev));
1594d5a7680SRobin Murphy 	struct arm_ni_event_attr *eattr;
1604d5a7680SRobin Murphy 
1614d5a7680SRobin Murphy 	eattr = container_of(attr, typeof(*eattr), attr.attr);
1624d5a7680SRobin Murphy 
1634d5a7680SRobin Murphy 	cd_for_each_unit(cd, unit) {
1644d5a7680SRobin Murphy 		if (unit->type == eattr->type && unit->ns)
1654d5a7680SRobin Murphy 			return attr->mode;
1664d5a7680SRobin Murphy 	}
1674d5a7680SRobin Murphy 
1684d5a7680SRobin Murphy 	return 0;
1694d5a7680SRobin Murphy }
1704d5a7680SRobin Murphy 
1714d5a7680SRobin Murphy static struct attribute *arm_ni_event_attrs[] = {
1724d5a7680SRobin Murphy 	NI_EVENT_ATTR(asni, NI_ASNI),
1734d5a7680SRobin Murphy 	NI_EVENT_ATTR(amni, NI_AMNI),
1744d5a7680SRobin Murphy 	NI_EVENT_ATTR(cycles, NI_PMU),
1754d5a7680SRobin Murphy 	NI_EVENT_ATTR(hsni, NI_HSNI),
1764d5a7680SRobin Murphy 	NI_EVENT_ATTR(hmni, NI_HMNI),
1774d5a7680SRobin Murphy 	NI_EVENT_ATTR(pmni, NI_PMNI),
1784d5a7680SRobin Murphy 	NULL
1794d5a7680SRobin Murphy };
1804d5a7680SRobin Murphy 
1814d5a7680SRobin Murphy static const struct attribute_group arm_ni_event_attrs_group = {
1824d5a7680SRobin Murphy 	.name = "events",
1834d5a7680SRobin Murphy 	.attrs = arm_ni_event_attrs,
1844d5a7680SRobin Murphy 	.is_visible = arm_ni_event_attr_is_visible,
1854d5a7680SRobin Murphy };
1864d5a7680SRobin Murphy 
1874d5a7680SRobin Murphy struct arm_ni_format_attr {
1884d5a7680SRobin Murphy 	struct device_attribute attr;
1894d5a7680SRobin Murphy 	u64 field;
1904d5a7680SRobin Murphy };
1914d5a7680SRobin Murphy 
1924d5a7680SRobin Murphy #define NI_FORMAT_ATTR(_name, _fld)					\
1934d5a7680SRobin Murphy 	(&((struct arm_ni_format_attr[]) {{				\
1944d5a7680SRobin Murphy 		.attr = __ATTR(_name, 0444, arm_ni_format_show, NULL),	\
1954d5a7680SRobin Murphy 		.field = _fld,						\
1964d5a7680SRobin Murphy 	}})[0].attr.attr)
1974d5a7680SRobin Murphy 
arm_ni_format_show(struct device * dev,struct device_attribute * attr,char * buf)1984d5a7680SRobin Murphy static ssize_t arm_ni_format_show(struct device *dev,
1994d5a7680SRobin Murphy 				  struct device_attribute *attr, char *buf)
2004d5a7680SRobin Murphy {
2014d5a7680SRobin Murphy 	struct arm_ni_format_attr *fmt = container_of(attr, typeof(*fmt), attr);
2024d5a7680SRobin Murphy 
2034d5a7680SRobin Murphy 	return sysfs_emit(buf, "config:%*pbl\n", 64, &fmt->field);
2044d5a7680SRobin Murphy }
2054d5a7680SRobin Murphy 
2064d5a7680SRobin Murphy static struct attribute *arm_ni_format_attrs[] = {
2074d5a7680SRobin Murphy 	NI_FORMAT_ATTR(type, NI_CONFIG_TYPE),
2084d5a7680SRobin Murphy 	NI_FORMAT_ATTR(nodeid, NI_CONFIG_NODEID),
2094d5a7680SRobin Murphy 	NI_FORMAT_ATTR(eventid, NI_CONFIG_EVENTID),
2104d5a7680SRobin Murphy 	NULL
2114d5a7680SRobin Murphy };
2124d5a7680SRobin Murphy 
2134d5a7680SRobin Murphy static const struct attribute_group arm_ni_format_attrs_group = {
2144d5a7680SRobin Murphy 	.name = "format",
2154d5a7680SRobin Murphy 	.attrs = arm_ni_format_attrs,
2164d5a7680SRobin Murphy };
2174d5a7680SRobin Murphy 
arm_ni_cpumask_show(struct device * dev,struct device_attribute * attr,char * buf)2184d5a7680SRobin Murphy static ssize_t arm_ni_cpumask_show(struct device *dev,
2194d5a7680SRobin Murphy 				   struct device_attribute *attr, char *buf)
2204d5a7680SRobin Murphy {
2214d5a7680SRobin Murphy 	struct arm_ni_cd *cd = pmu_to_cd(dev_get_drvdata(dev));
2224d5a7680SRobin Murphy 
2234d5a7680SRobin Murphy 	return cpumap_print_to_pagebuf(true, buf, cpumask_of(cd->cpu));
2244d5a7680SRobin Murphy }
2254d5a7680SRobin Murphy 
2264d5a7680SRobin Murphy static struct device_attribute arm_ni_cpumask_attr =
2274d5a7680SRobin Murphy 		__ATTR(cpumask, 0444, arm_ni_cpumask_show, NULL);
2284d5a7680SRobin Murphy 
arm_ni_identifier_show(struct device * dev,struct device_attribute * attr,char * buf)2294d5a7680SRobin Murphy static ssize_t arm_ni_identifier_show(struct device *dev,
2304d5a7680SRobin Murphy 				      struct device_attribute *attr, char *buf)
2314d5a7680SRobin Murphy {
2324d5a7680SRobin Murphy 	struct arm_ni *ni = cd_to_ni(pmu_to_cd(dev_get_drvdata(dev)));
2334d5a7680SRobin Murphy 	u32 reg = readl_relaxed(ni->base + NI_PERIPHERAL_ID2);
2344d5a7680SRobin Murphy 	int version = FIELD_GET(NI_PIDR2_VERSION, reg);
2354d5a7680SRobin Murphy 
2364d5a7680SRobin Murphy 	return sysfs_emit(buf, "%03x%02x\n", ni->part, version);
2374d5a7680SRobin Murphy }
2384d5a7680SRobin Murphy 
2394d5a7680SRobin Murphy static struct device_attribute arm_ni_identifier_attr =
2404d5a7680SRobin Murphy 		__ATTR(identifier, 0444, arm_ni_identifier_show, NULL);
2414d5a7680SRobin Murphy 
2424d5a7680SRobin Murphy static struct attribute *arm_ni_other_attrs[] = {
2434d5a7680SRobin Murphy 	&arm_ni_cpumask_attr.attr,
2444d5a7680SRobin Murphy 	&arm_ni_identifier_attr.attr,
2454d5a7680SRobin Murphy 	NULL
2464d5a7680SRobin Murphy };
2474d5a7680SRobin Murphy 
2484d5a7680SRobin Murphy static const struct attribute_group arm_ni_other_attr_group = {
2494d5a7680SRobin Murphy 	.attrs = arm_ni_other_attrs,
2504d5a7680SRobin Murphy 	NULL
2514d5a7680SRobin Murphy };
2524d5a7680SRobin Murphy 
2534d5a7680SRobin Murphy static const struct attribute_group *arm_ni_attr_groups[] = {
2544d5a7680SRobin Murphy 	&arm_ni_event_attrs_group,
2554d5a7680SRobin Murphy 	&arm_ni_format_attrs_group,
2564d5a7680SRobin Murphy 	&arm_ni_other_attr_group,
2574d5a7680SRobin Murphy 	NULL
2584d5a7680SRobin Murphy };
2594d5a7680SRobin Murphy 
arm_ni_pmu_enable(struct pmu * pmu)2604d5a7680SRobin Murphy static void arm_ni_pmu_enable(struct pmu *pmu)
2614d5a7680SRobin Murphy {
2624d5a7680SRobin Murphy 	writel_relaxed(NI_PMCR_ENABLE, pmu_to_cd(pmu)->pmu_base + NI_PMCR);
2634d5a7680SRobin Murphy }
2644d5a7680SRobin Murphy 
arm_ni_pmu_disable(struct pmu * pmu)2654d5a7680SRobin Murphy static void arm_ni_pmu_disable(struct pmu *pmu)
2664d5a7680SRobin Murphy {
2674d5a7680SRobin Murphy 	writel_relaxed(0, pmu_to_cd(pmu)->pmu_base + NI_PMCR);
2684d5a7680SRobin Murphy }
2694d5a7680SRobin Murphy 
2704d5a7680SRobin Murphy struct arm_ni_val {
2714d5a7680SRobin Murphy 	unsigned int evcnt;
2724d5a7680SRobin Murphy 	unsigned int ccnt;
2734d5a7680SRobin Murphy };
2744d5a7680SRobin Murphy 
arm_ni_val_count_event(struct perf_event * evt,struct arm_ni_val * val)2754d5a7680SRobin Murphy static bool arm_ni_val_count_event(struct perf_event *evt, struct arm_ni_val *val)
2764d5a7680SRobin Murphy {
2774d5a7680SRobin Murphy 	if (is_software_event(evt))
2784d5a7680SRobin Murphy 		return true;
2794d5a7680SRobin Murphy 
2804d5a7680SRobin Murphy 	if (NI_EVENT_TYPE(evt) == NI_PMU) {
2814d5a7680SRobin Murphy 		val->ccnt++;
2824d5a7680SRobin Murphy 		return val->ccnt <= 1;
2834d5a7680SRobin Murphy 	}
2844d5a7680SRobin Murphy 
2854d5a7680SRobin Murphy 	val->evcnt++;
2864d5a7680SRobin Murphy 	return val->evcnt <= NI_NUM_COUNTERS;
2874d5a7680SRobin Murphy }
2884d5a7680SRobin Murphy 
arm_ni_validate_group(struct perf_event * event)2894d5a7680SRobin Murphy static int arm_ni_validate_group(struct perf_event *event)
2904d5a7680SRobin Murphy {
2914d5a7680SRobin Murphy 	struct perf_event *sibling, *leader = event->group_leader;
2924d5a7680SRobin Murphy 	struct arm_ni_val val = { 0 };
2934d5a7680SRobin Murphy 
2944d5a7680SRobin Murphy 	if (leader == event)
2954d5a7680SRobin Murphy 		return 0;
2964d5a7680SRobin Murphy 
2974d5a7680SRobin Murphy 	arm_ni_val_count_event(event, &val);
2984d5a7680SRobin Murphy 	if (!arm_ni_val_count_event(leader, &val))
2994d5a7680SRobin Murphy 		return -EINVAL;
3004d5a7680SRobin Murphy 
3014d5a7680SRobin Murphy 	for_each_sibling_event(sibling, leader) {
3024d5a7680SRobin Murphy 		if (!arm_ni_val_count_event(sibling, &val))
3034d5a7680SRobin Murphy 			return -EINVAL;
3044d5a7680SRobin Murphy 	}
3054d5a7680SRobin Murphy 	return 0;
3064d5a7680SRobin Murphy }
3074d5a7680SRobin Murphy 
arm_ni_event_init(struct perf_event * event)3084d5a7680SRobin Murphy static int arm_ni_event_init(struct perf_event *event)
3094d5a7680SRobin Murphy {
3104d5a7680SRobin Murphy 	struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
3114d5a7680SRobin Murphy 
3124d5a7680SRobin Murphy 	if (event->attr.type != event->pmu->type)
3134d5a7680SRobin Murphy 		return -ENOENT;
3144d5a7680SRobin Murphy 
3154d5a7680SRobin Murphy 	if (is_sampling_event(event))
3164d5a7680SRobin Murphy 		return -EINVAL;
3174d5a7680SRobin Murphy 
3184d5a7680SRobin Murphy 	event->cpu = cd->cpu;
3194d5a7680SRobin Murphy 	if (NI_EVENT_TYPE(event) == NI_PMU)
3204d5a7680SRobin Murphy 		return arm_ni_validate_group(event);
3214d5a7680SRobin Murphy 
3224d5a7680SRobin Murphy 	cd_for_each_unit(cd, unit) {
3234d5a7680SRobin Murphy 		if (unit->type == NI_EVENT_TYPE(event) &&
3244d5a7680SRobin Murphy 		    unit->id == NI_EVENT_NODEID(event) && unit->ns) {
3254d5a7680SRobin Murphy 			event->hw.config_base = (unsigned long)unit;
3264d5a7680SRobin Murphy 			return arm_ni_validate_group(event);
3274d5a7680SRobin Murphy 		}
3284d5a7680SRobin Murphy 	}
3294d5a7680SRobin Murphy 	return -EINVAL;
3304d5a7680SRobin Murphy }
3314d5a7680SRobin Murphy 
arm_ni_read_ccnt(struct arm_ni_cd * cd)3324d5a7680SRobin Murphy static u64 arm_ni_read_ccnt(struct arm_ni_cd *cd)
3334d5a7680SRobin Murphy {
3344d5a7680SRobin Murphy 	u64 l, u_old, u_new;
3354d5a7680SRobin Murphy 	int retries = 3; /* 1st time unlucky, 2nd improbable, 3rd just broken */
3364d5a7680SRobin Murphy 
3374d5a7680SRobin Murphy 	u_new = readl_relaxed(cd->pmu_base + NI_PMCCNTR_U);
3384d5a7680SRobin Murphy 	do {
3394d5a7680SRobin Murphy 		u_old = u_new;
3404d5a7680SRobin Murphy 		l = readl_relaxed(cd->pmu_base + NI_PMCCNTR_L);
3414d5a7680SRobin Murphy 		u_new = readl_relaxed(cd->pmu_base + NI_PMCCNTR_U);
3424d5a7680SRobin Murphy 	} while (u_new != u_old && --retries);
3434d5a7680SRobin Murphy 	WARN_ON(!retries);
3444d5a7680SRobin Murphy 
3454d5a7680SRobin Murphy 	return (u_new << 32) | l;
3464d5a7680SRobin Murphy }
3474d5a7680SRobin Murphy 
arm_ni_event_read(struct perf_event * event)3484d5a7680SRobin Murphy static void arm_ni_event_read(struct perf_event *event)
3494d5a7680SRobin Murphy {
3504d5a7680SRobin Murphy 	struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
3514d5a7680SRobin Murphy 	struct hw_perf_event *hw = &event->hw;
3524d5a7680SRobin Murphy 	u64 count, prev;
3534d5a7680SRobin Murphy 	bool ccnt = hw->idx == NI_CCNT_IDX;
3544d5a7680SRobin Murphy 
3554d5a7680SRobin Murphy 	do {
3564d5a7680SRobin Murphy 		prev = local64_read(&hw->prev_count);
3574d5a7680SRobin Murphy 		if (ccnt)
3584d5a7680SRobin Murphy 			count = arm_ni_read_ccnt(cd);
3594d5a7680SRobin Murphy 		else
3604d5a7680SRobin Murphy 			count = readl_relaxed(cd->pmu_base + NI_PMEVCNTR(hw->idx));
3614d5a7680SRobin Murphy 	} while (local64_cmpxchg(&hw->prev_count, prev, count) != prev);
3624d5a7680SRobin Murphy 
3634d5a7680SRobin Murphy 	count -= prev;
3644d5a7680SRobin Murphy 	if (!ccnt)
3654d5a7680SRobin Murphy 		count = (u32)count;
3664d5a7680SRobin Murphy 	local64_add(count, &event->count);
3674d5a7680SRobin Murphy }
3684d5a7680SRobin Murphy 
arm_ni_event_start(struct perf_event * event,int flags)3694d5a7680SRobin Murphy static void arm_ni_event_start(struct perf_event *event, int flags)
3704d5a7680SRobin Murphy {
3714d5a7680SRobin Murphy 	struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
3724d5a7680SRobin Murphy 
3734d5a7680SRobin Murphy 	writel_relaxed(1U << event->hw.idx, cd->pmu_base + NI_PMCNTENSET);
3744d5a7680SRobin Murphy }
3754d5a7680SRobin Murphy 
arm_ni_event_stop(struct perf_event * event,int flags)3764d5a7680SRobin Murphy static void arm_ni_event_stop(struct perf_event *event, int flags)
3774d5a7680SRobin Murphy {
3784d5a7680SRobin Murphy 	struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
3794d5a7680SRobin Murphy 
3804d5a7680SRobin Murphy 	writel_relaxed(1U << event->hw.idx, cd->pmu_base + NI_PMCNTENCLR);
3814d5a7680SRobin Murphy 	if (flags & PERF_EF_UPDATE)
3824d5a7680SRobin Murphy 		arm_ni_event_read(event);
3834d5a7680SRobin Murphy }
3844d5a7680SRobin Murphy 
arm_ni_init_ccnt(struct arm_ni_cd * cd)3854d5a7680SRobin Murphy static void arm_ni_init_ccnt(struct arm_ni_cd *cd)
3864d5a7680SRobin Murphy {
3874d5a7680SRobin Murphy 	local64_set(&cd->ccnt->hw.prev_count, S64_MIN);
3884d5a7680SRobin Murphy 	lo_hi_writeq_relaxed(S64_MIN, cd->pmu_base + NI_PMCCNTR_L);
3894d5a7680SRobin Murphy }
3904d5a7680SRobin Murphy 
arm_ni_init_evcnt(struct arm_ni_cd * cd,int idx)3914d5a7680SRobin Murphy static void arm_ni_init_evcnt(struct arm_ni_cd *cd, int idx)
3924d5a7680SRobin Murphy {
3934d5a7680SRobin Murphy 	local64_set(&cd->evcnt[idx]->hw.prev_count, S32_MIN);
3944d5a7680SRobin Murphy 	writel_relaxed(S32_MIN, cd->pmu_base + NI_PMEVCNTR(idx));
3954d5a7680SRobin Murphy }
3964d5a7680SRobin Murphy 
arm_ni_event_add(struct perf_event * event,int flags)3974d5a7680SRobin Murphy static int arm_ni_event_add(struct perf_event *event, int flags)
3984d5a7680SRobin Murphy {
3994d5a7680SRobin Murphy 	struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
4004d5a7680SRobin Murphy 	struct hw_perf_event *hw = &event->hw;
4014d5a7680SRobin Murphy 	struct arm_ni_unit *unit;
4024d5a7680SRobin Murphy 	enum ni_node_type type = NI_EVENT_TYPE(event);
4034d5a7680SRobin Murphy 	u32 reg;
4044d5a7680SRobin Murphy 
4054d5a7680SRobin Murphy 	if (type == NI_PMU) {
4064d5a7680SRobin Murphy 		if (cd->ccnt)
4074d5a7680SRobin Murphy 			return -ENOSPC;
4084d5a7680SRobin Murphy 		hw->idx = NI_CCNT_IDX;
4094d5a7680SRobin Murphy 		cd->ccnt = event;
4104d5a7680SRobin Murphy 		arm_ni_init_ccnt(cd);
4114d5a7680SRobin Murphy 	} else {
4124d5a7680SRobin Murphy 		hw->idx = 0;
4134d5a7680SRobin Murphy 		while (cd->evcnt[hw->idx]) {
4144d5a7680SRobin Murphy 			if (++hw->idx == NI_NUM_COUNTERS)
4154d5a7680SRobin Murphy 				return -ENOSPC;
4164d5a7680SRobin Murphy 		}
4174d5a7680SRobin Murphy 		cd->evcnt[hw->idx] = event;
4184d5a7680SRobin Murphy 		unit = (void *)hw->config_base;
4194d5a7680SRobin Murphy 		unit->event[hw->idx] = NI_EVENT_EVENTID(event);
4204d5a7680SRobin Murphy 		arm_ni_init_evcnt(cd, hw->idx);
4214d5a7680SRobin Murphy 		lo_hi_writeq_relaxed(le64_to_cpu(unit->pmusel), unit->pmusela);
4224d5a7680SRobin Murphy 
4234d5a7680SRobin Murphy 		reg = FIELD_PREP(NI_PMEVTYPER_NODE_TYPE, type) |
4244d5a7680SRobin Murphy 		      FIELD_PREP(NI_PMEVTYPER_NODE_ID, NI_EVENT_NODEID(event));
4254d5a7680SRobin Murphy 		writel_relaxed(reg, cd->pmu_base + NI_PMEVTYPER(hw->idx));
4264d5a7680SRobin Murphy 	}
4274d5a7680SRobin Murphy 	if (flags & PERF_EF_START)
4284d5a7680SRobin Murphy 		arm_ni_event_start(event, 0);
4294d5a7680SRobin Murphy 	return 0;
4304d5a7680SRobin Murphy }
4314d5a7680SRobin Murphy 
arm_ni_event_del(struct perf_event * event,int flags)4324d5a7680SRobin Murphy static void arm_ni_event_del(struct perf_event *event, int flags)
4334d5a7680SRobin Murphy {
4344d5a7680SRobin Murphy 	struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
4354d5a7680SRobin Murphy 	struct hw_perf_event *hw = &event->hw;
4364d5a7680SRobin Murphy 
4374d5a7680SRobin Murphy 	arm_ni_event_stop(event, PERF_EF_UPDATE);
4384d5a7680SRobin Murphy 
4394d5a7680SRobin Murphy 	if (hw->idx == NI_CCNT_IDX)
4404d5a7680SRobin Murphy 		cd->ccnt = NULL;
4414d5a7680SRobin Murphy 	else
4424d5a7680SRobin Murphy 		cd->evcnt[hw->idx] = NULL;
4434d5a7680SRobin Murphy }
4444d5a7680SRobin Murphy 
arm_ni_handle_irq(int irq,void * dev_id)4454d5a7680SRobin Murphy static irqreturn_t arm_ni_handle_irq(int irq, void *dev_id)
4464d5a7680SRobin Murphy {
4474d5a7680SRobin Murphy 	struct arm_ni_cd *cd = dev_id;
4484d5a7680SRobin Murphy 	irqreturn_t ret = IRQ_NONE;
4494d5a7680SRobin Murphy 	u32 reg = readl_relaxed(cd->pmu_base + NI_PMOVSCLR);
4504d5a7680SRobin Murphy 
4514d5a7680SRobin Murphy 	if (reg & (1U << NI_CCNT_IDX)) {
4524d5a7680SRobin Murphy 		ret = IRQ_HANDLED;
4534d5a7680SRobin Murphy 		if (!(WARN_ON(!cd->ccnt))) {
4544d5a7680SRobin Murphy 			arm_ni_event_read(cd->ccnt);
4554d5a7680SRobin Murphy 			arm_ni_init_ccnt(cd);
4564d5a7680SRobin Murphy 		}
4574d5a7680SRobin Murphy 	}
4584d5a7680SRobin Murphy 	for (int i = 0; i < NI_NUM_COUNTERS; i++) {
4594d5a7680SRobin Murphy 		if (!(reg & (1U << i)))
4604d5a7680SRobin Murphy 			continue;
4614d5a7680SRobin Murphy 		ret = IRQ_HANDLED;
4624d5a7680SRobin Murphy 		if (!(WARN_ON(!cd->evcnt[i]))) {
4634d5a7680SRobin Murphy 			arm_ni_event_read(cd->evcnt[i]);
4644d5a7680SRobin Murphy 			arm_ni_init_evcnt(cd, i);
4654d5a7680SRobin Murphy 		}
4664d5a7680SRobin Murphy 	}
4674d5a7680SRobin Murphy 	writel_relaxed(reg, cd->pmu_base + NI_PMOVSCLR);
4684d5a7680SRobin Murphy 	return ret;
4694d5a7680SRobin Murphy }
4704d5a7680SRobin Murphy 
arm_ni_init_cd(struct arm_ni * ni,struct arm_ni_node * node,u64 res_start)4714d5a7680SRobin Murphy static int arm_ni_init_cd(struct arm_ni *ni, struct arm_ni_node *node, u64 res_start)
4724d5a7680SRobin Murphy {
4734d5a7680SRobin Murphy 	struct arm_ni_cd *cd = ni->cds + node->id;
4744d5a7680SRobin Murphy 	const char *name;
4754d5a7680SRobin Murphy 	int err;
4764d5a7680SRobin Murphy 
4774d5a7680SRobin Murphy 	cd->id = node->id;
4784d5a7680SRobin Murphy 	cd->num_units = node->num_components;
4794d5a7680SRobin Murphy 	cd->units = devm_kcalloc(ni->dev, cd->num_units, sizeof(*(cd->units)), GFP_KERNEL);
4804d5a7680SRobin Murphy 	if (!cd->units)
4814d5a7680SRobin Murphy 		return -ENOMEM;
4824d5a7680SRobin Murphy 
4834d5a7680SRobin Murphy 	for (int i = 0; i < cd->num_units; i++) {
4844d5a7680SRobin Murphy 		u32 reg = readl_relaxed(node->base + NI_CHILD_PTR(i));
4854d5a7680SRobin Murphy 		void __iomem *unit_base = ni->base + reg;
4864d5a7680SRobin Murphy 		struct arm_ni_unit *unit = cd->units + i;
4874d5a7680SRobin Murphy 
4884d5a7680SRobin Murphy 		reg = readl_relaxed(unit_base + NI_NODE_TYPE);
4894d5a7680SRobin Murphy 		unit->type = FIELD_GET(NI_NODE_TYPE_NODE_TYPE, reg);
4904d5a7680SRobin Murphy 		unit->id = FIELD_GET(NI_NODE_TYPE_NODE_ID, reg);
4914d5a7680SRobin Murphy 
4924d5a7680SRobin Murphy 		switch (unit->type) {
4934d5a7680SRobin Murphy 		case NI_PMU:
4944d5a7680SRobin Murphy 			reg = readl_relaxed(unit_base + NI_PMCFGR);
4954d5a7680SRobin Murphy 			if (!reg) {
4964d5a7680SRobin Murphy 				dev_info(ni->dev, "No access to PMU %d\n", cd->id);
4974d5a7680SRobin Murphy 				devm_kfree(ni->dev, cd->units);
4984d5a7680SRobin Murphy 				return 0;
4994d5a7680SRobin Murphy 			}
5004d5a7680SRobin Murphy 			unit->ns = true;
5014d5a7680SRobin Murphy 			cd->pmu_base = unit_base;
5024d5a7680SRobin Murphy 			break;
5034d5a7680SRobin Murphy 		case NI_ASNI:
5044d5a7680SRobin Murphy 		case NI_AMNI:
5054d5a7680SRobin Murphy 		case NI_HSNI:
5064d5a7680SRobin Murphy 		case NI_HMNI:
5074d5a7680SRobin Murphy 		case NI_PMNI:
5084d5a7680SRobin Murphy 			unit->pmusela = unit_base + NI700_PMUSELA;
5094d5a7680SRobin Murphy 			writel_relaxed(1, unit->pmusela);
5104d5a7680SRobin Murphy 			if (readl_relaxed(unit->pmusela) != 1)
5114d5a7680SRobin Murphy 				dev_info(ni->dev, "No access to node 0x%04x%04x\n", unit->id, unit->type);
5124d5a7680SRobin Murphy 			else
5134d5a7680SRobin Murphy 				unit->ns = true;
5144d5a7680SRobin Murphy 			break;
5154d5a7680SRobin Murphy 		default:
5164d5a7680SRobin Murphy 			/*
5174d5a7680SRobin Murphy 			 * e.g. FMU - thankfully bits 3:2 of FMU_ERR_FR0 are RES0 so
5184d5a7680SRobin Murphy 			 * can't alias any of the leaf node types we're looking for.
5194d5a7680SRobin Murphy 			 */
5204d5a7680SRobin Murphy 			dev_dbg(ni->dev, "Mystery node 0x%04x%04x\n", unit->id, unit->type);
5214d5a7680SRobin Murphy 			break;
5224d5a7680SRobin Murphy 		}
5234d5a7680SRobin Murphy 	}
5244d5a7680SRobin Murphy 
5254d5a7680SRobin Murphy 	res_start += cd->pmu_base - ni->base;
5264d5a7680SRobin Murphy 	if (!devm_request_mem_region(ni->dev, res_start, SZ_4K, dev_name(ni->dev))) {
5274d5a7680SRobin Murphy 		dev_err(ni->dev, "Failed to request PMU region 0x%llx\n", res_start);
5284d5a7680SRobin Murphy 		return -EBUSY;
5294d5a7680SRobin Murphy 	}
5304d5a7680SRobin Murphy 
5314d5a7680SRobin Murphy 	writel_relaxed(NI_PMCR_RESET_CCNT | NI_PMCR_RESET_EVCNT,
5324d5a7680SRobin Murphy 		       cd->pmu_base + NI_PMCR);
5334d5a7680SRobin Murphy 	writel_relaxed(U32_MAX, cd->pmu_base + NI_PMCNTENCLR);
5344d5a7680SRobin Murphy 	writel_relaxed(U32_MAX, cd->pmu_base + NI_PMOVSCLR);
5354d5a7680SRobin Murphy 	writel_relaxed(U32_MAX, cd->pmu_base + NI_PMINTENSET);
5364d5a7680SRobin Murphy 
5374d5a7680SRobin Murphy 	cd->irq = platform_get_irq(to_platform_device(ni->dev), cd->id);
5384d5a7680SRobin Murphy 	if (cd->irq < 0)
5394d5a7680SRobin Murphy 		return cd->irq;
5404d5a7680SRobin Murphy 
5414d5a7680SRobin Murphy 	err = devm_request_irq(ni->dev, cd->irq, arm_ni_handle_irq,
5424d5a7680SRobin Murphy 			       IRQF_NOBALANCING | IRQF_NO_THREAD,
5434d5a7680SRobin Murphy 			       dev_name(ni->dev), cd);
5444d5a7680SRobin Murphy 	if (err)
5454d5a7680SRobin Murphy 		return err;
5464d5a7680SRobin Murphy 
5474d5a7680SRobin Murphy 	cd->cpu = cpumask_local_spread(0, dev_to_node(ni->dev));
5484d5a7680SRobin Murphy 	cd->pmu = (struct pmu) {
5494d5a7680SRobin Murphy 		.module = THIS_MODULE,
5504d5a7680SRobin Murphy 		.parent = ni->dev,
5514d5a7680SRobin Murphy 		.attr_groups = arm_ni_attr_groups,
5524d5a7680SRobin Murphy 		.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
5534d5a7680SRobin Murphy 		.task_ctx_nr = perf_invalid_context,
5544d5a7680SRobin Murphy 		.pmu_enable = arm_ni_pmu_enable,
5554d5a7680SRobin Murphy 		.pmu_disable = arm_ni_pmu_disable,
5564d5a7680SRobin Murphy 		.event_init = arm_ni_event_init,
5574d5a7680SRobin Murphy 		.add = arm_ni_event_add,
5584d5a7680SRobin Murphy 		.del = arm_ni_event_del,
5594d5a7680SRobin Murphy 		.start = arm_ni_event_start,
5604d5a7680SRobin Murphy 		.stop = arm_ni_event_stop,
5614d5a7680SRobin Murphy 		.read = arm_ni_event_read,
5624d5a7680SRobin Murphy 	};
5634d5a7680SRobin Murphy 
5644d5a7680SRobin Murphy 	name = devm_kasprintf(ni->dev, GFP_KERNEL, "arm_ni_%d_cd_%d", ni->id, cd->id);
5654d5a7680SRobin Murphy 	if (!name)
5664d5a7680SRobin Murphy 		return -ENOMEM;
5674d5a7680SRobin Murphy 
5684d5a7680SRobin Murphy 	err = cpuhp_state_add_instance_nocalls(arm_ni_hp_state, &cd->cpuhp_node);
5694d5a7680SRobin Murphy 	if (err)
5704d5a7680SRobin Murphy 		return err;
5714d5a7680SRobin Murphy 
5724d5a7680SRobin Murphy 	err = perf_pmu_register(&cd->pmu, name, -1);
5734d5a7680SRobin Murphy 	if (err)
5744d5a7680SRobin Murphy 		cpuhp_state_remove_instance_nocalls(arm_ni_hp_state, &cd->cpuhp_node);
5754d5a7680SRobin Murphy 
5764d5a7680SRobin Murphy 	return err;
5774d5a7680SRobin Murphy }
5784d5a7680SRobin Murphy 
arm_ni_probe_domain(void __iomem * base,struct arm_ni_node * node)5794d5a7680SRobin Murphy static void arm_ni_probe_domain(void __iomem *base, struct arm_ni_node *node)
5804d5a7680SRobin Murphy {
5814d5a7680SRobin Murphy 	u32 reg = readl_relaxed(base + NI_NODE_TYPE);
5824d5a7680SRobin Murphy 
5834d5a7680SRobin Murphy 	node->base = base;
5844d5a7680SRobin Murphy 	node->type = FIELD_GET(NI_NODE_TYPE_NODE_TYPE, reg);
5854d5a7680SRobin Murphy 	node->id = FIELD_GET(NI_NODE_TYPE_NODE_ID, reg);
5864d5a7680SRobin Murphy 	node->num_components = readl_relaxed(base + NI_CHILD_NODE_INFO);
5874d5a7680SRobin Murphy }
5884d5a7680SRobin Murphy 
arm_ni_probe(struct platform_device * pdev)5894d5a7680SRobin Murphy static int arm_ni_probe(struct platform_device *pdev)
5904d5a7680SRobin Murphy {
5914d5a7680SRobin Murphy 	struct arm_ni_node cfg, vd, pd, cd;
5924d5a7680SRobin Murphy 	struct arm_ni *ni;
5934d5a7680SRobin Murphy 	struct resource *res;
5944d5a7680SRobin Murphy 	void __iomem *base;
5954d5a7680SRobin Murphy 	static atomic_t id;
5964d5a7680SRobin Murphy 	int num_cds;
5974d5a7680SRobin Murphy 	u32 reg, part;
5984d5a7680SRobin Murphy 
5994d5a7680SRobin Murphy 	/*
6004d5a7680SRobin Murphy 	 * We want to map the whole configuration space for ease of discovery,
6014d5a7680SRobin Murphy 	 * but the PMU pages are the only ones for which we can honestly claim
6024d5a7680SRobin Murphy 	 * exclusive ownership, so we'll request them explicitly once found.
6034d5a7680SRobin Murphy 	 */
6044d5a7680SRobin Murphy 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
6054d5a7680SRobin Murphy 	base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
606*2e091a80SDan Carpenter 	if (!base)
607*2e091a80SDan Carpenter 		return -ENOMEM;
6084d5a7680SRobin Murphy 
6094d5a7680SRobin Murphy 	arm_ni_probe_domain(base, &cfg);
6104d5a7680SRobin Murphy 	if (cfg.type != NI_GLOBAL)
6114d5a7680SRobin Murphy 		return -ENODEV;
6124d5a7680SRobin Murphy 
6134d5a7680SRobin Murphy 	reg = readl_relaxed(cfg.base + NI_PERIPHERAL_ID0);
6144d5a7680SRobin Murphy 	part = FIELD_GET(NI_PIDR0_PART_7_0, reg);
6154d5a7680SRobin Murphy 	reg = readl_relaxed(cfg.base + NI_PERIPHERAL_ID1);
6164d5a7680SRobin Murphy 	part |= FIELD_GET(NI_PIDR1_PART_11_8, reg) << 8;
6174d5a7680SRobin Murphy 
6184d5a7680SRobin Murphy 	switch (part) {
6194d5a7680SRobin Murphy 	case PART_NI_700:
6204d5a7680SRobin Murphy 	case PART_NI_710AE:
6214d5a7680SRobin Murphy 		break;
6224d5a7680SRobin Murphy 	default:
6234d5a7680SRobin Murphy 		dev_WARN(&pdev->dev, "Unknown part number: 0x%03x, this may go badly\n", part);
6244d5a7680SRobin Murphy 		break;
6254d5a7680SRobin Murphy 	}
6264d5a7680SRobin Murphy 
6274d5a7680SRobin Murphy 	num_cds = 0;
6284d5a7680SRobin Murphy 	for (int v = 0; v < cfg.num_components; v++) {
6294d5a7680SRobin Murphy 		reg = readl_relaxed(cfg.base + NI_CHILD_PTR(v));
6304d5a7680SRobin Murphy 		arm_ni_probe_domain(base + reg, &vd);
6314d5a7680SRobin Murphy 		for (int p = 0; p < vd.num_components; p++) {
6324d5a7680SRobin Murphy 			reg = readl_relaxed(vd.base + NI_CHILD_PTR(p));
6334d5a7680SRobin Murphy 			arm_ni_probe_domain(base + reg, &pd);
6344d5a7680SRobin Murphy 			num_cds += pd.num_components;
6354d5a7680SRobin Murphy 		}
6364d5a7680SRobin Murphy 	}
6374d5a7680SRobin Murphy 
6384d5a7680SRobin Murphy 	ni = devm_kzalloc(&pdev->dev, struct_size(ni, cds, num_cds), GFP_KERNEL);
6394d5a7680SRobin Murphy 	if (!ni)
6404d5a7680SRobin Murphy 		return -ENOMEM;
6414d5a7680SRobin Murphy 
6424d5a7680SRobin Murphy 	ni->dev = &pdev->dev;
6434d5a7680SRobin Murphy 	ni->base = base;
6444d5a7680SRobin Murphy 	ni->num_cds = num_cds;
6454d5a7680SRobin Murphy 	ni->part = part;
6464d5a7680SRobin Murphy 	ni->id = atomic_fetch_inc(&id);
6474d5a7680SRobin Murphy 
6484d5a7680SRobin Murphy 	for (int v = 0; v < cfg.num_components; v++) {
6494d5a7680SRobin Murphy 		reg = readl_relaxed(cfg.base + NI_CHILD_PTR(v));
6504d5a7680SRobin Murphy 		arm_ni_probe_domain(base + reg, &vd);
6514d5a7680SRobin Murphy 		for (int p = 0; p < vd.num_components; p++) {
6524d5a7680SRobin Murphy 			reg = readl_relaxed(vd.base + NI_CHILD_PTR(p));
6534d5a7680SRobin Murphy 			arm_ni_probe_domain(base + reg, &pd);
6544d5a7680SRobin Murphy 			for (int c = 0; c < pd.num_components; c++) {
6554d5a7680SRobin Murphy 				int ret;
6564d5a7680SRobin Murphy 
6574d5a7680SRobin Murphy 				reg = readl_relaxed(pd.base + NI_CHILD_PTR(c));
6584d5a7680SRobin Murphy 				arm_ni_probe_domain(base + reg, &cd);
6594d5a7680SRobin Murphy 				ret = arm_ni_init_cd(ni, &cd, res->start);
6604d5a7680SRobin Murphy 				if (ret)
6614d5a7680SRobin Murphy 					return ret;
6624d5a7680SRobin Murphy 			}
6634d5a7680SRobin Murphy 		}
6644d5a7680SRobin Murphy 	}
6654d5a7680SRobin Murphy 
6664d5a7680SRobin Murphy 	return 0;
6674d5a7680SRobin Murphy }
6684d5a7680SRobin Murphy 
arm_ni_remove(struct platform_device * pdev)6694d5a7680SRobin Murphy static void arm_ni_remove(struct platform_device *pdev)
6704d5a7680SRobin Murphy {
6714d5a7680SRobin Murphy 	struct arm_ni *ni = platform_get_drvdata(pdev);
6724d5a7680SRobin Murphy 
6734d5a7680SRobin Murphy 	for (int i = 0; i < ni->num_cds; i++) {
6744d5a7680SRobin Murphy 		struct arm_ni_cd *cd = ni->cds + i;
6754d5a7680SRobin Murphy 
6764d5a7680SRobin Murphy 		if (!cd->pmu_base)
6774d5a7680SRobin Murphy 			continue;
6784d5a7680SRobin Murphy 
6794d5a7680SRobin Murphy 		writel_relaxed(0, cd->pmu_base + NI_PMCR);
6804d5a7680SRobin Murphy 		writel_relaxed(U32_MAX, cd->pmu_base + NI_PMINTENCLR);
6814d5a7680SRobin Murphy 		perf_pmu_unregister(&cd->pmu);
6824d5a7680SRobin Murphy 		cpuhp_state_remove_instance_nocalls(arm_ni_hp_state, &cd->cpuhp_node);
6834d5a7680SRobin Murphy 	}
6844d5a7680SRobin Murphy }
6854d5a7680SRobin Murphy 
6864d5a7680SRobin Murphy #ifdef CONFIG_OF
6874d5a7680SRobin Murphy static const struct of_device_id arm_ni_of_match[] = {
6884d5a7680SRobin Murphy 	{ .compatible = "arm,ni-700" },
6894d5a7680SRobin Murphy 	{}
6904d5a7680SRobin Murphy };
6914d5a7680SRobin Murphy MODULE_DEVICE_TABLE(of, arm_ni_of_match);
6924d5a7680SRobin Murphy #endif
6934d5a7680SRobin Murphy 
6944d5a7680SRobin Murphy #ifdef CONFIG_ACPI
6954d5a7680SRobin Murphy static const struct acpi_device_id arm_ni_acpi_match[] = {
6964d5a7680SRobin Murphy 	{ "ARMHCB70" },
6974d5a7680SRobin Murphy 	{}
6984d5a7680SRobin Murphy };
6994d5a7680SRobin Murphy MODULE_DEVICE_TABLE(acpi, arm_ni_acpi_match);
7004d5a7680SRobin Murphy #endif
7014d5a7680SRobin Murphy 
7024d5a7680SRobin Murphy static struct platform_driver arm_ni_driver = {
7034d5a7680SRobin Murphy 	.driver = {
7044d5a7680SRobin Murphy 		.name = "arm-ni",
7054d5a7680SRobin Murphy 		.of_match_table = of_match_ptr(arm_ni_of_match),
7064d5a7680SRobin Murphy 		.acpi_match_table = ACPI_PTR(arm_ni_acpi_match),
7074d5a7680SRobin Murphy 	},
7084d5a7680SRobin Murphy 	.probe = arm_ni_probe,
7094d5a7680SRobin Murphy 	.remove = arm_ni_remove,
7104d5a7680SRobin Murphy };
7114d5a7680SRobin Murphy 
arm_ni_pmu_migrate(struct arm_ni_cd * cd,unsigned int cpu)7124d5a7680SRobin Murphy static void arm_ni_pmu_migrate(struct arm_ni_cd *cd, unsigned int cpu)
7134d5a7680SRobin Murphy {
7144d5a7680SRobin Murphy 	perf_pmu_migrate_context(&cd->pmu, cd->cpu, cpu);
7154d5a7680SRobin Murphy 	irq_set_affinity(cd->irq, cpumask_of(cpu));
7164d5a7680SRobin Murphy 	cd->cpu = cpu;
7174d5a7680SRobin Murphy }
7184d5a7680SRobin Murphy 
arm_ni_pmu_online_cpu(unsigned int cpu,struct hlist_node * cpuhp_node)7194d5a7680SRobin Murphy static int arm_ni_pmu_online_cpu(unsigned int cpu, struct hlist_node *cpuhp_node)
7204d5a7680SRobin Murphy {
7214d5a7680SRobin Murphy 	struct arm_ni_cd *cd;
7224d5a7680SRobin Murphy 	int node;
7234d5a7680SRobin Murphy 
7244d5a7680SRobin Murphy 	cd = hlist_entry_safe(cpuhp_node, struct arm_ni_cd, cpuhp_node);
7254d5a7680SRobin Murphy 	node = dev_to_node(cd_to_ni(cd)->dev);
7264d5a7680SRobin Murphy 	if (cpu_to_node(cd->cpu) != node && cpu_to_node(cpu) == node)
7274d5a7680SRobin Murphy 		arm_ni_pmu_migrate(cd, cpu);
7284d5a7680SRobin Murphy 	return 0;
7294d5a7680SRobin Murphy }
7304d5a7680SRobin Murphy 
arm_ni_pmu_offline_cpu(unsigned int cpu,struct hlist_node * cpuhp_node)7314d5a7680SRobin Murphy static int arm_ni_pmu_offline_cpu(unsigned int cpu, struct hlist_node *cpuhp_node)
7324d5a7680SRobin Murphy {
7334d5a7680SRobin Murphy 	struct arm_ni_cd *cd;
7344d5a7680SRobin Murphy 	unsigned int target;
7354d5a7680SRobin Murphy 	int node;
7364d5a7680SRobin Murphy 
7374d5a7680SRobin Murphy 	cd = hlist_entry_safe(cpuhp_node, struct arm_ni_cd, cpuhp_node);
7384d5a7680SRobin Murphy 	if (cpu != cd->cpu)
7394d5a7680SRobin Murphy 		return 0;
7404d5a7680SRobin Murphy 
7414d5a7680SRobin Murphy 	node = dev_to_node(cd_to_ni(cd)->dev);
7424d5a7680SRobin Murphy 	target = cpumask_any_and_but(cpumask_of_node(node), cpu_online_mask, cpu);
7434d5a7680SRobin Murphy 	if (target >= nr_cpu_ids)
7444d5a7680SRobin Murphy 		target = cpumask_any_but(cpu_online_mask, cpu);
7454d5a7680SRobin Murphy 
7464d5a7680SRobin Murphy 	if (target < nr_cpu_ids)
7474d5a7680SRobin Murphy 		arm_ni_pmu_migrate(cd, target);
7484d5a7680SRobin Murphy 	return 0;
7494d5a7680SRobin Murphy }
7504d5a7680SRobin Murphy 
arm_ni_init(void)7514d5a7680SRobin Murphy static int __init arm_ni_init(void)
7524d5a7680SRobin Murphy {
7534d5a7680SRobin Murphy 	int ret;
7544d5a7680SRobin Murphy 
7554d5a7680SRobin Murphy 	ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
7564d5a7680SRobin Murphy 				      "perf/arm/ni:online",
7574d5a7680SRobin Murphy 				      arm_ni_pmu_online_cpu,
7584d5a7680SRobin Murphy 				      arm_ni_pmu_offline_cpu);
7594d5a7680SRobin Murphy 	if (ret < 0)
7604d5a7680SRobin Murphy 		return ret;
7614d5a7680SRobin Murphy 
7624d5a7680SRobin Murphy 	arm_ni_hp_state = ret;
7634d5a7680SRobin Murphy 
7644d5a7680SRobin Murphy 	ret = platform_driver_register(&arm_ni_driver);
7654d5a7680SRobin Murphy 	if (ret)
7664d5a7680SRobin Murphy 		cpuhp_remove_multi_state(arm_ni_hp_state);
7674d5a7680SRobin Murphy 	return ret;
7684d5a7680SRobin Murphy }
7694d5a7680SRobin Murphy 
arm_ni_exit(void)7704d5a7680SRobin Murphy static void __exit arm_ni_exit(void)
7714d5a7680SRobin Murphy {
7724d5a7680SRobin Murphy 	platform_driver_unregister(&arm_ni_driver);
7734d5a7680SRobin Murphy 	cpuhp_remove_multi_state(arm_ni_hp_state);
7744d5a7680SRobin Murphy }
7754d5a7680SRobin Murphy 
7764d5a7680SRobin Murphy module_init(arm_ni_init);
7774d5a7680SRobin Murphy module_exit(arm_ni_exit);
7784d5a7680SRobin Murphy 
7794d5a7680SRobin Murphy MODULE_AUTHOR("Robin Murphy <robin.murphy@arm.com>");
7804d5a7680SRobin Murphy MODULE_DESCRIPTION("Arm NI-700 PMU driver");
7814d5a7680SRobin Murphy MODULE_LICENSE("GPL v2");
782