blob: 19e836bfec01b6bd6c48aae763d176fc66f16c9d [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
17#include "cpu_pmu.h"
18#include "mips_pmu_name.h"
19
20struct chip_pmu {
21 enum cpu_type_enum type;
22 struct pmu_desc **desc;
23 void *refptr;
24 const char *cpu_name;
25 unsigned int pmu_desc_size;
26 unsigned int max_hw_events;
27 unsigned int max_reg_count;
28};
29
30struct pmu_desc *mips_pmu_desc[MIPS_MAX_HWEVENTS];
31
32static struct chip_pmu chips[] = {
33 {CPU_1004K, mips_pmu_desc, (void *)mips_1004k_pmu_desc, "MIPS_1004K",
34 MIPS_1004K_PMU_DESC_SIZE, MIPS_1004K_PMU_DESC_COUNT, PMU_1004K_MAX_HW_REGS},
35};
36
37static struct chip_pmu chip_unknown = { CPU_UNKNOWN, NULL, NULL, "Unknown CPU", 0, 0, 0 };
38
39#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
40static struct chip_pmu *chip;
41#define M_CONFIG1_PC (1 << 4)
42
43#define M_PERFCTL_EXL (1 << 0)
44#define M_PERFCTL_KERNEL (1 << 1)
45#define M_PERFCTL_SUPERVISOR (1 << 2)
46#define M_PERFCTL_USER (1 << 3)
47#define M_PERFCTL_INTERRUPT_ENABLE (1 << 4)
48#define M_PERFCTL_EVENT(event) (((event) & 0x3ff) << 5)
49#define M_PERFCTL_VPEID(vpe) ((vpe) << 16)
50
51#ifdef CONFIG_CPU_BMIPS5000
52#define M_PERFCTL_MT_EN(filter) 0
53#else /* !CONFIG_CPU_BMIPS5000 */
54#define M_PERFCTL_MT_EN(filter) ((filter) << 20)
55#endif /* CONFIG_CPU_BMIPS5000 */
56
57#define M_TC_EN_ALL M_PERFCTL_MT_EN(0)
58#define M_TC_EN_VPE M_PERFCTL_MT_EN(1)
59#define M_TC_EN_TC M_PERFCTL_MT_EN(2)
60#define M_PERFCTL_TCID(tcid) ((tcid) << 22)
61#define M_PERFCTL_WIDE (1 << 30)
62#define M_PERFCTL_MORE (1 << 31)
63#define M_PERFCTL_TC (1 << 30)
64
65#define M_PERFCTL_COUNT_EVENT_WHENEVER (M_PERFCTL_EXL | \
66 M_PERFCTL_KERNEL | \
67 M_PERFCTL_USER | \
68 M_PERFCTL_SUPERVISOR | \
69 M_PERFCTL_INTERRUPT_ENABLE)
70
71#ifdef CONFIG_MIPS_MT_SMP
72#define M_PERFCTL_CONFIG_MASK 0x3fff801f
73#else
74#define M_PERFCTL_CONFIG_MASK 0x1f
75#endif
76#define M_PERFCTL_EVENT_MASK 0xfe0
77
78#define vpe_id() 0
79
80/* To get current TCID*/
81#define read_c0_tcbind() __read_32bit_c0_register($2, 2)
82
83struct cpu_hw_events {
84 unsigned int config_base[MIPS_MAX_HWEVENTS];
85 unsigned int saved_ctrl[MIPS_MAX_HWEVENTS];
86};
87
88DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
89 .config_base = {
90 0, 0, 0, 0}, .saved_ctrl = {
910, 0, 0, 0},};
92
93static enum cpu_type_enum mips_get_ic(void)
94{
95 unsigned int value = current_cpu_type();
96
97 /* pr_debug("ic value: %X\n", value); */
98 return value;
99}
100
101static int __n_counters(void)
102{
103 if (!(read_c0_config1() & M_CONFIG1_PC))
104 return 0;
105 if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
106 return 1;
107 if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
108 return 2;
109 if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
110 return 3;
111
112 return 4;
113}
114
115static int n_counters(void)
116{
117 int counters;
118
119 switch (current_cpu_type()) {
120 case CPU_R10000:
121 counters = 2;
122 break;
123 case CPU_R12000:
124 case CPU_R14000:
125 counters = 4;
126 break;
127 default:
128 counters = __n_counters();
129 break;
130 }
131
132 return counters;
133}
134
135static int mips_pmu_hw_get_counters(void)
136{
137 int count = n_counters();
138
139 /* pr_debug("pmu hw event nr: %d\n", count); */
140 return count;
141}
142
143static unsigned int mipsxx_pmu_swizzle_perf_idx(unsigned int idx)
144{
145 if (vpe_id() == 1)
146 idx = (idx + 2) & 3;
147 return idx;
148}
149
150static void mipsxx_pmu_write_counter(unsigned int idx, u64 val)
151{
152 idx = mipsxx_pmu_swizzle_perf_idx(idx);
153
154 switch (idx) {
155 case 0:
156 write_c0_perfcntr0(val);
157 return;
158 case 1:
159 write_c0_perfcntr1(val);
160 return;
161 case 2:
162 write_c0_perfcntr2(val);
163 return;
164 case 3:
165 write_c0_perfcntr3(val);
166 return;
167 }
168}
169
170static u64 mipsxx_pmu_read_counter(unsigned int idx)
171{
172 idx = mipsxx_pmu_swizzle_perf_idx(idx);
173
174 switch (idx) {
175 case 0:
176 /*
177 * The counters are unsigned, we must cast to truncate
178 * off the high bits.
179 */
180 return (u32) read_c0_perfcntr0();
181 case 1:
182 return (u32) read_c0_perfcntr1();
183 case 2:
184 return (u32) read_c0_perfcntr2();
185 case 3:
186 return (u32) read_c0_perfcntr3();
187 default:
188 WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
189 return 0;
190 }
191}
192
193
194static unsigned int mipsxx_pmu_read_control(unsigned int idx)
195{
196 idx = mipsxx_pmu_swizzle_perf_idx(idx);
197
198 switch (idx) {
199 case 0:
200 return read_c0_perfctrl0();
201 case 1:
202 return read_c0_perfctrl1();
203 case 2:
204 return read_c0_perfctrl2();
205 case 3:
206 return read_c0_perfctrl3();
207 default:
208 WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
209 return 0;
210 }
211}
212
213static void mipsxx_pmu_write_control(unsigned int idx, unsigned int val)
214{
215 idx = mipsxx_pmu_swizzle_perf_idx(idx);
216
217 switch (idx) {
218 case 0:
219 write_c0_perfctrl0(val);
220 return;
221 case 1:
222 write_c0_perfctrl1(val);
223 return;
224 case 2:
225 write_c0_perfctrl2(val);
226 return;
227 case 3:
228 write_c0_perfctrl3(val);
229 return;
230 }
231}
232
233static int mipsxx_pmu_get_vpeid(void)
234{
235 return read_c0_tcbind() & 0xF;
236}
237
238static void mipsxx_pmu_reset_counters(int idx)
239{
240 switch (idx) {
241 case 3:
242 mipsxx_pmu_write_control(3, 0);
243 mipsxx_pmu_write_counter(3, 0);
244 break;
245 case 2:
246 mipsxx_pmu_write_control(2, 0);
247 mipsxx_pmu_write_counter(2, 0);
248 break;
249 case 1:
250 mipsxx_pmu_write_control(1, 0);
251 mipsxx_pmu_write_counter(1, 0);
252 break;
253 case 0:
254 mipsxx_pmu_write_control(0, 0);
255 mipsxx_pmu_write_counter(0, 0);
256 break;
257 }
258}
259
260static void mipsxx_pmu_enable_event(int idx, int event)
261{
262 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
263 unsigned long flags;
264
265 WARN_ON(idx < 0 || idx >= chip->max_hw_events);
266 cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(event & 0xff) |
267 M_PERFCTL_VPEID(mipsxx_pmu_get_vpeid()) |
268 (cpuc->config_base[idx] & M_PERFCTL_CONFIG_MASK);
269#ifdef CONFIG_CPU_BMIPS5000
270 /* if (IS_ENABLED(CONFIG_CPU_BMIPS5000)) */
271 /* enable the counter for the calling thread */
272 cpuc->saved_ctrl[idx] |= (1 << (12 + vpe_id())) | M_PERFCTL_TC;
273#endif
274 /*
275 * To enable pmu count
276 */
277 local_irq_save(flags);
278 mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
279 local_irq_restore(flags);
280}
281
282static void mipsxx_pmu_disable_event(int idx)
283{
284 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
285 unsigned long flags;
286
287 /* WARN_ON(idx < 0 || idx >= mipspmu.num_counters); */
288 WARN_ON(idx < 0 || idx >= chip->max_hw_events);
289
290 local_irq_save(flags);
291 cpuc->saved_ctrl[idx] = mipsxx_pmu_read_control(idx) & ~M_PERFCTL_COUNT_EVENT_WHENEVER;
292 mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
293 local_irq_restore(flags);
294}
295
296static int mips_pmu_hw_get_event_desc(int idx, int event, char *event_desc)
297{
298 int i;
299
300 if (event_desc == NULL) {
301 pr_debug("event_desc is NULL\n");
302 return -1;
303 }
304
305 for (i = 0; i < chip->max_reg_count; i++) {
306 if (chip->desc[idx][i].event == event) {
307 strncpy(event_desc, chip->desc[idx][i].name, MXSIZE_PMU_DESC - 1);
308 break;
309 }
310 }
311 if (i == chip->max_reg_count)
312 return -1;
313
314 return 0;
315}
316
317
318static int mips_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
319{
320 int i;
321
322 /* to check index over run */
323 if (!chip)
324 return -1;
325
326 if (idx >= chip->max_hw_events)
327 return -1;
328
329 for (i = 0; i < chip->max_reg_count; i++) {
330 if (chip->desc[idx][i].event == event)
331 break;
332 }
333 if (i == chip->max_reg_count)
334 return -1;
335
336 return 0;
337}
338
339static void mips_pmu_hw_start(struct met_pmu *pmu, int count)
340{
341 int i;
342 int generic = count - 1;
343 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
344
345 /* pr_debug("hw_start generic: %d\n", generic); */
346 for (i = 0; i < generic; i++) {
347 /* init config */
348 cpuc->config_base[i] = 0;
349 cpuc->config_base[i] |= M_TC_EN_VPE;
350 cpuc->config_base[i] |= M_PERFCTL_USER;
351 cpuc->config_base[i] |= M_PERFCTL_KERNEL;
352 cpuc->config_base[i] |= M_PERFCTL_EXL;
353 cpuc->config_base[i] |= M_PERFCTL_SUPERVISOR;
354 cpuc->config_base[i] &= M_PERFCTL_CONFIG_MASK;
355 /**/ mipsxx_pmu_reset_counters(i);
356 if (pmu[i].mode == MODE_POLLING)
357 mipsxx_pmu_enable_event(i, pmu[i].event);
358 }
359 if (pmu[count - 1].mode == MODE_POLLING)
360 pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
361}
362
363static void mips_pmu_hw_stop(int count)
364{
365 int idx = 0;
366 int generic = count - 1;
367 /* pr_debug("reset %d\n", generic); */
368 for (idx = 0; idx < generic; idx++) {
369 mipsxx_pmu_reset_counters(idx);
370 mipsxx_pmu_disable_event(idx);
371 }
372}
373
374
375static unsigned int mips_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
376{
377 int i, cnt = 0;
378 int generic = count - 1;
379
380 for (i = 0; i < generic; i++) {
381 if (pmu[i].mode == MODE_POLLING) {
382 pmu_value[cnt] = mipsxx_pmu_read_counter(i);
383 cnt++;
384 mipsxx_pmu_reset_counters(i);
385 mipsxx_pmu_enable_event(i, pmu[i].event);
386 }
387 }
388 if (pmu[count - 1].mode == MODE_POLLING) {
389 pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
390 pmu_value[cnt] = 0xFFFF;
391 cnt++;
392 }
393
394 return cnt;
395}
396
397
398
399struct cpu_pmu_hw mips_pmu = {
400 .name = "mips_pmu",
401 .get_event_desc = mips_pmu_hw_get_event_desc,
402 .check_event = mips_pmu_hw_check_event,
403 .start = mips_pmu_hw_start,
404 .stop = mips_pmu_hw_stop,
405 .polling = mips_pmu_hw_polling,
406};
407
408struct cpu_pmu_hw *cpu_pmu_hw_init(void)
409{
410 int i = 0;
411 enum cpu_type_enum type;
412 int pmu_hw_count = 0;
413
414 type = mips_get_ic();
415
416 if (CPU_UNKNOWN == type || CPU_LAST == type) {
417 chip = &chip_unknown;
418 return NULL;
419 }
420 for (i = 0; i < CHIP_PMU_COUNT; i++) {
421 if (chips[i].type == type) {
422 chip = &(chips[i]);
423 break;
424 }
425 }
426 if (i == CHIP_PMU_COUNT) {
427 chip = &chip_unknown;
428 return NULL;
429 }
430
431 pmu_hw_count = mips_pmu_hw_get_counters();
432 for (i = 0; i < pmu_hw_count; i++)
433 chip->desc[i] = chip->refptr + (chip->pmu_desc_size * i);
434
435 mips_pmu.nr_cnt = pmu_hw_count + 1;
436 mips_pmu.cpu_name = chip->cpu_name;
437 return &mips_pmu;
438}