blob: 5fdc90925d8bd09b3bf8363c9ecf68f888189182 [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/debugfs.h>
10#include <linux/err.h>
11#include <linux/io.h>
12#include <linux/mutex.h>
13#include <linux/of.h>
14#include <linux/of_address.h>
15#include <linux/platform_device.h>
16#include <linux/printk.h>
17#include <linux/relay.h>
18#include <linux/types.h>
19
20#include "mbox.h"
21#include "mcu.h"
22#include "netsys.h"
23#include "trm-fs.h"
24#include "trm-mcu.h"
25#include "trm.h"
26
27#define TRM_HDR_LEN (sizeof(struct trm_header))
28
29#define RLY_DUMP_SUBBUF_DATA_MAX (RLY_DUMP_SUBBUF_SZ - TRM_HDR_LEN)
30
31struct trm_info {
32 char name[TRM_CONFIG_NAME_MAX_LEN];
33 u64 dump_time;
34 u32 start_addr;
35 u32 size;
36 u32 rsn; /* TRM_RSN_* */
37};
38
39struct trm_header {
40 struct trm_info info;
41 u32 data_offset;
42 u32 data_len;
43 u8 last_frag;
44};
45
46struct device *trm_dev;
47
48static struct trm_hw_config *trm_hw_configs[__TRM_HARDWARE_MAX];
49struct mutex trm_lock;
50
51static inline void trm_hdr_init(struct trm_header *trm_hdr,
52 struct trm_config *trm_cfg,
53 u32 size,
54 u64 dump_time,
55 u32 dump_rsn)
56{
57 if (unlikely(!trm_hdr || !trm_cfg))
58 return;
59
60 memset(trm_hdr, 0, TRM_HDR_LEN);
61
62 strncpy(trm_hdr->info.name, trm_cfg->name, TRM_CONFIG_NAME_MAX_LEN);
63 trm_hdr->info.start_addr = trm_cfg->addr + trm_cfg->offset;
64 trm_hdr->info.size = size;
65 trm_hdr->info.dump_time = dump_time;
66 trm_hdr->info.rsn = dump_rsn;
67}
68
69static inline int trm_cfg_sanity_check(struct trm_config *trm_cfg)
70{
71 u32 start = trm_cfg->addr + trm_cfg->offset;
72 u32 end = start + trm_cfg->size;
73
74 if (start < trm_cfg->addr || end > trm_cfg->addr + trm_cfg->len)
75 return -1;
76
77 return 0;
78}
79
80static inline bool trm_cfg_is_core_dump_en(struct trm_config *trm_cfg)
81{
82 return trm_cfg->flag & TRM_CONFIG_F_CORE_DUMP;
83}
84
85static inline bool trm_cfg_is_en(struct trm_config *trm_cfg)
86{
87 return trm_cfg->flag & TRM_CONFIG_F_ENABLE;
88}
89
90static inline int __mtk_trm_cfg_setup(struct trm_config *trm_cfg,
91 u32 offset, u32 size, u8 enable)
92{
93 struct trm_config tmp = { 0 };
94
95 if (!enable) {
96 trm_cfg->flag &= ~TRM_CONFIG_F_ENABLE;
97 } else {
98 tmp.addr = trm_cfg->addr;
99 tmp.len = trm_cfg->len;
100 tmp.offset = offset;
101 tmp.size = size;
102
103 if (trm_cfg_sanity_check(&tmp))
104 return -EINVAL;
105
106 trm_cfg->offset = offset;
107 trm_cfg->size = size;
108 trm_cfg->flag |= TRM_CONFIG_F_ENABLE;
109 }
110
111 return 0;
112}
113
114int mtk_trm_cfg_setup(char *name, u32 offset, u32 size, u8 enable)
115{
116 struct trm_hw_config *trm_hw_cfg;
117 struct trm_config *trm_cfg;
118 int ret = 0;
119 u32 i, j;
120
121 for (i = 0; i < __TRM_HARDWARE_MAX; i++) {
122 trm_hw_cfg = trm_hw_configs[i];
developer0b3c7712023-08-24 16:23:03 +0800123 if (unlikely(!trm_hw_cfg || !trm_hw_cfg->trm_cfgs))
developere5e687d2023-08-08 16:05:33 +0800124 continue;
125
126 for (j = 0; j < trm_hw_cfg->cfg_len; j++) {
127 trm_cfg = &trm_hw_cfg->trm_cfgs[j];
developere5e687d2023-08-08 16:05:33 +0800128
129 if (!strncmp(trm_cfg->name, name, strlen(name))) {
130 mutex_lock(&trm_lock);
131
132 ret = __mtk_trm_cfg_setup(trm_cfg,
133 offset,
134 size,
135 enable);
136
137 mutex_unlock(&trm_lock);
138 }
139 }
140 }
141
142 return ret;
143}
144
145/* append core dump(via ocd) in bottom of core-x-dtcm file */
146static inline void __mtk_trm_save_core_dump(struct trm_config *trm_cfg,
147 void *dst,
148 u32 *frag_len)
149{
150 *frag_len -= CORE_DUMP_FRAME_LEN;
151 memcpy(dst + *frag_len, &cd_frams[trm_cfg->core], CORE_DUMP_FRAME_LEN);
152}
153
154static int __mtk_trm_dump(struct trm_hw_config *trm_hw_cfg,
155 struct trm_config *trm_cfg,
156 u64 dump_time,
157 u32 dump_rsn)
158{
159 struct trm_header trm_hdr;
160 u32 total = trm_cfg->size;
161 u32 i = 0;
162 u32 frag_len;
163 u32 ofs;
164 void *dst;
165
166 /* reserve core dump frame len if core dump enabled */
167 if (trm_cfg_is_core_dump_en(trm_cfg))
168 total += CORE_DUMP_FRAME_LEN;
169
170 /* fill in trm inforamtion */
171 trm_hdr_init(&trm_hdr, trm_cfg, total, dump_time, dump_rsn);
172
173 while (total > 0) {
174 if (total >= RLY_DUMP_SUBBUF_DATA_MAX) {
175 frag_len = RLY_DUMP_SUBBUF_DATA_MAX;
176 total -= RLY_DUMP_SUBBUF_DATA_MAX;
177 } else {
178 frag_len = total;
developer0b3c7712023-08-24 16:23:03 +0800179 total = 0;
developere5e687d2023-08-08 16:05:33 +0800180 trm_hdr.last_frag = true;
181 }
182
183 trm_hdr.data_offset = i++ * RLY_DUMP_SUBBUF_DATA_MAX;
184 trm_hdr.data_len = frag_len;
185
186 dst = mtk_trm_fs_relay_reserve(frag_len + TRM_HDR_LEN);
187 if (IS_ERR(dst))
188 return PTR_ERR(dst);
189
190 memcpy(dst, &trm_hdr, TRM_HDR_LEN);
191 dst += TRM_HDR_LEN;
192
193 /* TODO: what if core dump is being cut between 2 fragment? */
194 if (trm_hdr.last_frag && trm_cfg_is_core_dump_en(trm_cfg))
195 __mtk_trm_save_core_dump(trm_cfg, dst, &frag_len);
196
197 ofs = trm_hdr.info.start_addr + trm_hdr.data_offset;
198
199 /* let TRM HW write memory to destination */
200 trm_hw_cfg->trm_hw_dump(dst, ofs, frag_len);
201
202 mtk_trm_fs_relay_flush();
203 }
204
205 return 0;
206}
207
208int mtk_trm_dump(u32 rsn)
209{
210 u64 time = ktime_to_ns(ktime_get_real()) / 1000000000;
211 struct trm_hw_config *trm_hw_cfg;
212 struct trm_config *trm_cfg;
213 int ret = 0;
214 u32 i, j;
215
216 if (!mtk_trm_fs_is_init())
217 return -EINVAL;
218
219 mutex_lock(&trm_lock);
220
221 mtk_trm_mcu_core_dump();
222
223 for (i = 0; i < __TRM_HARDWARE_MAX; i++) {
224 trm_hw_cfg = trm_hw_configs[i];
225 if (unlikely(!trm_hw_cfg || !trm_hw_cfg->trm_hw_dump))
226 continue;
227
228 for (j = 0; j < trm_hw_cfg->cfg_len; j++) {
229 trm_cfg = &trm_hw_cfg->trm_cfgs[j];
230 if (unlikely(!trm_cfg || !trm_cfg_is_en(trm_cfg)))
231 continue;
232
233 if (unlikely(trm_cfg_sanity_check(trm_cfg))) {
234 TRM_ERR("trm %s: sanity check fail\n", trm_cfg->name);
235 ret = -EINVAL;
236 goto out;
237 }
238
239 ret = __mtk_trm_dump(trm_hw_cfg, trm_cfg, time, rsn);
240 if (ret) {
241 TRM_ERR("trm %s: trm dump fail: %d\n",
242 trm_cfg->name, ret);
243 goto out;
244 }
245 }
246 }
247
248 TRM_NOTICE("TOPS runtime monitor dump\n");
249
250out:
251 mutex_unlock(&trm_lock);
252
253 return ret;
254}
255
256int __init mtk_tops_trm_init(void)
257{
258 mutex_init(&trm_lock);
259
260 return mtk_tops_trm_mcu_init();
261}
262
263void __exit mtk_tops_trm_exit(void)
264{
265 mtk_tops_trm_mcu_exit();
266}
267
268int mtk_trm_hw_config_register(enum trm_hardware trm_hw,
269 struct trm_hw_config *trm_hw_cfg)
270{
271 if (unlikely(trm_hw >= __TRM_HARDWARE_MAX || !trm_hw_cfg))
272 return -ENODEV;
273
274 if (unlikely(!trm_hw_cfg->cfg_len || !trm_hw_cfg->trm_hw_dump))
275 return -EINVAL;
276
277 if (trm_hw_configs[trm_hw])
278 return -EBUSY;
279
280 trm_hw_configs[trm_hw] = trm_hw_cfg;
281
282 return 0;
283}
284
285void mtk_trm_hw_config_unregister(enum trm_hardware trm_hw,
286 struct trm_hw_config *trm_hw_cfg)
287{
288 if (unlikely(trm_hw >= __TRM_HARDWARE_MAX || !trm_hw_cfg))
289 return;
290
291 if (trm_hw_configs[trm_hw] != trm_hw_cfg)
292 return;
293
294 trm_hw_configs[trm_hw] = NULL;
295}