1 /** @file fwdnld_sdio.c
2 *
3 * @brief This file provides interface abstraction APIs for SDIO
4 *
5 * Copyright 2023-2024 NXP
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 *
9 */
10 #include <string.h>
11 #include <osa.h>
12 #include "fwdnld_intf_abs.h"
13 #include "fwdnld_sdio.h"
14 #include "mlan_sdio_defs.h"
15 #include <mlan_sdio_api.h>
16 #include "sdio.h"
17
18 fwdnld_intf_t sdio_intf_g;
19 fwdnld_sdio_intf_specific sdio_intf_specific_g;
20
21 /*
22 * FW dnld blocksize set 0x110 to 0 and 0x111 to 0x01 => 0x100 => 256
23 * Note this only applies to the blockmode we use 256 bytes
24 * as block because MLAN_SDIO_BLOCK_SIZE = 256
25 */
wlan_set_fw_dnld_size(void)26 static fwdnld_intf_ret_t wlan_set_fw_dnld_size(void)
27 {
28
29 uint32_t resp;
30
31 bool rv = sdio_drv_creg_write(FN1_BLOCK_SIZE_0, 0, 0, &resp);
32 if (rv == false)
33 {
34 return FWDNLD_INTF_FAIL;
35 }
36
37 rv = sdio_drv_creg_write(FN1_BLOCK_SIZE_1, 0, 1, &resp);
38 if (rv == false)
39 {
40 return FWDNLD_INTF_FAIL;
41 }
42
43 return FWDNLD_INTF_SUCCESS;
44 }
45
wlan_card_fw_status(t_u16 * dat)46 static void wlan_card_fw_status(t_u16 *dat)
47 {
48 uint32_t resp = 0;
49
50 (void)sdio_drv_creg_read(CARD_FW_STATUS0_REG, 1, &resp);
51 *dat = (t_u16)(resp & 0xffU);
52 (void)sdio_drv_creg_read(CARD_FW_STATUS1_REG, 1, &resp);
53 *dat |= (t_u16)((resp & 0xffU) << 8);
54 }
55
wlan_sdio_check_fw_status(t_u32 card_poll)56 static bool wlan_sdio_check_fw_status(t_u32 card_poll)
57 {
58 t_u16 dat = 0U;
59 t_u32 i = 0U;
60
61 for (i = 0; i < card_poll; i++)
62 {
63 (void)wlan_card_fw_status(&dat);
64 if (dat == FIRMWARE_READY)
65 {
66 sdio_io_d("Firmware Ready");
67 return true;
68 }
69 OSA_TimeDelay(5U);
70 }
71 return false;
72 }
73
74 #if CONFIG_WIFI_IND_DNLD
75
76 /** @brief This function disables the host interrupts mask.
77 *
78 * @param pmadapter A pointer to mlan_adapter structure
79 * @param mask the interrupt mask
80 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
81 */
wlan_sdio_disable_host_int_mask()82 static int32_t wlan_sdio_disable_host_int_mask()
83 {
84 uint32_t host_int_mask = 0;
85 uint32_t resp;
86
87 (void)sdio_drv_creg_read(HOST_INT_MASK_REG, 1, &host_int_mask);
88
89 /* Update with the mask and write back to the register */
90 host_int_mask &= ~HIM_DISABLE;
91
92 (void)sdio_drv_creg_write(HOST_INT_MASK_REG, 1, host_int_mask, &resp);
93
94 return FWDNLD_INTF_SUCCESS;
95 }
96
97 /**
98 * @brief This function probes the driver
99 *
100 * @param pmadapter A pointer to mlan_adapter structure
101 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
102 */
wlan_sdio_probe()103 static int32_t wlan_sdio_probe()
104 {
105 int32_t ret = FWDNLD_INTF_SUCCESS;
106 uint32_t sdio_ireg = 0;
107 uint32_t host_int_mask = 0;
108 uint32_t resp;
109
110 /*
111 * Read the HOST_INT_STATUS_REG for ACK the first interrupt got
112 * from the bootloader. If we don't do this we get a interrupt
113 * as soon as we register the irq.
114 */
115 (void)sdio_drv_creg_read(HOST_INT_STATUS_REG, 1, &sdio_ireg);
116
117 (void)sdio_drv_creg_read(HOST_INT_MASK_REG, 1, &host_int_mask);
118
119 /* Update with the mask and write back to the register */
120 host_int_mask &= ~HIM_DISABLE;
121
122 (void)sdio_drv_creg_write(HOST_INT_MASK_REG, 1, host_int_mask, &resp);
123
124 /* Get SDIO ioport */
125 ret = sdio_ioport_init();
126
127 return ret;
128 }
129
wlan_reset_fw()130 int32_t wlan_reset_fw()
131 {
132 t_u32 tries = 0;
133 int32_t ret = FWDNLD_INTF_SUCCESS;
134 bool rv;
135 uint32_t resp;
136
137 // wlan_pm_sdio_wakeup_card(pmadapter, MFALSE);
138
139 rv = sdio_drv_creg_write(HOST_TO_CARD_EVENT_REG, 1, HOST_POWER_UP, &resp);
140 if (rv == false)
141 {
142 return FWDNLD_INTF_FAIL;
143 }
144
145 /** wait SOC fully wake up */
146 for (tries = 0; tries < MAX_POLL_TRIES; ++tries)
147 {
148 rv = sdio_drv_creg_write(CARD_FW_RESET_REG, 1, 0xba, &resp);
149 if (rv == true)
150 {
151 (void)sdio_drv_creg_read(CARD_FW_RESET_REG, 1, &resp);
152
153 if (resp == 0xba)
154 {
155 sdio_io_d("FW wake up");
156 break;
157 }
158 }
159 OSA_TimeDelay(5U);
160 }
161 /* Write register to notify FW */
162 rv = sdio_drv_creg_write(CARD_FW_RESET_REG, 1, CARD_FW_RESET_VAL, &resp);
163 if (rv == false)
164 {
165 sdio_io_d("Failed to write register.");
166 ret = FWDNLD_INTF_FAIL;
167 goto done;
168 }
169 #if defined(SD8978) || defined(SD8987) || defined(SD9177)
170 (void)sdio_drv_creg_read(HOST_TO_CARD_EVENT_REG, 1, &resp);
171
172 rv = sdio_drv_creg_write(HOST_TO_CARD_EVENT_REG, 1, resp | HOST_POWER_UP, &resp);
173 if (rv == false)
174 {
175 sdio_io_e("Failed to write register.");
176 ret = FWDNLD_INTF_FAIL;
177 goto done;
178 }
179 #endif
180 /* Poll register around 100 ms */
181 for (tries = 0; tries < MAX_POLL_TRIES; ++tries)
182 {
183 (void)sdio_drv_creg_read(CARD_FW_RESET_REG, 1, &resp);
184 if (resp == 0)
185 {
186 /* FW is ready */
187 sdio_io_d("FW is ready");
188 break;
189 }
190 OSA_TimeDelay(5U);
191 }
192
193 if (resp)
194 {
195 sdio_io_e("Failed to poll FW reset register %X=0x%x", CARD_FW_RESET_REG, resp);
196 ret = FWDNLD_INTF_FAIL;
197 goto done;
198 }
199 sdio_io_d("FW Reset success");
200 ret = wlan_sdio_probe();
201 done:
202 return ret;
203 }
204 #endif
205
sdio_prep_for_fwdnld(fwdnld_intf_t * intf,void * settings)206 static fwdnld_intf_ret_t sdio_prep_for_fwdnld(fwdnld_intf_t *intf, void *settings)
207 {
208 /* set fw download block size */
209 return wlan_set_fw_dnld_size();
210 }
211
sdio_post_fwdnld_check_conn_ready(fwdnld_intf_t * intf,void * settings)212 static fwdnld_intf_ret_t sdio_post_fwdnld_check_conn_ready(fwdnld_intf_t *intf, void *settings)
213 {
214 if (wlan_sdio_check_fw_status(1000) != true)
215 {
216 sdio_io_e("SDIO - FW Ready Registers not set");
217 return FWDNLD_INTF_FAIL;
218 }
219 else
220 {
221 sdio_io_d("WLAN FW download Successful");
222 return FWDNLD_INTF_SUCCESS;
223 }
224 }
225
226 #if (CONFIG_WIFI_IND_DNLD)
sdio_fwdnld_check_reload(fwdnld_intf_t * intf,uint8_t fw_reload)227 static fwdnld_intf_ret_t sdio_fwdnld_check_reload(fwdnld_intf_t *intf, uint8_t fw_reload)
228 {
229 t_u32 poll_num = 10;
230 int32_t ret;
231
232 /* Check if firmware is already running */
233 ret = wlan_sdio_check_fw_status(poll_num);
234 if (ret == true)
235 {
236 if (fw_reload == FW_RELOAD_SDIO_INBAND_RESET)
237 {
238 sdio_io_d("Try reset fw in mlan");
239 ret = wlan_reset_fw();
240 if (ret == FWDNLD_INTF_FAIL)
241 {
242 sdio_io_e("FW reset failure!");
243 return FWDNLD_INTF_FAIL;
244 }
245 }
246 else
247 {
248 PRINTF("WLAN FW already running! Skip FW download\r\n");
249 PRINTF("FW download skipped because fly wire is not connected from MCU to Wi-Fi SoC\r\n");
250 return FWDNLD_INTF_SKIP;
251 }
252 }
253 else if (fw_reload == FW_RELOAD_NO_EMULATION)
254 {
255 ret = wlan_sdio_disable_host_int_mask();
256 }
257
258 return FWDNLD_INTF_SUCCESS;
259 }
260 #endif
261
sdio_interface_send(fwdnld_intf_t * intf,const uint8_t * buffer,uint32_t transfer_len,uint32_t * len)262 static fwdnld_intf_ret_t sdio_interface_send(fwdnld_intf_t *intf,
263 const uint8_t *buffer,
264 uint32_t transfer_len,
265 uint32_t *len)
266 {
267 uint32_t tx_blocks = 0, txlen = 0, buflen = 0, offset = 0;
268 uint32_t outbuf_len;
269 uint8_t *loutbuf = NULL;
270 uint32_t resp;
271 uint32_t tries = 0, ioport;
272 fwdnld_intf_ret_t ret = FWDNLD_INTF_SUCCESS;
273
274 loutbuf = GET_INTF_OUTBUF(intf);
275 outbuf_len = GET_INTF_OUTBUFLEN(intf);
276 (void)memset(loutbuf, 0, outbuf_len);
277 *len = 0;
278
279 do
280 {
281 /* Read CARD_STATUS_REG (0X30) FN =1 */
282 for (tries = 0; tries < MAX_POLL_TRIES; tries++)
283 {
284 if (wlan_card_status(DN_LD_CARD_RDY | CARD_IO_READY) == true)
285 {
286 *len = wlan_card_read_f1_base_regs();
287 }
288 else
289 {
290 sdio_io_e("Error in wlan_card_status()");
291 break;
292 }
293
294 // (void)PRINTF("len %d =>\r\n", *len);
295 if (*len != 0U)
296 {
297 break;
298 }
299 else if (offset > 0)
300 {
301 return FWDNLD_INTF_SUCCESS;
302 }
303 else
304 { /* Do Nothing */
305 }
306 }
307
308 if (*len == 0U)
309 {
310 if (offset > 0)
311 {
312 return FWDNLD_INTF_SUCCESS;
313 }
314 sdio_io_e("Card timeout %s:%d", __func__, __LINE__);
315 return FWDNLD_INTF_FAIL;
316 }
317 else if (*len > outbuf_len)
318 {
319 sdio_io_e("FW Download Failure. Invalid len");
320 return FWDNLD_INTF_FAIL;
321 }
322 else
323 { /* Do Nothing */
324 }
325
326 txlen = *len;
327
328 /* Set blocksize to transfer - checking for last block */
329 if (transfer_len < txlen)
330 {
331 txlen = transfer_len;
332 *len = txlen;
333 }
334
335 ioport = GET_INTF_SDIO_IOPORT(intf);
336 (void)memcpy((void *)loutbuf, (const void *)(buffer + offset), txlen);
337 calculate_sdio_write_params(txlen, (unsigned int *)&tx_blocks, (unsigned int *)&buflen);
338 (void)sdio_drv_write(ioport, 1, tx_blocks, buflen, (uint8_t *)loutbuf, &resp);
339
340 if (*len <= transfer_len)
341 {
342 transfer_len -= *len;
343 offset += *len;
344 }
345 else
346 {
347 ret = FWDNLD_INTF_FAIL;
348 break;
349 }
350 *len = 0;
351
352 } while (transfer_len > 0);
353
354 return ret;
355 }
356
sdio_init_interface(void * settings)357 fwdnld_intf_t *sdio_init_interface(void *settings)
358 {
359 int32_t ret;
360 ret = sdio_init();
361 if (ret != 0)
362 {
363 return NULL;
364 }
365 sdio_intf_g.intf_s.fwdnld_intf_send = sdio_interface_send;
366 sdio_intf_g.intf_s.fwdnld_intf_prepare = sdio_prep_for_fwdnld;
367 sdio_intf_g.intf_s.fwdnld_intf_check_ready = sdio_post_fwdnld_check_conn_ready;
368 #if (CONFIG_WIFI_IND_DNLD)
369 sdio_intf_g.intf_s.fwdnld_intf_check_reload = sdio_fwdnld_check_reload;
370 #endif
371 sdio_intf_g.intf_s.outbuf = wifi_get_sdio_outbuf(&sdio_intf_g.intf_s.outbuf_len);
372 sdio_intf_g.intf_s.intf_specific = &sdio_intf_specific_g;
373
374 ret = sdio_ioport_init();
375 if (ret != 0)
376 {
377 return NULL;
378 }
379 sdio_intf_specific_g.ioport = wifi_get_sdio_ioport();
380 return &sdio_intf_g;
381 }
382