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