• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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