18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2017 NXP
48c2ecf20Sopenharmony_ci * Copyright 2016 Freescale Semiconductor, Inc.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/of_address.h>
148c2ecf20Sopenharmony_ci#include <linux/of_device.h>
158c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
168c2ecf20Sopenharmony_ci#include <linux/perf_event.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define COUNTER_CNTL		0x0
208c2ecf20Sopenharmony_ci#define COUNTER_READ		0x20
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define COUNTER_DPCR1		0x30
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define CNTL_OVER		0x1
258c2ecf20Sopenharmony_ci#define CNTL_CLEAR		0x2
268c2ecf20Sopenharmony_ci#define CNTL_EN			0x4
278c2ecf20Sopenharmony_ci#define CNTL_EN_MASK		0xFFFFFFFB
288c2ecf20Sopenharmony_ci#define CNTL_CLEAR_MASK		0xFFFFFFFD
298c2ecf20Sopenharmony_ci#define CNTL_OVER_MASK		0xFFFFFFFE
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define CNTL_CSV_SHIFT		24
328c2ecf20Sopenharmony_ci#define CNTL_CSV_MASK		(0xFFU << CNTL_CSV_SHIFT)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define EVENT_CYCLES_ID		0
358c2ecf20Sopenharmony_ci#define EVENT_CYCLES_COUNTER	0
368c2ecf20Sopenharmony_ci#define NUM_COUNTERS		4
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define AXI_MASKING_REVERT	0xffff0000	/* AXI_MASKING(MSB 16bits) + AXI_ID(LSB 16bits) */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define to_ddr_pmu(p)		container_of(p, struct ddr_pmu, pmu)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define DDR_PERF_DEV_NAME	"imx8_ddr"
438c2ecf20Sopenharmony_ci#define DDR_CPUHP_CB_NAME	DDR_PERF_DEV_NAME "_perf_pmu"
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic DEFINE_IDA(ddr_ida);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* DDR Perf hardware feature */
488c2ecf20Sopenharmony_ci#define DDR_CAP_AXI_ID_FILTER			0x1     /* support AXI ID filter */
498c2ecf20Sopenharmony_ci#define DDR_CAP_AXI_ID_FILTER_ENHANCED		0x3     /* support enhanced AXI ID filter */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistruct fsl_ddr_devtype_data {
528c2ecf20Sopenharmony_ci	unsigned int quirks;    /* quirks needed for different DDR Perf core */
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic const struct fsl_ddr_devtype_data imx8_devtype_data;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic const struct fsl_ddr_devtype_data imx8m_devtype_data = {
588c2ecf20Sopenharmony_ci	.quirks = DDR_CAP_AXI_ID_FILTER,
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const struct fsl_ddr_devtype_data imx8mp_devtype_data = {
628c2ecf20Sopenharmony_ci	.quirks = DDR_CAP_AXI_ID_FILTER_ENHANCED,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic const struct of_device_id imx_ddr_pmu_dt_ids[] = {
668c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx8-ddr-pmu", .data = &imx8_devtype_data},
678c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx8m-ddr-pmu", .data = &imx8m_devtype_data},
688c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx8mp-ddr-pmu", .data = &imx8mp_devtype_data},
698c2ecf20Sopenharmony_ci	{ /* sentinel */ }
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx_ddr_pmu_dt_ids);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct ddr_pmu {
748c2ecf20Sopenharmony_ci	struct pmu pmu;
758c2ecf20Sopenharmony_ci	void __iomem *base;
768c2ecf20Sopenharmony_ci	unsigned int cpu;
778c2ecf20Sopenharmony_ci	struct	hlist_node node;
788c2ecf20Sopenharmony_ci	struct	device *dev;
798c2ecf20Sopenharmony_ci	struct perf_event *events[NUM_COUNTERS];
808c2ecf20Sopenharmony_ci	int active_events;
818c2ecf20Sopenharmony_ci	enum cpuhp_state cpuhp_state;
828c2ecf20Sopenharmony_ci	const struct fsl_ddr_devtype_data *devtype_data;
838c2ecf20Sopenharmony_ci	int irq;
848c2ecf20Sopenharmony_ci	int id;
858c2ecf20Sopenharmony_ci	int active_counter;
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cienum ddr_perf_filter_capabilities {
898c2ecf20Sopenharmony_ci	PERF_CAP_AXI_ID_FILTER = 0,
908c2ecf20Sopenharmony_ci	PERF_CAP_AXI_ID_FILTER_ENHANCED,
918c2ecf20Sopenharmony_ci	PERF_CAP_AXI_ID_FEAT_MAX,
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic u32 ddr_perf_filter_cap_get(struct ddr_pmu *pmu, int cap)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	u32 quirks = pmu->devtype_data->quirks;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	switch (cap) {
998c2ecf20Sopenharmony_ci	case PERF_CAP_AXI_ID_FILTER:
1008c2ecf20Sopenharmony_ci		return !!(quirks & DDR_CAP_AXI_ID_FILTER);
1018c2ecf20Sopenharmony_ci	case PERF_CAP_AXI_ID_FILTER_ENHANCED:
1028c2ecf20Sopenharmony_ci		quirks &= DDR_CAP_AXI_ID_FILTER_ENHANCED;
1038c2ecf20Sopenharmony_ci		return quirks == DDR_CAP_AXI_ID_FILTER_ENHANCED;
1048c2ecf20Sopenharmony_ci	default:
1058c2ecf20Sopenharmony_ci		WARN(1, "unknown filter cap %d\n", cap);
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic ssize_t ddr_perf_filter_cap_show(struct device *dev,
1128c2ecf20Sopenharmony_ci					struct device_attribute *attr,
1138c2ecf20Sopenharmony_ci					char *buf)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = dev_get_drvdata(dev);
1168c2ecf20Sopenharmony_ci	struct dev_ext_attribute *ea =
1178c2ecf20Sopenharmony_ci		container_of(attr, struct dev_ext_attribute, attr);
1188c2ecf20Sopenharmony_ci	int cap = (long)ea->var;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%u\n",
1218c2ecf20Sopenharmony_ci			ddr_perf_filter_cap_get(pmu, cap));
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#define PERF_EXT_ATTR_ENTRY(_name, _func, _var)				\
1258c2ecf20Sopenharmony_ci	(&((struct dev_ext_attribute) {					\
1268c2ecf20Sopenharmony_ci		__ATTR(_name, 0444, _func, NULL), (void *)_var		\
1278c2ecf20Sopenharmony_ci	}).attr.attr)
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci#define PERF_FILTER_EXT_ATTR_ENTRY(_name, _var)				\
1308c2ecf20Sopenharmony_ci	PERF_EXT_ATTR_ENTRY(_name, ddr_perf_filter_cap_show, _var)
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic struct attribute *ddr_perf_filter_cap_attr[] = {
1338c2ecf20Sopenharmony_ci	PERF_FILTER_EXT_ATTR_ENTRY(filter, PERF_CAP_AXI_ID_FILTER),
1348c2ecf20Sopenharmony_ci	PERF_FILTER_EXT_ATTR_ENTRY(enhanced_filter, PERF_CAP_AXI_ID_FILTER_ENHANCED),
1358c2ecf20Sopenharmony_ci	NULL,
1368c2ecf20Sopenharmony_ci};
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic struct attribute_group ddr_perf_filter_cap_attr_group = {
1398c2ecf20Sopenharmony_ci	.name = "caps",
1408c2ecf20Sopenharmony_ci	.attrs = ddr_perf_filter_cap_attr,
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic ssize_t ddr_perf_cpumask_show(struct device *dev,
1448c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = dev_get_drvdata(dev);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return cpumap_print_to_pagebuf(true, buf, cpumask_of(pmu->cpu));
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic struct device_attribute ddr_perf_cpumask_attr =
1528c2ecf20Sopenharmony_ci	__ATTR(cpumask, 0444, ddr_perf_cpumask_show, NULL);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic struct attribute *ddr_perf_cpumask_attrs[] = {
1558c2ecf20Sopenharmony_ci	&ddr_perf_cpumask_attr.attr,
1568c2ecf20Sopenharmony_ci	NULL,
1578c2ecf20Sopenharmony_ci};
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic struct attribute_group ddr_perf_cpumask_attr_group = {
1608c2ecf20Sopenharmony_ci	.attrs = ddr_perf_cpumask_attrs,
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic ssize_t
1648c2ecf20Sopenharmony_ciddr_pmu_event_show(struct device *dev, struct device_attribute *attr,
1658c2ecf20Sopenharmony_ci		   char *page)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct perf_pmu_events_attr *pmu_attr;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr);
1708c2ecf20Sopenharmony_ci	return sprintf(page, "event=0x%02llx\n", pmu_attr->id);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci#define IMX8_DDR_PMU_EVENT_ATTR(_name, _id)				\
1748c2ecf20Sopenharmony_ci	(&((struct perf_pmu_events_attr[]) {				\
1758c2ecf20Sopenharmony_ci		{ .attr = __ATTR(_name, 0444, ddr_pmu_event_show, NULL),\
1768c2ecf20Sopenharmony_ci		  .id = _id, }						\
1778c2ecf20Sopenharmony_ci	})[0].attr.attr)
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic struct attribute *ddr_perf_events_attrs[] = {
1808c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(cycles, EVENT_CYCLES_ID),
1818c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(selfresh, 0x01),
1828c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(read-accesses, 0x04),
1838c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(write-accesses, 0x05),
1848c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(read-queue-depth, 0x08),
1858c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(write-queue-depth, 0x09),
1868c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(lp-read-credit-cnt, 0x10),
1878c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(hp-read-credit-cnt, 0x11),
1888c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(write-credit-cnt, 0x12),
1898c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(read-command, 0x20),
1908c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(write-command, 0x21),
1918c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(read-modify-write-command, 0x22),
1928c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(hp-read, 0x23),
1938c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(hp-req-nocredit, 0x24),
1948c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(hp-xact-credit, 0x25),
1958c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(lp-req-nocredit, 0x26),
1968c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(lp-xact-credit, 0x27),
1978c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(wr-xact-credit, 0x29),
1988c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(read-cycles, 0x2a),
1998c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(write-cycles, 0x2b),
2008c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(read-write-transition, 0x30),
2018c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(precharge, 0x31),
2028c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(activate, 0x32),
2038c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(load-mode, 0x33),
2048c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(perf-mwr, 0x34),
2058c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(read, 0x35),
2068c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(read-activate, 0x36),
2078c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(refresh, 0x37),
2088c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(write, 0x38),
2098c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(raw-hazard, 0x39),
2108c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(axid-read, 0x41),
2118c2ecf20Sopenharmony_ci	IMX8_DDR_PMU_EVENT_ATTR(axid-write, 0x42),
2128c2ecf20Sopenharmony_ci	NULL,
2138c2ecf20Sopenharmony_ci};
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic struct attribute_group ddr_perf_events_attr_group = {
2168c2ecf20Sopenharmony_ci	.name = "events",
2178c2ecf20Sopenharmony_ci	.attrs = ddr_perf_events_attrs,
2188c2ecf20Sopenharmony_ci};
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ciPMU_FORMAT_ATTR(event, "config:0-7");
2218c2ecf20Sopenharmony_ciPMU_FORMAT_ATTR(axi_id, "config1:0-15");
2228c2ecf20Sopenharmony_ciPMU_FORMAT_ATTR(axi_mask, "config1:16-31");
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic struct attribute *ddr_perf_format_attrs[] = {
2258c2ecf20Sopenharmony_ci	&format_attr_event.attr,
2268c2ecf20Sopenharmony_ci	&format_attr_axi_id.attr,
2278c2ecf20Sopenharmony_ci	&format_attr_axi_mask.attr,
2288c2ecf20Sopenharmony_ci	NULL,
2298c2ecf20Sopenharmony_ci};
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic struct attribute_group ddr_perf_format_attr_group = {
2328c2ecf20Sopenharmony_ci	.name = "format",
2338c2ecf20Sopenharmony_ci	.attrs = ddr_perf_format_attrs,
2348c2ecf20Sopenharmony_ci};
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic const struct attribute_group *attr_groups[] = {
2378c2ecf20Sopenharmony_ci	&ddr_perf_events_attr_group,
2388c2ecf20Sopenharmony_ci	&ddr_perf_format_attr_group,
2398c2ecf20Sopenharmony_ci	&ddr_perf_cpumask_attr_group,
2408c2ecf20Sopenharmony_ci	&ddr_perf_filter_cap_attr_group,
2418c2ecf20Sopenharmony_ci	NULL,
2428c2ecf20Sopenharmony_ci};
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic bool ddr_perf_is_filtered(struct perf_event *event)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	return event->attr.config == 0x41 || event->attr.config == 0x42;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic u32 ddr_perf_filter_val(struct perf_event *event)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	return event->attr.config1;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic bool ddr_perf_filters_compatible(struct perf_event *a,
2558c2ecf20Sopenharmony_ci					struct perf_event *b)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	if (!ddr_perf_is_filtered(a))
2588c2ecf20Sopenharmony_ci		return true;
2598c2ecf20Sopenharmony_ci	if (!ddr_perf_is_filtered(b))
2608c2ecf20Sopenharmony_ci		return true;
2618c2ecf20Sopenharmony_ci	return ddr_perf_filter_val(a) == ddr_perf_filter_val(b);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic bool ddr_perf_is_enhanced_filtered(struct perf_event *event)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	unsigned int filt;
2678c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	filt = pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER_ENHANCED;
2708c2ecf20Sopenharmony_ci	return (filt == DDR_CAP_AXI_ID_FILTER_ENHANCED) &&
2718c2ecf20Sopenharmony_ci		ddr_perf_is_filtered(event);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic u32 ddr_perf_alloc_counter(struct ddr_pmu *pmu, int event)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	int i;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/*
2798c2ecf20Sopenharmony_ci	 * Always map cycle event to counter 0
2808c2ecf20Sopenharmony_ci	 * Cycles counter is dedicated for cycle event
2818c2ecf20Sopenharmony_ci	 * can't used for the other events
2828c2ecf20Sopenharmony_ci	 */
2838c2ecf20Sopenharmony_ci	if (event == EVENT_CYCLES_ID) {
2848c2ecf20Sopenharmony_ci		if (pmu->events[EVENT_CYCLES_COUNTER] == NULL)
2858c2ecf20Sopenharmony_ci			return EVENT_CYCLES_COUNTER;
2868c2ecf20Sopenharmony_ci		else
2878c2ecf20Sopenharmony_ci			return -ENOENT;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	for (i = 1; i < NUM_COUNTERS; i++) {
2918c2ecf20Sopenharmony_ci		if (pmu->events[i] == NULL)
2928c2ecf20Sopenharmony_ci			return i;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return -ENOENT;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic void ddr_perf_free_counter(struct ddr_pmu *pmu, int counter)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	pmu->events[counter] = NULL;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic u32 ddr_perf_read_counter(struct ddr_pmu *pmu, int counter)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct perf_event *event = pmu->events[counter];
3068c2ecf20Sopenharmony_ci	void __iomem *base = pmu->base;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/*
3098c2ecf20Sopenharmony_ci	 * return bytes instead of bursts from ddr transaction for
3108c2ecf20Sopenharmony_ci	 * axid-read and axid-write event if PMU core supports enhanced
3118c2ecf20Sopenharmony_ci	 * filter.
3128c2ecf20Sopenharmony_ci	 */
3138c2ecf20Sopenharmony_ci	base += ddr_perf_is_enhanced_filtered(event) ? COUNTER_DPCR1 :
3148c2ecf20Sopenharmony_ci						       COUNTER_READ;
3158c2ecf20Sopenharmony_ci	return readl_relaxed(base + counter * 4);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int ddr_perf_event_init(struct perf_event *event)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
3218c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
3228c2ecf20Sopenharmony_ci	struct perf_event *sibling;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (event->attr.type != event->pmu->type)
3258c2ecf20Sopenharmony_ci		return -ENOENT;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
3288c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (event->cpu < 0) {
3318c2ecf20Sopenharmony_ci		dev_warn(pmu->dev, "Can't provide per-task data!\n");
3328c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/*
3368c2ecf20Sopenharmony_ci	 * We must NOT create groups containing mixed PMUs, although software
3378c2ecf20Sopenharmony_ci	 * events are acceptable (for example to create a CCN group
3388c2ecf20Sopenharmony_ci	 * periodically read when a hrtimer aka cpu-clock leader triggers).
3398c2ecf20Sopenharmony_ci	 */
3408c2ecf20Sopenharmony_ci	if (event->group_leader->pmu != event->pmu &&
3418c2ecf20Sopenharmony_ci			!is_software_event(event->group_leader))
3428c2ecf20Sopenharmony_ci		return -EINVAL;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) {
3458c2ecf20Sopenharmony_ci		if (!ddr_perf_filters_compatible(event, event->group_leader))
3468c2ecf20Sopenharmony_ci			return -EINVAL;
3478c2ecf20Sopenharmony_ci		for_each_sibling_event(sibling, event->group_leader) {
3488c2ecf20Sopenharmony_ci			if (!ddr_perf_filters_compatible(event, sibling))
3498c2ecf20Sopenharmony_ci				return -EINVAL;
3508c2ecf20Sopenharmony_ci		}
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	for_each_sibling_event(sibling, event->group_leader) {
3548c2ecf20Sopenharmony_ci		if (sibling->pmu != event->pmu &&
3558c2ecf20Sopenharmony_ci				!is_software_event(sibling))
3568c2ecf20Sopenharmony_ci			return -EINVAL;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	event->cpu = pmu->cpu;
3608c2ecf20Sopenharmony_ci	hwc->idx = -1;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	return 0;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic void ddr_perf_event_update(struct perf_event *event)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
3698c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
3708c2ecf20Sopenharmony_ci	u64 delta, prev_raw_count, new_raw_count;
3718c2ecf20Sopenharmony_ci	int counter = hwc->idx;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	do {
3748c2ecf20Sopenharmony_ci		prev_raw_count = local64_read(&hwc->prev_count);
3758c2ecf20Sopenharmony_ci		new_raw_count = ddr_perf_read_counter(pmu, counter);
3768c2ecf20Sopenharmony_ci	} while (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
3778c2ecf20Sopenharmony_ci			new_raw_count) != prev_raw_count);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	delta = (new_raw_count - prev_raw_count) & 0xFFFFFFFF;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	local64_add(delta, &event->count);
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic void ddr_perf_counter_enable(struct ddr_pmu *pmu, int config,
3858c2ecf20Sopenharmony_ci				  int counter, bool enable)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	u8 reg = counter * 4 + COUNTER_CNTL;
3888c2ecf20Sopenharmony_ci	int val;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (enable) {
3918c2ecf20Sopenharmony_ci		/*
3928c2ecf20Sopenharmony_ci		 * cycle counter is special which should firstly write 0 then
3938c2ecf20Sopenharmony_ci		 * write 1 into CLEAR bit to clear it. Other counters only
3948c2ecf20Sopenharmony_ci		 * need write 0 into CLEAR bit and it turns out to be 1 by
3958c2ecf20Sopenharmony_ci		 * hardware. Below enable flow is harmless for all counters.
3968c2ecf20Sopenharmony_ci		 */
3978c2ecf20Sopenharmony_ci		writel(0, pmu->base + reg);
3988c2ecf20Sopenharmony_ci		val = CNTL_EN | CNTL_CLEAR;
3998c2ecf20Sopenharmony_ci		val |= FIELD_PREP(CNTL_CSV_MASK, config);
4008c2ecf20Sopenharmony_ci		writel(val, pmu->base + reg);
4018c2ecf20Sopenharmony_ci	} else {
4028c2ecf20Sopenharmony_ci		/* Disable counter */
4038c2ecf20Sopenharmony_ci		val = readl_relaxed(pmu->base + reg) & CNTL_EN_MASK;
4048c2ecf20Sopenharmony_ci		writel(val, pmu->base + reg);
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic void ddr_perf_event_start(struct perf_event *event, int flags)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
4118c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
4128c2ecf20Sopenharmony_ci	int counter = hwc->idx;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	local64_set(&hwc->prev_count, 0);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	ddr_perf_counter_enable(pmu, event->attr.config, counter, true);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (!pmu->active_counter++)
4198c2ecf20Sopenharmony_ci		ddr_perf_counter_enable(pmu, EVENT_CYCLES_ID,
4208c2ecf20Sopenharmony_ci			EVENT_CYCLES_COUNTER, true);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	hwc->state = 0;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic int ddr_perf_event_add(struct perf_event *event, int flags)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
4288c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
4298c2ecf20Sopenharmony_ci	int counter;
4308c2ecf20Sopenharmony_ci	int cfg = event->attr.config;
4318c2ecf20Sopenharmony_ci	int cfg1 = event->attr.config1;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) {
4348c2ecf20Sopenharmony_ci		int i;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		for (i = 1; i < NUM_COUNTERS; i++) {
4378c2ecf20Sopenharmony_ci			if (pmu->events[i] &&
4388c2ecf20Sopenharmony_ci			    !ddr_perf_filters_compatible(event, pmu->events[i]))
4398c2ecf20Sopenharmony_ci				return -EINVAL;
4408c2ecf20Sopenharmony_ci		}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci		if (ddr_perf_is_filtered(event)) {
4438c2ecf20Sopenharmony_ci			/* revert axi id masking(axi_mask) value */
4448c2ecf20Sopenharmony_ci			cfg1 ^= AXI_MASKING_REVERT;
4458c2ecf20Sopenharmony_ci			writel(cfg1, pmu->base + COUNTER_DPCR1);
4468c2ecf20Sopenharmony_ci		}
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	counter = ddr_perf_alloc_counter(pmu, cfg);
4508c2ecf20Sopenharmony_ci	if (counter < 0) {
4518c2ecf20Sopenharmony_ci		dev_dbg(pmu->dev, "There are not enough counters\n");
4528c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	pmu->events[counter] = event;
4568c2ecf20Sopenharmony_ci	pmu->active_events++;
4578c2ecf20Sopenharmony_ci	hwc->idx = counter;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	hwc->state |= PERF_HES_STOPPED;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (flags & PERF_EF_START)
4628c2ecf20Sopenharmony_ci		ddr_perf_event_start(event, flags);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	return 0;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic void ddr_perf_event_stop(struct perf_event *event, int flags)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
4708c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
4718c2ecf20Sopenharmony_ci	int counter = hwc->idx;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	ddr_perf_counter_enable(pmu, event->attr.config, counter, false);
4748c2ecf20Sopenharmony_ci	ddr_perf_event_update(event);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (!--pmu->active_counter)
4778c2ecf20Sopenharmony_ci		ddr_perf_counter_enable(pmu, EVENT_CYCLES_ID,
4788c2ecf20Sopenharmony_ci			EVENT_CYCLES_COUNTER, false);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	hwc->state |= PERF_HES_STOPPED;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic void ddr_perf_event_del(struct perf_event *event, int flags)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
4868c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
4878c2ecf20Sopenharmony_ci	int counter = hwc->idx;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	ddr_perf_event_stop(event, PERF_EF_UPDATE);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	ddr_perf_free_counter(pmu, counter);
4928c2ecf20Sopenharmony_ci	pmu->active_events--;
4938c2ecf20Sopenharmony_ci	hwc->idx = -1;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic void ddr_perf_pmu_enable(struct pmu *pmu)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic void ddr_perf_pmu_disable(struct pmu *pmu)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic int ddr_perf_init(struct ddr_pmu *pmu, void __iomem *base,
5058c2ecf20Sopenharmony_ci			 struct device *dev)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	*pmu = (struct ddr_pmu) {
5088c2ecf20Sopenharmony_ci		.pmu = (struct pmu) {
5098c2ecf20Sopenharmony_ci			.module	      = THIS_MODULE,
5108c2ecf20Sopenharmony_ci			.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
5118c2ecf20Sopenharmony_ci			.task_ctx_nr = perf_invalid_context,
5128c2ecf20Sopenharmony_ci			.attr_groups = attr_groups,
5138c2ecf20Sopenharmony_ci			.event_init  = ddr_perf_event_init,
5148c2ecf20Sopenharmony_ci			.add	     = ddr_perf_event_add,
5158c2ecf20Sopenharmony_ci			.del	     = ddr_perf_event_del,
5168c2ecf20Sopenharmony_ci			.start	     = ddr_perf_event_start,
5178c2ecf20Sopenharmony_ci			.stop	     = ddr_perf_event_stop,
5188c2ecf20Sopenharmony_ci			.read	     = ddr_perf_event_update,
5198c2ecf20Sopenharmony_ci			.pmu_enable  = ddr_perf_pmu_enable,
5208c2ecf20Sopenharmony_ci			.pmu_disable = ddr_perf_pmu_disable,
5218c2ecf20Sopenharmony_ci		},
5228c2ecf20Sopenharmony_ci		.base = base,
5238c2ecf20Sopenharmony_ci		.dev = dev,
5248c2ecf20Sopenharmony_ci	};
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	pmu->id = ida_simple_get(&ddr_ida, 0, 0, GFP_KERNEL);
5278c2ecf20Sopenharmony_ci	return pmu->id;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic irqreturn_t ddr_perf_irq_handler(int irq, void *p)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	int i;
5338c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = (struct ddr_pmu *) p;
5348c2ecf20Sopenharmony_ci	struct perf_event *event, *cycle_event = NULL;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	/* all counter will stop if cycle counter disabled */
5378c2ecf20Sopenharmony_ci	ddr_perf_counter_enable(pmu,
5388c2ecf20Sopenharmony_ci			      EVENT_CYCLES_ID,
5398c2ecf20Sopenharmony_ci			      EVENT_CYCLES_COUNTER,
5408c2ecf20Sopenharmony_ci			      false);
5418c2ecf20Sopenharmony_ci	/*
5428c2ecf20Sopenharmony_ci	 * When the cycle counter overflows, all counters are stopped,
5438c2ecf20Sopenharmony_ci	 * and an IRQ is raised. If any other counter overflows, it
5448c2ecf20Sopenharmony_ci	 * continues counting, and no IRQ is raised.
5458c2ecf20Sopenharmony_ci	 *
5468c2ecf20Sopenharmony_ci	 * Cycles occur at least 4 times as often as other events, so we
5478c2ecf20Sopenharmony_ci	 * can update all events on a cycle counter overflow and not
5488c2ecf20Sopenharmony_ci	 * lose events.
5498c2ecf20Sopenharmony_ci	 *
5508c2ecf20Sopenharmony_ci	 */
5518c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_COUNTERS; i++) {
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci		if (!pmu->events[i])
5548c2ecf20Sopenharmony_ci			continue;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci		event = pmu->events[i];
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci		ddr_perf_event_update(event);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci		if (event->hw.idx == EVENT_CYCLES_COUNTER)
5618c2ecf20Sopenharmony_ci			cycle_event = event;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	ddr_perf_counter_enable(pmu,
5658c2ecf20Sopenharmony_ci			      EVENT_CYCLES_ID,
5668c2ecf20Sopenharmony_ci			      EVENT_CYCLES_COUNTER,
5678c2ecf20Sopenharmony_ci			      true);
5688c2ecf20Sopenharmony_ci	if (cycle_event)
5698c2ecf20Sopenharmony_ci		ddr_perf_event_update(cycle_event);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_cistatic int ddr_perf_offline_cpu(unsigned int cpu, struct hlist_node *node)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = hlist_entry_safe(node, struct ddr_pmu, node);
5778c2ecf20Sopenharmony_ci	int target;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	if (cpu != pmu->cpu)
5808c2ecf20Sopenharmony_ci		return 0;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	target = cpumask_any_but(cpu_online_mask, cpu);
5838c2ecf20Sopenharmony_ci	if (target >= nr_cpu_ids)
5848c2ecf20Sopenharmony_ci		return 0;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	perf_pmu_migrate_context(&pmu->pmu, cpu, target);
5878c2ecf20Sopenharmony_ci	pmu->cpu = target;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	WARN_ON(irq_set_affinity_hint(pmu->irq, cpumask_of(pmu->cpu)));
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	return 0;
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic int ddr_perf_probe(struct platform_device *pdev)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu;
5978c2ecf20Sopenharmony_ci	struct device_node *np;
5988c2ecf20Sopenharmony_ci	void __iomem *base;
5998c2ecf20Sopenharmony_ci	char *name;
6008c2ecf20Sopenharmony_ci	int num;
6018c2ecf20Sopenharmony_ci	int ret;
6028c2ecf20Sopenharmony_ci	int irq;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
6058c2ecf20Sopenharmony_ci	if (IS_ERR(base))
6068c2ecf20Sopenharmony_ci		return PTR_ERR(base);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	np = pdev->dev.of_node;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	pmu = devm_kzalloc(&pdev->dev, sizeof(*pmu), GFP_KERNEL);
6118c2ecf20Sopenharmony_ci	if (!pmu)
6128c2ecf20Sopenharmony_ci		return -ENOMEM;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	num = ddr_perf_init(pmu, base, &pdev->dev);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, pmu);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	name = devm_kasprintf(&pdev->dev, GFP_KERNEL, DDR_PERF_DEV_NAME "%d",
6198c2ecf20Sopenharmony_ci			      num);
6208c2ecf20Sopenharmony_ci	if (!name) {
6218c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6228c2ecf20Sopenharmony_ci		goto cpuhp_state_err;
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	pmu->devtype_data = of_device_get_match_data(&pdev->dev);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	pmu->cpu = raw_smp_processor_id();
6288c2ecf20Sopenharmony_ci	ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
6298c2ecf20Sopenharmony_ci				      DDR_CPUHP_CB_NAME,
6308c2ecf20Sopenharmony_ci				      NULL,
6318c2ecf20Sopenharmony_ci				      ddr_perf_offline_cpu);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	if (ret < 0) {
6348c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cpuhp_setup_state_multi failed\n");
6358c2ecf20Sopenharmony_ci		goto cpuhp_state_err;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	pmu->cpuhp_state = ret;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	/* Register the pmu instance for cpu hotplug */
6418c2ecf20Sopenharmony_ci	ret = cpuhp_state_add_instance_nocalls(pmu->cpuhp_state, &pmu->node);
6428c2ecf20Sopenharmony_ci	if (ret) {
6438c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
6448c2ecf20Sopenharmony_ci		goto cpuhp_instance_err;
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	/* Request irq */
6488c2ecf20Sopenharmony_ci	irq = of_irq_get(np, 0);
6498c2ecf20Sopenharmony_ci	if (irq < 0) {
6508c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get irq: %d", irq);
6518c2ecf20Sopenharmony_ci		ret = irq;
6528c2ecf20Sopenharmony_ci		goto ddr_perf_err;
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq,
6568c2ecf20Sopenharmony_ci					ddr_perf_irq_handler,
6578c2ecf20Sopenharmony_ci					IRQF_NOBALANCING | IRQF_NO_THREAD,
6588c2ecf20Sopenharmony_ci					DDR_CPUHP_CB_NAME,
6598c2ecf20Sopenharmony_ci					pmu);
6608c2ecf20Sopenharmony_ci	if (ret < 0) {
6618c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Request irq failed: %d", ret);
6628c2ecf20Sopenharmony_ci		goto ddr_perf_err;
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	pmu->irq = irq;
6668c2ecf20Sopenharmony_ci	ret = irq_set_affinity_hint(pmu->irq, cpumask_of(pmu->cpu));
6678c2ecf20Sopenharmony_ci	if (ret) {
6688c2ecf20Sopenharmony_ci		dev_err(pmu->dev, "Failed to set interrupt affinity!\n");
6698c2ecf20Sopenharmony_ci		goto ddr_perf_err;
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	ret = perf_pmu_register(&pmu->pmu, name, -1);
6738c2ecf20Sopenharmony_ci	if (ret)
6748c2ecf20Sopenharmony_ci		goto ddr_perf_err;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	return 0;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ciddr_perf_err:
6798c2ecf20Sopenharmony_ci	cpuhp_state_remove_instance_nocalls(pmu->cpuhp_state, &pmu->node);
6808c2ecf20Sopenharmony_cicpuhp_instance_err:
6818c2ecf20Sopenharmony_ci	cpuhp_remove_multi_state(pmu->cpuhp_state);
6828c2ecf20Sopenharmony_cicpuhp_state_err:
6838c2ecf20Sopenharmony_ci	ida_simple_remove(&ddr_ida, pmu->id);
6848c2ecf20Sopenharmony_ci	dev_warn(&pdev->dev, "i.MX8 DDR Perf PMU failed (%d), disabled\n", ret);
6858c2ecf20Sopenharmony_ci	return ret;
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_cistatic int ddr_perf_remove(struct platform_device *pdev)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	struct ddr_pmu *pmu = platform_get_drvdata(pdev);
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	cpuhp_state_remove_instance_nocalls(pmu->cpuhp_state, &pmu->node);
6938c2ecf20Sopenharmony_ci	cpuhp_remove_multi_state(pmu->cpuhp_state);
6948c2ecf20Sopenharmony_ci	irq_set_affinity_hint(pmu->irq, NULL);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	perf_pmu_unregister(&pmu->pmu);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	ida_simple_remove(&ddr_ida, pmu->id);
6998c2ecf20Sopenharmony_ci	return 0;
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic struct platform_driver imx_ddr_pmu_driver = {
7038c2ecf20Sopenharmony_ci	.driver         = {
7048c2ecf20Sopenharmony_ci		.name   = "imx-ddr-pmu",
7058c2ecf20Sopenharmony_ci		.of_match_table = imx_ddr_pmu_dt_ids,
7068c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
7078c2ecf20Sopenharmony_ci	},
7088c2ecf20Sopenharmony_ci	.probe          = ddr_perf_probe,
7098c2ecf20Sopenharmony_ci	.remove         = ddr_perf_remove,
7108c2ecf20Sopenharmony_ci};
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cimodule_platform_driver(imx_ddr_pmu_driver);
7138c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
714