1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * HiSilicon SoC UC (unified cache) uncore Hardware event counters support |
4 | * |
5 | * Copyright (C) 2023 HiSilicon Limited |
6 | * |
7 | * This code is based on the uncore PMUs like hisi_uncore_l3c_pmu. |
8 | */ |
9 | #include <linux/cpuhotplug.h> |
10 | #include <linux/interrupt.h> |
11 | #include <linux/irq.h> |
12 | #include <linux/list.h> |
13 | #include <linux/mod_devicetable.h> |
14 | #include <linux/property.h> |
15 | |
16 | #include "hisi_uncore_pmu.h" |
17 | |
18 | /* Dynamic CPU hotplug state used by UC PMU */ |
19 | static enum cpuhp_state hisi_uc_pmu_online; |
20 | |
21 | /* UC register definition */ |
22 | #define HISI_UC_INT_MASK_REG 0x0800 |
23 | #define HISI_UC_INT_STS_REG 0x0808 |
24 | #define HISI_UC_INT_CLEAR_REG 0x080c |
25 | #define HISI_UC_TRACETAG_CTRL_REG 0x1b2c |
26 | #define HISI_UC_TRACETAG_REQ_MSK GENMASK(9, 7) |
27 | #define HISI_UC_TRACETAG_MARK_EN BIT(0) |
28 | #define HISI_UC_TRACETAG_REQ_EN (HISI_UC_TRACETAG_MARK_EN | BIT(2)) |
29 | #define HISI_UC_TRACETAG_SRCID_EN BIT(3) |
30 | #define HISI_UC_SRCID_CTRL_REG 0x1b40 |
31 | #define HISI_UC_SRCID_MSK GENMASK(14, 1) |
32 | #define HISI_UC_EVENT_CTRL_REG 0x1c00 |
33 | #define HISI_UC_EVENT_TRACETAG_EN BIT(29) |
34 | #define HISI_UC_EVENT_URING_MSK GENMASK(28, 27) |
35 | #define HISI_UC_EVENT_GLB_EN BIT(26) |
36 | #define HISI_UC_VERSION_REG 0x1cf0 |
37 | #define HISI_UC_EVTYPE_REGn(n) (0x1d00 + (n) * 4) |
38 | #define HISI_UC_EVTYPE_MASK GENMASK(7, 0) |
39 | #define HISI_UC_CNTR_REGn(n) (0x1e00 + (n) * 8) |
40 | |
41 | #define HISI_UC_NR_COUNTERS 0x8 |
42 | #define HISI_UC_V2_NR_EVENTS 0xFF |
43 | #define HISI_UC_CNTR_REG_BITS 64 |
44 | |
45 | #define HISI_UC_RD_REQ_TRACETAG 0x4 |
46 | #define HISI_UC_URING_EVENT_MIN 0x47 |
47 | #define HISI_UC_URING_EVENT_MAX 0x59 |
48 | |
49 | HISI_PMU_EVENT_ATTR_EXTRACTOR(rd_req_en, config1, 0, 0); |
50 | HISI_PMU_EVENT_ATTR_EXTRACTOR(uring_channel, config1, 5, 4); |
51 | HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid, config1, 19, 6); |
52 | HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_en, config1, 20, 20); |
53 | |
54 | static int hisi_uc_pmu_check_filter(struct perf_event *event) |
55 | { |
56 | struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); |
57 | |
58 | if (hisi_get_srcid_en(event) && !hisi_get_rd_req_en(event)) { |
59 | dev_err(uc_pmu->dev, |
60 | "rcid_en depends on rd_req_en being enabled!\n" ); |
61 | return -EINVAL; |
62 | } |
63 | |
64 | if (!hisi_get_uring_channel(event)) |
65 | return 0; |
66 | |
67 | if ((HISI_GET_EVENTID(event) < HISI_UC_URING_EVENT_MIN) || |
68 | (HISI_GET_EVENTID(event) > HISI_UC_URING_EVENT_MAX)) |
69 | dev_warn(uc_pmu->dev, |
70 | "Only events: [%#x ~ %#x] support channel filtering!" , |
71 | HISI_UC_URING_EVENT_MIN, HISI_UC_URING_EVENT_MAX); |
72 | |
73 | return 0; |
74 | } |
75 | |
76 | static void hisi_uc_pmu_config_req_tracetag(struct perf_event *event) |
77 | { |
78 | struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); |
79 | u32 val; |
80 | |
81 | if (!hisi_get_rd_req_en(event)) |
82 | return; |
83 | |
84 | val = readl(addr: uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); |
85 | |
86 | /* The request-type has been configured */ |
87 | if (FIELD_GET(HISI_UC_TRACETAG_REQ_MSK, val) == HISI_UC_RD_REQ_TRACETAG) |
88 | return; |
89 | |
90 | /* Set request-type for tracetag, only read request is supported! */ |
91 | val &= ~HISI_UC_TRACETAG_REQ_MSK; |
92 | val |= FIELD_PREP(HISI_UC_TRACETAG_REQ_MSK, HISI_UC_RD_REQ_TRACETAG); |
93 | val |= HISI_UC_TRACETAG_REQ_EN; |
94 | writel(val, addr: uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); |
95 | } |
96 | |
97 | static void hisi_uc_pmu_clear_req_tracetag(struct perf_event *event) |
98 | { |
99 | struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); |
100 | u32 val; |
101 | |
102 | if (!hisi_get_rd_req_en(event)) |
103 | return; |
104 | |
105 | val = readl(addr: uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); |
106 | |
107 | /* Do nothing, the request-type tracetag has been cleaned up */ |
108 | if (FIELD_GET(HISI_UC_TRACETAG_REQ_MSK, val) == 0) |
109 | return; |
110 | |
111 | /* Clear request-type */ |
112 | val &= ~HISI_UC_TRACETAG_REQ_MSK; |
113 | val &= ~HISI_UC_TRACETAG_REQ_EN; |
114 | writel(val, addr: uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); |
115 | } |
116 | |
117 | static void hisi_uc_pmu_config_srcid_tracetag(struct perf_event *event) |
118 | { |
119 | struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); |
120 | u32 val; |
121 | |
122 | if (!hisi_get_srcid_en(event)) |
123 | return; |
124 | |
125 | val = readl(addr: uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); |
126 | |
127 | /* Do nothing, the source id has been configured */ |
128 | if (FIELD_GET(HISI_UC_TRACETAG_SRCID_EN, val)) |
129 | return; |
130 | |
131 | /* Enable source id tracetag */ |
132 | val |= HISI_UC_TRACETAG_SRCID_EN; |
133 | writel(val, addr: uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); |
134 | |
135 | val = readl(addr: uc_pmu->base + HISI_UC_SRCID_CTRL_REG); |
136 | val &= ~HISI_UC_SRCID_MSK; |
137 | val |= FIELD_PREP(HISI_UC_SRCID_MSK, hisi_get_srcid(event)); |
138 | writel(val, addr: uc_pmu->base + HISI_UC_SRCID_CTRL_REG); |
139 | |
140 | /* Depend on request-type tracetag enabled */ |
141 | hisi_uc_pmu_config_req_tracetag(event); |
142 | } |
143 | |
144 | static void hisi_uc_pmu_clear_srcid_tracetag(struct perf_event *event) |
145 | { |
146 | struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); |
147 | u32 val; |
148 | |
149 | if (!hisi_get_srcid_en(event)) |
150 | return; |
151 | |
152 | val = readl(addr: uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); |
153 | |
154 | /* Do nothing, the source id has been cleaned up */ |
155 | if (FIELD_GET(HISI_UC_TRACETAG_SRCID_EN, val) == 0) |
156 | return; |
157 | |
158 | hisi_uc_pmu_clear_req_tracetag(event); |
159 | |
160 | /* Disable source id tracetag */ |
161 | val &= ~HISI_UC_TRACETAG_SRCID_EN; |
162 | writel(val, addr: uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); |
163 | |
164 | val = readl(addr: uc_pmu->base + HISI_UC_SRCID_CTRL_REG); |
165 | val &= ~HISI_UC_SRCID_MSK; |
166 | writel(val, addr: uc_pmu->base + HISI_UC_SRCID_CTRL_REG); |
167 | } |
168 | |
169 | static void hisi_uc_pmu_config_uring_channel(struct perf_event *event) |
170 | { |
171 | struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); |
172 | u32 uring_channel = hisi_get_uring_channel(event); |
173 | u32 val; |
174 | |
175 | /* Do nothing if not being set or is set explicitly to zero (default) */ |
176 | if (uring_channel == 0) |
177 | return; |
178 | |
179 | val = readl(addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
180 | |
181 | /* Do nothing, the uring_channel has been configured */ |
182 | if (uring_channel == FIELD_GET(HISI_UC_EVENT_URING_MSK, val)) |
183 | return; |
184 | |
185 | val &= ~HISI_UC_EVENT_URING_MSK; |
186 | val |= FIELD_PREP(HISI_UC_EVENT_URING_MSK, uring_channel); |
187 | writel(val, addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
188 | } |
189 | |
190 | static void hisi_uc_pmu_clear_uring_channel(struct perf_event *event) |
191 | { |
192 | struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); |
193 | u32 val; |
194 | |
195 | /* Do nothing if not being set or is set explicitly to zero (default) */ |
196 | if (hisi_get_uring_channel(event) == 0) |
197 | return; |
198 | |
199 | val = readl(addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
200 | |
201 | /* Do nothing, the uring_channel has been cleaned up */ |
202 | if (FIELD_GET(HISI_UC_EVENT_URING_MSK, val) == 0) |
203 | return; |
204 | |
205 | val &= ~HISI_UC_EVENT_URING_MSK; |
206 | writel(val, addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
207 | } |
208 | |
209 | static void hisi_uc_pmu_enable_filter(struct perf_event *event) |
210 | { |
211 | if (event->attr.config1 == 0) |
212 | return; |
213 | |
214 | hisi_uc_pmu_config_uring_channel(event); |
215 | hisi_uc_pmu_config_req_tracetag(event); |
216 | hisi_uc_pmu_config_srcid_tracetag(event); |
217 | } |
218 | |
219 | static void hisi_uc_pmu_disable_filter(struct perf_event *event) |
220 | { |
221 | if (event->attr.config1 == 0) |
222 | return; |
223 | |
224 | hisi_uc_pmu_clear_srcid_tracetag(event); |
225 | hisi_uc_pmu_clear_req_tracetag(event); |
226 | hisi_uc_pmu_clear_uring_channel(event); |
227 | } |
228 | |
229 | static void hisi_uc_pmu_write_evtype(struct hisi_pmu *uc_pmu, int idx, u32 type) |
230 | { |
231 | u32 val; |
232 | |
233 | /* |
234 | * Select the appropriate event select register. |
235 | * There are 2 32-bit event select registers for the |
236 | * 8 hardware counters, each event code is 8-bit wide. |
237 | */ |
238 | val = readl(addr: uc_pmu->base + HISI_UC_EVTYPE_REGn(idx / 4)); |
239 | val &= ~(HISI_UC_EVTYPE_MASK << HISI_PMU_EVTYPE_SHIFT(idx)); |
240 | val |= (type << HISI_PMU_EVTYPE_SHIFT(idx)); |
241 | writel(val, addr: uc_pmu->base + HISI_UC_EVTYPE_REGn(idx / 4)); |
242 | } |
243 | |
244 | static void hisi_uc_pmu_start_counters(struct hisi_pmu *uc_pmu) |
245 | { |
246 | u32 val; |
247 | |
248 | val = readl(addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
249 | val |= HISI_UC_EVENT_GLB_EN; |
250 | writel(val, addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
251 | } |
252 | |
253 | static void hisi_uc_pmu_stop_counters(struct hisi_pmu *uc_pmu) |
254 | { |
255 | u32 val; |
256 | |
257 | val = readl(addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
258 | val &= ~HISI_UC_EVENT_GLB_EN; |
259 | writel(val, addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
260 | } |
261 | |
262 | static void hisi_uc_pmu_enable_counter(struct hisi_pmu *uc_pmu, |
263 | struct hw_perf_event *hwc) |
264 | { |
265 | u32 val; |
266 | |
267 | /* Enable counter index */ |
268 | val = readl(addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
269 | val |= (1 << hwc->idx); |
270 | writel(val, addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
271 | } |
272 | |
273 | static void hisi_uc_pmu_disable_counter(struct hisi_pmu *uc_pmu, |
274 | struct hw_perf_event *hwc) |
275 | { |
276 | u32 val; |
277 | |
278 | /* Clear counter index */ |
279 | val = readl(addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
280 | val &= ~(1 << hwc->idx); |
281 | writel(val, addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
282 | } |
283 | |
284 | static u64 hisi_uc_pmu_read_counter(struct hisi_pmu *uc_pmu, |
285 | struct hw_perf_event *hwc) |
286 | { |
287 | return readq(addr: uc_pmu->base + HISI_UC_CNTR_REGn(hwc->idx)); |
288 | } |
289 | |
290 | static bool hisi_uc_pmu_get_glb_en_state(struct hisi_pmu *uc_pmu) |
291 | { |
292 | u32 val; |
293 | |
294 | val = readl(addr: uc_pmu->base + HISI_UC_EVENT_CTRL_REG); |
295 | return !!FIELD_GET(HISI_UC_EVENT_GLB_EN, val); |
296 | } |
297 | |
298 | static void hisi_uc_pmu_write_counter_normal(struct hisi_pmu *uc_pmu, |
299 | struct hw_perf_event *hwc, u64 val) |
300 | { |
301 | writeq(val, addr: uc_pmu->base + HISI_UC_CNTR_REGn(hwc->idx)); |
302 | } |
303 | |
304 | static void hisi_uc_pmu_write_counter_quirk_v2(struct hisi_pmu *uc_pmu, |
305 | struct hw_perf_event *hwc, u64 val) |
306 | { |
307 | hisi_uc_pmu_start_counters(uc_pmu); |
308 | hisi_uc_pmu_write_counter_normal(uc_pmu, hwc, val); |
309 | hisi_uc_pmu_stop_counters(uc_pmu); |
310 | } |
311 | |
312 | static void hisi_uc_pmu_write_counter(struct hisi_pmu *uc_pmu, |
313 | struct hw_perf_event *hwc, u64 val) |
314 | { |
315 | bool enable = hisi_uc_pmu_get_glb_en_state(uc_pmu); |
316 | bool erratum = uc_pmu->identifier == HISI_PMU_V2; |
317 | |
318 | /* |
319 | * HiSilicon UC PMU v2 suffers the erratum 162700402 that the |
320 | * PMU counter cannot be set due to the lack of clock under power |
321 | * saving mode. This will lead to error or inaccurate counts. |
322 | * The clock can be enabled by the PMU global enabling control. |
323 | * The irq handler and pmu_start() will call the function to set |
324 | * period. If the function under irq context, the PMU has been |
325 | * enabled therefore we set counter directly. Other situations |
326 | * the PMU is disabled, we need to enable it to turn on the |
327 | * counter clock to set period, and then restore PMU enable |
328 | * status, the counter can hold its value without a clock. |
329 | */ |
330 | if (enable || !erratum) |
331 | hisi_uc_pmu_write_counter_normal(uc_pmu, hwc, val); |
332 | else |
333 | hisi_uc_pmu_write_counter_quirk_v2(uc_pmu, hwc, val); |
334 | } |
335 | |
336 | static void hisi_uc_pmu_enable_counter_int(struct hisi_pmu *uc_pmu, |
337 | struct hw_perf_event *hwc) |
338 | { |
339 | u32 val; |
340 | |
341 | val = readl(addr: uc_pmu->base + HISI_UC_INT_MASK_REG); |
342 | val &= ~(1 << hwc->idx); |
343 | writel(val, addr: uc_pmu->base + HISI_UC_INT_MASK_REG); |
344 | } |
345 | |
346 | static void hisi_uc_pmu_disable_counter_int(struct hisi_pmu *uc_pmu, |
347 | struct hw_perf_event *hwc) |
348 | { |
349 | u32 val; |
350 | |
351 | val = readl(addr: uc_pmu->base + HISI_UC_INT_MASK_REG); |
352 | val |= (1 << hwc->idx); |
353 | writel(val, addr: uc_pmu->base + HISI_UC_INT_MASK_REG); |
354 | } |
355 | |
356 | static u32 hisi_uc_pmu_get_int_status(struct hisi_pmu *uc_pmu) |
357 | { |
358 | return readl(addr: uc_pmu->base + HISI_UC_INT_STS_REG); |
359 | } |
360 | |
361 | static void hisi_uc_pmu_clear_int_status(struct hisi_pmu *uc_pmu, int idx) |
362 | { |
363 | writel(val: 1 << idx, addr: uc_pmu->base + HISI_UC_INT_CLEAR_REG); |
364 | } |
365 | |
366 | static int hisi_uc_pmu_init_data(struct platform_device *pdev, |
367 | struct hisi_pmu *uc_pmu) |
368 | { |
369 | /* |
370 | * Use SCCL (Super CPU Cluster) ID and CCL (CPU Cluster) ID to |
371 | * identify the topology information of UC PMU devices in the chip. |
372 | * They have some CCLs per SCCL and then 4 UC PMU per CCL. |
373 | */ |
374 | if (device_property_read_u32(dev: &pdev->dev, propname: "hisilicon,scl-id" , |
375 | val: &uc_pmu->sccl_id)) { |
376 | dev_err(&pdev->dev, "Can not read uc sccl-id!\n" ); |
377 | return -EINVAL; |
378 | } |
379 | |
380 | if (device_property_read_u32(dev: &pdev->dev, propname: "hisilicon,ccl-id" , |
381 | val: &uc_pmu->ccl_id)) { |
382 | dev_err(&pdev->dev, "Can not read uc ccl-id!\n" ); |
383 | return -EINVAL; |
384 | } |
385 | |
386 | if (device_property_read_u32(dev: &pdev->dev, propname: "hisilicon,sub-id" , |
387 | val: &uc_pmu->sub_id)) { |
388 | dev_err(&pdev->dev, "Can not read sub-id!\n" ); |
389 | return -EINVAL; |
390 | } |
391 | |
392 | uc_pmu->base = devm_platform_ioremap_resource(pdev, index: 0); |
393 | if (IS_ERR(ptr: uc_pmu->base)) { |
394 | dev_err(&pdev->dev, "ioremap failed for uc_pmu resource\n" ); |
395 | return PTR_ERR(ptr: uc_pmu->base); |
396 | } |
397 | |
398 | uc_pmu->identifier = readl(addr: uc_pmu->base + HISI_UC_VERSION_REG); |
399 | |
400 | return 0; |
401 | } |
402 | |
403 | static struct attribute *hisi_uc_pmu_format_attr[] = { |
404 | HISI_PMU_FORMAT_ATTR(event, "config:0-7" ), |
405 | HISI_PMU_FORMAT_ATTR(rd_req_en, "config1:0-0" ), |
406 | HISI_PMU_FORMAT_ATTR(uring_channel, "config1:4-5" ), |
407 | HISI_PMU_FORMAT_ATTR(srcid, "config1:6-19" ), |
408 | HISI_PMU_FORMAT_ATTR(srcid_en, "config1:20-20" ), |
409 | NULL |
410 | }; |
411 | |
412 | static const struct attribute_group hisi_uc_pmu_format_group = { |
413 | .name = "format" , |
414 | .attrs = hisi_uc_pmu_format_attr, |
415 | }; |
416 | |
417 | static struct attribute *hisi_uc_pmu_events_attr[] = { |
418 | HISI_PMU_EVENT_ATTR(sq_time, 0x00), |
419 | HISI_PMU_EVENT_ATTR(pq_time, 0x01), |
420 | HISI_PMU_EVENT_ATTR(hbm_time, 0x02), |
421 | HISI_PMU_EVENT_ATTR(iq_comp_time_cring, 0x03), |
422 | HISI_PMU_EVENT_ATTR(iq_comp_time_uring, 0x05), |
423 | HISI_PMU_EVENT_ATTR(cpu_rd, 0x10), |
424 | HISI_PMU_EVENT_ATTR(cpu_rd64, 0x17), |
425 | HISI_PMU_EVENT_ATTR(cpu_rs64, 0x19), |
426 | HISI_PMU_EVENT_ATTR(cpu_mru, 0x1c), |
427 | HISI_PMU_EVENT_ATTR(cycles, 0x95), |
428 | HISI_PMU_EVENT_ATTR(spipe_hit, 0xb3), |
429 | HISI_PMU_EVENT_ATTR(hpipe_hit, 0xdb), |
430 | HISI_PMU_EVENT_ATTR(cring_rxdat_cnt, 0xfa), |
431 | HISI_PMU_EVENT_ATTR(cring_txdat_cnt, 0xfb), |
432 | HISI_PMU_EVENT_ATTR(uring_rxdat_cnt, 0xfc), |
433 | HISI_PMU_EVENT_ATTR(uring_txdat_cnt, 0xfd), |
434 | NULL |
435 | }; |
436 | |
437 | static const struct attribute_group hisi_uc_pmu_events_group = { |
438 | .name = "events" , |
439 | .attrs = hisi_uc_pmu_events_attr, |
440 | }; |
441 | |
442 | static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); |
443 | |
444 | static struct attribute *hisi_uc_pmu_cpumask_attrs[] = { |
445 | &dev_attr_cpumask.attr, |
446 | NULL, |
447 | }; |
448 | |
449 | static const struct attribute_group hisi_uc_pmu_cpumask_attr_group = { |
450 | .attrs = hisi_uc_pmu_cpumask_attrs, |
451 | }; |
452 | |
453 | static struct device_attribute hisi_uc_pmu_identifier_attr = |
454 | __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); |
455 | |
456 | static struct attribute *hisi_uc_pmu_identifier_attrs[] = { |
457 | &hisi_uc_pmu_identifier_attr.attr, |
458 | NULL |
459 | }; |
460 | |
461 | static const struct attribute_group hisi_uc_pmu_identifier_group = { |
462 | .attrs = hisi_uc_pmu_identifier_attrs, |
463 | }; |
464 | |
465 | static const struct attribute_group *hisi_uc_pmu_attr_groups[] = { |
466 | &hisi_uc_pmu_format_group, |
467 | &hisi_uc_pmu_events_group, |
468 | &hisi_uc_pmu_cpumask_attr_group, |
469 | &hisi_uc_pmu_identifier_group, |
470 | NULL |
471 | }; |
472 | |
473 | static const struct hisi_uncore_ops hisi_uncore_uc_pmu_ops = { |
474 | .check_filter = hisi_uc_pmu_check_filter, |
475 | .write_evtype = hisi_uc_pmu_write_evtype, |
476 | .get_event_idx = hisi_uncore_pmu_get_event_idx, |
477 | .start_counters = hisi_uc_pmu_start_counters, |
478 | .stop_counters = hisi_uc_pmu_stop_counters, |
479 | .enable_counter = hisi_uc_pmu_enable_counter, |
480 | .disable_counter = hisi_uc_pmu_disable_counter, |
481 | .enable_counter_int = hisi_uc_pmu_enable_counter_int, |
482 | .disable_counter_int = hisi_uc_pmu_disable_counter_int, |
483 | .write_counter = hisi_uc_pmu_write_counter, |
484 | .read_counter = hisi_uc_pmu_read_counter, |
485 | .get_int_status = hisi_uc_pmu_get_int_status, |
486 | .clear_int_status = hisi_uc_pmu_clear_int_status, |
487 | .enable_filter = hisi_uc_pmu_enable_filter, |
488 | .disable_filter = hisi_uc_pmu_disable_filter, |
489 | }; |
490 | |
491 | static int hisi_uc_pmu_dev_probe(struct platform_device *pdev, |
492 | struct hisi_pmu *uc_pmu) |
493 | { |
494 | int ret; |
495 | |
496 | ret = hisi_uc_pmu_init_data(pdev, uc_pmu); |
497 | if (ret) |
498 | return ret; |
499 | |
500 | ret = hisi_uncore_pmu_init_irq(hisi_pmu: uc_pmu, pdev); |
501 | if (ret) |
502 | return ret; |
503 | |
504 | uc_pmu->pmu_events.attr_groups = hisi_uc_pmu_attr_groups; |
505 | uc_pmu->check_event = HISI_UC_EVTYPE_MASK; |
506 | uc_pmu->ops = &hisi_uncore_uc_pmu_ops; |
507 | uc_pmu->counter_bits = HISI_UC_CNTR_REG_BITS; |
508 | uc_pmu->num_counters = HISI_UC_NR_COUNTERS; |
509 | uc_pmu->dev = &pdev->dev; |
510 | uc_pmu->on_cpu = -1; |
511 | |
512 | return 0; |
513 | } |
514 | |
515 | static void hisi_uc_pmu_remove_cpuhp_instance(void *hotplug_node) |
516 | { |
517 | cpuhp_state_remove_instance_nocalls(state: hisi_uc_pmu_online, node: hotplug_node); |
518 | } |
519 | |
520 | static void hisi_uc_pmu_unregister_pmu(void *pmu) |
521 | { |
522 | perf_pmu_unregister(pmu); |
523 | } |
524 | |
525 | static int hisi_uc_pmu_probe(struct platform_device *pdev) |
526 | { |
527 | struct hisi_pmu *uc_pmu; |
528 | char *name; |
529 | int ret; |
530 | |
531 | uc_pmu = devm_kzalloc(dev: &pdev->dev, size: sizeof(*uc_pmu), GFP_KERNEL); |
532 | if (!uc_pmu) |
533 | return -ENOMEM; |
534 | |
535 | platform_set_drvdata(pdev, data: uc_pmu); |
536 | |
537 | ret = hisi_uc_pmu_dev_probe(pdev, uc_pmu); |
538 | if (ret) |
539 | return ret; |
540 | |
541 | name = devm_kasprintf(dev: &pdev->dev, GFP_KERNEL, fmt: "hisi_sccl%d_uc%d_%u" , |
542 | uc_pmu->sccl_id, uc_pmu->ccl_id, uc_pmu->sub_id); |
543 | if (!name) |
544 | return -ENOMEM; |
545 | |
546 | ret = cpuhp_state_add_instance(state: hisi_uc_pmu_online, node: &uc_pmu->node); |
547 | if (ret) |
548 | return dev_err_probe(dev: &pdev->dev, err: ret, fmt: "Error registering hotplug\n" ); |
549 | |
550 | ret = devm_add_action_or_reset(&pdev->dev, |
551 | hisi_uc_pmu_remove_cpuhp_instance, |
552 | &uc_pmu->node); |
553 | if (ret) |
554 | return ret; |
555 | |
556 | hisi_pmu_init(hisi_pmu: uc_pmu, THIS_MODULE); |
557 | |
558 | ret = perf_pmu_register(pmu: &uc_pmu->pmu, name, type: -1); |
559 | if (ret) |
560 | return ret; |
561 | |
562 | return devm_add_action_or_reset(&pdev->dev, |
563 | hisi_uc_pmu_unregister_pmu, |
564 | &uc_pmu->pmu); |
565 | } |
566 | |
567 | static const struct acpi_device_id hisi_uc_pmu_acpi_match[] = { |
568 | { "HISI0291" , }, |
569 | {} |
570 | }; |
571 | MODULE_DEVICE_TABLE(acpi, hisi_uc_pmu_acpi_match); |
572 | |
573 | static struct platform_driver hisi_uc_pmu_driver = { |
574 | .driver = { |
575 | .name = "hisi_uc_pmu" , |
576 | .acpi_match_table = hisi_uc_pmu_acpi_match, |
577 | /* |
578 | * We have not worked out a safe bind/unbind process, |
579 | * Forcefully unbinding during sampling will lead to a |
580 | * kernel panic, so this is not supported yet. |
581 | */ |
582 | .suppress_bind_attrs = true, |
583 | }, |
584 | .probe = hisi_uc_pmu_probe, |
585 | }; |
586 | |
587 | static int __init hisi_uc_pmu_module_init(void) |
588 | { |
589 | int ret; |
590 | |
591 | ret = cpuhp_setup_state_multi(state: CPUHP_AP_ONLINE_DYN, |
592 | name: "perf/hisi/uc:online" , |
593 | startup: hisi_uncore_pmu_online_cpu, |
594 | teardown: hisi_uncore_pmu_offline_cpu); |
595 | if (ret < 0) { |
596 | pr_err("UC PMU: Error setup hotplug, ret = %d\n" , ret); |
597 | return ret; |
598 | } |
599 | hisi_uc_pmu_online = ret; |
600 | |
601 | ret = platform_driver_register(&hisi_uc_pmu_driver); |
602 | if (ret) |
603 | cpuhp_remove_multi_state(state: hisi_uc_pmu_online); |
604 | |
605 | return ret; |
606 | } |
607 | module_init(hisi_uc_pmu_module_init); |
608 | |
609 | static void __exit hisi_uc_pmu_module_exit(void) |
610 | { |
611 | platform_driver_unregister(&hisi_uc_pmu_driver); |
612 | cpuhp_remove_multi_state(state: hisi_uc_pmu_online); |
613 | } |
614 | module_exit(hisi_uc_pmu_module_exit); |
615 | |
616 | MODULE_DESCRIPTION("HiSilicon SoC UC uncore PMU driver" ); |
617 | MODULE_LICENSE("GPL" ); |
618 | MODULE_AUTHOR("Junhao He <hejunhao3@huawei.com>" ); |
619 | |