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 <linux/kernel_stat.h>
59 #include <linux/irq.h>
60 #include <linux/delay.h>
61
62 #include "fiq_debugger/fiq_debugger_priv.h"
63 #include "rockchip_debug.h"
64
65 #define EDPCSR_LO 0x0a0
66 #define EDPCSR_HI 0x0ac
67 #define EDLAR 0xfb0
68 #define EDLAR_UNLOCK 0xc5acce55
69
70 #define EDPRSR 0x314
71 #define EDPRSR_PU 0x1
72 #define EDDEVID 0xFC8
73
74 #define PMPCSR_LO 0x200
75 #define PMPCSR_HI 0x204
76
77 #define NUM_CPU_SAMPLES 100
78 #define NUM_SAMPLES_TO_PRINT 32
79
80 static void __iomem *rockchip_cpu_debug[16];
81 static void __iomem *rockchip_cs_pmu[16];
82 static bool edpcsr_present;
83 static char log_buf[1024];
84
85 extern struct atomic_notifier_head hardlock_notifier_list;
86 extern struct atomic_notifier_head rcu_stall_notifier_list;
87
88 #if IS_ENABLED(CONFIG_FIQ_DEBUGGER)
rockchip_debug_dump_edpcsr(struct fiq_debugger_output * output)89 static int rockchip_debug_dump_edpcsr(struct fiq_debugger_output *output)
90 {
91 unsigned long edpcsr;
92 int i = 0, j = 0;
93 void *pc = NULL;
94 void *prev_pc = NULL;
95 int printed = 0;
96 void __iomem *base;
97 u32 pu = 0, online = 0;
98
99 #ifdef CONFIG_ARM64
100 /* disable SError */
101 asm volatile("msr daifset, #0x4");
102 #endif
103
104 while (rockchip_cpu_debug[i]) {
105 online = cpu_online(i);
106 output->printf(output,
107 "CPU%d online:%d\n", i, online);
108 if (online == 0) {
109 i++;
110 continue;
111 }
112
113 base = rockchip_cpu_debug[i];
114 pu = (u32)readl(base + EDPRSR) & EDPRSR_PU;
115 if (pu != EDPRSR_PU) {
116 output->printf(output,
117 "CPU%d power down\n", i);
118 i++;
119 continue;
120 }
121 /* Unlock EDLSR.SLK so that EDPCSRhi gets populated */
122 writel(EDLAR_UNLOCK, base + EDLAR);
123
124 /* Try to read a bunch of times if CPU is actually running */
125 for (j = 0; j < NUM_CPU_SAMPLES &&
126 printed < NUM_SAMPLES_TO_PRINT; j++) {
127 pu = (u32)readl(base + EDPRSR) & EDPRSR_PU;
128 if (pu != EDPRSR_PU) {
129 output->printf(output,
130 "CPU%d power down\n", i);
131 break;
132 }
133
134 if (sizeof(edpcsr) == 8)
135 edpcsr = ((u64)readl(base + EDPCSR_LO)) |
136 ((u64)readl(base + EDPCSR_HI) << 32);
137 else
138 edpcsr = (u32)readl(base + EDPCSR_LO);
139
140 /* NOTE: no offset on ARMv8; see DBGDEVID1.PCSROffset */
141 pc = (void *)(edpcsr & ~1);
142
143 if (pc != prev_pc) {
144 output->printf(output,
145 "\tPC: <0x%px> %pS\n", pc, pc);
146 printed++;
147 }
148 prev_pc = pc;
149 }
150
151 output->printf(output, "\n");
152 i++;
153 prev_pc = NULL;
154 printed = 0;
155 }
156
157 #ifdef CONFIG_ARM64
158 /* enable SError */
159 asm volatile("msr daifclr, #0x4");
160 #endif
161
162 return NOTIFY_OK;
163 }
164
165 #ifdef CONFIG_ARM64
rockchip_debug_dump_pmpcsr(struct fiq_debugger_output * output)166 static int rockchip_debug_dump_pmpcsr(struct fiq_debugger_output *output)
167 {
168 u64 pmpcsr;
169 int i = 0, j = 0, el, ns;
170 void *pc = NULL;
171 void *prev_pc = NULL;
172 int printed = 0;
173 void __iomem *base;
174 u32 pu = 0, online = 0;
175
176 /* disable SError */
177 asm volatile("msr daifset, #0x4");
178
179 while (rockchip_cs_pmu[i]) {
180 online = cpu_online(i);
181 output->printf(output,
182 "CPU%d online:%d\n", i, online);
183 if (online == 0) {
184 i++;
185 continue;
186 }
187
188 pu = (u32)readl(rockchip_cpu_debug[i] + EDPRSR) & EDPRSR_PU;
189 if (pu != EDPRSR_PU) {
190 output->printf(output,
191 "CPU%d power down\n", i);
192 i++;
193 continue;
194 }
195
196 base = rockchip_cs_pmu[i];
197 /* Try to read a bunch of times if CPU is actually running */
198 for (j = 0; j < NUM_CPU_SAMPLES &&
199 printed < NUM_SAMPLES_TO_PRINT; j++) {
200 pu = (u32)readl(rockchip_cpu_debug[i] + EDPRSR) & EDPRSR_PU;
201 if (pu != EDPRSR_PU) {
202 output->printf(output,
203 "CPU%d power down\n", i);
204 break;
205 }
206
207 pmpcsr = ((u64)readl(base + PMPCSR_LO)) |
208 ((u64)readl(base + PMPCSR_HI) << 32);
209
210 el = (pmpcsr >> 61) & 0x3;
211 if (pmpcsr & 0x8000000000000000)
212 ns = 1;
213 else
214 ns = 0;
215
216 if (el == 2)
217 pmpcsr |= 0xff00000000000000;
218 else
219 pmpcsr &= 0x0fffffffffffffff;
220 /* NOTE: no offset on ARMv8; see DBGDEVID1.PCSROffset */
221 pc = (void *)(pmpcsr & ~1);
222
223 if (pc != prev_pc) {
224 output->printf(output, "\tEL%d(%s) PC: <0x%px> %pS\n",
225 el, ns?"NS":"S", pc, pc);
226 printed++;
227 }
228 prev_pc = pc;
229 }
230
231 output->printf(output, "\n");
232 i++;
233 prev_pc = NULL;
234 printed = 0;
235 }
236 /* enable SError */
237 asm volatile("msr daifclr, #0x4");
238 return NOTIFY_OK;
239 }
240 #else
rockchip_debug_dump_pmpcsr(struct fiq_debugger_output * output)241 static int rockchip_debug_dump_pmpcsr(struct fiq_debugger_output *output)
242 {
243 return 0;
244 }
245 #endif
246
247
rockchip_debug_dump_pcsr(struct fiq_debugger_output * output)248 int rockchip_debug_dump_pcsr(struct fiq_debugger_output *output)
249 {
250 if (edpcsr_present)
251 rockchip_debug_dump_edpcsr(output);
252 else
253 rockchip_debug_dump_pmpcsr(output);
254 return 0;
255 }
256 EXPORT_SYMBOL_GPL(rockchip_debug_dump_pcsr);
257 #endif
258
rockchip_panic_notify_edpcsr(struct notifier_block * nb,unsigned long event,void * p)259 static int rockchip_panic_notify_edpcsr(struct notifier_block *nb,
260 unsigned long event, void *p)
261 {
262 unsigned long edpcsr;
263 int i = 0, j;
264 void *pc = NULL;
265 void *prev_pc = NULL;
266 int printed = 0;
267 void __iomem *base;
268 u32 pu = 0;
269
270 #ifdef CONFIG_ARM64
271 /* disable SError */
272 asm volatile("msr daifset, #0x4");
273 #endif
274
275 /*
276 * The panic handler will try to shut down the other CPUs.
277 * If any of them are still online at this point, this loop attempts
278 * to determine the program counter value. If there are no wedged
279 * CPUs, this loop will do nothing.
280 */
281
282 while (rockchip_cpu_debug[i]) {
283 base = rockchip_cpu_debug[i];
284 pu = (u32)readl(base + EDPRSR) & EDPRSR_PU;
285 if (pu != EDPRSR_PU) {
286 pr_err("CPU%d power down\n", i);
287 i++;
288 continue;
289 }
290
291 /* Unlock EDLSR.SLK so that EDPCSRhi gets populated */
292 writel(EDLAR_UNLOCK, base + EDLAR);
293
294 pr_err("CPU%d online:%d\n", i, cpu_online(i));
295
296 /* Try to read a bunch of times if CPU is actually running */
297 for (j = 0; j < NUM_CPU_SAMPLES &&
298 printed < NUM_SAMPLES_TO_PRINT; j++) {
299 pu = (u32)readl(base + EDPRSR) & EDPRSR_PU;
300 if (pu != EDPRSR_PU) {
301 pr_err("CPU%d power down\n", i);
302 break;
303 }
304
305 if (sizeof(edpcsr) == 8)
306 edpcsr = ((u64)readl(base + EDPCSR_LO)) |
307 ((u64)readl(base + EDPCSR_HI) << 32);
308 else
309 edpcsr = (u32)readl(base + EDPCSR_LO);
310
311 /* NOTE: no offset on ARMv8; see DBGDEVID1.PCSROffset */
312 pc = (void *)(edpcsr & ~1);
313
314 if (pc != prev_pc) {
315 pr_err("\tPC: <0x%px> %pS\n", pc, pc);
316 printed++;
317 }
318 prev_pc = pc;
319 }
320
321 pr_err("\n");
322 i++;
323 prev_pc = NULL;
324 printed = 0;
325 }
326
327 #ifdef CONFIG_ARM64
328 /* enable SError */
329 asm volatile("msr daifclr, #0x4");
330 #endif
331
332 return NOTIFY_OK;
333 }
334
335 #ifdef CONFIG_ARM64
rockchip_panic_notify_pmpcsr(struct notifier_block * nb,unsigned long event,void * p)336 static int rockchip_panic_notify_pmpcsr(struct notifier_block *nb,
337 unsigned long event, void *p)
338 {
339 u64 pmpcsr;
340 int i = 0, j, el, ns;
341 void *pc = NULL;
342 void *prev_pc = NULL;
343 int printed = 0;
344 void __iomem *base;
345 u32 pu = 0;
346
347 /* disable SError */
348 asm volatile("msr daifset, #0x4");
349
350 /*
351 * The panic handler will try to shut down the other CPUs.
352 * If any of them are still online at this point, this loop attempts
353 * to determine the program counter value. If there are no wedged
354 * CPUs, this loop will do nothing.
355 */
356
357 while (rockchip_cs_pmu[i]) {
358 base = rockchip_cs_pmu[i];
359
360 pr_err("CPU%d online:%d\n", i, cpu_online(i));
361
362 pu = (u32)readl(rockchip_cpu_debug[i] + EDPRSR) & EDPRSR_PU;
363 if (pu != EDPRSR_PU) {
364 pr_err("CPU%d power down\n", i);
365 i++;
366 continue;
367 }
368
369 /* Try to read a bunch of times if CPU is actually running */
370 for (j = 0; j < NUM_CPU_SAMPLES &&
371 printed < NUM_SAMPLES_TO_PRINT; j++) {
372 pu = (u32)readl(rockchip_cpu_debug[i] + EDPRSR) & EDPRSR_PU;
373 if (pu != EDPRSR_PU) {
374 pr_err("CPU%d power down\n", i);
375 break;
376 }
377 pmpcsr = ((u64)readl(base + PMPCSR_LO)) |
378 ((u64)readl(base + PMPCSR_HI) << 32);
379
380 el = (pmpcsr >> 61) & 0x3;
381 if (pmpcsr & 0x8000000000000000)
382 ns = 1;
383 else
384 ns = 0;
385
386 if (el == 2)
387 pmpcsr |= 0xff00000000000000;
388 else
389 pmpcsr &= 0x0fffffffffffffff;
390 /* NOTE: no offset on ARMv8; see DBGDEVID1.PCSROffset */
391 pc = (void *)(pmpcsr & ~1);
392
393 if (pc != prev_pc) {
394 pr_err("\tEL%d(%s) PC: <0x%px> %pS\n",
395 el, ns?"NS":"S", pc, pc);
396 printed++;
397 }
398 prev_pc = pc;
399 }
400
401 pr_err("\n");
402 i++;
403 prev_pc = NULL;
404 printed = 0;
405 }
406 /* enable SError */
407 asm volatile("msr daifclr, #0x4");
408 return NOTIFY_OK;
409 }
410 #else
rockchip_panic_notify_pmpcsr(struct notifier_block * nb,unsigned long event,void * p)411 static int rockchip_panic_notify_pmpcsr(struct notifier_block *nb,
412 unsigned long event, void *p)
413 {
414 return NOTIFY_OK;
415 }
416 #endif
417
rockchip_show_interrupts(char * p,int irq)418 static int rockchip_show_interrupts(char *p, int irq)
419 {
420 static int prec;
421 char *buf = p;
422 unsigned long any_count = 0;
423 int i = irq, j;
424 struct irqaction *action;
425 struct irq_desc *desc;
426
427 if (i > nr_irqs)
428 return -1;
429
430 /* print header and calculate the width of the first column */
431 if (i == 0) {
432 for (prec = 3, j = 1000; prec < 10 && j <= nr_irqs; ++prec)
433 j *= 10;
434
435 buf += sprintf(buf, "%*s", prec + 8, "");
436 for_each_possible_cpu(j)
437 buf += sprintf(buf, "CPU%-8d", j);
438 buf += sprintf(buf, "\n");
439 }
440
441 desc = irq_to_desc(i);
442 if (!desc || (desc->status_use_accessors & IRQ_HIDDEN))
443 goto outsparse;
444
445 if (desc->kstat_irqs)
446 for_each_possible_cpu(j)
447 any_count |= *per_cpu_ptr(desc->kstat_irqs, j);
448
449 if ((!desc->action) && !any_count)
450 goto outsparse;
451
452 buf += sprintf(buf, "%*d: ", prec, i);
453 for_each_possible_cpu(j)
454 buf += sprintf(buf, "%10u ", desc->kstat_irqs ?
455 *per_cpu_ptr(desc->kstat_irqs, j) : 0);
456
457 if (desc->irq_data.chip) {
458 if (desc->irq_data.chip->name)
459 buf += sprintf(buf, " %8s", desc->irq_data.chip->name);
460 else
461 buf += sprintf(buf, " %8s", "-");
462 } else {
463 buf += sprintf(buf, " %8s", "None");
464 }
465 if (desc->irq_data.domain)
466 buf += sprintf(buf, " %*lu", prec, desc->irq_data.hwirq);
467 else
468 buf += sprintf(buf, " %*s", prec, "");
469 #ifdef CONFIG_GENERIC_IRQ_SHOW_LEVEL
470 buf += sprintf(buf, " %-8s", irqd_is_level_type(&desc->irq_data) ? "Level" : "Edge");
471 #endif
472 if (desc->name)
473 buf += sprintf(buf, "-%-8s", desc->name);
474
475 action = desc->action;
476 if (action) {
477 buf += sprintf(buf, " %s", action->name);
478 while ((action = action->next) != NULL)
479 buf += sprintf(buf, ", %s", action->name);
480 }
481
482 sprintf(buf, "\n");
483 return 0;
484 outsparse:
485 return -1;
486 }
487
rockchip_panic_notify_dump_irqs(void)488 static void rockchip_panic_notify_dump_irqs(void)
489 {
490 int i = 0;
491
492 for (i = 0; i < nr_irqs; i++) {
493 if (!rockchip_show_interrupts(log_buf, i) || i == 0)
494 printk("%s", log_buf);
495 }
496 }
497
rockchip_panic_notify(struct notifier_block * nb,unsigned long event,void * p)498 static int rockchip_panic_notify(struct notifier_block *nb, unsigned long event,
499 void *p)
500 {
501 if (edpcsr_present)
502 rockchip_panic_notify_edpcsr(nb, event, p);
503 else
504 rockchip_panic_notify_pmpcsr(nb, event, p);
505
506 rockchip_panic_notify_dump_irqs();
507 mdelay(1000);
508 rockchip_panic_notify_dump_irqs();
509 return NOTIFY_OK;
510 }
511 static struct notifier_block rockchip_panic_nb = {
512 .notifier_call = rockchip_panic_notify,
513 };
514
515 static const struct of_device_id rockchip_debug_dt_match[] __initconst = {
516 /* external debug */
517 {
518 .compatible = "rockchip,debug",
519 },
520 { /* sentinel */ },
521 };
522
523 static const struct of_device_id rockchip_cspmu_dt_match[] __initconst = {
524 /* coresight pmu */
525 {
526 .compatible = "rockchip,cspmu",
527 },
528 { /* sentinel */ },
529 };
530
531
rockchip_debug_init(void)532 static int __init rockchip_debug_init(void)
533 {
534 int i;
535 u32 pcs;
536 struct device_node *debug_np = NULL, *cspmu_np = NULL;
537
538 debug_np = of_find_matching_node_and_match(NULL,
539 rockchip_debug_dt_match, NULL);
540
541 if (debug_np) {
542 i = -1;
543 do {
544 i++;
545 rockchip_cpu_debug[i] = of_iomap(debug_np, i);
546 } while (rockchip_cpu_debug[i]);
547 of_node_put(debug_np);
548 }
549
550 cspmu_np = of_find_matching_node_and_match(NULL,
551 rockchip_cspmu_dt_match, NULL);
552
553 if (cspmu_np) {
554 i = -1;
555 do {
556 i++;
557 rockchip_cs_pmu[i] = of_iomap(cspmu_np, i);
558 } while (rockchip_cs_pmu[i]);
559 of_node_put(cspmu_np);
560 }
561
562 if (!debug_np)
563 return -ENODEV;
564
565 pcs = readl(rockchip_cpu_debug[0] + EDDEVID) & 0xf;
566 /* 0x3 EDPCSR, EDCIDSR, and EDVIDSR are implemented */
567 if (pcs == 0x3)
568 edpcsr_present = true;
569
570 if (!edpcsr_present && !cspmu_np)
571 return -ENODEV;
572
573 atomic_notifier_chain_register(&panic_notifier_list,
574 &rockchip_panic_nb);
575 if (IS_ENABLED(CONFIG_NO_GKI)) {
576 if (IS_ENABLED(CONFIG_HARDLOCKUP_DETECTOR))
577 atomic_notifier_chain_register(&hardlock_notifier_list,
578 &rockchip_panic_nb);
579
580 atomic_notifier_chain_register(&rcu_stall_notifier_list,
581 &rockchip_panic_nb);
582 }
583
584 return 0;
585 }
586 arch_initcall(rockchip_debug_init);
587
rockchip_debug_exit(void)588 static void __exit rockchip_debug_exit(void)
589 {
590 int i = 0;
591
592 atomic_notifier_chain_unregister(&panic_notifier_list,
593 &rockchip_panic_nb);
594 if (IS_ENABLED(CONFIG_NO_GKI)) {
595 if (IS_ENABLED(CONFIG_HARDLOCKUP_DETECTOR))
596 atomic_notifier_chain_unregister(&hardlock_notifier_list,
597 &rockchip_panic_nb);
598
599 atomic_notifier_chain_unregister(&rcu_stall_notifier_list,
600 &rockchip_panic_nb);
601 }
602
603 while (rockchip_cpu_debug[i])
604 iounmap(rockchip_cpu_debug[i++]);
605
606 i = 0;
607 while (rockchip_cs_pmu[i])
608 iounmap(rockchip_cs_pmu[i++]);
609 }
610 module_exit(rockchip_debug_exit);
611
612 MODULE_AUTHOR("Huibin Hong <huibin.hong@rock-chips.com>");
613 MODULE_DESCRIPTION("Rockchip Debugger");
614 MODULE_LICENSE("GPL");
615 MODULE_ALIAS("platform:rockchip-debugger");
616