1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * drivers/soc/rockchip/rockchip_debug.c
4 *
5 * Arm debug driver
6 *
7 * Copyright (C) 2019 ROCKCHIP, Inc.
8 */
9
10 /* RK3399
11 * debug {
12 * compatible = "rockchip,debug";
13 * reg = <0x0 0xfe430000 0x0 0x1000>,
14 * <0x0 0xfe432000 0x0 0x1000>,
15 * <0x0 0xfe434000 0x0 0x1000>,
16 * <0x0 0xfe436000 0x0 0x1000>,
17 * <0x0 0xfe610000 0x0 0x1000>,
18 * <0x0 0xfe710000 0x0 0x1000>;
19 * };
20 */
21
22 /* RK3326
23 * debug {
24 * compatible = "rockchip,debug";
25 * reg = <0x0 0xff690000 0x0 0x1000>,
26 * <0x0 0xff692000 0x0 0x1000>,
27 * <0x0 0xff694000 0x0 0x1000>,
28 * <0x0 0xff696000 0x0 0x1000>;
29 * };
30 */
31
32 /* RK3308
33 * debug {
34 * compatible = "rockchip,debug";
35 * reg = <0x0 0xff810000 0x0 0x1000>,
36 * <0x0 0xff812000 0x0 0x1000>,
37 * <0x0 0xff814000 0x0 0x1000>,
38 * <0x0 0xff816000 0x0 0x1000>;
39 * };
40 */
41
42 /* RK3288
43 * debug {
44 * compatible = "rockchip,debug";
45 * reg = <0x0 0xffbb0000 0x0 0x1000>,
46 * <0x0 0xffbb2000 0x0 0x1000>,
47 * <0x0 0xffbb4000 0x0 0x1000>,
48 * <0x0 0xffbb6000 0x0 0x1000>;
49 * };
50 */
51
52 #include <linux/init.h>
53 #include <linux/io.h>
54 #include <linux/kernel.h>
55 #include <linux/module.h>
56 #include <linux/of.h>
57 #include <linux/of_address.h>
58 #include "../staging/android/fiq_debugger/fiq_debugger_priv.h"
59 #include "rockchip_debug.h"
60
61 #define EDPCSR_LO 0x0a0
62 #define EDPCSR_HI 0x0ac
63 #define EDLAR 0xfb0
64 #define EDLAR_UNLOCK 0xc5acce55
65
66 #define EDPRSR 0x314
67 #define EDPRSR_PU 0x1
68 #define EDDEVID 0xFC8
69
70 #define PMPCSR_LO 0x200
71 #define PMPCSR_HI 0x204
72
73 #define NUM_CPU_SAMPLES 100
74 #define NUM_SAMPLES_TO_PRINT 32
75
76 static void IO_MEM *rockchip_cpu_debug[16];
77 static void IO_MEM *rockchip_cs_pmu[16];
78 static bool edpcsr_present;
79
80 #if IS_ENABLED(CONFIG_FIQ_DEBUGGER)
rockchip_debug_dump_edpcsr(struct fiq_debugger_output * output)81 static int rockchip_debug_dump_edpcsr(struct fiq_debugger_output *output)
82 {
83 unsigned long edpcsr;
84 int i = 0, j = 0;
85 void *pc = NULL;
86 void *prev_pc = NULL;
87 int printed = 0;
88 void IO_MEM *base;
89 u32 pu = 0;
90
91 while (rockchip_cpu_debug[i]) {
92 base = rockchip_cpu_debug[i];
93
94 pu = (u32)readl(base + EDPRSR) & EDPRSR_PU;
95 if (pu != EDPRSR_PU) {
96 i++;
97 continue;
98 }
99 /* Unlock EDLSR.SLK so that EDPCSRhi gets populated */
100 writel(EDLAR_UNLOCK, base + EDLAR);
101
102 output->printf(output, "CPU%d online:%d\n", i, cpu_online(i));
103
104 /* Try to read a bunch of times if CPU is actually running */
105 for (j = 0; j < NUM_CPU_SAMPLES && printed < NUM_SAMPLES_TO_PRINT; j++) {
106 if (sizeof(edpcsr) == 0x08) {
107 edpcsr = ((u64)readl(base + EDPCSR_LO)) | ((u64)readl(base + EDPCSR_HI) << 0x20);
108 } else {
109 edpcsr = (u32)readl(base + EDPCSR_LO);
110 }
111
112 /* NOTE: no offset on ARMv8; see DBGDEVID1.PCSROffset */
113 pc = (void *)(edpcsr & ~1);
114
115 if (pc != prev_pc) {
116 output->printf(output, "\tPC: <0x%px> %pS\n", pc, pc);
117 printed++;
118 }
119 prev_pc = pc;
120 }
121
122 output->printf(output, "\n");
123 i++;
124 prev_pc = NULL;
125 printed = 0;
126 }
127 return NOTIFY_OK;
128 }
129
130 #ifdef CONFIG_ARM64
rockchip_debug_dump_pmpcsr(struct fiq_debugger_output * output)131 static int rockchip_debug_dump_pmpcsr(struct fiq_debugger_output *output)
132 {
133 u64 pmpcsr;
134 int i = 0, j = 0, el, ns;
135 void *pc = NULL;
136 void *prev_pc = NULL;
137 int printed = 0;
138 void IO_MEM *base;
139
140 while (rockchip_cs_pmu[i]) {
141 base = rockchip_cs_pmu[i];
142
143 output->printf(output, "CPU%d online:%d\n", i, cpu_online(i));
144
145 /* Try to read a bunch of times if CPU is actually running */
146 for (j = 0; j < NUM_CPU_SAMPLES && printed < NUM_SAMPLES_TO_PRINT; j++) {
147 pmpcsr = ((u64)readl(base + PMPCSR_LO)) | ((u64)readl(base + PMPCSR_HI) << 0x20);
148
149 el = (pmpcsr >> 0x3D) & 0x3;
150 if (pmpcsr & 0x8000000000000000) {
151 ns = 1;
152 } else {
153 ns = 0;
154 }
155
156 if (el == 0x02) {
157 pmpcsr |= 0xff00000000000000;
158 } else {
159 pmpcsr &= 0x0fffffffffffffff;
160 }
161 /* NOTE: no offset on ARMv8; see DBGDEVID1.PCSROffset */
162 pc = (void *)(pmpcsr & ~1);
163
164 if (pc != prev_pc) {
165 output->printf(output, "\tEL%d(%s) PC: <0x%px> %pS\n", el, ns ? "NS" : "S", pc, pc);
166 printed++;
167 }
168 prev_pc = pc;
169 }
170
171 output->printf(output, "\n");
172 i++;
173 prev_pc = NULL;
174 printed = 0;
175 }
176 return NOTIFY_OK;
177 }
178 #else
rockchip_debug_dump_pmpcsr(struct fiq_debugger_output * output)179 static int rockchip_debug_dump_pmpcsr(struct fiq_debugger_output *output)
180 {
181 return 0;
182 }
183 #endif
184
rockchip_debug_dump_pcsr(struct fiq_debugger_output * output)185 int rockchip_debug_dump_pcsr(struct fiq_debugger_output *output)
186 {
187 if (edpcsr_present) {
188 rockchip_debug_dump_edpcsr(output);
189 } else {
190 rockchip_debug_dump_pmpcsr(output);
191 }
192 return 0;
193 }
194 EXPORT_SYMBOL_GPL(rockchip_debug_dump_pcsr);
195 #endif
196
rockchip_panic_notify_edpcsr(struct notifier_block * nb,unsigned long event,void * p)197 static int rockchip_panic_notify_edpcsr(struct notifier_block *nb, unsigned long event, void *p)
198 {
199 unsigned long edpcsr;
200 int i = 0, j;
201 void *pc = NULL;
202 void *prev_pc = NULL;
203 int printed = 0;
204 void IO_MEM *base;
205 u32 pu = 0;
206
207 /*
208 * The panic handler will try to shut down the other CPUs.
209 * If any of them are still online at this point, this loop attempts
210 * to determine the program counter value. If there are no wedged
211 * CPUs, this loop will do nothing.
212 */
213
214 while (rockchip_cpu_debug[i]) {
215 base = rockchip_cpu_debug[i];
216
217 pu = (u32)readl(base + EDPRSR) & EDPRSR_PU;
218 if (pu != EDPRSR_PU) {
219 i++;
220 continue;
221 }
222 /* Unlock EDLSR.SLK so that EDPCSRhi gets populated */
223 writel(EDLAR_UNLOCK, base + EDLAR);
224
225 pr_err("CPU%d online:%d\n", i, cpu_online(i));
226
227 /* Try to read a bunch of times if CPU is actually running */
228 for (j = 0; j < NUM_CPU_SAMPLES && printed < NUM_SAMPLES_TO_PRINT; j++) {
229 if (sizeof(edpcsr) == 0x08) {
230 edpcsr = ((u64)readl(base + EDPCSR_LO)) | ((u64)readl(base + EDPCSR_HI) << 0x20);
231 } else {
232 edpcsr = (u32)readl(base + EDPCSR_LO);
233 }
234
235 /* NOTE: no offset on ARMv8; see DBGDEVID1.PCSROffset */
236 pc = (void *)(edpcsr & ~1);
237
238 if (pc != prev_pc) {
239 pr_err("\tPC: <0x%px> %pS\n", pc, pc);
240 printed++;
241 }
242 prev_pc = pc;
243 }
244
245 pr_err("\n");
246 i++;
247 prev_pc = NULL;
248 printed = 0;
249 }
250 return NOTIFY_OK;
251 }
252
253 #ifdef CONFIG_ARM64
rockchip_panic_notify_pmpcsr(struct notifier_block * nb,unsigned long event,void * p)254 static int rockchip_panic_notify_pmpcsr(struct notifier_block *nb, unsigned long event, void *p)
255 {
256 u64 pmpcsr;
257 int i = 0, j, el, ns;
258 void *pc = NULL;
259 void *prev_pc = NULL;
260 int printed = 0;
261 void IO_MEM *base;
262
263 /*
264 * The panic handler will try to shut down the other CPUs.
265 * If any of them are still online at this point, this loop attempts
266 * to determine the program counter value. If there are no wedged
267 * CPUs, this loop will do nothing.
268 */
269
270 while (rockchip_cs_pmu[i]) {
271 base = rockchip_cs_pmu[i];
272
273 pr_err("CPU%d online:%d\n", i, cpu_online(i));
274
275 /* Try to read a bunch of times if CPU is actually running */
276 for (j = 0; j < NUM_CPU_SAMPLES && printed < NUM_SAMPLES_TO_PRINT; j++) {
277 pmpcsr = ((u64)readl(base + PMPCSR_LO)) | ((u64)readl(base + PMPCSR_HI) << 0x20);
278
279 el = (pmpcsr >> 0x3D) & 0x3;
280 if (pmpcsr & 0x8000000000000000) {
281 ns = 1;
282 } else {
283 ns = 0;
284 }
285
286 if (el == 0x02) {
287 pmpcsr |= 0xff00000000000000;
288 } else {
289 pmpcsr &= 0x0fffffffffffffff;
290 }
291 /* NOTE: no offset on ARMv8; see DBGDEVID1.PCSROffset */
292 pc = (void *)(pmpcsr & ~1);
293
294 if (pc != prev_pc) {
295 pr_err("\tEL%d(%s) PC: <0x%px> %pS\n", el, ns ? "NS" : "S", pc, pc);
296 printed++;
297 }
298 prev_pc = pc;
299 }
300
301 pr_err("\n");
302 i++;
303 prev_pc = NULL;
304 printed = 0;
305 }
306 return NOTIFY_OK;
307 }
308 #else
rockchip_panic_notify_pmpcsr(struct notifier_block * nb,unsigned long event,void * p)309 static int rockchip_panic_notify_pmpcsr(struct notifier_block *nb, unsigned long event, void *p)
310 {
311 return NOTIFY_OK;
312 }
313 #endif
314
rockchip_panic_notify(struct notifier_block * nb,unsigned long event,void * p)315 static int rockchip_panic_notify(struct notifier_block *nb, unsigned long event, void *p)
316 {
317 if (edpcsr_present) {
318 rockchip_panic_notify_edpcsr(nb, event, p);
319 } else {
320 rockchip_panic_notify_pmpcsr(nb, event, p);
321 }
322 return NOTIFY_OK;
323 }
324 static struct notifier_block rockchip_panic_nb = {
325 .notifier_call = rockchip_panic_notify,
326 };
327
328 static const struct of_device_id rockchip_debug_dt_match[] __initconst = {
329 /* external debug */
330 {
331 .compatible = "rockchip,debug",
332 },
333 {},
334 };
335
336 static const struct of_device_id rockchip_cspmu_dt_match[] __initconst = {
337 /* coresight pmu */
338 {
339 .compatible = "rockchip,cspmu",
340 },
341 {},
342 };
343
rockchip_debug_init(void)344 static int __init rockchip_debug_init(void)
345 {
346 int i;
347 u32 pcs;
348 struct device_node *debug_np = NULL, *cspmu_np = NULL;
349
350 debug_np = of_find_matching_node_and_match(NULL, rockchip_debug_dt_match, NULL);
351
352 if (debug_np) {
353 i = -1;
354 do {
355 i++;
356 rockchip_cpu_debug[i] = of_iomap(debug_np, i);
357 } while (rockchip_cpu_debug[i]);
358 of_node_put(debug_np);
359 }
360
361 cspmu_np = of_find_matching_node_and_match(NULL, rockchip_cspmu_dt_match, NULL);
362
363 if (cspmu_np) {
364 i = -1;
365 do {
366 i++;
367 rockchip_cs_pmu[i] = of_iomap(cspmu_np, i);
368 } while (rockchip_cs_pmu[i]);
369 of_node_put(cspmu_np);
370 }
371
372 if (!debug_np) {
373 return -ENODEV;
374 }
375
376 pcs = readl(rockchip_cpu_debug[0] + EDDEVID) & 0xf;
377 /* 0x3 EDPCSR, EDCIDSR, and EDVIDSR are implemented */
378 if (pcs == 0x3) {
379 edpcsr_present = true;
380 }
381
382 if (!edpcsr_present && !cspmu_np) {
383 return -ENODEV;
384 }
385
386 atomic_notifier_chain_register(&panic_notifier_list, &rockchip_panic_nb);
387 return 0;
388 }
389 arch_initcall(rockchip_debug_init);
390
rockchip_debug_exit(void)391 static void __exit rockchip_debug_exit(void)
392 {
393 int i = 0;
394
395 atomic_notifier_chain_unregister(&panic_notifier_list, &rockchip_panic_nb);
396
397 while (rockchip_cpu_debug[i]) {
398 iounmap(rockchip_cpu_debug[i++]);
399 }
400
401 i = 0;
402 while (rockchip_cs_pmu[i]) {
403 iounmap(rockchip_cs_pmu[i++]);
404 }
405 }
406 module_exit(rockchip_debug_exit);
407
408 MODULE_AUTHOR("Huibin Hong <huibin.hong@rock-chips.com>");
409 MODULE_DESCRIPTION("Rockchip Debugger");
410 MODULE_LICENSE("GPL");
411 MODULE_ALIAS("platform:rockchip-debugger");
412