1 // SPDX-License-Identifier: GPL-2.0-only
3 * HiSilicon PA uncore Hardware event counters support
5 * Copyright (C) 2020 HiSilicon Limited
6 * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
8 * This code is based on the uncore PMUs like arm-cci and arm-ccn.
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>
17 #include "hisi_uncore_pmu.h"
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
26 /* H32 PA interrupt registers */
27 #define PA_INT_MASK 0x1c70
28 #define PA_INT_STATUS 0x1c78
29 #define PA_INT_CLEAR 0x1c7c
31 #define H60PA_INT_STATUS 0x1c70
32 #define H60PA_INT_MASK 0x1c74
34 #define PA_EVENT_TYPE0 0x1c80
35 #define PA_PMU_VERSION 0x1cf0
36 #define PA_EVENT_CNT0_L 0x1d00
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
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);
55 struct hisi_pa_pmu_int_regs
{
61 static void hisi_pa_pmu_enable_tracetag(struct perf_event
*event
)
63 struct hisi_pmu
*pa_pmu
= to_hisi_pmu(event
->pmu
);
64 u32 tt_en
= hisi_get_tracetag_en(event
);
69 val
= readl(pa_pmu
->base
+ PA_TT_CTRL
);
70 val
|= PA_TRACETAG_EN
;
71 writel(val
, pa_pmu
->base
+ PA_TT_CTRL
);
75 static void hisi_pa_pmu_clear_tracetag(struct perf_event
*event
)
77 struct hisi_pmu
*pa_pmu
= to_hisi_pmu(event
->pmu
);
78 u32 tt_en
= hisi_get_tracetag_en(event
);
83 val
= readl(pa_pmu
->base
+ PA_TT_CTRL
);
84 val
&= ~PA_TRACETAG_EN
;
85 writel(val
, pa_pmu
->base
+ PA_TT_CTRL
);
89 static void hisi_pa_pmu_config_tgtid(struct perf_event
*event
)
91 struct hisi_pmu
*pa_pmu
= to_hisi_pmu(event
->pmu
);
92 u32 cmd
= hisi_get_tgtid_cmd(event
);
95 u32 msk
= hisi_get_tgtid_msk(event
);
96 u32 val
= cmd
| PA_TGTID_EN
| (msk
<< PA_TGTID_MSK_SHIFT
);
98 writel(val
, pa_pmu
->base
+ PA_TGTID_CTRL
);
102 static void hisi_pa_pmu_clear_tgtid(struct perf_event
*event
)
104 struct hisi_pmu
*pa_pmu
= to_hisi_pmu(event
->pmu
);
105 u32 cmd
= hisi_get_tgtid_cmd(event
);
108 writel(PA_TGTID_NONE
, pa_pmu
->base
+ PA_TGTID_CTRL
);
111 static void hisi_pa_pmu_config_srcid(struct perf_event
*event
)
113 struct hisi_pmu
*pa_pmu
= to_hisi_pmu(event
->pmu
);
114 u32 cmd
= hisi_get_srcid_cmd(event
);
117 u32 msk
= hisi_get_srcid_msk(event
);
118 u32 val
= cmd
| PA_SRCID_EN
| (msk
<< PA_SRCID_MSK_SHIFT
);
120 writel(val
, pa_pmu
->base
+ PA_SRCID_CTRL
);
124 static void hisi_pa_pmu_clear_srcid(struct perf_event
*event
)
126 struct hisi_pmu
*pa_pmu
= to_hisi_pmu(event
->pmu
);
127 u32 cmd
= hisi_get_srcid_cmd(event
);
130 writel(PA_SRCID_NONE
, pa_pmu
->base
+ PA_SRCID_CTRL
);
133 static void hisi_pa_pmu_enable_filter(struct perf_event
*event
)
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
);
142 static void hisi_pa_pmu_disable_filter(struct perf_event
*event
)
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
);
151 static u32
hisi_pa_pmu_get_counter_offset(int idx
)
153 return (PA_EVENT_CNT0_L
+ idx
* 8);
156 static u64
hisi_pa_pmu_read_counter(struct hisi_pmu
*pa_pmu
,
157 struct hw_perf_event
*hwc
)
159 return readq(pa_pmu
->base
+ hisi_pa_pmu_get_counter_offset(hwc
->idx
));
162 static void hisi_pa_pmu_write_counter(struct hisi_pmu
*pa_pmu
,
163 struct hw_perf_event
*hwc
, u64 val
)
165 writeq(val
, pa_pmu
->base
+ hisi_pa_pmu_get_counter_offset(hwc
->idx
));
168 static void hisi_pa_pmu_write_evtype(struct hisi_pmu
*pa_pmu
, int idx
,
171 u32 reg
, reg_idx
, shift
, val
;
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.
180 reg
= PA_EVENT_TYPE0
+ (idx
/ 4) * 4;
184 /* Write event code to pa_EVENT_TYPEx Register */
185 val
= readl(pa_pmu
->base
+ reg
);
186 val
&= ~(PA_EVTYPE_MASK
<< shift
);
187 val
|= (type
<< shift
);
188 writel(val
, pa_pmu
->base
+ reg
);
191 static void hisi_pa_pmu_start_counters(struct hisi_pmu
*pa_pmu
)
195 val
= readl(pa_pmu
->base
+ PA_PERF_CTRL
);
196 val
|= PA_PERF_CTRL_EN
;
197 writel(val
, pa_pmu
->base
+ PA_PERF_CTRL
);
200 static void hisi_pa_pmu_stop_counters(struct hisi_pmu
*pa_pmu
)
204 val
= readl(pa_pmu
->base
+ PA_PERF_CTRL
);
205 val
&= ~(PA_PERF_CTRL_EN
);
206 writel(val
, pa_pmu
->base
+ PA_PERF_CTRL
);
209 static void hisi_pa_pmu_enable_counter(struct hisi_pmu
*pa_pmu
,
210 struct hw_perf_event
*hwc
)
214 /* Enable counter index in PA_EVENT_CTRL register */
215 val
= readl(pa_pmu
->base
+ PA_EVENT_CTRL
);
216 val
|= 1 << hwc
->idx
;
217 writel(val
, pa_pmu
->base
+ PA_EVENT_CTRL
);
220 static void hisi_pa_pmu_disable_counter(struct hisi_pmu
*pa_pmu
,
221 struct hw_perf_event
*hwc
)
225 /* Clear counter index in PA_EVENT_CTRL register */
226 val
= readl(pa_pmu
->base
+ PA_EVENT_CTRL
);
227 val
&= ~(1 << hwc
->idx
);
228 writel(val
, pa_pmu
->base
+ PA_EVENT_CTRL
);
231 static void hisi_pa_pmu_enable_counter_int(struct hisi_pmu
*pa_pmu
,
232 struct hw_perf_event
*hwc
)
234 struct hisi_pa_pmu_int_regs
*regs
= pa_pmu
->dev_info
->private;
237 /* Write 0 to enable interrupt */
238 val
= readl(pa_pmu
->base
+ regs
->mask_offset
);
239 val
&= ~(1 << hwc
->idx
);
240 writel(val
, pa_pmu
->base
+ regs
->mask_offset
);
243 static void hisi_pa_pmu_disable_counter_int(struct hisi_pmu
*pa_pmu
,
244 struct hw_perf_event
*hwc
)
246 struct hisi_pa_pmu_int_regs
*regs
= pa_pmu
->dev_info
->private;
249 /* Write 1 to mask interrupt */
250 val
= readl(pa_pmu
->base
+ regs
->mask_offset
);
251 val
|= 1 << hwc
->idx
;
252 writel(val
, pa_pmu
->base
+ regs
->mask_offset
);
255 static u32
hisi_pa_pmu_get_int_status(struct hisi_pmu
*pa_pmu
)
257 struct hisi_pa_pmu_int_regs
*regs
= pa_pmu
->dev_info
->private;
259 return readl(pa_pmu
->base
+ regs
->status_offset
);
262 static void hisi_pa_pmu_clear_int_status(struct hisi_pmu
*pa_pmu
, int idx
)
264 struct hisi_pa_pmu_int_regs
*regs
= pa_pmu
->dev_info
->private;
266 writel(1 << idx
, pa_pmu
->base
+ regs
->clear_offset
);
269 static int hisi_pa_pmu_init_data(struct platform_device
*pdev
,
270 struct hisi_pmu
*pa_pmu
)
273 * As PA PMU is in a SICL, use the SICL_ID and the index ID
274 * to identify the PA PMU.
276 if (device_property_read_u32(&pdev
->dev
, "hisilicon,scl-id",
278 dev_err(&pdev
->dev
, "Cannot read sicl-id!\n");
282 if (device_property_read_u32(&pdev
->dev
, "hisilicon,idx-id",
283 &pa_pmu
->index_id
)) {
284 dev_err(&pdev
->dev
, "Cannot read idx-id!\n");
289 pa_pmu
->sccl_id
= -1;
291 pa_pmu
->dev_info
= device_get_match_data(&pdev
->dev
);
292 if (!pa_pmu
->dev_info
)
295 pa_pmu
->base
= devm_platform_ioremap_resource(pdev
, 0);
296 if (IS_ERR(pa_pmu
->base
)) {
297 dev_err(&pdev
->dev
, "ioremap failed for pa_pmu resource.\n");
298 return PTR_ERR(pa_pmu
->base
);
301 pa_pmu
->identifier
= readl(pa_pmu
->base
+ PA_PMU_VERSION
);
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"),
316 static const struct attribute_group hisi_pa_pmu_v2_format_group
= {
318 .attrs
= hisi_pa_pmu_v2_format_attr
,
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),
328 static const struct attribute_group hisi_pa_pmu_v2_events_group
= {
330 .attrs
= hisi_pa_pmu_v2_events_attr
,
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),
343 static const struct attribute_group hisi_pa_pmu_v3_events_group
= {
345 .attrs
= hisi_pa_pmu_v3_events_attr
,
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),
354 static const struct attribute_group hisi_h60pa_pmu_events_group
= {
356 .attrs
= hisi_h60pa_pmu_events_attr
,
359 static DEVICE_ATTR(cpumask
, 0444, hisi_cpumask_sysfs_show
, NULL
);
361 static struct attribute
*hisi_pa_pmu_cpumask_attrs
[] = {
362 &dev_attr_cpumask
.attr
,
366 static const struct attribute_group hisi_pa_pmu_cpumask_attr_group
= {
367 .attrs
= hisi_pa_pmu_cpumask_attrs
,
370 static struct device_attribute hisi_pa_pmu_identifier_attr
=
371 __ATTR(identifier
, 0444, hisi_uncore_pmu_identifier_attr_show
, NULL
);
373 static struct attribute
*hisi_pa_pmu_identifier_attrs
[] = {
374 &hisi_pa_pmu_identifier_attr
.attr
,
378 static const struct attribute_group hisi_pa_pmu_identifier_group
= {
379 .attrs
= hisi_pa_pmu_identifier_attrs
,
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
,
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
,
396 static const struct hisi_pmu_dev_info hisi_h32pa_v2
= {
398 .attr_groups
= hisi_pa_pmu_v2_attr_groups
,
399 .private = &hisi_pa_pmu_regs
,
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
,
410 static const struct hisi_pmu_dev_info hisi_h32pa_v3
= {
412 .attr_groups
= hisi_pa_pmu_v3_attr_groups
,
413 .private = &hisi_pa_pmu_regs
,
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
,
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
,
430 static const struct hisi_pmu_dev_info hisi_h60pa
= {
432 .attr_groups
= hisi_h60pa_pmu_attr_groups
,
433 .private = &hisi_h60pa_pmu_regs
,
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
,
453 static int hisi_pa_pmu_dev_probe(struct platform_device
*pdev
,
454 struct hisi_pmu
*pa_pmu
)
458 ret
= hisi_pa_pmu_init_data(pdev
, pa_pmu
);
462 ret
= hisi_uncore_pmu_init_irq(pa_pmu
, pdev
);
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
;
477 static int hisi_pa_pmu_probe(struct platform_device
*pdev
)
479 struct hisi_pmu
*pa_pmu
;
483 pa_pmu
= devm_kzalloc(&pdev
->dev
, sizeof(*pa_pmu
), GFP_KERNEL
);
487 ret
= hisi_pa_pmu_dev_probe(pdev
, pa_pmu
);
491 name
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
, "hisi_sicl%d_%s%u",
492 pa_pmu
->sicl_id
, pa_pmu
->dev_info
->name
,
497 ret
= cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE
,
500 dev_err(&pdev
->dev
, "Error %d registering hotplug\n", ret
);
504 hisi_pmu_init(pa_pmu
, THIS_MODULE
);
505 ret
= perf_pmu_register(&pa_pmu
->pmu
, name
, -1);
507 dev_err(pa_pmu
->dev
, "PMU register failed, ret = %d\n", ret
);
508 cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE
,
513 platform_set_drvdata(pdev
, pa_pmu
);
517 static void hisi_pa_pmu_remove(struct platform_device
*pdev
)
519 struct hisi_pmu
*pa_pmu
= platform_get_drvdata(pdev
);
521 perf_pmu_unregister(&pa_pmu
->pmu
);
522 cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE
,
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
},
532 MODULE_DEVICE_TABLE(acpi
, hisi_pa_pmu_acpi_match
);
534 static struct platform_driver hisi_pa_pmu_driver
= {
536 .name
= "hisi_pa_pmu",
537 .acpi_match_table
= hisi_pa_pmu_acpi_match
,
538 .suppress_bind_attrs
= true,
540 .probe
= hisi_pa_pmu_probe
,
541 .remove
= hisi_pa_pmu_remove
,
544 static int __init
hisi_pa_pmu_module_init(void)
548 ret
= cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE
,
549 "AP_PERF_ARM_HISI_PA_ONLINE",
550 hisi_uncore_pmu_online_cpu
,
551 hisi_uncore_pmu_offline_cpu
);
553 pr_err("PA PMU: cpuhp state setup failed, ret = %d\n", ret
);
557 ret
= platform_driver_register(&hisi_pa_pmu_driver
);
559 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE
);
563 module_init(hisi_pa_pmu_module_init
);
565 static void __exit
hisi_pa_pmu_module_exit(void)
567 platform_driver_unregister(&hisi_pa_pmu_driver
);
568 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_PA_ONLINE
);
570 module_exit(hisi_pa_pmu_module_exit
);
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>");