blob: e7a826811e8e8765f4153aadfbed8ad9d52edf51 [file] [log] [blame]
developere5e687d2023-08-08 16:05:33 +08001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
4 *
5 * Author: Alvin Kuo <alvin.kuog@mediatek.com>
6 * Ren-Ting Wang <ren-ting.wang@mediatek.com>
7 */
8
9#include <linux/delay.h>
10#include <linux/device.h>
11#include <linux/dma-mapping.h>
12#include <linux/io.h>
13#include <linux/of.h>
14
developer0fb30d52023-12-04 09:51:36 +080015#include "tops/internal.h"
16#include "tops/mcu.h"
17#include "tops/trm-debugfs.h"
18#include "tops/trm-fs.h"
19#include "tops/trm-mcu.h"
20#include "tops/trm.h"
developere5e687d2023-08-08 16:05:33 +080021
22#define TOPS_OCD_RETRY_TIMES (3)
23
24#define TOPS_OCD_DCRSET (0x200C)
25#define ENABLE_OCD (1 << 0)
26#define DEBUG_INT (1 << 1)
27
28#define TOPS_OCD_DSR (0x2010)
29#define EXEC_DONE (1 << 0)
30#define EXEC_EXCE (1 << 1)
31#define EXEC_BUSY (1 << 2)
32#define STOPPED (1 << 4)
33#define DEBUG_PEND_HOST (1 << 17)
34
35#define TOPS_OCD_DDR (0x2014)
36
37#define TOPS_OCD_DIR0EXEC (0x201C)
38
39struct tops_ocd_dev {
40 void __iomem *base;
41 u32 base_offset;
42 struct clk *debugsys_clk;
43};
44
45static struct tops_ocd_dev ocd;
46
47struct core_dump_fram cd_frams[CORE_TOPS_NUM];
48
49static inline void ocd_write(struct tops_ocd_dev *ocd, u32 reg, u32 val)
50{
51 writel(val, ocd->base + ocd->base_offset + reg);
52}
53
54static inline u32 ocd_read(struct tops_ocd_dev *ocd, u32 reg)
55{
56 return readl(ocd->base + ocd->base_offset + reg);
57}
58
59static inline void ocd_set(struct tops_ocd_dev *ocd, u32 reg, u32 mask)
60{
61 setbits(ocd->base + ocd->base_offset + reg, mask);
62}
63
64static inline void ocd_clr(struct tops_ocd_dev *ocd, u32 reg, u32 mask)
65{
66 clrbits(ocd->base + ocd->base_offset + reg, mask);
67}
68
69static int core_exec_instr(u32 instr)
70{
71 u32 rty = 0;
72 int ret;
73
74 ocd_set(&ocd, TOPS_OCD_DSR, EXEC_DONE);
75 ocd_set(&ocd, TOPS_OCD_DSR, EXEC_EXCE);
76
77 ocd_write(&ocd, TOPS_OCD_DIR0EXEC, instr);
78
79 while ((ocd_read(&ocd, TOPS_OCD_DSR) & EXEC_BUSY)) {
80 if (rty++ < TOPS_OCD_RETRY_TIMES) {
81 usleep_range(1000, 1500);
82 } else {
83 TRM_ERR("run instruction(0x%x) timeout\n", instr);
84 ret = -1;
85 goto out;
86 }
87 }
88
89 ret = ocd_read(&ocd, TOPS_OCD_DSR) & EXEC_EXCE ? -1 : 0;
90 if (ret)
91 TRM_ERR("run instruction(0x%x) fail\n", instr);
92
93out:
94 return ret;
95}
96
97static int core_dump(struct core_dump_fram *cd_fram)
98{
99 cd_fram->magic = CORE_DUMP_FRAM_MAGIC;
100 cd_fram->num_areg = XCHAL_NUM_AREG;
101
102 /*
103 * save
104 * PC, PS, WINDOWSTART, WINDOWBASE,
105 * EPC1, EXCCAUSE, EXCVADDR, EXCSAVE1
106 */
107 core_exec_instr(0x13f500);
108
109 core_exec_instr(0x03b500);
110 core_exec_instr(0x136800);
111 cd_fram->pc = ocd_read(&ocd, TOPS_OCD_DDR);
112
113 core_exec_instr(0x03e600);
114 core_exec_instr(0x136800);
115 cd_fram->ps = ocd_read(&ocd, TOPS_OCD_DDR);
116
117 core_exec_instr(0x034900);
118 core_exec_instr(0x136800);
119 cd_fram->windowstart = ocd_read(&ocd, TOPS_OCD_DDR);
120
121 core_exec_instr(0x034800);
122 core_exec_instr(0x136800);
123 cd_fram->windowbase = ocd_read(&ocd, TOPS_OCD_DDR);
124
125 core_exec_instr(0x03b100);
126 core_exec_instr(0x136800);
127 cd_fram->epc1 = ocd_read(&ocd, TOPS_OCD_DDR);
128
129 core_exec_instr(0x03e800);
130 core_exec_instr(0x136800);
131 cd_fram->exccause = ocd_read(&ocd, TOPS_OCD_DDR);
132
133 core_exec_instr(0x03ee00);
134 core_exec_instr(0x136800);
135 cd_fram->excvaddr = ocd_read(&ocd, TOPS_OCD_DDR);
136
137 core_exec_instr(0x03d100);
138 core_exec_instr(0x136800);
139 cd_fram->excsave1 = ocd_read(&ocd, TOPS_OCD_DDR);
140
141 core_exec_instr(0x03f500);
142
143 /*
144 * save
145 * a0, a1, a2, a3, a4, a5, a6, a7
146 */
147 core_exec_instr(0x136800);
148 cd_fram->areg[0] = ocd_read(&ocd, TOPS_OCD_DDR);
149
150 core_exec_instr(0x136810);
151 cd_fram->areg[1] = ocd_read(&ocd, TOPS_OCD_DDR);
152
153 core_exec_instr(0x136820);
154 cd_fram->areg[2] = ocd_read(&ocd, TOPS_OCD_DDR);
155
156 core_exec_instr(0x136830);
157 cd_fram->areg[3] = ocd_read(&ocd, TOPS_OCD_DDR);
158
159 core_exec_instr(0x136840);
160 cd_fram->areg[4] = ocd_read(&ocd, TOPS_OCD_DDR);
161
162 core_exec_instr(0x136850);
163 cd_fram->areg[5] = ocd_read(&ocd, TOPS_OCD_DDR);
164
165 core_exec_instr(0x136860);
166 cd_fram->areg[6] = ocd_read(&ocd, TOPS_OCD_DDR);
167
168 core_exec_instr(0x136870);
169 cd_fram->areg[7] = ocd_read(&ocd, TOPS_OCD_DDR);
170
171 /*
172 * save
173 * a8, a9, a10, a11, a12, a13, a14, a15
174 */
175 core_exec_instr(0x136880);
176 cd_fram->areg[8] = ocd_read(&ocd, TOPS_OCD_DDR);
177
178 core_exec_instr(0x136890);
179 cd_fram->areg[9] = ocd_read(&ocd, TOPS_OCD_DDR);
180
181 core_exec_instr(0x1368a0);
182 cd_fram->areg[10] = ocd_read(&ocd, TOPS_OCD_DDR);
183
184 core_exec_instr(0x1368b0);
185 cd_fram->areg[11] = ocd_read(&ocd, TOPS_OCD_DDR);
186
187 core_exec_instr(0x1368c0);
188 cd_fram->areg[12] = ocd_read(&ocd, TOPS_OCD_DDR);
189
190 core_exec_instr(0x1368d0);
191 cd_fram->areg[13] = ocd_read(&ocd, TOPS_OCD_DDR);
192
193 core_exec_instr(0x1368e0);
194 cd_fram->areg[14] = ocd_read(&ocd, TOPS_OCD_DDR);
195
196 core_exec_instr(0x1368f0);
197 cd_fram->areg[15] = ocd_read(&ocd, TOPS_OCD_DDR);
198
199 core_exec_instr(0x408020);
200
201 /*
202 * save
203 * a16, a17, a18, a19, a20, a21, a22, a23
204 */
205 core_exec_instr(0x136880);
206 cd_fram->areg[16] = ocd_read(&ocd, TOPS_OCD_DDR);
207
208 core_exec_instr(0x136890);
209 cd_fram->areg[17] = ocd_read(&ocd, TOPS_OCD_DDR);
210
211 core_exec_instr(0x1368a0);
212 cd_fram->areg[18] = ocd_read(&ocd, TOPS_OCD_DDR);
213
214 core_exec_instr(0x1368b0);
215 cd_fram->areg[19] = ocd_read(&ocd, TOPS_OCD_DDR);
216
217 core_exec_instr(0x1368c0);
218 cd_fram->areg[20] = ocd_read(&ocd, TOPS_OCD_DDR);
219
220 core_exec_instr(0x1368d0);
221 cd_fram->areg[21] = ocd_read(&ocd, TOPS_OCD_DDR);
222
223 core_exec_instr(0x1368e0);
224 cd_fram->areg[22] = ocd_read(&ocd, TOPS_OCD_DDR);
225
226 core_exec_instr(0x1368f0);
227 cd_fram->areg[23] = ocd_read(&ocd, TOPS_OCD_DDR);
228
229 core_exec_instr(0x408020);
230
231 /*
232 * save
233 * a24, a25, a26, a27, a28, a29, a30, a31
234 */
235 core_exec_instr(0x136880);
236 cd_fram->areg[24] = ocd_read(&ocd, TOPS_OCD_DDR);
237
238 core_exec_instr(0x136890);
239 cd_fram->areg[25] = ocd_read(&ocd, TOPS_OCD_DDR);
240
241 core_exec_instr(0x1368a0);
242 cd_fram->areg[26] = ocd_read(&ocd, TOPS_OCD_DDR);
243
244 core_exec_instr(0x1368b0);
245 cd_fram->areg[27] = ocd_read(&ocd, TOPS_OCD_DDR);
246
247 core_exec_instr(0x1368c0);
248 cd_fram->areg[28] = ocd_read(&ocd, TOPS_OCD_DDR);
249
250 core_exec_instr(0x1368d0);
251 cd_fram->areg[29] = ocd_read(&ocd, TOPS_OCD_DDR);
252
253 core_exec_instr(0x1368e0);
254 cd_fram->areg[30] = ocd_read(&ocd, TOPS_OCD_DDR);
255
256 core_exec_instr(0x1368f0);
257 cd_fram->areg[31] = ocd_read(&ocd, TOPS_OCD_DDR);
258
259 core_exec_instr(0x408040);
260
261 core_exec_instr(0xf1e000);
262
263 return 0;
264}
265
266static int __mtk_trm_mcu_core_dump(enum core_id core)
267{
268 u32 rty = 0;
269 int ret;
270
271 ocd.base_offset = (core == CORE_MGMT) ? (0x0) : (0x5000 + (core * 0x4000));
272
273 /* enable OCD */
274 ocd_set(&ocd, TOPS_OCD_DCRSET, ENABLE_OCD);
275
276 /* assert debug interrupt to core */
277 ocd_set(&ocd, TOPS_OCD_DCRSET, DEBUG_INT);
278
279 /* wait core into stopped state */
280 while (!(ocd_read(&ocd, TOPS_OCD_DSR) & STOPPED)) {
281 if (rty++ < TOPS_OCD_RETRY_TIMES) {
282 usleep_range(10000, 15000);
283 } else {
284 TRM_ERR("wait core(%d) into stopped state timeout\n", core);
285 ret = -1;
286 goto out;
287 }
288 }
289
290 /* deassert debug interrupt to core */
291 ocd_set(&ocd, TOPS_OCD_DSR, DEBUG_PEND_HOST);
292
293 /* dump core's registers and let core into running state */
294 ret = core_dump(&cd_frams[core]);
295
296out:
297 return ret;
298}
299
300int mtk_trm_mcu_core_dump(void)
301{
302 enum core_id core;
303 int ret;
304
305 ret = clk_prepare_enable(ocd.debugsys_clk);
306 if (ret) {
307 TRM_ERR("debugsys clk enable failed: %d\n", ret);
308 goto out;
309 }
310
311 memset(cd_frams, 0, sizeof(cd_frams));
312
313 for (core = CORE_OFFLOAD_0; core <= CORE_MGMT; core++) {
314 ret = __mtk_trm_mcu_core_dump(core);
315 if (ret)
316 break;
317 }
318
319 clk_disable_unprepare(ocd.debugsys_clk);
320
321out:
322 return ret;
323}
324
325static int mtk_tops_ocd_probe(struct platform_device *pdev)
326{
327 struct resource *res = NULL;
328 int ret;
329
330 trm_dev = &pdev->dev;
331
332 ocd.debugsys_clk = devm_clk_get(trm_dev, "debugsys");
333 if (IS_ERR(ocd.debugsys_clk)) {
334 TRM_ERR("get debugsys clk failed: %ld\n", PTR_ERR(ocd.debugsys_clk));
335 return PTR_ERR(ocd.debugsys_clk);
336 }
337
338 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tops-ocd-base");
339 if (!res)
340 return -ENXIO;
341
342 ocd.base = devm_ioremap(trm_dev, res->start, resource_size(res));
343 if (!ocd.base)
344 return -ENOMEM;
345
developerfbdb5112023-08-21 15:12:14 +0800346 ret = mtk_trm_debugfs_init();
347 if (ret)
348 return ret;
349
developere5e687d2023-08-08 16:05:33 +0800350 ret = mtk_trm_fs_init();
351 if (ret)
352 return ret;
353
354 TRM_INFO("tops-ocd init done\n");
355
356 return 0;
357}
358
359static int mtk_tops_ocd_remove(struct platform_device *pdev)
360{
361 mtk_trm_fs_deinit();
362
developerfbdb5112023-08-21 15:12:14 +0800363 mtk_trm_debugfs_deinit();
364
developere5e687d2023-08-08 16:05:33 +0800365 return 0;
366}
367
368static struct of_device_id mtk_tops_ocd_match[] = {
369 { .compatible = "mediatek,tops-ocd", },
370 { },
371};
372
373static struct platform_driver mtk_tops_ocd_driver = {
374 .probe = mtk_tops_ocd_probe,
375 .remove = mtk_tops_ocd_remove,
376 .driver = {
377 .name = "mediatek,tops-ocd",
378 .owner = THIS_MODULE,
379 .of_match_table = mtk_tops_ocd_match,
380 },
381};
382
383int __init mtk_tops_trm_mcu_init(void)
384{
385 return platform_driver_register(&mtk_tops_ocd_driver);
386}
387
388void __exit mtk_tops_trm_mcu_exit(void)
389{
390 platform_driver_unregister(&mtk_tops_ocd_driver);
391}