MAINTAINERS: Remove Noralf Trønnes as driver maintainer
[drm/drm-misc.git] / drivers / net / wireless / mediatek / mt76 / mt7996 / coredump.c
blobccab0d7b9be474e16d8888a9d46ec194209aabf2
1 // SPDX-License-Identifier: ISC
2 /* Copyright (C) 2023 MediaTek Inc. */
4 #include <linux/devcoredump.h>
5 #include <linux/kernel.h>
6 #include <linux/types.h>
7 #include <linux/utsname.h>
8 #include "coredump.h"
10 static bool coredump_memdump;
11 module_param(coredump_memdump, bool, 0644);
12 MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory");
14 static const struct mt7996_mem_region mt7996_mem_regions[] = {
16 .start = 0x00800000,
17 .len = 0x0004ffff,
18 .name = "ULM0",
21 .start = 0x00900000,
22 .len = 0x00037fff,
23 .name = "ULM1",
26 .start = 0x02200000,
27 .len = 0x0003ffff,
28 .name = "ULM2",
31 .start = 0x00400000,
32 .len = 0x00067fff,
33 .name = "SRAM",
36 .start = 0xe0000000,
37 .len = 0x0015ffff,
38 .name = "CRAM0",
41 .start = 0xe0160000,
42 .len = 0x0011bfff,
43 .name = "CRAM1",
47 const struct mt7996_mem_region*
48 mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
50 switch (mt76_chip(&dev->mt76)) {
51 case 0x7990:
52 case 0x7991:
53 *num = ARRAY_SIZE(mt7996_mem_regions);
54 return &mt7996_mem_regions[0];
55 default:
56 return NULL;
60 static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
62 const struct mt7996_mem_region *mem_region;
63 size_t size = 0;
64 u32 num;
65 int i;
67 mem_region = mt7996_coredump_get_mem_layout(dev, &num);
68 if (!mem_region)
69 return 0;
71 for (i = 0; i < num; i++) {
72 size += mem_region->len;
73 mem_region++;
76 /* reserve space for the headers */
77 size += num * sizeof(struct mt7996_mem_hdr);
78 /* make sure it is aligned 4 bytes for debug message print out */
79 size = ALIGN(size, 4);
81 return size;
84 struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
86 struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
88 lockdep_assert_held(&dev->dump_mutex);
90 if (coredump_memdump &&
91 !mt76_poll_msec(dev, MT_FW_DUMP_STATE, 0x3, 0x2, 500))
92 return NULL;
94 guid_gen(&crash_data->guid);
95 ktime_get_real_ts64(&crash_data->timestamp);
97 return crash_data;
100 static void
101 mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
102 bool *exception)
104 u32 count;
106 count = mt76_rr(dev, MT_FW_ASSERT_CNT);
108 /* normal mode: driver can manually trigger assert for detail info */
109 if (!count)
110 strscpy(dump->fw_state, "normal", sizeof(dump->fw_state));
111 else
112 strscpy(dump->fw_state, "exception", sizeof(dump->fw_state));
114 *exception = !!count;
117 static void
118 mt7996_coredump_fw_stack(struct mt7996_dev *dev, struct mt7996_coredump *dump,
119 bool exception)
121 u32 oldest, i, idx;
123 strscpy(dump->pc_current, "program counter", sizeof(dump->pc_current));
125 /* 0: WM PC log output */
126 mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, 0);
127 /* choose 33th PC log buffer to read current PC index */
128 mt76_wr(dev, MT_CONN_DBG_CTL_PC_LOG_SEL, 0x3f);
130 /* read current PC */
131 dump->pc_stack[0] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
133 /* stop call stack record */
134 if (!exception) {
135 mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
136 mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
139 oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_PC_CTRL,
140 GENMASK(20, 16)) + 2;
141 for (i = 0; i < 16; i++) {
142 idx = ((oldest + 2 * i + 1) % 32);
143 dump->pc_stack[i + 1] =
144 mt76_rr(dev, MT_MCU_WM_EXCP_PC_LOG + idx * 4);
147 oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_LR_CTRL,
148 GENMASK(20, 16)) + 2;
149 for (i = 0; i < 16; i++) {
150 idx = ((oldest + 2 * i + 1) % 32);
151 dump->lr_stack[i] =
152 mt76_rr(dev, MT_MCU_WM_EXCP_LR_LOG + idx * 4);
155 /* start call stack record */
156 if (!exception) {
157 mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
158 mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
162 static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
164 struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
165 struct mt7996_coredump *dump;
166 struct mt7996_coredump_mem *dump_mem;
167 size_t len, sofar = 0, hdr_len = sizeof(*dump);
168 unsigned char *buf;
169 bool exception;
171 len = hdr_len;
173 if (coredump_memdump && crash_data->memdump_buf_len)
174 len += sizeof(*dump_mem) + crash_data->memdump_buf_len;
176 sofar += hdr_len;
178 /* this is going to get big when we start dumping memory and such,
179 * so go ahead and use vmalloc.
181 buf = vzalloc(len);
182 if (!buf)
183 return NULL;
185 mutex_lock(&dev->dump_mutex);
187 dump = (struct mt7996_coredump *)(buf);
188 dump->len = len;
190 /* plain text */
191 strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic));
192 strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel));
193 strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version,
194 sizeof(dump->fw_ver));
196 guid_copy(&dump->guid, &crash_data->guid);
197 dump->tv_sec = crash_data->timestamp.tv_sec;
198 dump->tv_nsec = crash_data->timestamp.tv_nsec;
199 dump->device_id = mt76_chip(&dev->mt76);
201 mt7996_coredump_fw_state(dev, dump, &exception);
202 mt7996_coredump_fw_stack(dev, dump, exception);
204 /* gather memory content */
205 dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
206 dump_mem->len = crash_data->memdump_buf_len;
207 if (coredump_memdump && crash_data->memdump_buf_len)
208 memcpy(dump_mem->data, crash_data->memdump_buf,
209 crash_data->memdump_buf_len);
211 mutex_unlock(&dev->dump_mutex);
213 return dump;
216 int mt7996_coredump_submit(struct mt7996_dev *dev)
218 struct mt7996_coredump *dump;
220 dump = mt7996_coredump_build(dev);
221 if (!dump) {
222 dev_warn(dev->mt76.dev, "no crash dump data found\n");
223 return -ENODATA;
226 dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL);
228 return 0;
231 int mt7996_coredump_register(struct mt7996_dev *dev)
233 struct mt7996_crash_data *crash_data;
235 crash_data = vzalloc(sizeof(*dev->coredump.crash_data));
236 if (!crash_data)
237 return -ENOMEM;
239 dev->coredump.crash_data = crash_data;
241 if (coredump_memdump) {
242 crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev);
243 if (!crash_data->memdump_buf_len)
244 /* no memory content */
245 return 0;
247 crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
248 if (!crash_data->memdump_buf) {
249 vfree(crash_data);
250 return -ENOMEM;
254 return 0;
257 void mt7996_coredump_unregister(struct mt7996_dev *dev)
259 if (dev->coredump.crash_data->memdump_buf) {
260 vfree(dev->coredump.crash_data->memdump_buf);
261 dev->coredump.crash_data->memdump_buf = NULL;
262 dev->coredump.crash_data->memdump_buf_len = 0;
265 vfree(dev->coredump.crash_data);
266 dev->coredump.crash_data = NULL;