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