13bf30882SShaokun Zhang // SPDX-License-Identifier: GPL-2.0-only
23bf30882SShaokun Zhang /*
33bf30882SShaokun Zhang  * HiSilicon SLLC uncore Hardware event counters support
43bf30882SShaokun Zhang  *
52db52237SHao Fang  * Copyright (C) 2020 HiSilicon Limited
63bf30882SShaokun Zhang  * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
73bf30882SShaokun Zhang  *
83bf30882SShaokun Zhang  * This code is based on the uncore PMUs like arm-cci and arm-ccn.
93bf30882SShaokun Zhang  */
103bf30882SShaokun Zhang #include <linux/acpi.h>
113bf30882SShaokun Zhang #include <linux/cpuhotplug.h>
123bf30882SShaokun Zhang #include <linux/interrupt.h>
133bf30882SShaokun Zhang #include <linux/irq.h>
143bf30882SShaokun Zhang #include <linux/list.h>
153bf30882SShaokun Zhang #include <linux/smp.h>
163bf30882SShaokun Zhang 
173bf30882SShaokun Zhang #include "hisi_uncore_pmu.h"
183bf30882SShaokun Zhang 
193bf30882SShaokun Zhang /* SLLC register definition */
203bf30882SShaokun Zhang #define SLLC_INT_MASK			0x0814
213bf30882SShaokun Zhang #define SLLC_INT_STATUS			0x0818
223bf30882SShaokun Zhang #define SLLC_INT_CLEAR			0x081c
233bf30882SShaokun Zhang #define SLLC_PERF_CTRL			0x1c00
243bf30882SShaokun Zhang #define SLLC_SRCID_CTRL			0x1c04
253bf30882SShaokun Zhang #define SLLC_TGTID_CTRL			0x1c08
263bf30882SShaokun Zhang #define SLLC_EVENT_CTRL			0x1c14
273bf30882SShaokun Zhang #define SLLC_EVENT_TYPE0		0x1c18
283bf30882SShaokun Zhang #define SLLC_VERSION			0x1cf0
293bf30882SShaokun Zhang #define SLLC_EVENT_CNT0_L		0x1d00
303bf30882SShaokun Zhang 
313bf30882SShaokun Zhang #define SLLC_EVTYPE_MASK		0xff
323bf30882SShaokun Zhang #define SLLC_PERF_CTRL_EN		BIT(0)
333bf30882SShaokun Zhang #define SLLC_FILT_EN			BIT(1)
343bf30882SShaokun Zhang #define SLLC_TRACETAG_EN		BIT(2)
353bf30882SShaokun Zhang #define SLLC_SRCID_EN			BIT(4)
363bf30882SShaokun Zhang #define SLLC_SRCID_NONE			0x0
373bf30882SShaokun Zhang #define SLLC_TGTID_EN			BIT(5)
383bf30882SShaokun Zhang #define SLLC_TGTID_NONE			0x0
393bf30882SShaokun Zhang #define SLLC_TGTID_MIN_SHIFT		1
403bf30882SShaokun Zhang #define SLLC_TGTID_MAX_SHIFT		12
413bf30882SShaokun Zhang #define SLLC_SRCID_CMD_SHIFT		1
423bf30882SShaokun Zhang #define SLLC_SRCID_MSK_SHIFT		12
433bf30882SShaokun Zhang #define SLLC_NR_EVENTS			0x80
443bf30882SShaokun Zhang 
453bf30882SShaokun Zhang HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_min, config1, 10, 0);
463bf30882SShaokun Zhang HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_max, config1, 21, 11);
473bf30882SShaokun Zhang HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 32, 22);
483bf30882SShaokun Zhang HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 43, 33);
493bf30882SShaokun Zhang HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 44, 44);
503bf30882SShaokun Zhang 
tgtid_is_valid(u32 max,u32 min)513bf30882SShaokun Zhang static bool tgtid_is_valid(u32 max, u32 min)
523bf30882SShaokun Zhang {
533bf30882SShaokun Zhang 	return max > 0 && max >= min;
543bf30882SShaokun Zhang }
553bf30882SShaokun Zhang 
hisi_sllc_pmu_enable_tracetag(struct perf_event * event)563bf30882SShaokun Zhang static void hisi_sllc_pmu_enable_tracetag(struct perf_event *event)
573bf30882SShaokun Zhang {
583bf30882SShaokun Zhang 	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
593bf30882SShaokun Zhang 	u32 tt_en = hisi_get_tracetag_en(event);
603bf30882SShaokun Zhang 
613bf30882SShaokun Zhang 	if (tt_en) {
623bf30882SShaokun Zhang 		u32 val;
633bf30882SShaokun Zhang 
643bf30882SShaokun Zhang 		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
653bf30882SShaokun Zhang 		val |= SLLC_TRACETAG_EN | SLLC_FILT_EN;
663bf30882SShaokun Zhang 		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
673bf30882SShaokun Zhang 	}
683bf30882SShaokun Zhang }
693bf30882SShaokun Zhang 
hisi_sllc_pmu_disable_tracetag(struct perf_event * event)703bf30882SShaokun Zhang static void hisi_sllc_pmu_disable_tracetag(struct perf_event *event)
713bf30882SShaokun Zhang {
723bf30882SShaokun Zhang 	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
733bf30882SShaokun Zhang 	u32 tt_en = hisi_get_tracetag_en(event);
743bf30882SShaokun Zhang 
753bf30882SShaokun Zhang 	if (tt_en) {
763bf30882SShaokun Zhang 		u32 val;
773bf30882SShaokun Zhang 
783bf30882SShaokun Zhang 		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
793bf30882SShaokun Zhang 		val &= ~(SLLC_TRACETAG_EN | SLLC_FILT_EN);
803bf30882SShaokun Zhang 		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
813bf30882SShaokun Zhang 	}
823bf30882SShaokun Zhang }
833bf30882SShaokun Zhang 
hisi_sllc_pmu_config_tgtid(struct perf_event * event)843bf30882SShaokun Zhang static void hisi_sllc_pmu_config_tgtid(struct perf_event *event)
853bf30882SShaokun Zhang {
863bf30882SShaokun Zhang 	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
873bf30882SShaokun Zhang 	u32 min = hisi_get_tgtid_min(event);
883bf30882SShaokun Zhang 	u32 max = hisi_get_tgtid_max(event);
893bf30882SShaokun Zhang 
903bf30882SShaokun Zhang 	if (tgtid_is_valid(max, min)) {
913bf30882SShaokun Zhang 		u32 val = (max << SLLC_TGTID_MAX_SHIFT) | (min << SLLC_TGTID_MIN_SHIFT);
923bf30882SShaokun Zhang 
933bf30882SShaokun Zhang 		writel(val, sllc_pmu->base + SLLC_TGTID_CTRL);
943bf30882SShaokun Zhang 		/* Enable the tgtid */
953bf30882SShaokun Zhang 		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
963bf30882SShaokun Zhang 		val |= SLLC_TGTID_EN | SLLC_FILT_EN;
973bf30882SShaokun Zhang 		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
983bf30882SShaokun Zhang 	}
993bf30882SShaokun Zhang }
1003bf30882SShaokun Zhang 
hisi_sllc_pmu_clear_tgtid(struct perf_event * event)1013bf30882SShaokun Zhang static void hisi_sllc_pmu_clear_tgtid(struct perf_event *event)
1023bf30882SShaokun Zhang {
1033bf30882SShaokun Zhang 	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
1043bf30882SShaokun Zhang 	u32 min = hisi_get_tgtid_min(event);
1053bf30882SShaokun Zhang 	u32 max = hisi_get_tgtid_max(event);
1063bf30882SShaokun Zhang 
1073bf30882SShaokun Zhang 	if (tgtid_is_valid(max, min)) {
1083bf30882SShaokun Zhang 		u32 val;
1093bf30882SShaokun Zhang 
1103bf30882SShaokun Zhang 		writel(SLLC_TGTID_NONE, sllc_pmu->base + SLLC_TGTID_CTRL);
1113bf30882SShaokun Zhang 		/* Disable the tgtid */
1123bf30882SShaokun Zhang 		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
1133bf30882SShaokun Zhang 		val &= ~(SLLC_TGTID_EN | SLLC_FILT_EN);
1143bf30882SShaokun Zhang 		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
1153bf30882SShaokun Zhang 	}
1163bf30882SShaokun Zhang }
1173bf30882SShaokun Zhang 
hisi_sllc_pmu_config_srcid(struct perf_event * event)1183bf30882SShaokun Zhang static void hisi_sllc_pmu_config_srcid(struct perf_event *event)
1193bf30882SShaokun Zhang {
1203bf30882SShaokun Zhang 	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
1213bf30882SShaokun Zhang 	u32 cmd = hisi_get_srcid_cmd(event);
1223bf30882SShaokun Zhang 
1233bf30882SShaokun Zhang 	if (cmd) {
1243bf30882SShaokun Zhang 		u32 val, msk;
1253bf30882SShaokun Zhang 
1263bf30882SShaokun Zhang 		msk = hisi_get_srcid_msk(event);
1273bf30882SShaokun Zhang 		val = (cmd << SLLC_SRCID_CMD_SHIFT) | (msk << SLLC_SRCID_MSK_SHIFT);
1283bf30882SShaokun Zhang 		writel(val, sllc_pmu->base + SLLC_SRCID_CTRL);
1293bf30882SShaokun Zhang 		/* Enable the srcid */
1303bf30882SShaokun Zhang 		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
1313bf30882SShaokun Zhang 		val |= SLLC_SRCID_EN | SLLC_FILT_EN;
1323bf30882SShaokun Zhang 		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
1333bf30882SShaokun Zhang 	}
1343bf30882SShaokun Zhang }
1353bf30882SShaokun Zhang 
hisi_sllc_pmu_clear_srcid(struct perf_event * event)1363bf30882SShaokun Zhang static void hisi_sllc_pmu_clear_srcid(struct perf_event *event)
1373bf30882SShaokun Zhang {
1383bf30882SShaokun Zhang 	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
1393bf30882SShaokun Zhang 	u32 cmd = hisi_get_srcid_cmd(event);
1403bf30882SShaokun Zhang 
1413bf30882SShaokun Zhang 	if (cmd) {
1423bf30882SShaokun Zhang 		u32 val;
1433bf30882SShaokun Zhang 
1443bf30882SShaokun Zhang 		writel(SLLC_SRCID_NONE, sllc_pmu->base + SLLC_SRCID_CTRL);
1453bf30882SShaokun Zhang 		/* Disable the srcid */
1463bf30882SShaokun Zhang 		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
1473bf30882SShaokun Zhang 		val &= ~(SLLC_SRCID_EN | SLLC_FILT_EN);
1483bf30882SShaokun Zhang 		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
1493bf30882SShaokun Zhang 	}
1503bf30882SShaokun Zhang }
1513bf30882SShaokun Zhang 
hisi_sllc_pmu_enable_filter(struct perf_event * event)1523bf30882SShaokun Zhang static void hisi_sllc_pmu_enable_filter(struct perf_event *event)
1533bf30882SShaokun Zhang {
1543bf30882SShaokun Zhang 	if (event->attr.config1 != 0x0) {
1553bf30882SShaokun Zhang 		hisi_sllc_pmu_enable_tracetag(event);
1563bf30882SShaokun Zhang 		hisi_sllc_pmu_config_srcid(event);
1573bf30882SShaokun Zhang 		hisi_sllc_pmu_config_tgtid(event);
1583bf30882SShaokun Zhang 	}
1593bf30882SShaokun Zhang }
1603bf30882SShaokun Zhang 
hisi_sllc_pmu_clear_filter(struct perf_event * event)1613bf30882SShaokun Zhang static void hisi_sllc_pmu_clear_filter(struct perf_event *event)
1623bf30882SShaokun Zhang {
1633bf30882SShaokun Zhang 	if (event->attr.config1 != 0x0) {
1643bf30882SShaokun Zhang 		hisi_sllc_pmu_disable_tracetag(event);
1653bf30882SShaokun Zhang 		hisi_sllc_pmu_clear_srcid(event);
1663bf30882SShaokun Zhang 		hisi_sllc_pmu_clear_tgtid(event);
1673bf30882SShaokun Zhang 	}
1683bf30882SShaokun Zhang }
1693bf30882SShaokun Zhang 
hisi_sllc_pmu_get_counter_offset(int idx)1703bf30882SShaokun Zhang static u32 hisi_sllc_pmu_get_counter_offset(int idx)
1713bf30882SShaokun Zhang {
1723bf30882SShaokun Zhang 	return (SLLC_EVENT_CNT0_L + idx * 8);
1733bf30882SShaokun Zhang }
1743bf30882SShaokun Zhang 
hisi_sllc_pmu_read_counter(struct hisi_pmu * sllc_pmu,struct hw_perf_event * hwc)1753bf30882SShaokun Zhang static u64 hisi_sllc_pmu_read_counter(struct hisi_pmu *sllc_pmu,
1763bf30882SShaokun Zhang 				      struct hw_perf_event *hwc)
1773bf30882SShaokun Zhang {
1783bf30882SShaokun Zhang 	return readq(sllc_pmu->base +
1793bf30882SShaokun Zhang 		     hisi_sllc_pmu_get_counter_offset(hwc->idx));
1803bf30882SShaokun Zhang }
1813bf30882SShaokun Zhang 
hisi_sllc_pmu_write_counter(struct hisi_pmu * sllc_pmu,struct hw_perf_event * hwc,u64 val)1823bf30882SShaokun Zhang static void hisi_sllc_pmu_write_counter(struct hisi_pmu *sllc_pmu,
1833bf30882SShaokun Zhang 					struct hw_perf_event *hwc, u64 val)
1843bf30882SShaokun Zhang {
1853bf30882SShaokun Zhang 	writeq(val, sllc_pmu->base +
1863bf30882SShaokun Zhang 	       hisi_sllc_pmu_get_counter_offset(hwc->idx));
1873bf30882SShaokun Zhang }
1883bf30882SShaokun Zhang 
hisi_sllc_pmu_write_evtype(struct hisi_pmu * sllc_pmu,int idx,u32 type)1893bf30882SShaokun Zhang static void hisi_sllc_pmu_write_evtype(struct hisi_pmu *sllc_pmu, int idx,
1903bf30882SShaokun Zhang 				       u32 type)
1913bf30882SShaokun Zhang {
1923bf30882SShaokun Zhang 	u32 reg, reg_idx, shift, val;
1933bf30882SShaokun Zhang 
1943bf30882SShaokun Zhang 	/*
1953bf30882SShaokun Zhang 	 * Select the appropriate event select register(SLLC_EVENT_TYPE0/1).
1963bf30882SShaokun Zhang 	 * There are 2 event select registers for the 8 hardware counters.
1973bf30882SShaokun Zhang 	 * Event code is 8-bits and for the former 4 hardware counters,
1983bf30882SShaokun Zhang 	 * SLLC_EVENT_TYPE0 is chosen. For the latter 4 hardware counters,
1993bf30882SShaokun Zhang 	 * SLLC_EVENT_TYPE1 is chosen.
2003bf30882SShaokun Zhang 	 */
2013bf30882SShaokun Zhang 	reg = SLLC_EVENT_TYPE0 + (idx / 4) * 4;
2023bf30882SShaokun Zhang 	reg_idx = idx % 4;
2033bf30882SShaokun Zhang 	shift = 8 * reg_idx;
2043bf30882SShaokun Zhang 
2053bf30882SShaokun Zhang 	/* Write event code to SLLC_EVENT_TYPEx Register */
2063bf30882SShaokun Zhang 	val = readl(sllc_pmu->base + reg);
2073bf30882SShaokun Zhang 	val &= ~(SLLC_EVTYPE_MASK << shift);
2083bf30882SShaokun Zhang 	val |= (type << shift);
2093bf30882SShaokun Zhang 	writel(val, sllc_pmu->base + reg);
2103bf30882SShaokun Zhang }
2113bf30882SShaokun Zhang 
hisi_sllc_pmu_start_counters(struct hisi_pmu * sllc_pmu)2123bf30882SShaokun Zhang static void hisi_sllc_pmu_start_counters(struct hisi_pmu *sllc_pmu)
2133bf30882SShaokun Zhang {
2143bf30882SShaokun Zhang 	u32 val;
2153bf30882SShaokun Zhang 
2163bf30882SShaokun Zhang 	val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
2173bf30882SShaokun Zhang 	val |= SLLC_PERF_CTRL_EN;
2183bf30882SShaokun Zhang 	writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
2193bf30882SShaokun Zhang }
2203bf30882SShaokun Zhang 
hisi_sllc_pmu_stop_counters(struct hisi_pmu * sllc_pmu)2213bf30882SShaokun Zhang static void hisi_sllc_pmu_stop_counters(struct hisi_pmu *sllc_pmu)
2223bf30882SShaokun Zhang {
2233bf30882SShaokun Zhang 	u32 val;
2243bf30882SShaokun Zhang 
2253bf30882SShaokun Zhang 	val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
2263bf30882SShaokun Zhang 	val &= ~(SLLC_PERF_CTRL_EN);
2273bf30882SShaokun Zhang 	writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
2283bf30882SShaokun Zhang }
2293bf30882SShaokun Zhang 
hisi_sllc_pmu_enable_counter(struct hisi_pmu * sllc_pmu,struct hw_perf_event * hwc)2303bf30882SShaokun Zhang static void hisi_sllc_pmu_enable_counter(struct hisi_pmu *sllc_pmu,
2313bf30882SShaokun Zhang 					 struct hw_perf_event *hwc)
2323bf30882SShaokun Zhang {
2333bf30882SShaokun Zhang 	u32 val;
2343bf30882SShaokun Zhang 
2353bf30882SShaokun Zhang 	val = readl(sllc_pmu->base + SLLC_EVENT_CTRL);
2363bf30882SShaokun Zhang 	val |= 1 << hwc->idx;
2373bf30882SShaokun Zhang 	writel(val, sllc_pmu->base + SLLC_EVENT_CTRL);
2383bf30882SShaokun Zhang }
2393bf30882SShaokun Zhang 
hisi_sllc_pmu_disable_counter(struct hisi_pmu * sllc_pmu,struct hw_perf_event * hwc)2403bf30882SShaokun Zhang static void hisi_sllc_pmu_disable_counter(struct hisi_pmu *sllc_pmu,
2413bf30882SShaokun Zhang 					  struct hw_perf_event *hwc)
2423bf30882SShaokun Zhang {
2433bf30882SShaokun Zhang 	u32 val;
2443bf30882SShaokun Zhang 
2453bf30882SShaokun Zhang 	val = readl(sllc_pmu->base + SLLC_EVENT_CTRL);
2463bf30882SShaokun Zhang 	val &= ~(1 << hwc->idx);
2473bf30882SShaokun Zhang 	writel(val, sllc_pmu->base + SLLC_EVENT_CTRL);
2483bf30882SShaokun Zhang }
2493bf30882SShaokun Zhang 
hisi_sllc_pmu_enable_counter_int(struct hisi_pmu * sllc_pmu,struct hw_perf_event * hwc)2503bf30882SShaokun Zhang static void hisi_sllc_pmu_enable_counter_int(struct hisi_pmu *sllc_pmu,
2513bf30882SShaokun Zhang 					     struct hw_perf_event *hwc)
2523bf30882SShaokun Zhang {
2533bf30882SShaokun Zhang 	u32 val;
2543bf30882SShaokun Zhang 
2553bf30882SShaokun Zhang 	val = readl(sllc_pmu->base + SLLC_INT_MASK);
2563bf30882SShaokun Zhang 	/* Write 0 to enable interrupt */
2573bf30882SShaokun Zhang 	val &= ~(1 << hwc->idx);
2583bf30882SShaokun Zhang 	writel(val, sllc_pmu->base + SLLC_INT_MASK);
2593bf30882SShaokun Zhang }
2603bf30882SShaokun Zhang 
hisi_sllc_pmu_disable_counter_int(struct hisi_pmu * sllc_pmu,struct hw_perf_event * hwc)2613bf30882SShaokun Zhang static void hisi_sllc_pmu_disable_counter_int(struct hisi_pmu *sllc_pmu,
2623bf30882SShaokun Zhang 					      struct hw_perf_event *hwc)
2633bf30882SShaokun Zhang {
2643bf30882SShaokun Zhang 	u32 val;
2653bf30882SShaokun Zhang 
2663bf30882SShaokun Zhang 	val = readl(sllc_pmu->base + SLLC_INT_MASK);
2673bf30882SShaokun Zhang 	/* Write 1 to mask interrupt */
2683bf30882SShaokun Zhang 	val |= 1 << hwc->idx;
2693bf30882SShaokun Zhang 	writel(val, sllc_pmu->base + SLLC_INT_MASK);
2703bf30882SShaokun Zhang }
2713bf30882SShaokun Zhang 
hisi_sllc_pmu_get_int_status(struct hisi_pmu * sllc_pmu)2723bf30882SShaokun Zhang static u32 hisi_sllc_pmu_get_int_status(struct hisi_pmu *sllc_pmu)
2733bf30882SShaokun Zhang {
2743bf30882SShaokun Zhang 	return readl(sllc_pmu->base + SLLC_INT_STATUS);
2753bf30882SShaokun Zhang }
2763bf30882SShaokun Zhang 
hisi_sllc_pmu_clear_int_status(struct hisi_pmu * sllc_pmu,int idx)2773bf30882SShaokun Zhang static void hisi_sllc_pmu_clear_int_status(struct hisi_pmu *sllc_pmu, int idx)
2783bf30882SShaokun Zhang {
2793bf30882SShaokun Zhang 	writel(1 << idx, sllc_pmu->base + SLLC_INT_CLEAR);
2803bf30882SShaokun Zhang }
2813bf30882SShaokun Zhang 
2823bf30882SShaokun Zhang static const struct acpi_device_id hisi_sllc_pmu_acpi_match[] = {
2833bf30882SShaokun Zhang 	{ "HISI0263", },
2843bf30882SShaokun Zhang 	{}
2853bf30882SShaokun Zhang };
2863bf30882SShaokun Zhang MODULE_DEVICE_TABLE(acpi, hisi_sllc_pmu_acpi_match);
2873bf30882SShaokun Zhang 
hisi_sllc_pmu_init_data(struct platform_device * pdev,struct hisi_pmu * sllc_pmu)2883bf30882SShaokun Zhang static int hisi_sllc_pmu_init_data(struct platform_device *pdev,
2893bf30882SShaokun Zhang 				   struct hisi_pmu *sllc_pmu)
2903bf30882SShaokun Zhang {
2913bf30882SShaokun Zhang 	/*
2923bf30882SShaokun Zhang 	 * Use the SCCL_ID and the index ID to identify the SLLC PMU,
2933bf30882SShaokun Zhang 	 * while SCCL_ID is from MPIDR_EL1 by CPU.
2943bf30882SShaokun Zhang 	 */
2953bf30882SShaokun Zhang 	if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id",
2963bf30882SShaokun Zhang 				     &sllc_pmu->sccl_id)) {
2973bf30882SShaokun Zhang 		dev_err(&pdev->dev, "Cannot read sccl-id!\n");
2983bf30882SShaokun Zhang 		return -EINVAL;
2993bf30882SShaokun Zhang 	}
3003bf30882SShaokun Zhang 
3013bf30882SShaokun Zhang 	if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id",
3023bf30882SShaokun Zhang 				     &sllc_pmu->index_id)) {
3033bf30882SShaokun Zhang 		dev_err(&pdev->dev, "Cannot read idx-id!\n");
3043bf30882SShaokun Zhang 		return -EINVAL;
3053bf30882SShaokun Zhang 	}
3063bf30882SShaokun Zhang 
3073bf30882SShaokun Zhang 	/* SLLC PMUs only share the same SCCL */
3083bf30882SShaokun Zhang 	sllc_pmu->ccl_id = -1;
3093bf30882SShaokun Zhang 
3103bf30882SShaokun Zhang 	sllc_pmu->base = devm_platform_ioremap_resource(pdev, 0);
3113bf30882SShaokun Zhang 	if (IS_ERR(sllc_pmu->base)) {
3123bf30882SShaokun Zhang 		dev_err(&pdev->dev, "ioremap failed for sllc_pmu resource.\n");
3133bf30882SShaokun Zhang 		return PTR_ERR(sllc_pmu->base);
3143bf30882SShaokun Zhang 	}
3153bf30882SShaokun Zhang 
3163bf30882SShaokun Zhang 	sllc_pmu->identifier = readl(sllc_pmu->base + SLLC_VERSION);
3173bf30882SShaokun Zhang 
3183bf30882SShaokun Zhang 	return 0;
3193bf30882SShaokun Zhang }
3203bf30882SShaokun Zhang 
3213bf30882SShaokun Zhang static struct attribute *hisi_sllc_pmu_v2_format_attr[] = {
3223bf30882SShaokun Zhang 	HISI_PMU_FORMAT_ATTR(event, "config:0-7"),
3233bf30882SShaokun Zhang 	HISI_PMU_FORMAT_ATTR(tgtid_min, "config1:0-10"),
3243bf30882SShaokun Zhang 	HISI_PMU_FORMAT_ATTR(tgtid_max, "config1:11-21"),
3253bf30882SShaokun Zhang 	HISI_PMU_FORMAT_ATTR(srcid_cmd, "config1:22-32"),
3263bf30882SShaokun Zhang 	HISI_PMU_FORMAT_ATTR(srcid_msk, "config1:33-43"),
3273bf30882SShaokun Zhang 	HISI_PMU_FORMAT_ATTR(tracetag_en, "config1:44"),
3283bf30882SShaokun Zhang 	NULL
3293bf30882SShaokun Zhang };
3303bf30882SShaokun Zhang 
3313bf30882SShaokun Zhang static const struct attribute_group hisi_sllc_pmu_v2_format_group = {
3323bf30882SShaokun Zhang 	.name = "format",
3333bf30882SShaokun Zhang 	.attrs = hisi_sllc_pmu_v2_format_attr,
3343bf30882SShaokun Zhang };
3353bf30882SShaokun Zhang 
3363bf30882SShaokun Zhang static struct attribute *hisi_sllc_pmu_v2_events_attr[] = {
3373bf30882SShaokun Zhang 	HISI_PMU_EVENT_ATTR(rx_req,             0x30),
3383bf30882SShaokun Zhang 	HISI_PMU_EVENT_ATTR(rx_data,            0x31),
3393bf30882SShaokun Zhang 	HISI_PMU_EVENT_ATTR(tx_req,             0x34),
3403bf30882SShaokun Zhang 	HISI_PMU_EVENT_ATTR(tx_data,            0x35),
3413bf30882SShaokun Zhang 	HISI_PMU_EVENT_ATTR(cycles,             0x09),
3423bf30882SShaokun Zhang 	NULL
3433bf30882SShaokun Zhang };
3443bf30882SShaokun Zhang 
3453bf30882SShaokun Zhang static const struct attribute_group hisi_sllc_pmu_v2_events_group = {
3463bf30882SShaokun Zhang 	.name = "events",
3473bf30882SShaokun Zhang 	.attrs = hisi_sllc_pmu_v2_events_attr,
3483bf30882SShaokun Zhang };
3493bf30882SShaokun Zhang 
3503bf30882SShaokun Zhang static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
3513bf30882SShaokun Zhang 
3523bf30882SShaokun Zhang static struct attribute *hisi_sllc_pmu_cpumask_attrs[] = {
3533bf30882SShaokun Zhang 	&dev_attr_cpumask.attr,
3543bf30882SShaokun Zhang 	NULL
3553bf30882SShaokun Zhang };
3563bf30882SShaokun Zhang 
3573bf30882SShaokun Zhang static const struct attribute_group hisi_sllc_pmu_cpumask_attr_group = {
3583bf30882SShaokun Zhang 	.attrs = hisi_sllc_pmu_cpumask_attrs,
3593bf30882SShaokun Zhang };
3603bf30882SShaokun Zhang 
3613bf30882SShaokun Zhang static struct device_attribute hisi_sllc_pmu_identifier_attr =
3623bf30882SShaokun Zhang 	__ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL);
3633bf30882SShaokun Zhang 
3643bf30882SShaokun Zhang static struct attribute *hisi_sllc_pmu_identifier_attrs[] = {
3653bf30882SShaokun Zhang 	&hisi_sllc_pmu_identifier_attr.attr,
3663bf30882SShaokun Zhang 	NULL
3673bf30882SShaokun Zhang };
3683bf30882SShaokun Zhang 
36959d697a9SRikard Falkeborn static const struct attribute_group hisi_sllc_pmu_identifier_group = {
3703bf30882SShaokun Zhang 	.attrs = hisi_sllc_pmu_identifier_attrs,
3713bf30882SShaokun Zhang };
3723bf30882SShaokun Zhang 
3733bf30882SShaokun Zhang static const struct attribute_group *hisi_sllc_pmu_v2_attr_groups[] = {
3743bf30882SShaokun Zhang 	&hisi_sllc_pmu_v2_format_group,
3753bf30882SShaokun Zhang 	&hisi_sllc_pmu_v2_events_group,
3763bf30882SShaokun Zhang 	&hisi_sllc_pmu_cpumask_attr_group,
3773bf30882SShaokun Zhang 	&hisi_sllc_pmu_identifier_group,
3783bf30882SShaokun Zhang 	NULL
3793bf30882SShaokun Zhang };
3803bf30882SShaokun Zhang 
3813bf30882SShaokun Zhang static const struct hisi_uncore_ops hisi_uncore_sllc_ops = {
3823bf30882SShaokun Zhang 	.write_evtype		= hisi_sllc_pmu_write_evtype,
3833bf30882SShaokun Zhang 	.get_event_idx		= hisi_uncore_pmu_get_event_idx,
3843bf30882SShaokun Zhang 	.start_counters		= hisi_sllc_pmu_start_counters,
3853bf30882SShaokun Zhang 	.stop_counters		= hisi_sllc_pmu_stop_counters,
3863bf30882SShaokun Zhang 	.enable_counter		= hisi_sllc_pmu_enable_counter,
3873bf30882SShaokun Zhang 	.disable_counter	= hisi_sllc_pmu_disable_counter,
3883bf30882SShaokun Zhang 	.enable_counter_int	= hisi_sllc_pmu_enable_counter_int,
3893bf30882SShaokun Zhang 	.disable_counter_int	= hisi_sllc_pmu_disable_counter_int,
3903bf30882SShaokun Zhang 	.write_counter		= hisi_sllc_pmu_write_counter,
3913bf30882SShaokun Zhang 	.read_counter		= hisi_sllc_pmu_read_counter,
3923bf30882SShaokun Zhang 	.get_int_status		= hisi_sllc_pmu_get_int_status,
3933bf30882SShaokun Zhang 	.clear_int_status	= hisi_sllc_pmu_clear_int_status,
3943bf30882SShaokun Zhang 	.enable_filter		= hisi_sllc_pmu_enable_filter,
3953bf30882SShaokun Zhang 	.disable_filter		= hisi_sllc_pmu_clear_filter,
3963bf30882SShaokun Zhang };
3973bf30882SShaokun Zhang 
hisi_sllc_pmu_dev_probe(struct platform_device * pdev,struct hisi_pmu * sllc_pmu)3983bf30882SShaokun Zhang static int hisi_sllc_pmu_dev_probe(struct platform_device *pdev,
3993bf30882SShaokun Zhang 				   struct hisi_pmu *sllc_pmu)
4003bf30882SShaokun Zhang {
4013bf30882SShaokun Zhang 	int ret;
4023bf30882SShaokun Zhang 
4033bf30882SShaokun Zhang 	ret = hisi_sllc_pmu_init_data(pdev, sllc_pmu);
4043bf30882SShaokun Zhang 	if (ret)
4053bf30882SShaokun Zhang 		return ret;
4063bf30882SShaokun Zhang 
4073bf30882SShaokun Zhang 	ret = hisi_uncore_pmu_init_irq(sllc_pmu, pdev);
4083bf30882SShaokun Zhang 	if (ret)
4093bf30882SShaokun Zhang 		return ret;
4103bf30882SShaokun Zhang 
4113bf30882SShaokun Zhang 	sllc_pmu->pmu_events.attr_groups = hisi_sllc_pmu_v2_attr_groups;
4123bf30882SShaokun Zhang 	sllc_pmu->ops = &hisi_uncore_sllc_ops;
4133bf30882SShaokun Zhang 	sllc_pmu->check_event = SLLC_NR_EVENTS;
4143bf30882SShaokun Zhang 	sllc_pmu->counter_bits = 64;
4153bf30882SShaokun Zhang 	sllc_pmu->num_counters = 8;
4163bf30882SShaokun Zhang 	sllc_pmu->dev = &pdev->dev;
4173bf30882SShaokun Zhang 	sllc_pmu->on_cpu = -1;
4183bf30882SShaokun Zhang 
4193bf30882SShaokun Zhang 	return 0;
4203bf30882SShaokun Zhang }
4213bf30882SShaokun Zhang 
hisi_sllc_pmu_probe(struct platform_device * pdev)4223bf30882SShaokun Zhang static int hisi_sllc_pmu_probe(struct platform_device *pdev)
4233bf30882SShaokun Zhang {
4243bf30882SShaokun Zhang 	struct hisi_pmu *sllc_pmu;
4253bf30882SShaokun Zhang 	char *name;
4263bf30882SShaokun Zhang 	int ret;
4273bf30882SShaokun Zhang 
4283bf30882SShaokun Zhang 	sllc_pmu = devm_kzalloc(&pdev->dev, sizeof(*sllc_pmu), GFP_KERNEL);
4293bf30882SShaokun Zhang 	if (!sllc_pmu)
4303bf30882SShaokun Zhang 		return -ENOMEM;
4313bf30882SShaokun Zhang 
4323bf30882SShaokun Zhang 	ret = hisi_sllc_pmu_dev_probe(pdev, sllc_pmu);
4333bf30882SShaokun Zhang 	if (ret)
4343bf30882SShaokun Zhang 		return ret;
4353bf30882SShaokun Zhang 
4363bf30882SShaokun Zhang 	name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_sllc%u",
4373bf30882SShaokun Zhang 			      sllc_pmu->sccl_id, sllc_pmu->index_id);
4383bf30882SShaokun Zhang 	if (!name)
4393bf30882SShaokun Zhang 		return -ENOMEM;
4403bf30882SShaokun Zhang 
4413bf30882SShaokun Zhang 	ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
4423bf30882SShaokun Zhang 				       &sllc_pmu->node);
4433bf30882SShaokun Zhang 	if (ret) {
4443bf30882SShaokun Zhang 		dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
4453bf30882SShaokun Zhang 		return ret;
4463bf30882SShaokun Zhang 	}
4473bf30882SShaokun Zhang 
44825d8c250SJunhao He 	hisi_pmu_init(sllc_pmu, THIS_MODULE);
4493bf30882SShaokun Zhang 
4503bf30882SShaokun Zhang 	ret = perf_pmu_register(&sllc_pmu->pmu, name, -1);
4513bf30882SShaokun Zhang 	if (ret) {
4523bf30882SShaokun Zhang 		dev_err(sllc_pmu->dev, "PMU register failed, ret = %d\n", ret);
453*75bab28fSJunhao He 		cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
4543bf30882SShaokun Zhang 						    &sllc_pmu->node);
4553bf30882SShaokun Zhang 		return ret;
4563bf30882SShaokun Zhang 	}
4573bf30882SShaokun Zhang 
4583bf30882SShaokun Zhang 	platform_set_drvdata(pdev, sllc_pmu);
4593bf30882SShaokun Zhang 
4603bf30882SShaokun Zhang 	return ret;
4613bf30882SShaokun Zhang }
4623bf30882SShaokun Zhang 
hisi_sllc_pmu_remove(struct platform_device * pdev)4633bf30882SShaokun Zhang static int hisi_sllc_pmu_remove(struct platform_device *pdev)
4643bf30882SShaokun Zhang {
4653bf30882SShaokun Zhang 	struct hisi_pmu *sllc_pmu = platform_get_drvdata(pdev);
4663bf30882SShaokun Zhang 
4673bf30882SShaokun Zhang 	perf_pmu_unregister(&sllc_pmu->pmu);
4683bf30882SShaokun Zhang 	cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
4693bf30882SShaokun Zhang 					    &sllc_pmu->node);
4703bf30882SShaokun Zhang 	return 0;
4713bf30882SShaokun Zhang }
4723bf30882SShaokun Zhang 
4733bf30882SShaokun Zhang static struct platform_driver hisi_sllc_pmu_driver = {
4743bf30882SShaokun Zhang 	.driver = {
4753bf30882SShaokun Zhang 		.name = "hisi_sllc_pmu",
4763bf30882SShaokun Zhang 		.acpi_match_table = hisi_sllc_pmu_acpi_match,
4773bf30882SShaokun Zhang 		.suppress_bind_attrs = true,
4783bf30882SShaokun Zhang 	},
4793bf30882SShaokun Zhang 	.probe = hisi_sllc_pmu_probe,
4803bf30882SShaokun Zhang 	.remove = hisi_sllc_pmu_remove,
4813bf30882SShaokun Zhang };
4823bf30882SShaokun Zhang 
hisi_sllc_pmu_module_init(void)4833bf30882SShaokun Zhang static int __init hisi_sllc_pmu_module_init(void)
4843bf30882SShaokun Zhang {
4853bf30882SShaokun Zhang 	int ret;
4863bf30882SShaokun Zhang 
4873bf30882SShaokun Zhang 	ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
4883bf30882SShaokun Zhang 				      "AP_PERF_ARM_HISI_SLLC_ONLINE",
4893bf30882SShaokun Zhang 				      hisi_uncore_pmu_online_cpu,
4903bf30882SShaokun Zhang 				      hisi_uncore_pmu_offline_cpu);
4913bf30882SShaokun Zhang 	if (ret) {
4923bf30882SShaokun Zhang 		pr_err("SLLC PMU: cpuhp state setup failed, ret = %d\n", ret);
4933bf30882SShaokun Zhang 		return ret;
4943bf30882SShaokun Zhang 	}
4953bf30882SShaokun Zhang 
4963bf30882SShaokun Zhang 	ret = platform_driver_register(&hisi_sllc_pmu_driver);
4973bf30882SShaokun Zhang 	if (ret)
4983bf30882SShaokun Zhang 		cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE);
4993bf30882SShaokun Zhang 
5003bf30882SShaokun Zhang 	return ret;
5013bf30882SShaokun Zhang }
5023bf30882SShaokun Zhang module_init(hisi_sllc_pmu_module_init);
5033bf30882SShaokun Zhang 
hisi_sllc_pmu_module_exit(void)5043bf30882SShaokun Zhang static void __exit hisi_sllc_pmu_module_exit(void)
5053bf30882SShaokun Zhang {
5063bf30882SShaokun Zhang 	platform_driver_unregister(&hisi_sllc_pmu_driver);
5073bf30882SShaokun Zhang 	cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE);
5083bf30882SShaokun Zhang }
5093bf30882SShaokun Zhang module_exit(hisi_sllc_pmu_module_exit);
5103bf30882SShaokun Zhang 
5113bf30882SShaokun Zhang MODULE_DESCRIPTION("HiSilicon SLLC uncore PMU driver");
5123bf30882SShaokun Zhang MODULE_LICENSE("GPL v2");
5133bf30882SShaokun Zhang MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>");
5143bf30882SShaokun Zhang MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>");
515