blob: d5758ed5d1ff5918633441926793bb1677b28789 [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
15/* include <asm/system.h> */
16#include <linux/smp.h>
17#include "cpu_pmu.h"
18#include "v6_pmu_name.h"
19
20enum ARM_TYPE {
21 ARM1136 = 0xB36,
22 ARM1156 = 0xB56,
23 ARM1176 = 0xB76,
24 CHIP_UNKNOWN = 0xFFF
25};
26
27struct chip_pmu {
28 enum ARM_TYPE type;
29 struct pmu_desc *desc;
30 unsigned int count;
31 const char *cpu_name;
32};
33
34static struct chip_pmu chips[] = {
35 {ARM1136, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1136"},
36 {ARM1156, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1156"},
37 {ARM1176, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1176"},
38};
39static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
40
41#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
42
43static struct chip_pmu *chip;
44
45/* define V6_PMU_HW_DEBUG */
46#ifdef V6_PMU_HW_DEBUG
47#define v6pmu_hw_debug(fmt, arg...) pr_debug(fmt, ##arg)
48#else
49#define v6pmu_hw_debug(fmt, arg...) do {} while (0)
50#endif
51
52#define ARMV6_PMCR_ENABLE (1 << 0)
53#define ARMV6_PMCR_CTR01_RESET (1 << 1)
54#define ARMV6_PMCR_CCOUNT_RESET (1 << 2)
55#define ARMV6_PMCR_CCOUNT_DIV (1 << 3)
56#define ARMV6_PMCR_COUNT0_IEN (1 << 4)
57#define ARMV6_PMCR_COUNT1_IEN (1 << 5)
58#define ARMV6_PMCR_CCOUNT_IEN (1 << 6)
59#define ARMV6_PMCR_COUNT0_OVERFLOW (1 << 8)
60#define ARMV6_PMCR_COUNT1_OVERFLOW (1 << 9)
61#define ARMV6_PMCR_CCOUNT_OVERFLOW (1 << 10)
62#define ARMV6_PMCR_EVT_COUNT0_SHIFT 20
63#define ARMV6_PMCR_EVT_COUNT0_MASK (0xFF << ARMV6_PMCR_EVT_COUNT0_SHIFT)
64#define ARMV6_PMCR_EVT_COUNT1_SHIFT 12
65#define ARMV6_PMCR_EVT_COUNT1_MASK (0xFF << ARMV6_PMCR_EVT_COUNT1_SHIFT)
66
67#define ARMV6_PMCR_OVERFLOWED_MASK \
68 (ARMV6_PMCR_COUNT0_OVERFLOW | ARMV6_PMCR_COUNT1_OVERFLOW | \
69 ARMV6_PMCR_CCOUNT_OVERFLOW)
70
71enum armv6_counters {
72 ARMV6_COUNTER0 = 0,
73 ARMV6_COUNTER1,
74 ARMV6_CYCLE_COUNTER,
75};
76
77static inline unsigned long armv6_pmcr_read(void)
78{
79 u32 val;
80
81 asm volatile ("mrc p15, 0, %0, c15, c12, 0":"=r" (val));
82 return val;
83}
84
85static inline void armv6_pmcr_write(unsigned long val)
86{
87 asm volatile ("mcr p15, 0, %0, c15, c12, 0"::"r" (val));
88}
89
90static inline unsigned int armv6_pmu_read_count(unsigned int idx)
91{
92 unsigned long value = 0;
93
94 if (idx == ARMV6_CYCLE_COUNTER)
95 asm volatile ("mrc p15, 0, %0, c15, c12, 1":"=r" (value));
96 else
97if (idx == ARMV6_COUNTER0)
98 asm volatile ("mrc p15, 0, %0, c15, c12, 2":"=r" (value));
99 else
100if (idx == ARMV6_COUNTER1)
101 asm volatile ("mrc p15, 0, %0, c15, c12, 3":"=r" (value));
102
103 return value;
104}
105
106static inline void armv6_pmu_overflow(void)
107{
108 unsigned int val;
109
110 val = armv6_pmcr_read();
111 val |= ARMV6_PMCR_OVERFLOWED_MASK;
112 armv6_pmcr_write(val);
113}
114
115static inline unsigned int armv6_pmu_control_read(void)
116{
117 u32 val;
118
119 asm volatile ("mrc p15, 0, %0, c15, c12, 0":"=r" (val));
120 return val;
121}
122
123static inline void armv6_pmu_control_write(unsigned int setting)
124{
125 unsigned long val;
126
127 val = armv6_pmcr_read();
128 val |= setting;
129 armv6_pmcr_write(val);
130}
131
132static void armv6_pmu_hw_reset_all(void)
133{
134 unsigned long val;
135
136 val = armv6_pmcr_read();
137 val &= ~ARMV6_PMCR_ENABLE; /* disable all counters */
138 val |= (ARMV6_PMCR_CTR01_RESET | ARMV6_PMCR_CCOUNT_RESET); /* reset CCNT, PMNC1/2 counter to zero */
139 armv6_pmcr_write(val);
140
141 armv6_pmu_overflow();
142}
143
144static void armv6pmu_enable_event(int idx, unsigned short config)
145{
146 unsigned long val, mask, evt;
147
148 if (idx == ARMV6_CYCLE_COUNTER) {
149 mask = 0;
150 evt = ARMV6_PMCR_CCOUNT_IEN;
151 } else if (idx == ARMV6_COUNTER0) {
152 mask = ARMV6_PMCR_EVT_COUNT0_MASK;
153 evt = (config << ARMV6_PMCR_EVT_COUNT0_SHIFT) | ARMV6_PMCR_COUNT0_IEN;
154 } else if (idx == ARMV6_COUNTER1) {
155 mask = ARMV6_PMCR_EVT_COUNT1_MASK;
156 evt = (config << ARMV6_PMCR_EVT_COUNT1_SHIFT) | ARMV6_PMCR_COUNT1_IEN;
157 } else {
158 pr_debug("invalid counter number (%d)\n", idx);
159 return;
160 }
161
162 /*
163 * Mask out the current event and set the counter to count the event
164 * that we're interested in.
165 */
166 val = armv6_pmcr_read();
167 val &= ~mask;
168 val |= evt;
169 armv6_pmcr_write(val);
170}
171
172static int armv6_pmu_hw_get_event_desc(int i, int event, char *event_desc)
173{
174 if (event_desc == NULL)
175 return -1;
176
177 for (i = 0; i < chip->count; i++) {
178 if (chip->desc[i].event == event) {
179 strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
180 break;
181 }
182 }
183
184 if (i == chip->count)
185 return -1;
186
187 return 0;
188}
189
190static int armv6_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
191{
192 int i;
193
194 /* Check if event is duplicate */
195 for (i = 0; i < idx; i++) {
196 if (pmu[i].event == event)
197 break;
198 }
199 if (i < idx) {
200 /* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
201 return -1;
202 }
203
204 for (i = 0; i < chip->count; i++) {
205 if (chip->desc[i].event == event)
206 break;
207 }
208
209 if (i == chip->count)
210 return -1;
211
212 return 0;
213}
214
215static void armv6_pmu_hw_start(struct met_pmu *pmu, int count)
216{
217 int i;
218 int generic = count - 1;
219
220 armv6_pmu_hw_reset_all();
221
222 for (i = 0; i < generic; i++) {
223 if (pmu[i].mode == MODE_POLLING)
224 armv6pmu_enable_event(i, pmu[i].event);
225 }
226
227 if (pmu[count - 1].mode == MODE_POLLING)
228 armv6pmu_enable_event(2, pmu[2].event);
229
230 armv6_pmu_control_write(ARMV6_PMCR_ENABLE);
231}
232
233static void armv6_pmu_hw_stop(int count)
234{
235 armv6_pmu_hw_reset_all();
236}
237
238static unsigned int armv6_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
239{
240 int i, cnt = 0;
241 int generic = count - 1;
242
243 for (i = 0; i < generic; i++) {
244 if (pmu[i].mode == MODE_POLLING) {
245 pmu_value[cnt] = armv6_pmu_read_count(i);
246 cnt++;
247 }
248 }
249
250 if (pmu[count - 1].mode == MODE_POLLING) {
251 pmu_value[cnt] = armv6_pmu_read_count(2);
252 cnt++;
253 }
254
255 armv6_pmu_control_write(ARMV6_PMCR_ENABLE | ARMV6_PMCR_CTR01_RESET |
256 ARMV6_PMCR_CCOUNT_RESET);
257
258 return cnt;
259}
260
261struct cpu_pmu_hw armv6_pmu = {
262 .name = "armv6_pmu",
263 .get_event_desc = armv6_pmu_hw_get_event_desc,
264 .check_event = armv6_pmu_hw_check_event,
265 .start = armv6_pmu_hw_start,
266 .stop = armv6_pmu_hw_stop,
267 .polling = armv6_pmu_hw_polling,
268};
269
270struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid)
271{
272 int i;
273
274 for (i = 0; i < CHIP_PMU_COUNT; i++) {
275 if (chips[i].type == typeid) {
276 chip = &(chips[i]);
277
278 break;
279 }
280 }
281
282 if (chip == NULL) {
283 chip = &chip_unknown;
284
285 return NULL;
286 }
287
288 armv6_pmu.nr_cnt = 3;
289 armv6_pmu.cpu_name = chip->cpu_name;
290
291 return &armv6_pmu;
292}