1 // SPDX-License-Identifier: GPL-2.0-only
3 * HiSilicon SoC L3C uncore Hardware event counters support
5 * Copyright (C) 2017 Hisilicon Limited
6 * Author: Anurup M <anurup.m@huawei.com>
7 * Shaokun Zhang <zhangshaokun@hisilicon.com>
9 * This code is based on the uncore PMUs like arm-cci and arm-ccn.
11 #include <linux/acpi.h>
12 #include <linux/bug.h>
13 #include <linux/cpuhotplug.h>
14 #include <linux/interrupt.h>
15 #include <linux/irq.h>
16 #include <linux/list.h>
17 #include <linux/platform_device.h>
18 #include <linux/smp.h>
20 #include "hisi_uncore_pmu.h"
22 /* L3C register definition */
23 #define L3C_PERF_CTRL 0x0408
24 #define L3C_INT_MASK 0x0800
25 #define L3C_INT_STATUS 0x0808
26 #define L3C_INT_CLEAR 0x080c
27 #define L3C_EVENT_CTRL 0x1c00
28 #define L3C_VERSION 0x1cf0
29 #define L3C_EVENT_TYPE0 0x1d00
31 * Each counter is 48-bits and [48:63] are reserved
32 * which are Read-As-Zero and Writes-Ignored.
34 #define L3C_CNTR0_LOWER 0x1e00
36 /* L3C has 8-counters */
37 #define L3C_NR_COUNTERS 0x8
39 #define L3C_PERF_CTRL_EN 0x10000
40 #define L3C_EVTYPE_NONE 0xff
43 * Select the counter register offset using the counter index
45 static u32
hisi_l3c_pmu_get_counter_offset(int cntr_idx
)
47 return (L3C_CNTR0_LOWER
+ (cntr_idx
* 8));
50 static u64
hisi_l3c_pmu_read_counter(struct hisi_pmu
*l3c_pmu
,
51 struct hw_perf_event
*hwc
)
55 if (!hisi_uncore_pmu_counter_valid(l3c_pmu
, idx
)) {
56 dev_err(l3c_pmu
->dev
, "Unsupported event index:%d!\n", idx
);
60 /* Read 64-bits and the upper 16 bits are RAZ */
61 return readq(l3c_pmu
->base
+ hisi_l3c_pmu_get_counter_offset(idx
));
64 static void hisi_l3c_pmu_write_counter(struct hisi_pmu
*l3c_pmu
,
65 struct hw_perf_event
*hwc
, u64 val
)
69 if (!hisi_uncore_pmu_counter_valid(l3c_pmu
, idx
)) {
70 dev_err(l3c_pmu
->dev
, "Unsupported event index:%d!\n", idx
);
74 /* Write 64-bits and the upper 16 bits are WI */
75 writeq(val
, l3c_pmu
->base
+ hisi_l3c_pmu_get_counter_offset(idx
));
78 static void hisi_l3c_pmu_write_evtype(struct hisi_pmu
*l3c_pmu
, int idx
,
81 u32 reg
, reg_idx
, shift
, val
;
84 * Select the appropriate event select register(L3C_EVENT_TYPE0/1).
85 * There are 2 event select registers for the 8 hardware counters.
86 * Event code is 8-bits and for the former 4 hardware counters,
87 * L3C_EVENT_TYPE0 is chosen. For the latter 4 hardware counters,
88 * L3C_EVENT_TYPE1 is chosen.
90 reg
= L3C_EVENT_TYPE0
+ (idx
/ 4) * 4;
94 /* Write event code to L3C_EVENT_TYPEx Register */
95 val
= readl(l3c_pmu
->base
+ reg
);
96 val
&= ~(L3C_EVTYPE_NONE
<< shift
);
97 val
|= (type
<< shift
);
98 writel(val
, l3c_pmu
->base
+ reg
);
101 static void hisi_l3c_pmu_start_counters(struct hisi_pmu
*l3c_pmu
)
106 * Set perf_enable bit in L3C_PERF_CTRL register to start counting
107 * for all enabled counters.
109 val
= readl(l3c_pmu
->base
+ L3C_PERF_CTRL
);
110 val
|= L3C_PERF_CTRL_EN
;
111 writel(val
, l3c_pmu
->base
+ L3C_PERF_CTRL
);
114 static void hisi_l3c_pmu_stop_counters(struct hisi_pmu
*l3c_pmu
)
119 * Clear perf_enable bit in L3C_PERF_CTRL register to stop counting
120 * for all enabled counters.
122 val
= readl(l3c_pmu
->base
+ L3C_PERF_CTRL
);
123 val
&= ~(L3C_PERF_CTRL_EN
);
124 writel(val
, l3c_pmu
->base
+ L3C_PERF_CTRL
);
127 static void hisi_l3c_pmu_enable_counter(struct hisi_pmu
*l3c_pmu
,
128 struct hw_perf_event
*hwc
)
132 /* Enable counter index in L3C_EVENT_CTRL register */
133 val
= readl(l3c_pmu
->base
+ L3C_EVENT_CTRL
);
134 val
|= (1 << hwc
->idx
);
135 writel(val
, l3c_pmu
->base
+ L3C_EVENT_CTRL
);
138 static void hisi_l3c_pmu_disable_counter(struct hisi_pmu
*l3c_pmu
,
139 struct hw_perf_event
*hwc
)
143 /* Clear counter index in L3C_EVENT_CTRL register */
144 val
= readl(l3c_pmu
->base
+ L3C_EVENT_CTRL
);
145 val
&= ~(1 << hwc
->idx
);
146 writel(val
, l3c_pmu
->base
+ L3C_EVENT_CTRL
);
149 static void hisi_l3c_pmu_enable_counter_int(struct hisi_pmu
*l3c_pmu
,
150 struct hw_perf_event
*hwc
)
154 val
= readl(l3c_pmu
->base
+ L3C_INT_MASK
);
155 /* Write 0 to enable interrupt */
156 val
&= ~(1 << hwc
->idx
);
157 writel(val
, l3c_pmu
->base
+ L3C_INT_MASK
);
160 static void hisi_l3c_pmu_disable_counter_int(struct hisi_pmu
*l3c_pmu
,
161 struct hw_perf_event
*hwc
)
165 val
= readl(l3c_pmu
->base
+ L3C_INT_MASK
);
166 /* Write 1 to mask interrupt */
167 val
|= (1 << hwc
->idx
);
168 writel(val
, l3c_pmu
->base
+ L3C_INT_MASK
);
171 static irqreturn_t
hisi_l3c_pmu_isr(int irq
, void *dev_id
)
173 struct hisi_pmu
*l3c_pmu
= dev_id
;
174 struct perf_event
*event
;
175 unsigned long overflown
;
178 /* Read L3C_INT_STATUS register */
179 overflown
= readl(l3c_pmu
->base
+ L3C_INT_STATUS
);
184 * Find the counter index which overflowed if the bit was set
187 for_each_set_bit(idx
, &overflown
, L3C_NR_COUNTERS
) {
188 /* Write 1 to clear the IRQ status flag */
189 writel((1 << idx
), l3c_pmu
->base
+ L3C_INT_CLEAR
);
191 /* Get the corresponding event struct */
192 event
= l3c_pmu
->pmu_events
.hw_events
[idx
];
196 hisi_uncore_pmu_event_update(event
);
197 hisi_uncore_pmu_set_event_period(event
);
203 static int hisi_l3c_pmu_init_irq(struct hisi_pmu
*l3c_pmu
,
204 struct platform_device
*pdev
)
208 /* Read and init IRQ */
209 irq
= platform_get_irq(pdev
, 0);
213 ret
= devm_request_irq(&pdev
->dev
, irq
, hisi_l3c_pmu_isr
,
214 IRQF_NOBALANCING
| IRQF_NO_THREAD
,
215 dev_name(&pdev
->dev
), l3c_pmu
);
218 "Fail to request IRQ:%d ret:%d\n", irq
, ret
);
227 static const struct acpi_device_id hisi_l3c_pmu_acpi_match
[] = {
231 MODULE_DEVICE_TABLE(acpi
, hisi_l3c_pmu_acpi_match
);
233 static int hisi_l3c_pmu_init_data(struct platform_device
*pdev
,
234 struct hisi_pmu
*l3c_pmu
)
236 unsigned long long id
;
239 status
= acpi_evaluate_integer(ACPI_HANDLE(&pdev
->dev
),
241 if (ACPI_FAILURE(status
))
244 l3c_pmu
->index_id
= id
;
247 * Use the SCCL_ID and CCL_ID to identify the L3C PMU, while
248 * SCCL_ID is in MPIDR[aff2] and CCL_ID is in MPIDR[aff1].
250 if (device_property_read_u32(&pdev
->dev
, "hisilicon,scl-id",
251 &l3c_pmu
->sccl_id
)) {
252 dev_err(&pdev
->dev
, "Can not read l3c sccl-id!\n");
256 if (device_property_read_u32(&pdev
->dev
, "hisilicon,ccl-id",
258 dev_err(&pdev
->dev
, "Can not read l3c ccl-id!\n");
262 l3c_pmu
->base
= devm_platform_ioremap_resource(pdev
, 0);
263 if (IS_ERR(l3c_pmu
->base
)) {
264 dev_err(&pdev
->dev
, "ioremap failed for l3c_pmu resource\n");
265 return PTR_ERR(l3c_pmu
->base
);
268 l3c_pmu
->identifier
= readl(l3c_pmu
->base
+ L3C_VERSION
);
273 static struct attribute
*hisi_l3c_pmu_format_attr
[] = {
274 HISI_PMU_FORMAT_ATTR(event
, "config:0-7"),
278 static const struct attribute_group hisi_l3c_pmu_format_group
= {
280 .attrs
= hisi_l3c_pmu_format_attr
,
283 static struct attribute
*hisi_l3c_pmu_events_attr
[] = {
284 HISI_PMU_EVENT_ATTR(rd_cpipe
, 0x00),
285 HISI_PMU_EVENT_ATTR(wr_cpipe
, 0x01),
286 HISI_PMU_EVENT_ATTR(rd_hit_cpipe
, 0x02),
287 HISI_PMU_EVENT_ATTR(wr_hit_cpipe
, 0x03),
288 HISI_PMU_EVENT_ATTR(victim_num
, 0x04),
289 HISI_PMU_EVENT_ATTR(rd_spipe
, 0x20),
290 HISI_PMU_EVENT_ATTR(wr_spipe
, 0x21),
291 HISI_PMU_EVENT_ATTR(rd_hit_spipe
, 0x22),
292 HISI_PMU_EVENT_ATTR(wr_hit_spipe
, 0x23),
293 HISI_PMU_EVENT_ATTR(back_invalid
, 0x29),
294 HISI_PMU_EVENT_ATTR(retry_cpu
, 0x40),
295 HISI_PMU_EVENT_ATTR(retry_ring
, 0x41),
296 HISI_PMU_EVENT_ATTR(prefetch_drop
, 0x42),
300 static const struct attribute_group hisi_l3c_pmu_events_group
= {
302 .attrs
= hisi_l3c_pmu_events_attr
,
305 static DEVICE_ATTR(cpumask
, 0444, hisi_cpumask_sysfs_show
, NULL
);
307 static struct attribute
*hisi_l3c_pmu_cpumask_attrs
[] = {
308 &dev_attr_cpumask
.attr
,
312 static const struct attribute_group hisi_l3c_pmu_cpumask_attr_group
= {
313 .attrs
= hisi_l3c_pmu_cpumask_attrs
,
316 static struct device_attribute hisi_l3c_pmu_identifier_attr
=
317 __ATTR(identifier
, 0444, hisi_uncore_pmu_identifier_attr_show
, NULL
);
319 static struct attribute
*hisi_l3c_pmu_identifier_attrs
[] = {
320 &hisi_l3c_pmu_identifier_attr
.attr
,
324 static struct attribute_group hisi_l3c_pmu_identifier_group
= {
325 .attrs
= hisi_l3c_pmu_identifier_attrs
,
328 static const struct attribute_group
*hisi_l3c_pmu_attr_groups
[] = {
329 &hisi_l3c_pmu_format_group
,
330 &hisi_l3c_pmu_events_group
,
331 &hisi_l3c_pmu_cpumask_attr_group
,
332 &hisi_l3c_pmu_identifier_group
,
336 static const struct hisi_uncore_ops hisi_uncore_l3c_ops
= {
337 .write_evtype
= hisi_l3c_pmu_write_evtype
,
338 .get_event_idx
= hisi_uncore_pmu_get_event_idx
,
339 .start_counters
= hisi_l3c_pmu_start_counters
,
340 .stop_counters
= hisi_l3c_pmu_stop_counters
,
341 .enable_counter
= hisi_l3c_pmu_enable_counter
,
342 .disable_counter
= hisi_l3c_pmu_disable_counter
,
343 .enable_counter_int
= hisi_l3c_pmu_enable_counter_int
,
344 .disable_counter_int
= hisi_l3c_pmu_disable_counter_int
,
345 .write_counter
= hisi_l3c_pmu_write_counter
,
346 .read_counter
= hisi_l3c_pmu_read_counter
,
349 static int hisi_l3c_pmu_dev_probe(struct platform_device
*pdev
,
350 struct hisi_pmu
*l3c_pmu
)
354 ret
= hisi_l3c_pmu_init_data(pdev
, l3c_pmu
);
358 ret
= hisi_l3c_pmu_init_irq(l3c_pmu
, pdev
);
362 l3c_pmu
->num_counters
= L3C_NR_COUNTERS
;
363 l3c_pmu
->counter_bits
= 48;
364 l3c_pmu
->ops
= &hisi_uncore_l3c_ops
;
365 l3c_pmu
->dev
= &pdev
->dev
;
366 l3c_pmu
->on_cpu
= -1;
367 l3c_pmu
->check_event
= 0x59;
372 static int hisi_l3c_pmu_probe(struct platform_device
*pdev
)
374 struct hisi_pmu
*l3c_pmu
;
378 l3c_pmu
= devm_kzalloc(&pdev
->dev
, sizeof(*l3c_pmu
), GFP_KERNEL
);
382 platform_set_drvdata(pdev
, l3c_pmu
);
384 ret
= hisi_l3c_pmu_dev_probe(pdev
, l3c_pmu
);
388 ret
= cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE
,
391 dev_err(&pdev
->dev
, "Error %d registering hotplug\n", ret
);
395 name
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
, "hisi_sccl%u_l3c%u",
396 l3c_pmu
->sccl_id
, l3c_pmu
->index_id
);
397 l3c_pmu
->pmu
= (struct pmu
) {
399 .module
= THIS_MODULE
,
400 .task_ctx_nr
= perf_invalid_context
,
401 .event_init
= hisi_uncore_pmu_event_init
,
402 .pmu_enable
= hisi_uncore_pmu_enable
,
403 .pmu_disable
= hisi_uncore_pmu_disable
,
404 .add
= hisi_uncore_pmu_add
,
405 .del
= hisi_uncore_pmu_del
,
406 .start
= hisi_uncore_pmu_start
,
407 .stop
= hisi_uncore_pmu_stop
,
408 .read
= hisi_uncore_pmu_read
,
409 .attr_groups
= hisi_l3c_pmu_attr_groups
,
410 .capabilities
= PERF_PMU_CAP_NO_EXCLUDE
,
413 ret
= perf_pmu_register(&l3c_pmu
->pmu
, name
, -1);
415 dev_err(l3c_pmu
->dev
, "L3C PMU register failed!\n");
416 cpuhp_state_remove_instance_nocalls(
417 CPUHP_AP_PERF_ARM_HISI_L3_ONLINE
, &l3c_pmu
->node
);
418 irq_set_affinity_hint(l3c_pmu
->irq
, NULL
);
424 static int hisi_l3c_pmu_remove(struct platform_device
*pdev
)
426 struct hisi_pmu
*l3c_pmu
= platform_get_drvdata(pdev
);
428 perf_pmu_unregister(&l3c_pmu
->pmu
);
429 cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE
,
431 irq_set_affinity_hint(l3c_pmu
->irq
, NULL
);
436 static struct platform_driver hisi_l3c_pmu_driver
= {
438 .name
= "hisi_l3c_pmu",
439 .acpi_match_table
= ACPI_PTR(hisi_l3c_pmu_acpi_match
),
440 .suppress_bind_attrs
= true,
442 .probe
= hisi_l3c_pmu_probe
,
443 .remove
= hisi_l3c_pmu_remove
,
446 static int __init
hisi_l3c_pmu_module_init(void)
450 ret
= cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE
,
451 "AP_PERF_ARM_HISI_L3_ONLINE",
452 hisi_uncore_pmu_online_cpu
,
453 hisi_uncore_pmu_offline_cpu
);
455 pr_err("L3C PMU: Error setup hotplug, ret = %d\n", ret
);
459 ret
= platform_driver_register(&hisi_l3c_pmu_driver
);
461 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE
);
465 module_init(hisi_l3c_pmu_module_init
);
467 static void __exit
hisi_l3c_pmu_module_exit(void)
469 platform_driver_unregister(&hisi_l3c_pmu_driver
);
470 cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE
);
472 module_exit(hisi_l3c_pmu_module_exit
);
474 MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver");
475 MODULE_LICENSE("GPL v2");
476 MODULE_AUTHOR("Anurup M <anurup.m@huawei.com>");
477 MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>");