1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * HiSilicon PA uncore Hardware event counters support |
4 | * |
5 | * Copyright (C) 2020 HiSilicon Limited |
6 | * Author: Shaokun Zhang <zhangshaokun@hisilicon.com> |
7 | * |
8 | * This code is based on the uncore PMUs like arm-cci and arm-ccn. |
9 | */ |
10 | #include <linux/acpi.h> |
11 | #include <linux/cpuhotplug.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/irq.h> |
14 | #include <linux/list.h> |
15 | #include <linux/smp.h> |
16 | |
17 | #include "hisi_uncore_pmu.h" |
18 | |
19 | /* PA register definition */ |
20 | #define PA_PERF_CTRL 0x1c00 |
21 | #define PA_EVENT_CTRL 0x1c04 |
22 | #define PA_TT_CTRL 0x1c08 |
23 | #define PA_TGTID_CTRL 0x1c14 |
24 | #define PA_SRCID_CTRL 0x1c18 |
25 | |
26 | /* H32 PA interrupt registers */ |
27 | #define PA_INT_MASK 0x1c70 |
28 | #define PA_INT_STATUS 0x1c78 |
29 | #define PA_INT_CLEAR 0x1c7c |
30 | |
31 | #define H60PA_INT_STATUS 0x1c70 |
32 | #define H60PA_INT_MASK 0x1c74 |
33 | |
34 | #define PA_EVENT_TYPE0 0x1c80 |
35 | #define PA_PMU_VERSION 0x1cf0 |
36 | #define PA_EVENT_CNT0_L 0x1d00 |
37 | |
38 | #define PA_EVTYPE_MASK 0xff |
39 | #define PA_NR_COUNTERS 0x8 |
40 | #define PA_PERF_CTRL_EN BIT(0) |
41 | #define PA_TRACETAG_EN BIT(4) |
42 | #define PA_TGTID_EN BIT(11) |
43 | #define PA_SRCID_EN BIT(11) |
44 | #define PA_TGTID_NONE 0 |
45 | #define PA_SRCID_NONE 0 |
46 | #define PA_TGTID_MSK_SHIFT 12 |
47 | #define PA_SRCID_MSK_SHIFT 12 |
48 | |
49 | HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_cmd, config1, 10, 0); |
50 | HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_msk, config1, 21, 11); |
51 | HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 32, 22); |
52 | HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 43, 33); |
53 | HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 44, 44); |
54 | |
55 | struct hisi_pa_pmu_int_regs { |
56 | u32 mask_offset; |
57 | u32 clear_offset; |
58 | u32 status_offset; |
59 | }; |
60 | |
61 | static void hisi_pa_pmu_enable_tracetag(struct perf_event *event) |
62 | { |
63 | struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); |
64 | u32 tt_en = hisi_get_tracetag_en(event); |
65 | |
66 | if (tt_en) { |
67 | u32 val; |
68 | |
69 | val = readl(addr: pa_pmu->base + PA_TT_CTRL); |
70 | val |= PA_TRACETAG_EN; |
71 | writel(val, addr: pa_pmu->base + PA_TT_CTRL); |
72 | } |
73 | } |
74 | |
75 | static void hisi_pa_pmu_clear_tracetag(struct perf_event *event) |
76 | { |
77 | struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); |
78 | u32 tt_en = hisi_get_tracetag_en(event); |
79 | |
80 | if (tt_en) { |
81 | u32 val; |
82 | |
83 | val = readl(addr: pa_pmu->base + PA_TT_CTRL); |
84 | val &= ~PA_TRACETAG_EN; |
85 | writel(val, addr: pa_pmu->base + PA_TT_CTRL); |
86 | } |
87 | } |
88 | |
89 | static void hisi_pa_pmu_config_tgtid(struct perf_event *event) |
90 | { |
91 | struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); |
92 | u32 cmd = hisi_get_tgtid_cmd(event); |
93 | |
94 | if (cmd) { |
95 | u32 msk = hisi_get_tgtid_msk(event); |
96 | u32 val = cmd | PA_TGTID_EN | (msk << PA_TGTID_MSK_SHIFT); |
97 | |
98 | writel(val, addr: pa_pmu->base + PA_TGTID_CTRL); |
99 | } |
100 | } |
101 | |
102 | static void hisi_pa_pmu_clear_tgtid(struct perf_event *event) |
103 | { |
104 | struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); |
105 | u32 cmd = hisi_get_tgtid_cmd(event); |
106 | |
107 | if (cmd) |
108 | writel(PA_TGTID_NONE, addr: pa_pmu->base + PA_TGTID_CTRL); |
109 | } |
110 | |
111 | static void hisi_pa_pmu_config_srcid(struct perf_event *event) |
112 | { |
113 | struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); |
114 | u32 cmd = hisi_get_srcid_cmd(event); |
115 | |
116 | if (cmd) { |
117 | u32 msk = hisi_get_srcid_msk(event); |
118 | u32 val = cmd | PA_SRCID_EN | (msk << PA_SRCID_MSK_SHIFT); |
119 | |
120 | writel(val, addr: pa_pmu->base + PA_SRCID_CTRL); |
121 | } |
122 | } |
123 | |
124 | static void hisi_pa_pmu_clear_srcid(struct perf_event *event) |
125 | { |
126 | struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); |
127 | u32 cmd = hisi_get_srcid_cmd(event); |
128 | |
129 | if (cmd) |
130 | writel(PA_SRCID_NONE, addr: pa_pmu->base + PA_SRCID_CTRL); |
131 | } |
132 | |
133 | static void hisi_pa_pmu_enable_filter(struct perf_event *event) |
134 | { |
135 | if (event->attr.config1 != 0x0) { |
136 | hisi_pa_pmu_enable_tracetag(event); |
137 | hisi_pa_pmu_config_srcid(event); |
138 | hisi_pa_pmu_config_tgtid(event); |
139 | } |
140 | } |
141 | |
142 | static void hisi_pa_pmu_disable_filter(struct perf_event *event) |
143 | { |
144 | if (event->attr.config1 != 0x0) { |
145 | hisi_pa_pmu_clear_tgtid(event); |
146 | hisi_pa_pmu_clear_srcid(event); |
147 | hisi_pa_pmu_clear_tracetag(event); |
148 | } |
149 | } |
150 | |
151 | static u32 hisi_pa_pmu_get_counter_offset(int idx) |
152 | { |
153 | return (PA_EVENT_CNT0_L + idx * 8); |
154 | } |
155 | |
156 | static u64 hisi_pa_pmu_read_counter(struct hisi_pmu *pa_pmu, |
157 | struct hw_perf_event *hwc) |
158 | { |
159 | return readq(addr: pa_pmu->base + hisi_pa_pmu_get_counter_offset(idx: hwc->idx)); |
160 | } |
161 | |
162 | static void hisi_pa_pmu_write_counter(struct hisi_pmu *pa_pmu, |
163 | struct hw_perf_event *hwc, u64 val) |
164 | { |
165 | writeq(val, addr: pa_pmu->base + hisi_pa_pmu_get_counter_offset(idx: hwc->idx)); |
166 | } |
167 | |
168 | static void hisi_pa_pmu_write_evtype(struct hisi_pmu *pa_pmu, int idx, |
169 | u32 type) |
170 | { |
171 | u32 reg, reg_idx, shift, val; |
172 | |
173 | /* |
174 | * Select the appropriate event select register(PA_EVENT_TYPE0/1). |
175 | * There are 2 event select registers for the 8 hardware counters. |
176 | * Event code is 8-bits and for the former 4 hardware counters, |
177 | * PA_EVENT_TYPE0 is chosen. For the latter 4 hardware counters, |
178 | * PA_EVENT_TYPE1 is chosen. |
179 | */ |
180 | reg = PA_EVENT_TYPE0 + (idx / 4) * 4; |
181 | reg_idx = idx % 4; |
182 | shift = 8 * reg_idx; |
183 | |
184 | /* Write event code to pa_EVENT_TYPEx Register */ |
185 | val = readl(addr: pa_pmu->base + reg); |
186 | val &= ~(PA_EVTYPE_MASK << shift); |
187 | val |= (type << shift); |
188 | writel(val, addr: pa_pmu->base + reg); |
189 | } |
190 | |
191 | static void hisi_pa_pmu_start_counters(struct hisi_pmu *pa_pmu) |
192 | { |
193 | u32 val; |
194 | |
195 | val = readl(addr: pa_pmu->base + PA_PERF_CTRL); |
196 | val |= PA_PERF_CTRL_EN; |
197 | writel(val, addr: pa_pmu->base + PA_PERF_CTRL); |
198 | } |
199 | |
200 | static void hisi_pa_pmu_stop_counters(struct hisi_pmu *pa_pmu) |
201 | { |
202 | u32 val; |
203 | |
204 | val = readl(addr: pa_pmu->base + PA_PERF_CTRL); |
205 | val &= ~(PA_PERF_CTRL_EN); |
206 | writel(val, addr: pa_pmu->base + PA_PERF_CTRL); |
207 | } |
208 | |
209 | static void hisi_pa_pmu_enable_counter(struct hisi_pmu *pa_pmu, |
210 | struct hw_perf_event *hwc) |
211 | { |
212 | u32 val; |
213 | |
214 | /* Enable counter index in PA_EVENT_CTRL register */ |
215 | val = readl(addr: pa_pmu->base + PA_EVENT_CTRL); |
216 | val |= 1 << hwc->idx; |
217 | writel(val, addr: pa_pmu->base + PA_EVENT_CTRL); |
218 | } |
219 | |
220 | static void hisi_pa_pmu_disable_counter(struct hisi_pmu *pa_pmu, |
221 | struct hw_perf_event *hwc) |
222 | { |
223 | u32 val; |
224 | |
225 | /* Clear counter index in PA_EVENT_CTRL register */ |
226 | val = readl(addr: pa_pmu->base + PA_EVENT_CTRL); |
227 | val &= ~(1 << hwc->idx); |
228 | writel(val, addr: pa_pmu->base + PA_EVENT_CTRL); |
229 | } |
230 | |
231 | static void hisi_pa_pmu_enable_counter_int(struct hisi_pmu *pa_pmu, |
232 | struct hw_perf_event *hwc) |
233 | { |
234 | struct hisi_pa_pmu_int_regs *regs = pa_pmu->dev_info->private; |
235 | u32 val; |
236 | |
237 | /* Write 0 to enable interrupt */ |
238 | val = readl(addr: pa_pmu->base + regs->mask_offset); |
239 | val &= ~(1 << hwc->idx); |
240 | writel(val, addr: pa_pmu->base + regs->mask_offset); |
241 | } |
242 | |
243 | static void hisi_pa_pmu_disable_counter_int(struct hisi_pmu *pa_pmu, |
244 | struct hw_perf_event *hwc) |
245 | { |
246 | struct hisi_pa_pmu_int_regs *regs = pa_pmu->dev_info->private; |
247 | u32 val; |
248 | |
249 | /* Write 1 to mask interrupt */ |
250 | val = readl(addr: pa_pmu->base + regs->mask_offset); |
251 | val |= 1 << hwc->idx; |
252 | writel(val, addr: pa_pmu->base + regs->mask_offset); |
253 | } |
254 | |
255 | static u32 hisi_pa_pmu_get_int_status(struct hisi_pmu *pa_pmu) |
256 | { |
257 | struct hisi_pa_pmu_int_regs *regs = pa_pmu->dev_info->private; |
258 | |
259 | return readl(addr: pa_pmu->base + regs->status_offset); |
260 | } |
261 | |
262 | static void hisi_pa_pmu_clear_int_status(struct hisi_pmu *pa_pmu, int idx) |
263 | { |
264 | struct hisi_pa_pmu_int_regs *regs = pa_pmu->dev_info->private; |
265 | |
266 | writel(val: 1 << idx, addr: pa_pmu->base + regs->clear_offset); |
267 | } |
268 | |
269 | static int hisi_pa_pmu_init_data(struct platform_device *pdev, |
270 | struct hisi_pmu *pa_pmu) |
271 | { |
272 | /* |
273 | * As PA PMU is in a SICL, use the SICL_ID and the index ID |
274 | * to identify the PA PMU. |
275 | */ |
276 | if (device_property_read_u32(dev: &pdev->dev, propname: "hisilicon,scl-id" , |
277 | val: &pa_pmu->sicl_id)) { |
278 | dev_err(&pdev->dev, "Cannot read sicl-id!\n" ); |
279 | return -EINVAL; |
280 | } |
281 | |
282 | if (device_property_read_u32(dev: &pdev->dev, propname: "hisilicon,idx-id" , |
283 | val: &pa_pmu->index_id)) { |
284 | dev_err(&pdev->dev, "Cannot read idx-id!\n" ); |
285 | return -EINVAL; |
286 | } |
287 | |
288 | pa_pmu->ccl_id = -1; |
289 | pa_pmu->sccl_id = -1; |
290 | |
291 | pa_pmu->dev_info = device_get_match_data(dev: &pdev->dev); |
292 | if (!pa_pmu->dev_info) |
293 | return -ENODEV; |
294 | |
295 | pa_pmu->base = devm_platform_ioremap_resource(pdev, index: 0); |
296 | if (IS_ERR(ptr: pa_pmu->base)) { |
297 | dev_err(&pdev->dev, "ioremap failed for pa_pmu resource.\n" ); |
298 | return PTR_ERR(ptr: pa_pmu->base); |
299 | } |
300 | |
301 | pa_pmu->identifier = readl(addr: pa_pmu->base + PA_PMU_VERSION); |
302 | |
303 | return 0; |
304 | } |
305 | |
306 | static struct attribute *hisi_pa_pmu_v2_format_attr[] = { |
307 | HISI_PMU_FORMAT_ATTR(event, "config:0-7" ), |
308 | HISI_PMU_FORMAT_ATTR(tgtid_cmd, "config1:0-10" ), |
309 | HISI_PMU_FORMAT_ATTR(tgtid_msk, "config1:11-21" ), |
310 | HISI_PMU_FORMAT_ATTR(srcid_cmd, "config1:22-32" ), |
311 | HISI_PMU_FORMAT_ATTR(srcid_msk, "config1:33-43" ), |
312 | HISI_PMU_FORMAT_ATTR(tracetag_en, "config1:44" ), |
313 | NULL, |
314 | }; |
315 | |
316 | static const struct attribute_group hisi_pa_pmu_v2_format_group = { |
317 | .name = "format" , |
318 | .attrs = hisi_pa_pmu_v2_format_attr, |
319 | }; |
320 | |
321 | static struct attribute *hisi_pa_pmu_v2_events_attr[] = { |
322 | HISI_PMU_EVENT_ATTR(rx_req, 0x40), |
323 | HISI_PMU_EVENT_ATTR(tx_req, 0x5c), |
324 | HISI_PMU_EVENT_ATTR(cycle, 0x78), |
325 | NULL |
326 | }; |
327 | |
328 | static const struct attribute_group hisi_pa_pmu_v2_events_group = { |
329 | .name = "events" , |
330 | .attrs = hisi_pa_pmu_v2_events_attr, |
331 | }; |
332 | |
333 | static struct attribute *hisi_pa_pmu_v3_events_attr[] = { |
334 | HISI_PMU_EVENT_ATTR(tx_req, 0x0), |
335 | HISI_PMU_EVENT_ATTR(tx_dat, 0x1), |
336 | HISI_PMU_EVENT_ATTR(tx_snp, 0x2), |
337 | HISI_PMU_EVENT_ATTR(rx_req, 0x7), |
338 | HISI_PMU_EVENT_ATTR(rx_dat, 0x8), |
339 | HISI_PMU_EVENT_ATTR(rx_snp, 0x9), |
340 | NULL |
341 | }; |
342 | |
343 | static const struct attribute_group hisi_pa_pmu_v3_events_group = { |
344 | .name = "events" , |
345 | .attrs = hisi_pa_pmu_v3_events_attr, |
346 | }; |
347 | |
348 | static struct attribute *hisi_h60pa_pmu_events_attr[] = { |
349 | HISI_PMU_EVENT_ATTR(rx_flit, 0x50), |
350 | HISI_PMU_EVENT_ATTR(tx_flit, 0x65), |
351 | NULL |
352 | }; |
353 | |
354 | static const struct attribute_group hisi_h60pa_pmu_events_group = { |
355 | .name = "events" , |
356 | .attrs = hisi_h60pa_pmu_events_attr, |
357 | }; |
358 | |
359 | static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); |
360 | |
361 | static struct attribute *hisi_pa_pmu_cpumask_attrs[] = { |
362 | &dev_attr_cpumask.attr, |
363 | NULL |
364 | }; |
365 | |
366 | static const struct attribute_group hisi_pa_pmu_cpumask_attr_group = { |
367 | .attrs = hisi_pa_pmu_cpumask_attrs, |
368 | }; |
369 | |
370 | static struct device_attribute hisi_pa_pmu_identifier_attr = |
371 | __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); |
372 | |
373 | static struct attribute *hisi_pa_pmu_identifier_attrs[] = { |
374 | &hisi_pa_pmu_identifier_attr.attr, |
375 | NULL |
376 | }; |
377 | |
378 | static const struct attribute_group hisi_pa_pmu_identifier_group = { |
379 | .attrs = hisi_pa_pmu_identifier_attrs, |
380 | }; |
381 | |
382 | static struct hisi_pa_pmu_int_regs hisi_pa_pmu_regs = { |
383 | .mask_offset = PA_INT_MASK, |
384 | .clear_offset = PA_INT_CLEAR, |
385 | .status_offset = PA_INT_STATUS, |
386 | }; |
387 | |
388 | static const struct attribute_group *hisi_pa_pmu_v2_attr_groups[] = { |
389 | &hisi_pa_pmu_v2_format_group, |
390 | &hisi_pa_pmu_v2_events_group, |
391 | &hisi_pa_pmu_cpumask_attr_group, |
392 | &hisi_pa_pmu_identifier_group, |
393 | NULL |
394 | }; |
395 | |
396 | static const struct hisi_pmu_dev_info hisi_h32pa_v2 = { |
397 | .name = "pa" , |
398 | .attr_groups = hisi_pa_pmu_v2_attr_groups, |
399 | .private = &hisi_pa_pmu_regs, |
400 | }; |
401 | |
402 | static const struct attribute_group *hisi_pa_pmu_v3_attr_groups[] = { |
403 | &hisi_pa_pmu_v2_format_group, |
404 | &hisi_pa_pmu_v3_events_group, |
405 | &hisi_pa_pmu_cpumask_attr_group, |
406 | &hisi_pa_pmu_identifier_group, |
407 | NULL |
408 | }; |
409 | |
410 | static const struct hisi_pmu_dev_info hisi_h32pa_v3 = { |
411 | .name = "pa" , |
412 | .attr_groups = hisi_pa_pmu_v3_attr_groups, |
413 | .private = &hisi_pa_pmu_regs, |
414 | }; |
415 | |
416 | static struct hisi_pa_pmu_int_regs hisi_h60pa_pmu_regs = { |
417 | .mask_offset = H60PA_INT_MASK, |
418 | .clear_offset = H60PA_INT_STATUS, /* Clear on write */ |
419 | .status_offset = H60PA_INT_STATUS, |
420 | }; |
421 | |
422 | static const struct attribute_group *hisi_h60pa_pmu_attr_groups[] = { |
423 | &hisi_pa_pmu_v2_format_group, |
424 | &hisi_h60pa_pmu_events_group, |
425 | &hisi_pa_pmu_cpumask_attr_group, |
426 | &hisi_pa_pmu_identifier_group, |
427 | NULL |
428 | }; |
429 | |
430 | static const struct hisi_pmu_dev_info hisi_h60pa = { |
431 | .name = "h60pa" , |
432 | .attr_groups = hisi_h60pa_pmu_attr_groups, |
433 | .private = &hisi_h60pa_pmu_regs, |
434 | }; |
435 | |
436 | static const struct hisi_uncore_ops hisi_uncore_pa_ops = { |
437 | .write_evtype = hisi_pa_pmu_write_evtype, |
438 | .get_event_idx = hisi_uncore_pmu_get_event_idx, |
439 | .start_counters = hisi_pa_pmu_start_counters, |
440 | .stop_counters = hisi_pa_pmu_stop_counters, |
441 | .enable_counter = hisi_pa_pmu_enable_counter, |
442 | .disable_counter = hisi_pa_pmu_disable_counter, |
443 | .enable_counter_int = hisi_pa_pmu_enable_counter_int, |
444 | .disable_counter_int = hisi_pa_pmu_disable_counter_int, |
445 | .write_counter = hisi_pa_pmu_write_counter, |
446 | .read_counter = hisi_pa_pmu_read_counter, |
447 | .get_int_status = hisi_pa_pmu_get_int_status, |
448 | .clear_int_status = hisi_pa_pmu_clear_int_status, |
449 | .enable_filter = hisi_pa_pmu_enable_filter, |
450 | .disable_filter = hisi_pa_pmu_disable_filter, |
451 | }; |
452 | |
453 | static int hisi_pa_pmu_dev_probe(struct platform_device *pdev, |
454 | struct hisi_pmu *pa_pmu) |
455 | { |
456 | int ret; |
457 | |
458 | ret = hisi_pa_pmu_init_data(pdev, pa_pmu); |
459 | if (ret) |
460 | return ret; |
461 | |
462 | ret = hisi_uncore_pmu_init_irq(hisi_pmu: pa_pmu, pdev); |
463 | if (ret) |
464 | return ret; |
465 | |
466 | pa_pmu->pmu_events.attr_groups = pa_pmu->dev_info->attr_groups; |
467 | pa_pmu->num_counters = PA_NR_COUNTERS; |
468 | pa_pmu->ops = &hisi_uncore_pa_ops; |
469 | pa_pmu->check_event = 0xB0; |
470 | pa_pmu->counter_bits = 64; |
471 | pa_pmu->dev = &pdev->dev; |
472 | pa_pmu->on_cpu = -1; |
473 | |
474 | return 0; |
475 | } |
476 | |
477 | static int hisi_pa_pmu_probe(struct platform_device *pdev) |
478 | { |
479 | struct hisi_pmu *pa_pmu; |
480 | char *name; |
481 | int ret; |
482 | |
483 | pa_pmu = devm_kzalloc(dev: &pdev->dev, size: sizeof(*pa_pmu), GFP_KERNEL); |
484 | if (!pa_pmu) |
485 | return -ENOMEM; |
486 | |
487 | ret = hisi_pa_pmu_dev_probe(pdev, pa_pmu); |
488 | if (ret) |
489 | return ret; |
490 | |
491 | name = devm_kasprintf(dev: &pdev->dev, GFP_KERNEL, fmt: "hisi_sicl%d_%s%u" , |
492 | pa_pmu->sicl_id, pa_pmu->dev_info->name, |
493 | pa_pmu->index_id); |
494 | if (!name) |
495 | return -ENOMEM; |
496 | |
497 | ret = cpuhp_state_add_instance(state: CPUHP_AP_PERF_ARM_HISI_PA_ONLINE, |
498 | node: &pa_pmu->node); |
499 | if (ret) { |
500 | dev_err(&pdev->dev, "Error %d registering hotplug\n" , ret); |
501 | return ret; |
502 | } |
503 | |
504 | hisi_pmu_init(hisi_pmu: pa_pmu, THIS_MODULE); |
505 | ret = perf_pmu_register(pmu: &pa_pmu->pmu, name, type: -1); |
506 | if (ret) { |
507 | dev_err(pa_pmu->dev, "PMU register failed, ret = %d\n" , ret); |
508 | cpuhp_state_remove_instance_nocalls(state: CPUHP_AP_PERF_ARM_HISI_PA_ONLINE, |
509 | node: &pa_pmu->node); |
510 | return ret; |
511 | } |
512 | |
513 | platform_set_drvdata(pdev, data: pa_pmu); |
514 | return ret; |
515 | } |
516 | |
517 | static void hisi_pa_pmu_remove(struct platform_device *pdev) |
518 | { |
519 | struct hisi_pmu *pa_pmu = platform_get_drvdata(pdev); |
520 | |
521 | perf_pmu_unregister(pmu: &pa_pmu->pmu); |
522 | cpuhp_state_remove_instance_nocalls(state: CPUHP_AP_PERF_ARM_HISI_PA_ONLINE, |
523 | node: &pa_pmu->node); |
524 | } |
525 | |
526 | static const struct acpi_device_id hisi_pa_pmu_acpi_match[] = { |
527 | { "HISI0273" , (kernel_ulong_t)&hisi_h32pa_v2 }, |
528 | { "HISI0275" , (kernel_ulong_t)&hisi_h32pa_v3 }, |
529 | { "HISI0274" , (kernel_ulong_t)&hisi_h60pa }, |
530 | {} |
531 | }; |
532 | MODULE_DEVICE_TABLE(acpi, hisi_pa_pmu_acpi_match); |
533 | |
534 | static struct platform_driver hisi_pa_pmu_driver = { |
535 | .driver = { |
536 | .name = "hisi_pa_pmu" , |
537 | .acpi_match_table = hisi_pa_pmu_acpi_match, |
538 | .suppress_bind_attrs = true, |
539 | }, |
540 | .probe = hisi_pa_pmu_probe, |
541 | .remove_new = hisi_pa_pmu_remove, |
542 | }; |
543 | |
544 | static int __init hisi_pa_pmu_module_init(void) |
545 | { |
546 | int ret; |
547 | |
548 | ret = cpuhp_setup_state_multi(state: CPUHP_AP_PERF_ARM_HISI_PA_ONLINE, |
549 | name: "AP_PERF_ARM_HISI_PA_ONLINE" , |
550 | startup: hisi_uncore_pmu_online_cpu, |
551 | teardown: hisi_uncore_pmu_offline_cpu); |
552 | if (ret) { |
553 | pr_err("PA PMU: cpuhp state setup failed, ret = %d\n" , ret); |
554 | return ret; |
555 | } |
556 | |
557 | ret = platform_driver_register(&hisi_pa_pmu_driver); |
558 | if (ret) |
559 | cpuhp_remove_multi_state(state: CPUHP_AP_PERF_ARM_HISI_PA_ONLINE); |
560 | |
561 | return ret; |
562 | } |
563 | module_init(hisi_pa_pmu_module_init); |
564 | |
565 | static void __exit hisi_pa_pmu_module_exit(void) |
566 | { |
567 | platform_driver_unregister(&hisi_pa_pmu_driver); |
568 | cpuhp_remove_multi_state(state: CPUHP_AP_PERF_ARM_HISI_PA_ONLINE); |
569 | } |
570 | module_exit(hisi_pa_pmu_module_exit); |
571 | |
572 | MODULE_DESCRIPTION("HiSilicon Protocol Adapter uncore PMU driver" ); |
573 | MODULE_LICENSE("GPL v2" ); |
574 | MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>" ); |
575 | MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>" ); |
576 | |