blob: 11991cfe455252151ecb5e2b7fb972fda159c7c4 [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];
123 if (unlikely(!trm_hw_cfg))
124 continue;
125
126 for (j = 0; j < trm_hw_cfg->cfg_len; j++) {
127 trm_cfg = &trm_hw_cfg->trm_cfgs[j];
128 if (unlikely(!trm_cfg))
129 continue;
130
131 if (!strncmp(trm_cfg->name, name, strlen(name))) {
132 mutex_lock(&trm_lock);
133
134 ret = __mtk_trm_cfg_setup(trm_cfg,
135 offset,
136 size,
137 enable);
138
139 mutex_unlock(&trm_lock);
140 }
141 }
142 }
143
144 return ret;
145}
146
147/* append core dump(via ocd) in bottom of core-x-dtcm file */
148static inline void __mtk_trm_save_core_dump(struct trm_config *trm_cfg,
149 void *dst,
150 u32 *frag_len)
151{
152 *frag_len -= CORE_DUMP_FRAME_LEN;
153 memcpy(dst + *frag_len, &cd_frams[trm_cfg->core], CORE_DUMP_FRAME_LEN);
154}
155
156static int __mtk_trm_dump(struct trm_hw_config *trm_hw_cfg,
157 struct trm_config *trm_cfg,
158 u64 dump_time,
159 u32 dump_rsn)
160{
161 struct trm_header trm_hdr;
162 u32 total = trm_cfg->size;
163 u32 i = 0;
164 u32 frag_len;
165 u32 ofs;
166 void *dst;
167
168 /* reserve core dump frame len if core dump enabled */
169 if (trm_cfg_is_core_dump_en(trm_cfg))
170 total += CORE_DUMP_FRAME_LEN;
171
172 /* fill in trm inforamtion */
173 trm_hdr_init(&trm_hdr, trm_cfg, total, dump_time, dump_rsn);
174
175 while (total > 0) {
176 if (total >= RLY_DUMP_SUBBUF_DATA_MAX) {
177 frag_len = RLY_DUMP_SUBBUF_DATA_MAX;
178 total -= RLY_DUMP_SUBBUF_DATA_MAX;
179 } else {
180 frag_len = total;
181 total -= total;
182 trm_hdr.last_frag = true;
183 }
184
185 trm_hdr.data_offset = i++ * RLY_DUMP_SUBBUF_DATA_MAX;
186 trm_hdr.data_len = frag_len;
187
188 dst = mtk_trm_fs_relay_reserve(frag_len + TRM_HDR_LEN);
189 if (IS_ERR(dst))
190 return PTR_ERR(dst);
191
192 memcpy(dst, &trm_hdr, TRM_HDR_LEN);
193 dst += TRM_HDR_LEN;
194
195 /* TODO: what if core dump is being cut between 2 fragment? */
196 if (trm_hdr.last_frag && trm_cfg_is_core_dump_en(trm_cfg))
197 __mtk_trm_save_core_dump(trm_cfg, dst, &frag_len);
198
199 ofs = trm_hdr.info.start_addr + trm_hdr.data_offset;
200
201 /* let TRM HW write memory to destination */
202 trm_hw_cfg->trm_hw_dump(dst, ofs, frag_len);
203
204 mtk_trm_fs_relay_flush();
205 }
206
207 return 0;
208}
209
210int mtk_trm_dump(u32 rsn)
211{
212 u64 time = ktime_to_ns(ktime_get_real()) / 1000000000;
213 struct trm_hw_config *trm_hw_cfg;
214 struct trm_config *trm_cfg;
215 int ret = 0;
216 u32 i, j;
217
218 if (!mtk_trm_fs_is_init())
219 return -EINVAL;
220
221 mutex_lock(&trm_lock);
222
223 mtk_trm_mcu_core_dump();
224
225 for (i = 0; i < __TRM_HARDWARE_MAX; i++) {
226 trm_hw_cfg = trm_hw_configs[i];
227 if (unlikely(!trm_hw_cfg || !trm_hw_cfg->trm_hw_dump))
228 continue;
229
230 for (j = 0; j < trm_hw_cfg->cfg_len; j++) {
231 trm_cfg = &trm_hw_cfg->trm_cfgs[j];
232 if (unlikely(!trm_cfg || !trm_cfg_is_en(trm_cfg)))
233 continue;
234
235 if (unlikely(trm_cfg_sanity_check(trm_cfg))) {
236 TRM_ERR("trm %s: sanity check fail\n", trm_cfg->name);
237 ret = -EINVAL;
238 goto out;
239 }
240
241 ret = __mtk_trm_dump(trm_hw_cfg, trm_cfg, time, rsn);
242 if (ret) {
243 TRM_ERR("trm %s: trm dump fail: %d\n",
244 trm_cfg->name, ret);
245 goto out;
246 }
247 }
248 }
249
250 TRM_NOTICE("TOPS runtime monitor dump\n");
251
252out:
253 mutex_unlock(&trm_lock);
254
255 return ret;
256}
257
258int __init mtk_tops_trm_init(void)
259{
260 mutex_init(&trm_lock);
261
262 return mtk_tops_trm_mcu_init();
263}
264
265void __exit mtk_tops_trm_exit(void)
266{
267 mtk_tops_trm_mcu_exit();
268}
269
270int mtk_trm_hw_config_register(enum trm_hardware trm_hw,
271 struct trm_hw_config *trm_hw_cfg)
272{
273 if (unlikely(trm_hw >= __TRM_HARDWARE_MAX || !trm_hw_cfg))
274 return -ENODEV;
275
276 if (unlikely(!trm_hw_cfg->cfg_len || !trm_hw_cfg->trm_hw_dump))
277 return -EINVAL;
278
279 if (trm_hw_configs[trm_hw])
280 return -EBUSY;
281
282 trm_hw_configs[trm_hw] = trm_hw_cfg;
283
284 return 0;
285}
286
287void mtk_trm_hw_config_unregister(enum trm_hardware trm_hw,
288 struct trm_hw_config *trm_hw_cfg)
289{
290 if (unlikely(trm_hw >= __TRM_HARDWARE_MAX || !trm_hw_cfg))
291 return;
292
293 if (trm_hw_configs[trm_hw] != trm_hw_cfg)
294 return;
295
296 trm_hw_configs[trm_hw] = NULL;
297}