1 /*
2  * Copyright (c) 2025 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /* @file
8  * @brief NRF Wi-Fi debug shell module
9  */
10 #include <stdlib.h>
11 #include <zephyr/shell/shell.h>
12 #include "host_rpu_umac_if.h"
13 #include "fmac_main.h"
14 
15 extern struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep;
16 struct nrf_wifi_ctx_zep *dbg_ctx = &rpu_drv_priv_zep.rpu_ctx_zep;
17 
18 
nrf_wifi_dbg_read_mem(const struct shell * sh,size_t argc,const char * argv[])19 static int nrf_wifi_dbg_read_mem(const struct shell *sh,
20 				 size_t argc,
21 				 const char *argv[])
22 {
23 	enum nrf_wifi_status status;
24 	struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx;
25 	char *ptr;
26 	unsigned long mem_type;
27 	unsigned long mem_offset;
28 	unsigned long num_words;
29 	unsigned int mem_val;
30 	unsigned int mem_start_addr;
31 	unsigned int mem_end_addr;
32 	unsigned int i;
33 
34 	fmac_dev_ctx = dbg_ctx->rpu_ctx;
35 	mem_type = strtoul(argv[1], &ptr, 10);
36 	mem_offset = strtoul(argv[2], &ptr, 10);
37 	num_words = strtoul(argv[3], &ptr, 10);
38 
39 	if (mem_type == 0) {
40 		mem_start_addr = RPU_ADDR_PKTRAM_START + (mem_offset * 4);
41 		mem_end_addr = RPU_ADDR_PKTRAM_END;
42 	} else if (mem_type == 1) {
43 		mem_start_addr = RPU_ADDR_GRAM_START + (mem_offset * 4);
44 		mem_end_addr = RPU_ADDR_GRAM_END;
45 	} else {
46 		shell_fprintf(sh,
47 			      SHELL_ERROR,
48 			      "Invalid memory type(%lu).\n",
49 			      mem_type);
50 		return -ENOEXEC;
51 	}
52 
53 	if ((mem_start_addr % 4) != 0) {
54 		shell_fprintf(sh,
55 			      SHELL_ERROR,
56 			      "Invalid memory word offset(%lu). Needs to be a multiple of 4\n",
57 			      mem_offset);
58 		return -ENOEXEC;
59 	}
60 
61 	if (mem_start_addr + (num_words * 4) - 1 > mem_end_addr) {
62 		shell_fprintf(sh,
63 			      SHELL_ERROR,
64 			      "Invalid number of words(%lu). Exceeds memory region\n",
65 			      num_words);
66 		return -ENOEXEC;
67 	}
68 
69 	for (i = 0; i < (num_words * 4); i += 4) {
70 		status = hal_rpu_mem_read(fmac_dev_ctx->hal_dev_ctx,
71 					  &mem_val,
72 					  mem_start_addr + i,
73 					  sizeof(mem_val));
74 
75 		if (status != NRF_WIFI_STATUS_SUCCESS) {
76 			shell_fprintf(sh,
77 				      SHELL_ERROR,
78 				      "Failed to read memory at 0x%x.\n",
79 				      mem_start_addr + i);
80 			return -ENOEXEC;
81 		}
82 
83 		if (i % 16 == 0) {
84 			shell_fprintf(sh,
85 				      SHELL_INFO,
86 				      "\n0x%x: ", mem_start_addr + i);
87 		}
88 
89 		shell_fprintf(sh,
90 			      SHELL_INFO,
91 			      "0x%08x ",
92 			      mem_val);
93 	}
94 
95 	shell_fprintf(sh,
96 		      SHELL_INFO,
97 		      "\n");
98 	return 0;
99 }
100 
101 
nrf_wifi_dbg_write_mem(const struct shell * sh,size_t argc,const char * argv[])102 static int nrf_wifi_dbg_write_mem(const struct shell *sh,
103 				  size_t argc,
104 				  const char *argv[])
105 {
106 	enum nrf_wifi_status status;
107 	struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx;
108 	char *ptr;
109 	unsigned long mem_type;
110 	unsigned long mem_offset;
111 	unsigned int val;
112 	unsigned int mem_start_addr;
113 	unsigned int mem_end_addr;
114 
115 	fmac_dev_ctx = dbg_ctx->rpu_ctx;
116 	mem_type = strtoul(argv[1], &ptr, 10);
117 	mem_offset = strtoul(argv[2], &ptr, 10);
118 	val = strtoul(argv[3], &ptr, 10);
119 
120 	if (mem_type == 0) {
121 		mem_start_addr = RPU_ADDR_PKTRAM_START + (mem_offset * 4);
122 		mem_end_addr = RPU_ADDR_PKTRAM_END;
123 	} else if (mem_type == 1) {
124 		mem_start_addr = RPU_ADDR_GRAM_START + (mem_offset * 4);
125 		mem_end_addr = RPU_ADDR_GRAM_END;
126 	} else {
127 		shell_fprintf(sh,
128 			      SHELL_ERROR,
129 			      "Invalid memory type(%lu).\n",
130 			      mem_type);
131 		return -ENOEXEC;
132 	}
133 
134 	if ((mem_start_addr % 4) != 0) {
135 		shell_fprintf(sh,
136 			      SHELL_ERROR,
137 			      "Invalid memory word offset(%lu). Needs to be a multiple of 4\n",
138 			      mem_offset);
139 		return -ENOEXEC;
140 	}
141 
142 	if (mem_start_addr + 3 > mem_end_addr) {
143 		shell_fprintf(sh,
144 			      SHELL_ERROR,
145 			      "Invalid memory word offset. Exceeds memory region\n");
146 		return -ENOEXEC;
147 	}
148 
149 	status = hal_rpu_mem_write(fmac_dev_ctx->hal_dev_ctx,
150 				   mem_start_addr,
151 				   &val,
152 				   sizeof(val));
153 
154 	if (status != NRF_WIFI_STATUS_SUCCESS) {
155 		shell_fprintf(sh,
156 			      SHELL_ERROR,
157 			      "Failed to write memory at 0x%x.\n",
158 			      mem_start_addr);
159 		return -ENOEXEC;
160 	}
161 
162 	return 0;
163 }
164 
165 
nrf_wifi_dbg_read_reg(const struct shell * sh,size_t argc,const char * argv[])166 static int nrf_wifi_dbg_read_reg(const struct shell *sh,
167 				 size_t argc,
168 				 const char *argv[])
169 {
170 	enum nrf_wifi_status status;
171 	struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx;
172 	char *ptr;
173 	unsigned long reg_type;
174 	unsigned long reg_offset;
175 	unsigned long num_regs;
176 	unsigned int reg_val;
177 	unsigned int reg_start_addr;
178 	unsigned int reg_end_addr;
179 	unsigned int i;
180 
181 	fmac_dev_ctx = dbg_ctx->rpu_ctx;
182 	reg_type = strtoul(argv[1], &ptr, 10);
183 	reg_offset = strtoul(argv[2], &ptr, 10);
184 	num_regs = strtoul(argv[3], &ptr, 10);
185 
186 	if (reg_type == 0) {
187 		reg_start_addr = RPU_ADDR_SBUS_START + (reg_offset * 4);
188 		reg_end_addr = RPU_ADDR_SBUS_END;
189 	} else if (reg_type == 1) {
190 		reg_start_addr = RPU_ADDR_PBUS_START + (reg_offset * 4);
191 		reg_end_addr = RPU_ADDR_PBUS_END;
192 	} else {
193 		shell_fprintf(sh,
194 			      SHELL_ERROR,
195 			      "Invalid register type(%lu).\n",
196 			      reg_type);
197 		return -ENOEXEC;
198 	}
199 
200 	if ((reg_start_addr % 4) != 0) {
201 		shell_fprintf(sh,
202 			      SHELL_ERROR,
203 			      "Invalid register offset(%lu). Needs to be a multiple of 4\n",
204 			      reg_offset);
205 		return -ENOEXEC;
206 	}
207 
208 	if (reg_start_addr + (num_regs * 4) - 1 > reg_end_addr) {
209 		shell_fprintf(sh,
210 			      SHELL_ERROR,
211 			      "Invalid number of registers(%lu). Exceeds bus region\n",
212 			      num_regs);
213 		return -ENOEXEC;
214 	}
215 
216 	for (i = 0; i < num_regs * 4; i += 4) {
217 		status = hal_rpu_reg_read(fmac_dev_ctx->hal_dev_ctx,
218 					  &reg_val,
219 					  reg_start_addr + i);
220 
221 		if (status != NRF_WIFI_STATUS_SUCCESS) {
222 			shell_fprintf(sh,
223 				      SHELL_ERROR,
224 				      "Failed to read register at 0x%x.\n",
225 				      reg_start_addr + i);
226 			return -ENOEXEC;
227 		}
228 
229 		shell_fprintf(sh,
230 			      SHELL_INFO,
231 			      "0x%x: 0x%08x\n",
232 			      reg_start_addr + i,
233 			      reg_val);
234 	}
235 
236 	shell_fprintf(sh,
237 		      SHELL_INFO,
238 		      "\n");
239 	return 0;
240 }
241 
242 
nrf_wifi_dbg_write_reg(const struct shell * sh,size_t argc,const char * argv[])243 static int nrf_wifi_dbg_write_reg(const struct shell *sh,
244 				  size_t argc,
245 				  const char *argv[])
246 {
247 	enum nrf_wifi_status status;
248 	struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx;
249 	char *ptr;
250 	unsigned long reg_type;
251 	unsigned long reg_offset;
252 	unsigned int val;
253 	unsigned int reg_start_addr;
254 	unsigned int reg_end_addr;
255 
256 	fmac_dev_ctx = dbg_ctx->rpu_ctx;
257 	reg_type = strtoul(argv[1], &ptr, 10);
258 	reg_offset = strtoul(argv[2], &ptr, 10);
259 	val = strtoul(argv[3], &ptr, 10);
260 
261 	if (reg_type == 0) {
262 		reg_start_addr = RPU_ADDR_SBUS_START + (reg_offset * 4);
263 		reg_end_addr = RPU_ADDR_SBUS_END;
264 	} else if (reg_type == 1) {
265 		reg_start_addr = RPU_ADDR_PBUS_START + (reg_offset * 4);
266 		reg_end_addr = RPU_ADDR_PBUS_END;
267 	} else {
268 		shell_fprintf(sh,
269 			      SHELL_ERROR,
270 			      "Invalid register type(%lu).\n",
271 			      reg_type);
272 		return -ENOEXEC;
273 	}
274 
275 	if ((reg_start_addr % 4) != 0) {
276 		shell_fprintf(sh,
277 			      SHELL_ERROR,
278 			      "Invalid register offset(%lu). Needs to be a multiple of 4\n",
279 			      reg_offset);
280 		return -ENOEXEC;
281 	}
282 
283 	if (reg_start_addr + 3 > reg_end_addr) {
284 		shell_fprintf(sh,
285 			      SHELL_ERROR,
286 			      "Invalid register offset. Exceeds bus region\n");
287 		return -ENOEXEC;
288 	}
289 
290 	status = hal_rpu_reg_write(fmac_dev_ctx->hal_dev_ctx,
291 				   reg_start_addr,
292 				   val);
293 
294 	if (status != NRF_WIFI_STATUS_SUCCESS) {
295 		shell_fprintf(sh,
296 			      SHELL_ERROR,
297 			      "Failed to write register at 0x%x.\n",
298 			      reg_start_addr);
299 		return -ENOEXEC;
300 	}
301 
302 	return 0;
303 }
304 
305 
306 
307 SHELL_STATIC_SUBCMD_SET_CREATE(
308 	nrf70_dbg,
309 	SHELL_CMD_ARG(read_mem,
310 		      NULL,
311 		      "<mem_type> <offset> <num_words>\n"
312 		      "where:\n"
313 		      "<mem_type> : One of the memory regions below\n"
314 		      "0 - PKTRAM\n"
315 		      "1 - GRAM\n"
316 		      "<offset> : Word offset in the memory region\n"
317 		      "<num_words> : Number of words to read\n",
318 		      nrf_wifi_dbg_read_mem,
319 		      4,
320 		      0),
321 	SHELL_CMD_ARG(write_mem,
322 		      NULL,
323 		      "<mem_type> <offset> <val>\n"
324 		      "where:\n"
325 		      "<mem_type> : One of the memory regions below\n"
326 		      "0 - PKTRAM\n"
327 		      "1 - GRAM\n"
328 		      "<offset> : Word offset in the memory region\n"
329 		      "<val> : Value to be written\n",
330 		      nrf_wifi_dbg_write_mem,
331 		      4,
332 		      0),
333 	SHELL_CMD_ARG(read_reg,
334 		      NULL,
335 		      "<reg_type> <offset> <num_regs>\n"
336 		      "where:\n"
337 		      "<reg_type> : One of the bus regions below\n"
338 		      "0 - SYSBUS\n"
339 		      "1 - PBUS\n"
340 		      "<offset> : Register offset\n"
341 		      "<num_words> : Number of registers to read\n",
342 		      nrf_wifi_dbg_read_reg,
343 		      4,
344 		      0),
345 	SHELL_CMD_ARG(write_reg,
346 		      NULL,
347 		      "<reg_type> <offset> <val>\n"
348 		      "where:\n"
349 		      "<reg_type> : One of the bus regions below\n"
350 		      "0 - SYSBUS\n"
351 		      "1 - PBUS\n"
352 		      "<offset> : Register offset\n"
353 		      "<val> : Value to be written\n",
354 		      nrf_wifi_dbg_write_reg,
355 		      4,
356 		      0),
357 	SHELL_SUBCMD_SET_END
358 );
359 
360 
361 SHELL_SUBCMD_ADD((nrf70), dbg, &nrf70_dbg,
362 		 "nRF70 advanced debug commands\n",
363 		 NULL,
364 		 0, 0);
365