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