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