• 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 "../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