xref: /linux/drivers/perf/hisilicon/hisi_uncore_its_pmu.c (revision 303bff3637d13f0ecbb280c7cb403470c440a2a6)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Driver for HiSilicon Uncore ITS PMU device
4  *
5  * Copyright (c) 2026 HiSilicon Technologies Co., Ltd.
6  * Author: Yushan Wang <wangyushan12@huawei.com>
7  */
8 #include <linux/bitops.h>
9 #include <linux/cpuhotplug.h>
10 #include <linux/device.h>
11 #include <linux/io.h>
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/property.h>
15 #include <linux/spinlock.h>
16 #include <linux/sysfs.h>
17 
18 #include "hisi_uncore_pmu.h"
19 
20 #define ITS_PMU_VERSION			0x21000
21 #define ITS_PMU_GLOBAL_CTRL		0x21004
22 #define ITS_PMU_GLOBAL_CTRL_PMU_EN	BIT(0)
23 #define ITS_PMU_COUNTER_CTRL		0x21008
24 #define ITS_PMU_EVENT_CTRL		0x2100c
25 #define ITS_PMU_COUNTER0		0x21010
26 
27 #define ITS_PMU_INT_ID_MASK		0x20008
28 #define ITS_PMU_INT_ID_CTRL		0x20084
29 
30 #define ITS_PMU_NR_COUNTERS		4
31 
32 #define ITS_PMU_EVENT_CNTRn(cntr0, n)	((cntr0) + 8 * (n))
33 #define ITS_PMU_CNTR_CTRL_MASK(n)	GENMASK(8 * ((n) + 1) - 1, 8 * (n))
34 #define ITS_PMU_CNTR_EVENT_CFG(n, e)	((e) << ((n) * 8))
35 #define ITS_PMU_EVENT_CTRL_TYPE		GENMASK(12, 0)
36 
37 HISI_PMU_EVENT_ATTR_EXTRACTOR(int_id, config1, 31, 0);
38 HISI_PMU_EVENT_ATTR_EXTRACTOR(int_en, config1, 32, 32);
39 
40 struct hisi_its_pmu_regs {
41 	u32 version;
42 	u32 pmu_ctrl;
43 	u32 event_ctrl0;
44 	u32 event_cntr0;
45 	u32 cntr_ctrl;
46 };
47 
hisi_its_pmu_write_evtype(struct hisi_pmu * pmu,int idx,u32 type)48 static void hisi_its_pmu_write_evtype(struct hisi_pmu *pmu, int idx, u32 type)
49 {
50 	struct hisi_its_pmu_regs *reg_info = pmu->dev_info->private;
51 	u32 reg;
52 
53 	reg = readl(pmu->base + reg_info->event_ctrl0);
54 	reg &= ~ITS_PMU_CNTR_CTRL_MASK(idx);
55 	reg |= ITS_PMU_CNTR_EVENT_CFG(idx, type);
56 	writel(reg, pmu->base + reg_info->event_ctrl0);
57 }
58 
hisi_its_pmu_read_counter(struct hisi_pmu * pmu,struct hw_perf_event * hwc)59 static u64 hisi_its_pmu_read_counter(struct hisi_pmu *pmu,
60 				     struct hw_perf_event *hwc)
61 {
62 	struct hisi_its_pmu_regs *reg_info = pmu->dev_info->private;
63 
64 	return readq(pmu->base + ITS_PMU_EVENT_CNTRn(reg_info->event_cntr0, hwc->idx));
65 }
66 
hisi_its_pmu_write_counter(struct hisi_pmu * pmu,struct hw_perf_event * hwc,u64 val)67 static void hisi_its_pmu_write_counter(struct hisi_pmu *pmu,
68 				       struct hw_perf_event *hwc, u64 val)
69 {
70 	struct hisi_its_pmu_regs *reg_info = pmu->dev_info->private;
71 
72 	writeq(val, pmu->base + ITS_PMU_EVENT_CNTRn(reg_info->event_cntr0, hwc->idx));
73 }
74 
hisi_its_pmu_enable_counter(struct hisi_pmu * pmu,struct hw_perf_event * hwc)75 static void hisi_its_pmu_enable_counter(struct hisi_pmu *pmu,
76 					struct hw_perf_event *hwc)
77 {
78 	struct hisi_its_pmu_regs *reg_info = pmu->dev_info->private;
79 	u32 reg;
80 
81 	reg = readl(pmu->base + reg_info->cntr_ctrl);
82 	reg |= BIT(hwc->idx);
83 	writel(reg, pmu->base + reg_info->cntr_ctrl);
84 }
85 
hisi_its_pmu_disable_counter(struct hisi_pmu * pmu,struct hw_perf_event * hwc)86 static void hisi_its_pmu_disable_counter(struct hisi_pmu *pmu,
87 					 struct hw_perf_event *hwc)
88 {
89 	struct hisi_its_pmu_regs *reg_info = pmu->dev_info->private;
90 	u32 reg;
91 
92 	reg = readl(pmu->base + reg_info->cntr_ctrl);
93 	reg &= ~BIT(hwc->idx);
94 	writel(reg, pmu->base + reg_info->cntr_ctrl);
95 }
96 
hisi_its_pmu_enable_counter_int(struct hisi_pmu * pmu,struct hw_perf_event * hwc)97 static void hisi_its_pmu_enable_counter_int(struct hisi_pmu *pmu,
98 					    struct hw_perf_event *hwc)
99 {
100 	/* No overflow interrupt; counters rely on the framework's priming. */
101 }
102 
hisi_its_pmu_disable_counter_int(struct hisi_pmu * pmu,struct hw_perf_event * hwc)103 static void hisi_its_pmu_disable_counter_int(struct hisi_pmu *pmu,
104 					     struct hw_perf_event *hwc)
105 {
106 }
107 
hisi_its_pmu_start_counters(struct hisi_pmu * pmu)108 static void hisi_its_pmu_start_counters(struct hisi_pmu *pmu)
109 {
110 	struct hisi_its_pmu_regs *reg_info = pmu->dev_info->private;
111 	u32 reg;
112 
113 	reg = readl(pmu->base + reg_info->pmu_ctrl);
114 	reg |= ITS_PMU_GLOBAL_CTRL_PMU_EN;
115 	writel(reg, pmu->base + reg_info->pmu_ctrl);
116 }
117 
hisi_its_pmu_stop_counters(struct hisi_pmu * pmu)118 static void hisi_its_pmu_stop_counters(struct hisi_pmu *pmu)
119 {
120 	struct hisi_its_pmu_regs *reg_info = pmu->dev_info->private;
121 	u32 reg;
122 
123 	reg = readl(pmu->base + reg_info->pmu_ctrl);
124 	reg &= ~ITS_PMU_GLOBAL_CTRL_PMU_EN;
125 	writel(reg, pmu->base + reg_info->pmu_ctrl);
126 }
127 
hisi_its_pmu_enable_filter(struct perf_event * event)128 static void hisi_its_pmu_enable_filter(struct perf_event *event)
129 {
130 	struct hisi_pmu *pmu = to_hisi_pmu(event->pmu);
131 	u32 int_id = hisi_get_int_id(event);
132 	u32 int_en = hisi_get_int_en(event);
133 
134 	if (!int_en)
135 		return;
136 
137 	writel(int_id, pmu->base + ITS_PMU_INT_ID_CTRL);
138 	/* Write 0 to this register to enable filtering with int_id. */
139 	writel(0, pmu->base + ITS_PMU_INT_ID_MASK);
140 }
141 
hisi_its_pmu_disable_filter(struct perf_event * event)142 static void hisi_its_pmu_disable_filter(struct perf_event *event)
143 {
144 	struct hisi_pmu *pmu = to_hisi_pmu(event->pmu);
145 	u32 int_en = hisi_get_int_en(event);
146 
147 	if (!int_en)
148 		return;
149 
150 	/*
151 	 * The INT_ID filter is global, leave it alone if there are still
152 	 * active counters.
153 	 */
154 	if (bitmap_weight(pmu->pmu_events.used_mask, pmu->num_counters) > 1)
155 		return;
156 
157 	writel(0, pmu->base + ITS_PMU_INT_ID_CTRL);
158 	writel(-1U, pmu->base + ITS_PMU_INT_ID_MASK);
159 }
160 
161 /*
162  * The INT_ID filter is global to the PMU. Events can coexist only if they
163  * agree on it: both unfiltered, or both filtering the same int_id.
164  */
hisi_its_pmu_check_global_filter(struct perf_event * curr,struct perf_event * new)165 static bool hisi_its_pmu_check_global_filter(struct perf_event *curr,
166 					     struct perf_event *new)
167 {
168 	u32 curr_en = hisi_get_int_en(curr);
169 	u32 new_en = hisi_get_int_en(new);
170 
171 	if (curr_en != new_en)
172 		return false;
173 
174 	if (curr_en)
175 		return hisi_get_int_id(curr) == hisi_get_int_id(new);
176 
177 	return true;
178 }
179 
hisi_its_pmu_get_event_idx(struct perf_event * event)180 static int hisi_its_pmu_get_event_idx(struct perf_event *event)
181 {
182 	struct hisi_pmu *pmu = to_hisi_pmu(event->pmu);
183 	struct hisi_pmu_hwevents *pmu_events = &pmu->pmu_events;
184 	int cur_idx;
185 
186 	cur_idx = find_first_bit(pmu_events->used_mask, pmu->num_counters);
187 	if (cur_idx != pmu->num_counters &&
188 	    !hisi_its_pmu_check_global_filter(pmu_events->hw_events[cur_idx], event))
189 		return -EAGAIN;
190 
191 	return hisi_uncore_pmu_get_event_idx(event);
192 }
193 
194 static const struct hisi_uncore_ops hisi_uncore_its_ops = {
195 	.write_evtype		= hisi_its_pmu_write_evtype,
196 	.get_event_idx		= hisi_its_pmu_get_event_idx,
197 	.read_counter		= hisi_its_pmu_read_counter,
198 	.write_counter		= hisi_its_pmu_write_counter,
199 	.enable_counter		= hisi_its_pmu_enable_counter,
200 	.disable_counter	= hisi_its_pmu_disable_counter,
201 	.enable_counter_int	= hisi_its_pmu_enable_counter_int,
202 	.disable_counter_int	= hisi_its_pmu_disable_counter_int,
203 	.start_counters		= hisi_its_pmu_start_counters,
204 	.stop_counters		= hisi_its_pmu_stop_counters,
205 	.enable_filter		= hisi_its_pmu_enable_filter,
206 	.disable_filter		= hisi_its_pmu_disable_filter,
207 };
208 
209 static struct attribute *hisi_its_pmu_format_attrs[] = {
210 	HISI_PMU_FORMAT_ATTR(event, "config:0-7"),
211 	HISI_PMU_FORMAT_ATTR(int_id, "config1:0-31"),
212 	HISI_PMU_FORMAT_ATTR(int_en, "config1:32-32"),
213 	NULL
214 };
215 
216 static const struct attribute_group hisi_its_pmu_format_group = {
217 	.name = "format",
218 	.attrs = hisi_its_pmu_format_attrs,
219 };
220 
221 static struct attribute *hisi_its_pmu_events_attrs[] = {
222 	HISI_PMU_EVENT_ATTR(lpi_num, 0xc0),
223 	HISI_PMU_EVENT_ATTR(lpi_time, 0x80),
224 	HISI_PMU_EVENT_ATTR(sgi_num, 0xc1),
225 	HISI_PMU_EVENT_ATTR(sgi_time, 0x81),
226 	HISI_PMU_EVENT_ATTR(ppi_num, 0xc2),
227 	HISI_PMU_EVENT_ATTR(ppi_time, 0x82),
228 	HISI_PMU_EVENT_ATTR(sl3_lpi_num, 0xc3),
229 	HISI_PMU_EVENT_ATTR(sl3_sgi_num, 0xc4),
230 	HISI_PMU_EVENT_ATTR(sl3_ppi_num, 0xc5),
231 	HISI_PMU_EVENT_ATTR(sl0_ddr_read, 0xc9),
232 	HISI_PMU_EVENT_ATTR(sl0_ddr_time, 0x89),
233 	HISI_PMU_EVENT_ATTR(sl1_ddr_read, 0xca),
234 	HISI_PMU_EVENT_ATTR(sl1_ddr_time, 0x8a),
235 	HISI_PMU_EVENT_ATTR(sl2_ddr_read, 0xcb),
236 	HISI_PMU_EVENT_ATTR(sl2_ddr_time, 0x8b),
237 	HISI_PMU_EVENT_ATTR(cycles, 0xcc),
238 	NULL
239 };
240 
241 static const struct attribute_group hisi_its_pmu_events_group = {
242 	.name = "events",
243 	.attrs = hisi_its_pmu_events_attrs,
244 };
245 
246 static const struct attribute_group *hisi_its_pmu_attr_groups[] = {
247 	&hisi_its_pmu_format_group,
248 	&hisi_its_pmu_events_group,
249 	&hisi_pmu_cpumask_attr_group,
250 	&hisi_pmu_identifier_group,
251 	NULL
252 };
253 
hisi_its_pmu_dev_init(struct platform_device * pdev,struct hisi_pmu * pmu)254 static int hisi_its_pmu_dev_init(struct platform_device *pdev,
255 				 struct hisi_pmu *pmu)
256 {
257 	struct hisi_its_pmu_regs *reg_info;
258 
259 	hisi_uncore_pmu_init_topology(pmu, &pdev->dev);
260 
261 	if (pmu->topo.scl_id < 0)
262 		return dev_err_probe(&pdev->dev, -EINVAL, "failed to get scl-id\n");
263 
264 	if (pmu->topo.index_id < 0)
265 		return dev_err_probe(&pdev->dev, -EINVAL, "failed to get idx-id\n");
266 
267 	pmu->base = devm_platform_ioremap_resource(pdev, 0);
268 	if (IS_ERR(pmu->base))
269 		return dev_err_probe(&pdev->dev, PTR_ERR(pmu->base),
270 				     "fail to remap io memory\n");
271 
272 	pmu->dev_info = device_get_match_data(&pdev->dev);
273 	if (!pmu->dev_info)
274 		return -ENODEV;
275 
276 	pmu->pmu_events.attr_groups = pmu->dev_info->attr_groups;
277 	pmu->counter_bits = pmu->dev_info->counter_bits;
278 	pmu->check_event = pmu->dev_info->check_event;
279 	pmu->num_counters = ITS_PMU_NR_COUNTERS;
280 	pmu->ops = &hisi_uncore_its_ops;
281 	pmu->dev = &pdev->dev;
282 	pmu->on_cpu = -1;
283 
284 	reg_info = pmu->dev_info->private;
285 	pmu->identifier = readl(pmu->base + reg_info->version);
286 
287 	/* Reset the global INT_ID filter to disabled. */
288 	writel(0, pmu->base + ITS_PMU_INT_ID_CTRL);
289 	writel(-1U, pmu->base + ITS_PMU_INT_ID_MASK);
290 
291 	return 0;
292 }
293 
hisi_its_pmu_remove_cpuhp_instance(void * hotplug_node)294 static void hisi_its_pmu_remove_cpuhp_instance(void *hotplug_node)
295 {
296 	cpuhp_state_remove_instance_nocalls(hisi_uncore_pmu_cpuhp_state,
297 					    hotplug_node);
298 }
299 
hisi_its_pmu_unregister_pmu(void * pmu)300 static void hisi_its_pmu_unregister_pmu(void *pmu)
301 {
302 	perf_pmu_unregister(pmu);
303 }
304 
hisi_its_pmu_probe(struct platform_device * pdev)305 static int hisi_its_pmu_probe(struct platform_device *pdev)
306 {
307 	struct device *dev = &pdev->dev;
308 	struct hisi_pmu *pmu;
309 	char *name;
310 	int ret;
311 
312 	pmu = devm_kzalloc(dev, sizeof(*pmu), GFP_KERNEL);
313 	if (!pmu)
314 		return -ENOMEM;
315 
316 	/*
317 	 * HiSilicon Uncore PMU framework needs to get common hisi_pmu device
318 	 * from device's drvdata.
319 	 */
320 	platform_set_drvdata(pdev, pmu);
321 
322 	ret = hisi_its_pmu_dev_init(pdev, pmu);
323 	if (ret)
324 		return ret;
325 
326 	ret = cpuhp_state_add_instance(hisi_uncore_pmu_cpuhp_state, &pmu->node);
327 	if (ret)
328 		return dev_err_probe(dev, ret, "Fail to register cpuhp instance\n");
329 
330 	ret = devm_add_action_or_reset(dev, hisi_its_pmu_remove_cpuhp_instance,
331 				       &pmu->node);
332 	if (ret)
333 		return ret;
334 
335 	hisi_pmu_init(pmu, THIS_MODULE);
336 
337 	name = devm_kasprintf(dev, GFP_KERNEL, "hisi_scl%d_its%d",
338 			      pmu->topo.scl_id, pmu->topo.index_id);
339 	if (!name)
340 		return -ENOMEM;
341 
342 	ret = perf_pmu_register(&pmu->pmu, name, -1);
343 	if (ret)
344 		return dev_err_probe(dev, ret, "Fail to register PMU\n");
345 
346 	return devm_add_action_or_reset(dev, hisi_its_pmu_unregister_pmu,
347 					&pmu->pmu);
348 }
349 
350 static struct hisi_its_pmu_regs hisi_its_v1_pmu_regs = {
351 	.version = ITS_PMU_VERSION,
352 	.pmu_ctrl = ITS_PMU_GLOBAL_CTRL,
353 	.event_ctrl0 = ITS_PMU_EVENT_CTRL,
354 	.event_cntr0 = ITS_PMU_COUNTER0,
355 	.cntr_ctrl = ITS_PMU_COUNTER_CTRL,
356 };
357 
358 static const struct hisi_pmu_dev_info hisi_its_v1 = {
359 	.attr_groups = hisi_its_pmu_attr_groups,
360 	.counter_bits = 48,
361 	.check_event = ITS_PMU_EVENT_CTRL_TYPE,
362 	.private = &hisi_its_v1_pmu_regs,
363 };
364 
365 static const struct acpi_device_id hisi_its_pmu_ids[] = {
366 	{ "HISI0591", (kernel_ulong_t)&hisi_its_v1 },
367 	{ }
368 };
369 MODULE_DEVICE_TABLE(acpi, hisi_its_pmu_ids);
370 
371 static struct platform_driver hisi_its_pmu_driver = {
372 	.driver = {
373 		.name = "hisi_its_pmu",
374 		.acpi_match_table = hisi_its_pmu_ids,
375 		.suppress_bind_attrs = true,
376 	},
377 	.probe = hisi_its_pmu_probe,
378 };
379 
380 module_platform_driver(hisi_its_pmu_driver);
381 
382 MODULE_IMPORT_NS("HISI_PMU");
383 MODULE_DESCRIPTION("HiSilicon SoC Uncore ITS PMU driver");
384 MODULE_LICENSE("GPL");
385 MODULE_AUTHOR("Yushan Wang <wangyushan12@huawei.com>");
386