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