blob: 931d25e62cb494ed51d7e9285d6e4b4ca8dbabc7 [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 "cpu_pmu.h"
15#include "v6_pmu_hw.h"
16
17/*******************************
18 * ARM v7 operations *
19 *******************************/
20#define ARMV7_PMCR_E (1 << 0) /* enable all counters */
21#define ARMV7_PMCR_P (1 << 1)
22#define ARMV7_PMCR_C (1 << 2)
23#define ARMV7_PMCR_D (1 << 3)
24#define ARMV7_PMCR_X (1 << 4)
25#define ARMV7_PMCR_DP (1 << 5)
26#define ARMV7_PMCR_N_SHIFT 11 /* Number of counters supported */
27#define ARMV7_PMCR_N_MASK 0x1f
28#define ARMV7_PMCR_MASK 0x3f /* mask for writable bits */
29
30static unsigned int armv7_get_ic(void)
31{
32 unsigned int value;
33 /* Read Main ID Register */
34 asm volatile ("mrc p15, 0, %0, c0, c0, 0":"=r" (value));
35
36 value = (value & 0xffff) >> 4; /* primary part number */
37 return value;
38}
39
40static inline void armv7_pmu_counter_select(unsigned int idx)
41{
42 asm volatile ("mcr p15, 0, %0, c9, c12, 5"::"r" (idx));
43 isb();
44}
45
46static inline void armv7_pmu_type_select(unsigned int idx, unsigned int type)
47{
48 armv7_pmu_counter_select(idx);
49 asm volatile ("mcr p15, 0, %0, c9, c13, 1"::"r" (type));
50}
51
52static inline unsigned int armv7_pmu_read_count(unsigned int idx)
53{
54 unsigned int value;
55
56 if (idx == 31) {
57 asm volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (value));
58 } else {
59 armv7_pmu_counter_select(idx);
60 asm volatile ("mrc p15, 0, %0, c9, c13, 2":"=r" (value));
61 }
62 return value;
63}
64
65static inline void armv7_pmu_write_count(int idx, u32 value)
66{
67 if (idx == 31) {
68 asm volatile ("mcr p15, 0, %0, c9, c13, 0"::"r" (value));
69 } else {
70 armv7_pmu_counter_select(idx);
71 asm volatile ("mcr p15, 0, %0, c9, c13, 2"::"r" (value));
72 }
73}
74
75static inline void armv7_pmu_enable_count(unsigned int idx)
76{
77 asm volatile ("mcr p15, 0, %0, c9, c12, 1"::"r" (1 << idx));
78}
79
80static inline void armv7_pmu_disable_count(unsigned int idx)
81{
82 asm volatile ("mcr p15, 0, %0, c9, c12, 2"::"r" (1 << idx));
83}
84
85static inline void armv7_pmu_enable_intr(unsigned int idx)
86{
87 asm volatile ("mcr p15, 0, %0, c9, c14, 1"::"r" (1 << idx));
88}
89
90static inline void armv7_pmu_disable_intr(unsigned int idx)
91{
92 asm volatile ("mcr p15, 0, %0, c9, c14, 2"::"r" (1 << idx));
93}
94
95static inline unsigned int armv7_pmu_overflow(void)
96{
97 unsigned int val;
98
99 asm volatile ("mrc p15, 0, %0, c9, c12, 3":"=r" (val)); /* read */
100 asm volatile ("mcr p15, 0, %0, c9, c12, 3"::"r" (val));
101 return val;
102}
103
104static inline unsigned int armv7_pmu_control_read(void)
105{
106 u32 val;
107
108 asm volatile ("mrc p15, 0, %0, c9, c12, 0":"=r" (val));
109 return val;
110}
111
112static inline void armv7_pmu_control_write(unsigned int val)
113{
114 val &= ARMV7_PMCR_MASK;
115 isb();
116 asm volatile ("mcr p15, 0, %0, c9, c12, 0"::"r" (val));
117}
118
119static int armv7_pmu_hw_get_counters(void)
120{
121 int count = armv7_pmu_control_read();
122 /* N, bits[15:11] */
123 count = ((count >> ARMV7_PMCR_N_SHIFT) & ARMV7_PMCR_N_MASK);
124 return count;
125}
126
127static void armv7_pmu_hw_reset_all(int generic_counters)
128{
129 int i;
130
131 armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P);
132 /* generic counter */
133 for (i = 0; i < generic_counters; i++) {
134 armv7_pmu_disable_intr(i);
135 armv7_pmu_disable_count(i);
136 }
137 /* cycle counter */
138 armv7_pmu_disable_intr(31);
139 armv7_pmu_disable_count(31);
140 armv7_pmu_overflow(); /* clear overflow */
141}
142
143/***********************************
144 * MET ARM v7 operations *
145 ***********************************/
146enum ARM_TYPE {
147 CORTEX_A7 = 0xC07,
148 CORTEX_A9 = 0xC09,
149 CORTEX_A12 = 0xC0D,
150 CORTEX_A15 = 0xC0F,
151 CORTEX_A17 = 0xC0E,
152 CORTEX_A53 = 0xD03,
153 CORTEX_A57 = 0xD07,
154 CHIP_UNKNOWN = 0xFFF
155};
156
157struct chip_pmu {
158 enum ARM_TYPE type;
159};
160
161static struct chip_pmu chips[] = {
162 {CORTEX_A7},
163 {CORTEX_A9},
164 {CORTEX_A12},
165 {CORTEX_A15},
166 {CORTEX_A17},
167 {CORTEX_A53},
168 {CORTEX_A57},
169};
170
171static int armv7_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
172{
173 int i;
174
175 /* Check if event is duplicate */
176 for (i = 0; i < idx; i++) {
177 if (pmu[i].event == event)
178 break;
179 }
180 if (i < idx) {
181 /* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
182 return -1;
183 }
184
185 return 0;
186}
187
188static void armv7_pmu_hw_start(struct met_pmu *pmu, int count)
189{
190 int i;
191 int generic = count - 1;
192
193 armv7_pmu_hw_reset_all(generic);
194 for (i = 0; i < generic; i++) {
195 if (pmu[i].mode == MODE_POLLING) {
196 armv7_pmu_type_select(i, pmu[i].event);
197 armv7_pmu_enable_count(i);
198 }
199 }
200 if (pmu[count - 1].mode == MODE_POLLING) { /* cycle counter */
201 armv7_pmu_enable_count(31);
202 }
203 armv7_pmu_control_write(ARMV7_PMCR_E);
204}
205
206static void armv7_pmu_hw_stop(int count)
207{
208 int generic = count - 1;
209
210 armv7_pmu_hw_reset_all(generic);
211}
212
213static unsigned int armv7_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
214{
215 int i, cnt = 0;
216 int generic = count - 1;
217
218 for (i = 0; i < generic; i++) {
219 if (pmu[i].mode == MODE_POLLING) {
220 pmu_value[cnt] = armv7_pmu_read_count(i);
221 cnt++;
222 }
223 }
224 if (pmu[count - 1].mode == MODE_POLLING) {
225 pmu_value[cnt] = armv7_pmu_read_count(31);
226 cnt++;
227 }
228 armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P | ARMV7_PMCR_E);
229
230 return cnt;
231}
232
233static struct met_pmu pmus[MXNR_CPU][MXNR_PMU_EVENTS];
234
235struct cpu_pmu_hw armv7_pmu = {
236 .name = "armv7_pmu",
237 .check_event = armv7_pmu_hw_check_event,
238 .start = armv7_pmu_hw_start,
239 .stop = armv7_pmu_hw_stop,
240 .polling = armv7_pmu_hw_polling,
241};
242
243struct cpu_pmu_hw *cpu_pmu_hw_init(void)
244{
245 int i;
246 enum ARM_TYPE type;
247 struct cpu_pmu_hw *pmu;
248
249 type = (enum ARM_TYPE)armv7_get_ic();
250 for (i = 0; i < ARRAY_SIZE(chips); i++)
251 if (chips[i].type == type)
252 break;
253
254 if (i < ARRAY_SIZE(chips)) {
255 armv7_pmu.nr_cnt = armv7_pmu_hw_get_counters() + 1;
256 pmu = &armv7_pmu;
257 } else
258 pmu = v6_cpu_pmu_hw_init(type);
259
260 if (pmu == NULL)
261 return NULL;
262
263 for (i = 0; i < MXNR_CPU; i++) {
264 pmu->event_count[i] = pmu->nr_cnt;
265 pmu->pmu[i] = pmus[i];
266 }
267
268 return pmu;
269}