1 // SPDX-License-Identifier: GPL-2.0
3 * RISC-V performance counter support.
5 * Copyright (C) 2021 Western Digital Corporation or its affiliates.
7 * This implementation is based on old RISC-V perf and ARM perf event code
8 * which are in turn based on sparc64 and x86 code.
11 #include <linux/mod_devicetable.h>
12 #include <linux/perf/riscv_pmu.h>
13 #include <linux/platform_device.h>
15 #define RISCV_PMU_LEGACY_CYCLE 0
16 #define RISCV_PMU_LEGACY_INSTRET 2
18 static bool pmu_init_done
;
20 static int pmu_legacy_ctr_get_idx(struct perf_event
*event
)
22 struct perf_event_attr
*attr
= &event
->attr
;
24 if (event
->attr
.type
!= PERF_TYPE_HARDWARE
)
26 if (attr
->config
== PERF_COUNT_HW_CPU_CYCLES
)
27 return RISCV_PMU_LEGACY_CYCLE
;
28 else if (attr
->config
== PERF_COUNT_HW_INSTRUCTIONS
)
29 return RISCV_PMU_LEGACY_INSTRET
;
34 /* For legacy config & counter index are same */
35 static int pmu_legacy_event_map(struct perf_event
*event
, u64
*config
)
37 return pmu_legacy_ctr_get_idx(event
);
40 /* cycle & instret are always 64 bit, one bit less according to SBI spec */
41 static int pmu_legacy_ctr_get_width(int idx
)
46 static u64
pmu_legacy_read_ctr(struct perf_event
*event
)
48 struct hw_perf_event
*hwc
= &event
->hw
;
52 if (idx
== RISCV_PMU_LEGACY_CYCLE
) {
53 val
= riscv_pmu_ctr_read_csr(CSR_CYCLE
);
54 if (IS_ENABLED(CONFIG_32BIT
))
55 val
= (u64
)riscv_pmu_ctr_read_csr(CSR_CYCLEH
) << 32 | val
;
56 } else if (idx
== RISCV_PMU_LEGACY_INSTRET
) {
57 val
= riscv_pmu_ctr_read_csr(CSR_INSTRET
);
58 if (IS_ENABLED(CONFIG_32BIT
))
59 val
= ((u64
)riscv_pmu_ctr_read_csr(CSR_INSTRETH
)) << 32 | val
;
66 static void pmu_legacy_ctr_start(struct perf_event
*event
, u64 ival
)
68 struct hw_perf_event
*hwc
= &event
->hw
;
69 u64 initial_val
= pmu_legacy_read_ctr(event
);
72 * The legacy method doesn't really have a start/stop method.
73 * It also can not update the counter with a initial value.
74 * But we still need to set the prev_count so that read() can compute
75 * the delta. Just use the current counter value to set the prev_count.
77 local64_set(&hwc
->prev_count
, initial_val
);
80 static uint8_t pmu_legacy_csr_index(struct perf_event
*event
)
85 static void pmu_legacy_event_mapped(struct perf_event
*event
, struct mm_struct
*mm
)
87 if (event
->attr
.config
!= PERF_COUNT_HW_CPU_CYCLES
&&
88 event
->attr
.config
!= PERF_COUNT_HW_INSTRUCTIONS
)
91 event
->hw
.flags
|= PERF_EVENT_FLAG_USER_READ_CNT
;
94 static void pmu_legacy_event_unmapped(struct perf_event
*event
, struct mm_struct
*mm
)
96 if (event
->attr
.config
!= PERF_COUNT_HW_CPU_CYCLES
&&
97 event
->attr
.config
!= PERF_COUNT_HW_INSTRUCTIONS
)
100 event
->hw
.flags
&= ~PERF_EVENT_FLAG_USER_READ_CNT
;
104 * This is just a simple implementation to allow legacy implementations
105 * compatible with new RISC-V PMU driver framework.
106 * This driver only allows reading two counters i.e CYCLE & INSTRET.
107 * However, it can not start or stop the counter. Thus, it is not very useful
108 * will be removed in future.
110 static void pmu_legacy_init(struct riscv_pmu
*pmu
)
112 pr_info("Legacy PMU implementation is available\n");
114 pmu
->cmask
= BIT(RISCV_PMU_LEGACY_CYCLE
) |
115 BIT(RISCV_PMU_LEGACY_INSTRET
);
116 pmu
->ctr_start
= pmu_legacy_ctr_start
;
117 pmu
->ctr_stop
= NULL
;
118 pmu
->event_map
= pmu_legacy_event_map
;
119 pmu
->ctr_get_idx
= pmu_legacy_ctr_get_idx
;
120 pmu
->ctr_get_width
= pmu_legacy_ctr_get_width
;
121 pmu
->ctr_clear_idx
= NULL
;
122 pmu
->ctr_read
= pmu_legacy_read_ctr
;
123 pmu
->event_mapped
= pmu_legacy_event_mapped
;
124 pmu
->event_unmapped
= pmu_legacy_event_unmapped
;
125 pmu
->csr_index
= pmu_legacy_csr_index
;
126 pmu
->pmu
.capabilities
|= PERF_PMU_CAP_NO_INTERRUPT
;
127 pmu
->pmu
.capabilities
|= PERF_PMU_CAP_NO_EXCLUDE
;
129 perf_pmu_register(&pmu
->pmu
, "cpu", PERF_TYPE_RAW
);
132 static int pmu_legacy_device_probe(struct platform_device
*pdev
)
134 struct riscv_pmu
*pmu
= NULL
;
136 pmu
= riscv_pmu_alloc();
139 pmu
->pmu
.parent
= &pdev
->dev
;
140 pmu_legacy_init(pmu
);
145 static struct platform_driver pmu_legacy_driver
= {
146 .probe
= pmu_legacy_device_probe
,
148 .name
= RISCV_PMU_LEGACY_PDEV_NAME
,
152 static int __init
riscv_pmu_legacy_devinit(void)
155 struct platform_device
*pdev
;
157 if (likely(pmu_init_done
))
160 ret
= platform_driver_register(&pmu_legacy_driver
);
164 pdev
= platform_device_register_simple(RISCV_PMU_LEGACY_PDEV_NAME
, -1, NULL
, 0);
166 platform_driver_unregister(&pmu_legacy_driver
);
167 return PTR_ERR(pdev
);
172 late_initcall(riscv_pmu_legacy_devinit
);
174 void riscv_pmu_legacy_skip_init(void)
176 pmu_init_done
= true;