1// SPDX-License-Identifier: ISC
2/* Copyright (C) 2022 MediaTek Inc. */
3
4#include <linux/devcoredump.h>
5#include <linux/kernel.h>
6#include <linux/types.h>
7#include <linux/utsname.h>
8#include "coredump.h"
9
10static bool coredump_memdump;
11module_param(coredump_memdump, bool, 0644);
12MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory");
13
14static const struct mt7915_mem_region mt7915_mem_regions[] = {
15	{
16		.start = 0xe003b400,
17		.len = 0x00003bff,
18		.name = "CRAM",
19	},
20};
21
22static const struct mt7915_mem_region mt7916_mem_regions[] = {
23	{
24		.start = 0x00800000,
25		.len = 0x0005ffff,
26		.name = "ROM",
27	},
28	{
29		.start = 0x00900000,
30		.len = 0x00013fff,
31		.name = "ULM1",
32	},
33	{
34		.start = 0x02200000,
35		.len = 0x0004ffff,
36		.name = "ULM2",
37	},
38	{
39		.start = 0x02300000,
40		.len = 0x0004ffff,
41		.name = "ULM3",
42	},
43	{
44		.start = 0x00400000,
45		.len = 0x00027fff,
46		.name = "SRAM",
47	},
48	{
49		.start = 0xe0000000,
50		.len = 0x00157fff,
51		.name = "CRAM",
52	},
53};
54
55static const struct mt7915_mem_region mt798x_mem_regions[] = {
56	{
57		.start = 0x00800000,
58		.len = 0x0005ffff,
59		.name = "ROM",
60	},
61	{
62		.start = 0x00900000,
63		.len = 0x0000ffff,
64		.name = "ULM1",
65	},
66	{
67		.start = 0x02200000,
68		.len = 0x0004ffff,
69		.name = "ULM2",
70	},
71	{
72		.start = 0x02300000,
73		.len = 0x0004ffff,
74		.name = "ULM3",
75	},
76	{
77		.start = 0x00400000,
78		.len = 0x00017fff,
79		.name = "SRAM",
80	},
81	{
82		.start = 0xe0000000,
83		.len = 0x00113fff,
84		.name = "CRAM",
85	},
86};
87
88const struct mt7915_mem_region*
89mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num)
90{
91	switch (mt76_chip(&dev->mt76)) {
92	case 0x7915:
93		*num = ARRAY_SIZE(mt7915_mem_regions);
94		return &mt7915_mem_regions[0];
95	case 0x7981:
96	case 0x7986:
97		*num = ARRAY_SIZE(mt798x_mem_regions);
98		return &mt798x_mem_regions[0];
99	case 0x7916:
100		*num = ARRAY_SIZE(mt7916_mem_regions);
101		return &mt7916_mem_regions[0];
102	default:
103		return NULL;
104	}
105}
106
107static int mt7915_coredump_get_mem_size(struct mt7915_dev *dev)
108{
109	const struct mt7915_mem_region *mem_region;
110	size_t size = 0;
111	u32 num;
112	int i;
113
114	mem_region = mt7915_coredump_get_mem_layout(dev, &num);
115	if (!mem_region)
116		return 0;
117
118	for (i = 0; i < num; i++) {
119		size += mem_region->len;
120		mem_region++;
121	}
122
123	/* reserve space for the headers */
124	size += num * sizeof(struct mt7915_mem_hdr);
125	/* make sure it is aligned 4 bytes for debug message print out */
126	size = ALIGN(size, 4);
127
128	return size;
129}
130
131struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev)
132{
133	struct mt7915_crash_data *crash_data = dev->coredump.crash_data;
134
135	lockdep_assert_held(&dev->dump_mutex);
136
137	guid_gen(&crash_data->guid);
138	ktime_get_real_ts64(&crash_data->timestamp);
139
140	return crash_data;
141}
142
143static void
144mt7915_coredump_fw_state(struct mt7915_dev *dev, struct mt7915_coredump *dump,
145			 bool *exception)
146{
147	u32 state, count, type;
148
149	type = (u32)mt76_get_field(dev, MT_FW_EXCEPT_TYPE, GENMASK(7, 0));
150	state = (u32)mt76_get_field(dev, MT_FW_ASSERT_STAT, GENMASK(7, 0));
151	count = is_mt7915(&dev->mt76) ?
152		(u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(15, 8)) :
153		(u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(7, 0));
154
155	/* normal mode: driver can manually trigger assert��for detail info */
156	if (!count)
157		strscpy(dump->fw_state, "normal", sizeof(dump->fw_state));
158	else if (state > 1 && (count == 1) && type == 5)
159		strscpy(dump->fw_state, "assert", sizeof(dump->fw_state));
160	else if ((state > 1 && count == 1) || count > 1)
161		strscpy(dump->fw_state, "exception", sizeof(dump->fw_state));
162
163	*exception = !!count;
164}
165
166static void
167mt7915_coredump_fw_trace(struct mt7915_dev *dev, struct mt7915_coredump *dump,
168			 bool exception)
169{
170	u32 n, irq, sch, base = MT_FW_EINT_INFO;
171
172	/* trap or run? */
173	dump->last_msg_id = mt76_rr(dev, MT_FW_LAST_MSG_ID);
174
175	n = is_mt7915(&dev->mt76) ?
176	    (u32)mt76_get_field(dev, base, GENMASK(7, 0)) :
177	    (u32)mt76_get_field(dev, base, GENMASK(15, 8));
178	dump->eint_info_idx = n;
179
180	irq = mt76_rr(dev, base + 0x8);
181	n = is_mt7915(&dev->mt76) ?
182	    FIELD_GET(GENMASK(7, 0), irq) : FIELD_GET(GENMASK(23, 16), irq);
183	dump->irq_info_idx = n;
184
185	sch = mt76_rr(dev, MT_FW_SCHED_INFO);
186	n = is_mt7915(&dev->mt76) ?
187	    FIELD_GET(GENMASK(7, 0), sch) : FIELD_GET(GENMASK(15, 8), sch);
188	dump->sched_info_idx = n;
189
190	if (exception) {
191		u32 i, y;
192
193		/* sched trace */
194		n = is_mt7915(&dev->mt76) ?
195		    FIELD_GET(GENMASK(15, 8), sch) : FIELD_GET(GENMASK(7, 0), sch);
196		n = n > 60 ? 60 : n;
197
198		strscpy(dump->trace_sched, "(sched_info) id, time",
199			sizeof(dump->trace_sched));
200
201		for (y = dump->sched_info_idx, i = 0; i < n; i++, y++) {
202			mt7915_memcpy_fromio(dev, dump->sched, base + 0xc + y * 12,
203					     sizeof(dump->sched));
204			y = y >= n ? 0 : y;
205		}
206
207		/* irq trace */
208		n = is_mt7915(&dev->mt76) ?
209		    FIELD_GET(GENMASK(15, 8), irq) : FIELD_GET(GENMASK(7, 0), irq);
210		n = n > 60 ? 60 : n;
211
212		strscpy(dump->trace_irq, "(irq_info) id, time",
213			sizeof(dump->trace_irq));
214
215		for (y = dump->irq_info_idx, i = 0; i < n; i++, y++) {
216			mt7915_memcpy_fromio(dev, dump->irq, base + 0x4 + y * 16,
217					     sizeof(dump->irq));
218			y = y >= n ? 0 : y;
219		}
220	}
221}
222
223static void
224mt7915_coredump_fw_stack(struct mt7915_dev *dev, struct mt7915_coredump *dump,
225			 bool exception)
226{
227	u32 oldest, i, idx;
228
229	/* stop call stack record */
230	if (!exception)
231		mt76_clear(dev, 0x89050200, BIT(0));
232
233	oldest = (u32)mt76_get_field(dev, 0x89050200, GENMASK(20, 16)) + 2;
234	for (i = 0; i < 16; i++) {
235		idx = ((oldest + 2 * i + 1) % 32);
236		dump->call_stack[i] = mt76_rr(dev, 0x89050204 + idx * 4);
237	}
238
239	/* start call stack record */
240	if (!exception)
241		mt76_set(dev, 0x89050200, BIT(0));
242}
243
244static void
245mt7915_coredump_fw_task(struct mt7915_dev *dev, struct mt7915_coredump *dump)
246{
247	u32 offs = is_mt7915(&dev->mt76) ? 0xe0 : 0x170;
248
249	strscpy(dump->task_qid, "(task queue id) read, write",
250		sizeof(dump->task_qid));
251
252	dump->taskq[0].read = mt76_rr(dev, MT_FW_TASK_QID1);
253	dump->taskq[0].write = mt76_rr(dev, MT_FW_TASK_QID1 - 4);
254	dump->taskq[1].read = mt76_rr(dev, MT_FW_TASK_QID2);
255	dump->taskq[1].write = mt76_rr(dev, MT_FW_TASK_QID2 - 4);
256
257	strscpy(dump->task_info, "(task stack) start, end, size",
258		sizeof(dump->task_info));
259
260	dump->taski[0].start = mt76_rr(dev, MT_FW_TASK_START);
261	dump->taski[0].end = mt76_rr(dev, MT_FW_TASK_END);
262	dump->taski[0].size = mt76_rr(dev, MT_FW_TASK_SIZE);
263	dump->taski[1].start = mt76_rr(dev, MT_FW_TASK_START + offs);
264	dump->taski[1].end = mt76_rr(dev, MT_FW_TASK_END + offs);
265	dump->taski[1].size = mt76_rr(dev, MT_FW_TASK_SIZE + offs);
266}
267
268static void
269mt7915_coredump_fw_context(struct mt7915_dev *dev, struct mt7915_coredump *dump)
270{
271	u32 count, idx, id;
272
273	count = mt76_rr(dev, MT_FW_CIRQ_COUNT);
274
275	/* current context */
276	if (!count) {
277		strscpy(dump->fw_context, "(context) interrupt",
278			sizeof(dump->fw_context));
279
280		idx = is_mt7915(&dev->mt76) ?
281		      (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(31, 16)) :
282		      (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(15, 0));
283		dump->context.idx = idx;
284		dump->context.handler = mt76_rr(dev, MT_FW_CIRQ_LISR);
285	} else {
286		idx = mt76_rr(dev, MT_FW_TASK_IDX);
287		id = mt76_rr(dev, MT_FW_TASK_ID);
288
289		if (!id && idx == 3) {
290			strscpy(dump->fw_context, "(context) idle",
291				sizeof(dump->fw_context));
292		} else if (id && idx != 3) {
293			strscpy(dump->fw_context, "(context) task",
294				sizeof(dump->fw_context));
295
296			dump->context.idx = idx;
297			dump->context.handler = id;
298		}
299	}
300}
301
302static struct mt7915_coredump *mt7915_coredump_build(struct mt7915_dev *dev)
303{
304	struct mt7915_crash_data *crash_data = dev->coredump.crash_data;
305	struct mt7915_coredump *dump;
306	struct mt7915_coredump_mem *dump_mem;
307	size_t len, sofar = 0, hdr_len = sizeof(*dump);
308	unsigned char *buf;
309	bool exception;
310
311	len = hdr_len;
312
313	if (coredump_memdump && crash_data->memdump_buf_len)
314		len += sizeof(*dump_mem) + crash_data->memdump_buf_len;
315
316	sofar += hdr_len;
317
318	/* this is going to get big when we start dumping memory and such,
319	 * so go ahead and use vmalloc.
320	 */
321	buf = vzalloc(len);
322	if (!buf)
323		return NULL;
324
325	mutex_lock(&dev->dump_mutex);
326
327	dump = (struct mt7915_coredump *)(buf);
328	dump->len = len;
329
330	/* plain text */
331	strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic));
332	strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel));
333	strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version,
334		sizeof(dump->fw_ver));
335
336	guid_copy(&dump->guid, &crash_data->guid);
337	dump->tv_sec = crash_data->timestamp.tv_sec;
338	dump->tv_nsec = crash_data->timestamp.tv_nsec;
339	dump->device_id = mt76_chip(&dev->mt76);
340
341	mt7915_coredump_fw_state(dev, dump, &exception);
342	mt7915_coredump_fw_trace(dev, dump, exception);
343	mt7915_coredump_fw_task(dev, dump);
344	mt7915_coredump_fw_context(dev, dump);
345	mt7915_coredump_fw_stack(dev, dump, exception);
346
347	/* gather memory content */
348	dump_mem = (struct mt7915_coredump_mem *)(buf + sofar);
349	dump_mem->len = crash_data->memdump_buf_len;
350	if (coredump_memdump && crash_data->memdump_buf_len)
351		memcpy(dump_mem->data, crash_data->memdump_buf,
352		       crash_data->memdump_buf_len);
353
354	mutex_unlock(&dev->dump_mutex);
355
356	return dump;
357}
358
359int mt7915_coredump_submit(struct mt7915_dev *dev)
360{
361	struct mt7915_coredump *dump;
362
363	dump = mt7915_coredump_build(dev);
364	if (!dump) {
365		dev_warn(dev->mt76.dev, "no crash dump data found\n");
366		return -ENODATA;
367	}
368
369	dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL);
370
371	return 0;
372}
373
374int mt7915_coredump_register(struct mt7915_dev *dev)
375{
376	struct mt7915_crash_data *crash_data;
377
378	crash_data = vzalloc(sizeof(*dev->coredump.crash_data));
379	if (!crash_data)
380		return -ENOMEM;
381
382	dev->coredump.crash_data = crash_data;
383
384	if (coredump_memdump) {
385		crash_data->memdump_buf_len = mt7915_coredump_get_mem_size(dev);
386		if (!crash_data->memdump_buf_len)
387			/* no memory content */
388			return 0;
389
390		crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
391		if (!crash_data->memdump_buf) {
392			vfree(crash_data);
393			return -ENOMEM;
394		}
395	}
396
397	return 0;
398}
399
400void mt7915_coredump_unregister(struct mt7915_dev *dev)
401{
402	if (dev->coredump.crash_data->memdump_buf) {
403		vfree(dev->coredump.crash_data->memdump_buf);
404		dev->coredump.crash_data->memdump_buf = NULL;
405		dev->coredump.crash_data->memdump_buf_len = 0;
406	}
407
408	vfree(dev->coredump.crash_data);
409	dev->coredump.crash_data = NULL;
410}
411
412