1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 /**
8  * @brief File containing patch loader specific definitions for the
9  * HAL Layer of the Wi-Fi driver.
10  */
11 
12 #include "host_rpu_common_if.h"
13 #include "common/hal_fw_patch_loader.h"
14 #include "common/hal_mem.h"
15 #include "lmac_if_common.h"
16 #include "host_rpu_common_if.h"
17 
18 /* To reduce HEAP maximum usage */
19 #define MAX_PATCH_CHUNK_SIZE 8192
20 #ifndef ARRAY_SIZE
21 #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
22 #endif /* ARRAY_SIZE */
23 
24 const struct nrf70_fw_addr_info nrf70_fw_addr_info[] = {
25 	{ RPU_PROC_TYPE_MCU_LMAC, "LMAC bimg", RPU_MEM_LMAC_PATCH_BIMG },
26 	{ RPU_PROC_TYPE_MCU_LMAC, "LMAC bin", RPU_MEM_LMAC_PATCH_BIN },
27 	{ RPU_PROC_TYPE_MCU_UMAC, "UMAC bimg", RPU_MEM_UMAC_PATCH_BIMG },
28 	{ RPU_PROC_TYPE_MCU_UMAC, "UMAC bin", RPU_MEM_UMAC_PATCH_BIN },
29 };
30 
31 struct patch_contents {
32 	const char *id_str;
33 	const void *data;
34 	unsigned int size;
35 	unsigned int dest_addr;
36 };
37 
38 
39 static const struct rpu_mcu_boot_vectors RPU_MCU_BOOT_VECTORS[] = {
40 	/* MCU1 - LMAC */
41 	{
42 		{
43 			{RPU_REG_MIPS_MCU_BOOT_EXCP_INSTR_0, NRF_WIFI_LMAC_BOOT_EXCP_VECT_0},
44 			{RPU_REG_MIPS_MCU_BOOT_EXCP_INSTR_1, NRF_WIFI_LMAC_BOOT_EXCP_VECT_1},
45 			{RPU_REG_MIPS_MCU_BOOT_EXCP_INSTR_2, NRF_WIFI_LMAC_BOOT_EXCP_VECT_2},
46 			{RPU_REG_MIPS_MCU_BOOT_EXCP_INSTR_3, NRF_WIFI_LMAC_BOOT_EXCP_VECT_3},
47 		}
48 	},
49 	/* MCU2 - UMAC */
50 	{
51 		{
52 			{RPU_REG_MIPS_MCU2_BOOT_EXCP_INSTR_0, NRF_WIFI_UMAC_BOOT_EXCP_VECT_0},
53 			{RPU_REG_MIPS_MCU2_BOOT_EXCP_INSTR_1, NRF_WIFI_UMAC_BOOT_EXCP_VECT_1},
54 			{RPU_REG_MIPS_MCU2_BOOT_EXCP_INSTR_2, NRF_WIFI_UMAC_BOOT_EXCP_VECT_2},
55 			{RPU_REG_MIPS_MCU2_BOOT_EXCP_INSTR_3, NRF_WIFI_UMAC_BOOT_EXCP_VECT_3},
56 		}
57 	},
58 };
59 
60 
hal_fw_patch_chunk_load(struct nrf_wifi_hal_dev_ctx * hal_dev_ctx,enum RPU_PROC_TYPE rpu_proc,unsigned int dest_addr,const void * fw_chunk_data,unsigned int fw_chunk_size)61 enum nrf_wifi_status hal_fw_patch_chunk_load(struct nrf_wifi_hal_dev_ctx *hal_dev_ctx,
62 						enum RPU_PROC_TYPE rpu_proc,
63 						unsigned int dest_addr,
64 						const void *fw_chunk_data,
65 						unsigned int fw_chunk_size)
66 {
67 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
68 
69 	hal_dev_ctx->curr_proc = rpu_proc;
70 
71 	status = hal_rpu_mem_write(hal_dev_ctx,
72 				 dest_addr,
73 				 (void *)fw_chunk_data,
74 				 fw_chunk_size);
75 
76 	hal_dev_ctx->curr_proc = RPU_PROC_TYPE_MCU_LMAC;
77 
78 	return status;
79 }
80 
81 /* In order to save RAM, divide the patch in to chunks download */
hal_fw_patch_load(struct nrf_wifi_hal_dev_ctx * hal_dev_ctx,enum RPU_PROC_TYPE rpu_proc,const char * patch_id_str,unsigned int dest_addr,const void * fw_patch_data,unsigned int fw_patch_size)82 static enum nrf_wifi_status hal_fw_patch_load(struct nrf_wifi_hal_dev_ctx *hal_dev_ctx,
83 						enum RPU_PROC_TYPE rpu_proc,
84 						const char *patch_id_str,
85 						unsigned int dest_addr,
86 						const void *fw_patch_data,
87 						unsigned int fw_patch_size)
88 {
89 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
90 	int last_chunk_size = fw_patch_size % MAX_PATCH_CHUNK_SIZE;
91 	int num_chunks = fw_patch_size / MAX_PATCH_CHUNK_SIZE +
92 					(last_chunk_size ? 1 : 0);
93 	int chunk = 0;
94 
95 	for (chunk = 0; chunk < num_chunks; chunk++) {
96 		unsigned char *patch_data_ram;
97 		unsigned int patch_chunk_size =
98 			((chunk == num_chunks - 1) ? last_chunk_size : MAX_PATCH_CHUNK_SIZE);
99 		const void *src_patch_offset = (const char *)fw_patch_data +
100 			chunk * MAX_PATCH_CHUNK_SIZE;
101 		int dest_chunk_offset = dest_addr + chunk * MAX_PATCH_CHUNK_SIZE;
102 
103 		patch_data_ram = nrf_wifi_osal_mem_alloc(patch_chunk_size);
104 		if (!patch_data_ram) {
105 			nrf_wifi_osal_log_err("%s: Mem alloc failed for patch "
106 					      "%s-%s: chunk %d/%d, size: %d",
107 					      __func__,
108 					      rpu_proc_to_str(rpu_proc),
109 					      patch_id_str,
110 					      chunk + 1,
111 					      num_chunks,
112 					      patch_chunk_size);
113 			status = NRF_WIFI_STATUS_FAIL;
114 			goto out;
115 		}
116 
117 		nrf_wifi_osal_mem_cpy(patch_data_ram,
118 				      src_patch_offset,
119 				      patch_chunk_size);
120 
121 
122 		nrf_wifi_osal_log_dbg("%s: Copying patch %s-%s: chunk %d/%d, size: %d",
123 				      __func__,
124 				      rpu_proc_to_str(rpu_proc),
125 				      patch_id_str,
126 				      chunk + 1,
127 				      num_chunks,
128 				      patch_chunk_size);
129 
130 		status = hal_fw_patch_chunk_load(hal_dev_ctx,
131 						rpu_proc,
132 						dest_chunk_offset,
133 						patch_data_ram,
134 						patch_chunk_size);
135 		if (status != NRF_WIFI_STATUS_SUCCESS) {
136 			nrf_wifi_osal_log_err("%s: Patch copy %s-%s: chunk %d/%d, size: %d failed",
137 					      __func__,
138 					      rpu_proc_to_str(rpu_proc),
139 					      patch_id_str,
140 					      chunk + 1,
141 					      num_chunks,
142 					      patch_chunk_size);
143 			goto out;
144 		}
145 out:
146 		if (patch_data_ram)
147 			nrf_wifi_osal_mem_free(patch_data_ram);
148 		if (status != NRF_WIFI_STATUS_SUCCESS)
149 			break;
150 	}
151 
152 	return status;
153 }
154 
155 /*
156  * Copies the firmware patches to the RPU memory.
157  */
nrf_wifi_hal_fw_patch_load(struct nrf_wifi_hal_dev_ctx * hal_dev_ctx,enum RPU_PROC_TYPE rpu_proc,const void * fw_pri_patch_data,unsigned int fw_pri_patch_size,const void * fw_sec_patch_data,unsigned int fw_sec_patch_size)158 enum nrf_wifi_status nrf_wifi_hal_fw_patch_load(struct nrf_wifi_hal_dev_ctx *hal_dev_ctx,
159 						enum RPU_PROC_TYPE rpu_proc,
160 						const void *fw_pri_patch_data,
161 						unsigned int fw_pri_patch_size,
162 						const void *fw_sec_patch_data,
163 						unsigned int fw_sec_patch_size)
164 {
165 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
166 	unsigned int pri_dest_addr = 0;
167 	unsigned int sec_dest_addr = 0;
168 	int patch = 0;
169 
170 	if (!fw_pri_patch_data) {
171 		nrf_wifi_osal_log_err("%s: Primary patch missing for RPU (%d)",
172 				      __func__,
173 				      rpu_proc);
174 		status = NRF_WIFI_STATUS_FAIL;
175 		goto out;
176 	}
177 
178 	if (!fw_sec_patch_data) {
179 		nrf_wifi_osal_log_err("%s: Secondary patch missing for RPU (%d)",
180 				      __func__,
181 				      rpu_proc);
182 		status = NRF_WIFI_STATUS_FAIL;
183 		goto out;
184 	}
185 
186 	/* Set the HAL RPU context to the current required context */
187 	hal_dev_ctx->curr_proc = rpu_proc;
188 
189 	switch (rpu_proc) {
190 	case RPU_PROC_TYPE_MCU_LMAC:
191 		pri_dest_addr = RPU_MEM_LMAC_PATCH_BIMG;
192 		sec_dest_addr = RPU_MEM_LMAC_PATCH_BIN;
193 		break;
194 	case RPU_PROC_TYPE_MCU_UMAC:
195 		pri_dest_addr = RPU_MEM_UMAC_PATCH_BIMG;
196 		sec_dest_addr = RPU_MEM_UMAC_PATCH_BIN;
197 		break;
198 	default:
199 		nrf_wifi_osal_log_err("%s: Invalid RPU processor type[%d]",
200 				      __func__,
201 				      rpu_proc);
202 
203 		goto out;
204 	}
205 
206 	/* This extra block is needed to avoid compilation error for inline
207 	 * declaration but still keep using const data.
208 	 */
209 	{
210 		const struct patch_contents patches[] = {
211 			{ "bimg", fw_pri_patch_data, fw_pri_patch_size, pri_dest_addr },
212 			{ "bin", fw_sec_patch_data, fw_sec_patch_size, sec_dest_addr },
213 		};
214 
215 		for (patch = 0; patch < ARRAY_SIZE(patches); patch++) {
216 			status = hal_fw_patch_load(hal_dev_ctx,
217 						rpu_proc,
218 						patches[patch].id_str,
219 						patches[patch].dest_addr,
220 						patches[patch].data,
221 						patches[patch].size);
222 			if (status != NRF_WIFI_STATUS_SUCCESS)
223 				goto out;
224 		}
225 	}
226 out:
227 	/* Reset the HAL RPU context to the LMAC context */
228 	hal_dev_ctx->curr_proc = RPU_PROC_TYPE_MCU_LMAC;
229 
230 	return status;
231 }
232 
233 
nrf_wifi_hal_fw_patch_boot(struct nrf_wifi_hal_dev_ctx * hal_dev_ctx,enum RPU_PROC_TYPE rpu_proc,bool is_patch_present)234 enum nrf_wifi_status nrf_wifi_hal_fw_patch_boot(struct nrf_wifi_hal_dev_ctx *hal_dev_ctx,
235 						enum RPU_PROC_TYPE rpu_proc,
236 						bool is_patch_present)
237 {
238 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
239 	unsigned int boot_sig_addr = 0;
240 	unsigned int boot_sig_val = 0;
241 	unsigned int boot_vector_id;
242 	unsigned int sleepctrl_addr = 0;
243 	unsigned int sleepctrl_val = 0;
244 	unsigned int run_addr = 0;
245 	const struct rpu_mcu_boot_vectors *boot_vectors = &RPU_MCU_BOOT_VECTORS[rpu_proc];
246 
247 	if (rpu_proc == RPU_PROC_TYPE_MCU_LMAC) {
248 		boot_sig_addr = RPU_MEM_LMAC_BOOT_SIG;
249 		run_addr = RPU_REG_MIPS_MCU_CONTROL;
250 		if (is_patch_present) {
251 			sleepctrl_addr = RPU_REG_UCC_SLEEP_CTRL_DATA_0;
252 			sleepctrl_val = NRF_WIFI_LMAC_ROM_PATCH_OFFSET;
253 		}
254 	} else if (rpu_proc == RPU_PROC_TYPE_MCU_UMAC) {
255 		boot_sig_addr = RPU_MEM_UMAC_BOOT_SIG;
256 		run_addr = RPU_REG_MIPS_MCU2_CONTROL;
257 		if (is_patch_present) {
258 			sleepctrl_addr = RPU_REG_UCC_SLEEP_CTRL_DATA_1;
259 			sleepctrl_val = NRF_WIFI_UMAC_ROM_PATCH_OFFSET;
260 		}
261 	} else {
262 		nrf_wifi_osal_log_err("%s: Invalid RPU processor type %d",
263 				      __func__,
264 				      rpu_proc);
265 		goto out;
266 	}
267 
268 	/* Set the HAL RPU context to the current required context */
269 	hal_dev_ctx->curr_proc = rpu_proc;
270 
271 	/* Clear the firmware pass signature location */
272 	status = hal_rpu_mem_write(hal_dev_ctx,
273 				   boot_sig_addr,
274 				   &boot_sig_val,
275 				   sizeof(boot_sig_val));
276 
277 	if (status != NRF_WIFI_STATUS_SUCCESS) {
278 		nrf_wifi_osal_log_err("%s: Clearing of FW pass signature failed for RPU(%d)",
279 				      __func__,
280 				      rpu_proc);
281 
282 		goto out;
283 	}
284 
285 	if (is_patch_present) {
286 		/* Write to sleep control register */
287 		status = hal_rpu_reg_write(hal_dev_ctx,
288 					   sleepctrl_addr,
289 					   sleepctrl_val);
290 		if (status != NRF_WIFI_STATUS_SUCCESS) {
291 			nrf_wifi_osal_log_err("%s: Sleep control reg write failed for RPU(%d)\n",
292 					      __func__,
293 					      rpu_proc);
294 
295 			goto out;
296 		}
297 	}
298 
299 	for (boot_vector_id = 0; boot_vector_id < ARRAY_SIZE(boot_vectors->vectors); boot_vector_id++) {
300 		const struct rpu_mcu_boot_vector *boot_vector = &boot_vectors->vectors[boot_vector_id];
301 
302 		/* Write the boot vector to the RPU memory */
303 		status = hal_rpu_reg_write(hal_dev_ctx,
304 					   boot_vector->addr,
305 					   boot_vector->val);
306 		if (status != NRF_WIFI_STATUS_SUCCESS) {
307 			nrf_wifi_osal_log_err("%s: Writing boot vector failed for RPU(%d)\n",
308 					      __func__,
309 					      rpu_proc);
310 
311 			goto out;
312 		}
313 	}
314 
315 	/* Perform pulsed soft reset of MIPS - this should now run */
316 	status = hal_rpu_reg_write(hal_dev_ctx,
317 				   run_addr,
318 				   0x1);
319 
320 	if (status != NRF_WIFI_STATUS_SUCCESS) {
321 		nrf_wifi_osal_log_err("%s: RPU processor(%d) run failed",
322 				      __func__,
323 				      rpu_proc);
324 
325 		goto out;
326 	}
327 out:
328 	/* Reset the HAL RPU context to the LMAC context */
329 	hal_dev_ctx->curr_proc = RPU_PROC_TYPE_MCU_LMAC;
330 
331 	return status;
332 
333 }
334