xref: /OK3568_Linux_fs/kernel/drivers/soc/rockchip/rockchip_debug.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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