blob: 161bf22f85ff06cd9c4227ca3eece956176b6e04 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (C) 2018 MediaTek Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14/* include <asm/system.h> */
15#include <linux/smp.h>
16#include "cpu_pmu.h"
17#include "v6_pmu_hw.h"
18#include "v7_pmu_name.h"
19#include "v8_pmu_name.h" /* for 32-bit build of arm64 cpu */
20
21#define ARMV7_PMCR_E (1 << 0) /* enable all counters */
22#define ARMV7_PMCR_P (1 << 1)
23#define ARMV7_PMCR_C (1 << 2)
24#define ARMV7_PMCR_D (1 << 3)
25#define ARMV7_PMCR_X (1 << 4)
26#define ARMV7_PMCR_DP (1 << 5)
27#define ARMV7_PMCR_N_SHIFT 11 /* Number of counters supported */
28#define ARMV7_PMCR_N_MASK 0x1f
29#define ARMV7_PMCR_MASK 0x3f /* mask for writable bits */
30
31
32enum ARM_TYPE {
33 CORTEX_A7 = 0xC07,
34 CORTEX_A9 = 0xC09,
35 CORTEX_A12 = 0xC0D,
36 CORTEX_A15 = 0xC0F,
37 CORTEX_A17 = 0xC0E,
38 CORTEX_A53 = 0xD03,
39 CORTEX_A57 = 0xD07,
40 CHIP_UNKNOWN = 0xFFF
41};
42
43struct chip_pmu {
44 enum ARM_TYPE type;
45 struct pmu_desc *desc;
46 unsigned int count;
47 const char *cpu_name;
48};
49
50static struct chip_pmu chips[] = {
51 {CORTEX_A7, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A7"},
52 {CORTEX_A9, a9_pmu_desc, A9_PMU_DESC_COUNT, "Cortex-A9"},
53 {CORTEX_A12, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A12"},
54 {CORTEX_A15, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A15"},
55 {CORTEX_A17, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A17"},
56 {CORTEX_A53, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A53"},
57 {CORTEX_A57, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A57"},
58};
59static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
60
61#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
62
63static struct chip_pmu *chip;
64
65static enum ARM_TYPE armv7_get_ic(void)
66{
67 unsigned int value;
68 /* Read Main ID Register */
69 asm volatile ("mrc p15, 0, %0, c0, c0, 0":"=r" (value));
70
71 value = (value & 0xffff) >> 4; /* primary part number */
72 return value;
73}
74
75static inline void armv7_pmu_counter_select(unsigned int idx)
76{
77 asm volatile ("mcr p15, 0, %0, c9, c12, 5"::"r" (idx));
78 isb();
79}
80
81static inline void armv7_pmu_type_select(unsigned int idx, unsigned int type)
82{
83 armv7_pmu_counter_select(idx);
84 asm volatile ("mcr p15, 0, %0, c9, c13, 1"::"r" (type));
85}
86
87static inline unsigned int armv7_pmu_read_count(unsigned int idx)
88{
89 unsigned int value;
90
91 if (idx == 31) {
92 asm volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (value));
93 } else {
94 armv7_pmu_counter_select(idx);
95 asm volatile ("mrc p15, 0, %0, c9, c13, 2":"=r" (value));
96 }
97 return value;
98}
99
100static inline void armv7_pmu_write_count(int idx, u32 value)
101{
102 if (idx == 31) {
103 asm volatile ("mcr p15, 0, %0, c9, c13, 0"::"r" (value));
104 } else {
105 armv7_pmu_counter_select(idx);
106 asm volatile ("mcr p15, 0, %0, c9, c13, 2"::"r" (value));
107 }
108}
109
110static inline void armv7_pmu_enable_count(unsigned int idx)
111{
112 asm volatile ("mcr p15, 0, %0, c9, c12, 1"::"r" (1 << idx));
113}
114
115static inline void armv7_pmu_disable_count(unsigned int idx)
116{
117 asm volatile ("mcr p15, 0, %0, c9, c12, 2"::"r" (1 << idx));
118}
119
120static inline void armv7_pmu_enable_intr(unsigned int idx)
121{
122 asm volatile ("mcr p15, 0, %0, c9, c14, 1"::"r" (1 << idx));
123}
124
125static inline void armv7_pmu_disable_intr(unsigned int idx)
126{
127 asm volatile ("mcr p15, 0, %0, c9, c14, 2"::"r" (1 << idx));
128}
129
130static inline unsigned int armv7_pmu_overflow(void)
131{
132 unsigned int val;
133
134 asm volatile ("mrc p15, 0, %0, c9, c12, 3":"=r" (val)); /* read */
135 asm volatile ("mcr p15, 0, %0, c9, c12, 3"::"r" (val));
136 return val;
137}
138
139static inline unsigned int armv7_pmu_control_read(void)
140{
141 u32 val;
142
143 asm volatile ("mrc p15, 0, %0, c9, c12, 0":"=r" (val));
144 return val;
145}
146
147static inline void armv7_pmu_control_write(unsigned int val)
148{
149 val &= ARMV7_PMCR_MASK;
150 isb();
151 asm volatile ("mcr p15, 0, %0, c9, c12, 0"::"r" (val));
152}
153
154static int armv7_pmu_hw_get_counters(void)
155{
156 int count = armv7_pmu_control_read();
157 /* N, bits[15:11] */
158 count = ((count >> ARMV7_PMCR_N_SHIFT) & ARMV7_PMCR_N_MASK);
159 return count;
160}
161
162static void armv7_pmu_hw_reset_all(int generic_counters)
163{
164 int i;
165
166 armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P);
167 /* generic counter */
168 for (i = 0; i < generic_counters; i++) {
169 armv7_pmu_disable_intr(i);
170 armv7_pmu_disable_count(i);
171 }
172 /* cycle counter */
173 armv7_pmu_disable_intr(31);
174 armv7_pmu_disable_count(31);
175 armv7_pmu_overflow(); /* clear overflow */
176}
177
178static int armv7_pmu_hw_get_event_desc(int i, int event, char *event_desc)
179{
180 if (event_desc == NULL)
181 return -1;
182
183 for (i = 0; i < chip->count; i++) {
184 if (chip->desc[i].event == event) {
185 strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
186 break;
187 }
188 }
189 if (i == chip->count)
190 return -1;
191
192 return 0;
193}
194
195static int armv7_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
196{
197 int i;
198
199 /* Check if event is duplicate */
200 for (i = 0; i < idx; i++) {
201 if (pmu[i].event == event)
202 break;
203 }
204 if (i < idx) {
205 /* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
206 return -1;
207 }
208
209 for (i = 0; i < chip->count; i++) {
210 if (chip->desc[i].event == event)
211 break;
212 }
213
214 if (i == chip->count)
215 return -1;
216
217 return 0;
218}
219
220static void armv7_pmu_hw_start(struct met_pmu *pmu, int count)
221{
222 int i;
223 int generic = count - 1;
224
225 armv7_pmu_hw_reset_all(generic);
226 for (i = 0; i < generic; i++) {
227 if (pmu[i].mode == MODE_POLLING) {
228 armv7_pmu_type_select(i, pmu[i].event);
229 armv7_pmu_enable_count(i);
230 }
231 }
232 if (pmu[count - 1].mode == MODE_POLLING) { /* cycle counter */
233 armv7_pmu_enable_count(31);
234 }
235 armv7_pmu_control_write(ARMV7_PMCR_E);
236}
237
238static void armv7_pmu_hw_stop(int count)
239{
240 int generic = count - 1;
241
242 armv7_pmu_hw_reset_all(generic);
243}
244
245static unsigned int armv7_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
246{
247 int i, cnt = 0;
248 int generic = count - 1;
249
250 for (i = 0; i < generic; i++) {
251 if (pmu[i].mode == MODE_POLLING) {
252 pmu_value[cnt] = armv7_pmu_read_count(i);
253 cnt++;
254 }
255 }
256 if (pmu[count - 1].mode == MODE_POLLING) {
257 pmu_value[cnt] = armv7_pmu_read_count(31);
258 cnt++;
259 }
260 armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P | ARMV7_PMCR_E);
261
262 return cnt;
263}
264
265
266struct cpu_pmu_hw armv7_pmu = {
267 .name = "armv7_pmu",
268 .get_event_desc = armv7_pmu_hw_get_event_desc,
269 .check_event = armv7_pmu_hw_check_event,
270 .start = armv7_pmu_hw_start,
271 .stop = armv7_pmu_hw_stop,
272 .polling = armv7_pmu_hw_polling,
273};
274
275struct cpu_pmu_hw *cpu_pmu_hw_init(void)
276{
277 int i;
278 enum ARM_TYPE type;
279 struct cpu_pmu_hw *pmu = NULL;
280
281 type = armv7_get_ic();
282 for (i = 0; i < CHIP_PMU_COUNT; i++) {
283 if (chips[i].type == type) {
284 chip = &(chips[i]);
285 break;
286 }
287 }
288
289 if (chip != NULL) {
290 armv7_pmu.nr_cnt = armv7_pmu_hw_get_counters() + 1;
291 armv7_pmu.cpu_name = chip->cpu_name;
292 pmu = &armv7_pmu;
293 } else {
294 pmu = v6_cpu_pmu_hw_init(type);
295 }
296
297 if ((chip == NULL) && (pmu == NULL)) {
298 chip = &chip_unknown;
299 return NULL;
300 }
301
302 return pmu;
303}