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