1 /*
2  * Copyright 2021 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_sdioslv_sdu.h"
9 
10 /* Component ID definition, used by tools. */
11 #ifndef FSL_COMPONENT_ID
12 #define FSL_COMPONENT_ID "platform.drivers.sdu"
13 #endif
14 
15 /*******************************************************************************
16  * Definitions
17  ******************************************************************************/
18 
19 /*******************************************************************************
20  * Prototypes
21  ******************************************************************************/
22 static inline status_t SDIOSLV_GetScratchRegisterAddr(sdioslv_func_t fun_num,
23                                                       sdioslv_scratch_group_t group,
24                                                       sdioslv_scratch_offset_t offset,
25                                                       uint32_t *reg_addr);
26 
27 /*******************************************************************************
28  * Variables
29  ******************************************************************************/
30 
31 /*******************************************************************************
32  * Public Functions
33  ******************************************************************************/
34 
35 /*!
36  * @brief Signals the following 4 events to the host
37  *
38  * * SDU_FN_CARD_C2H_INTEVENT0_CIS_CARD_RDY_MASK (Firmware sets this bit after CIS table is initialized)
39  * * SDU_FN_CARD_C2H_INTEVENT0_IO_READY_MASK (Firmware)
40  * * SDU_FN_CARD_C2H_INTEVENT0_Q0_DNLD_CARD_RDY_MASK (Firmware sets this bit when ready to receive data.)
41  * * SDU_FN_CARD_C2H_INTEVENT0_Q0_UPLD_CARD_RDY_MASK (Firmware sets this bit when ready to transmit data.)
42  * * SDU_FN_CARD_C2H_INTEVENT0_CMD_PORT_DNLD_RDY_MASK
43  * * SDU_FN_CARD_C2H_INTEVENT0_CMD_PORT_UPLD_RDY_MASK
44  *
45  * @param base SDIO base pointer
46  * @return mask of events to signal to host
47  */
SDIOSLV_SetIntEvent(SDU_FN_CARD_Type * base,uint8_t mask)48 static inline void SDIOSLV_SetIntEvent(SDU_FN_CARD_Type *base, uint8_t mask)
49 {
50      uint8_t temp = (uint8_t)(base->C2H_INTEVENT0 & ~(SDU_FN_CARD_C2H_INTEVENT0_IO_READY_MASK
51                                            | SDU_FN_CARD_C2H_INTEVENT0_CIS_CARD_RDY_MASK
52                                            | SDU_FN_CARD_C2H_INTEVENT0_Q0_UPLD_CARD_RDY_MASK
53                                            | SDU_FN_CARD_C2H_INTEVENT0_Q0_DNLD_CARD_RDY_MASK
54                                            | SDU_FN_CARD_C2H_INTEVENT0_CMD_PORT_DNLD_RDY_MASK
55                                            | SDU_FN_CARD_C2H_INTEVENT0_CMD_PORT_UPLD_RDY_MASK));
56 
57     // INTEVENT0 is the poll register of the driver
58     base->C2H_INTEVENT0 = temp | mask;
59 }
60 
61 /*!
62  * @name Initialization and deinitialization
63  * @{
64  */
65 
66 /*!
67  * @brief SDIOSLV Init0.
68  *
69  * Call this API to Init SDIOSLV phase0.
70  *
71  * @param void None.
72  * @retval void None.
73  */
SDIOSLV_Init0(void)74 void SDIOSLV_Init0(void)
75 {
76     CLKCTL0->PSCCTL0_SET  = CLKCTL0_PSCCTL0_SET_SDIO(1);
77     RSTCTL0->PRSTCTL0_CLR = RSTCTL0_PRSTCTL0_CLR_SDIO(1);
78     CLKCTL0->PSCCTL1_SET  = CLKCTL0_PSCCTL1_SET_SDIO_SLV(1);
79     RSTCTL0->PRSTCTL1_CLR = RSTCTL0_PRSTCTL1_CLR_SDIO_SLV(1);
80 
81     set_iomux(20, 15);
82 
83     // via Abhijit
84     rmw2(&MCI_IO_MUX->FSEL, MCI_IO_MUX_FSEL_SEL_SDIO_MASK, MCI_IO_MUX_FSEL_SEL_SDIO_SHIFT, 1U);
85 
86     // 0 means we don't use standard defns (i.e. wireless). If you use standard defs
87     // then you need to comply totally with the standard (Microsoft et al). Left at
88     // 0, our driver performs the function.
89     rmwb(sdu_fbr_fnN_fn_code(1U), sdu_fbr_fnN_fn_code_code_HI, sdu_fbr_fnN_fn_code_code_LO, 0U);
90 
91     rmwb(sdu_fbr_fnN_fn_code(2U), sdu_fbr_fnN_fn_code_code_HI, sdu_fbr_fnN_fn_code_code_LO, 0U);
92 
93     rmwb(sdu_fbr_fnN_fn_code(3U), sdu_fbr_fnN_fn_code_code_HI, sdu_fbr_fnN_fn_code_code_LO, 0U);
94 }
95 
96 /*!
97  * @brief SDIOSLV Init1.
98  *
99  * Call this API to Init SDIOSLV phase1.
100  *
101  * @param base FN FSR pointer.
102  * @param config Configure for SDIO Slave.
103  * @retval #kStatus_Success command is ready to be sent to host driver.
104  * @retval #kStatus_InvalidArgument Invalid argument.
105  */
SDIOSLV_Init1(SDU_FN_CARD_Type * base,sdio_slave_config_t * config)106 status_t SDIOSLV_Init1(SDU_FN_CARD_Type *base, sdio_slave_config_t *config)
107 {
108     uint8_t reg;
109 
110     if ((config->cmd_tx_format > 2U) || (config->cmd_rd_format > 1U))
111     {
112         return (status_t)kStatus_Fail;
113     }
114 
115     if ((config->data_tx_format > 2U) || (config->data_rd_format > 2U))
116     {
117         return (status_t)kStatus_Fail;
118     }
119 
120     if (config->cis_table_callback == NULL)
121     {
122         return (status_t)kStatus_Fail;
123     }
124 
125     // Enable CMD5 R4 FN bypass and configure with only 1 FN
126     // Otherwise FN number would only be set when bootstrap is SDIO_BOOT_MODE
127     reg = SDU_FN0_CARD->CARD_CTRL5;
128     reg &= ~(uint8_t)(SDU_FN0_CARD_CARD_CTRL5_CMD5_R4_FN_BYPASS_EN_MASK | SDU_FN0_CARD_CARD_CTRL5_CMD5_R4_FN_BYPASS_VAL_MASK);
129     reg |= SDU_FN0_CARD_CARD_CTRL5_CMD5_R4_FN_BYPASS_VAL(1U);
130     reg |= (uint8_t)(1U << SDU_FN0_CARD_CARD_CTRL5_CMD5_R4_FN_BYPASS_EN_SHIFT);
131     SDU_FN0_CARD->CARD_CTRL5 = reg;
132 
133     REG32(&base->PKT_RD_BITMAP0) = 0U;
134     REG32(&base->PKT_WR_BITMAP0) = 0U;
135     base->FN_CARD_INTMASK = (uint8_t)config->cpu_num;
136 
137     // Enable Interrupts
138     base->CARD_INTMASK0 = SDU_FN_CARD_CARD_INTSTATUS0_Q0_DNLD_CARD_INT_MASK
139                         | SDU_FN_CARD_CARD_INTSTATUS0_Q0_UPLD_CARD_INT_MASK
140                         | SDU_FN_CARD_CARD_INTSTATUS0_ABORT_CARD_INT_MASK
141                         | SDU_FN_CARD_CARD_INTSTATUS0_HOST_PWR_UP_INT_MASK;
142     base->CARD_INTMASK1 = SDU_FN_CARD_CARD_INTSTATUS1_CMD_PORT_UPLD_CARD_INT_MASK
143                         | SDU_FN_CARD_CARD_INTSTATUS1_CMD_PORT_DNLD_CARD_INT_MASK;
144 
145     // Cargo cult: Toggle INTSTATUS0_Q0_DNLD_CARD_INT_MASK, taken from SDK driver
146     REG8(&base->CARD_INTSTATUS0) = SDU_FN_CARD_CARD_INTSTATUS0_Q0_DNLD_CARD_INT_MASK;
147     REG8(&base->CARD_INTSTATUS0) = 0U;
148 
149     // Default setting ISR bit clear after read
150     base->CARD_INTRSR0 = SDU_FN_CARD_CARD_INTSTATUS0_Q0_DNLD_CARD_INT_MASK
151                        | SDU_FN_CARD_CARD_INTSTATUS0_Q0_UPLD_CARD_INT_MASK;
152     base->CARD_INTRSR1 = SDU_FN_CARD_CARD_INTSTATUS1_CMD_PORT_DNLD_CARD_INT_MASK
153                        | SDU_FN_CARD_CARD_INTSTATUS1_CMD_PORT_UPLD_CARD_INT_MASK;
154 
155     // Settings for 6.2.3 Command Port Mode
156     base->CMD_PORT_CONFIG_0 = (config->cmd_tx_format << SDU_FN_CARD_CMD_PORT_CONFIG_0_CMD_PORT_TX_LEN_FORMAT_SHIFT)
157                             | (config->cmd_rd_format << SDU_FN_CARD_CMD_PORT_CONFIG_0_CMD_PORT_RD_LEN_EN_SHIFT);
158 
159     base->CARD_CONFIG2_0 = SDU_FN_CARD_CARD_CONFIG2_0_FORCE_ASYNC_4BIT_INT_EN_MASK;
160 
161     // Settings for 6.2.2 Normal Mode
162     base->CARD_CONFIG2_1 = SDU_FN_CARD_CARD_CONFIG2_1_CMD53_NEW_MODE_MASK
163                          | (config->data_tx_format << SDU_FN_CARD_CARD_CONFIG2_1_CMD53_TX_LEN_FORMAT_SHIFT)
164                          | (config->data_rd_format << SDU_FN_CARD_CARD_CONFIG2_1_CMD53_RD_LEN_FORMAT_SHIFT);
165 
166     // Callback code to populate CIS table
167     config->cis_table_callback(SDU_FN0_CARD_BASE);
168 
169     // 0 means we don't use standard defns (i.e. wireless). If you use standard defs
170     // then you need to comply totally with the standard (Microsoft et al). Left at
171     // 0, our driver performs the function.
172     SDU_FBR_CARD->FN_CODE &= ~(uint8_t)(SDU_FBR_CARD_FN_CODE_CODE_MASK);
173     SDU_FBR_CARD->FN_CODE |= SDU_FBR_CARD_FN_CODE_CODE(0x0U);
174 
175     // Indicate to host IO_READY and that the CIS table is initialized
176 
177     SDIOSLV_SetIntEvent(base, SDU_FN_CARD_C2H_INTEVENT0_IO_READY_MASK
178                             | SDU_FN_CARD_C2H_INTEVENT0_CIS_CARD_RDY_MASK);
179 
180     // If this is set to 1, IO_READY and CARD_READY status will be 0.
181     SDU_FN0_CARD->DEV_SLEEP = SDU_FN0_CARD_DEV_SLEEP_DEV_SLEEP(0U);
182 
183     return (status_t)kStatus_Success;
184 }
185 
186 /*!
187  * @brief SDIOSLV send command.
188  *
189  * Call this API to send command to host driver.
190  * The callback is always invoked from theinterrupt context.
191  *
192  * @param regmap FN FSR pointer.
193  * @param data_addr Data Address.
194  * @param data_len Data Length.
195  * @retval #kStatus_Success command is ready to be sent to host driver.
196  * @retval #kStatus_InvalidArgument Invalid argument.
197  */
SDIOSLV_SendCmdNonBlocking(sdioslv_sdu_regmap_t * regmap,uint8_t * data_addr,uint16_t data_len)198 status_t SDIOSLV_SendCmdNonBlocking(sdioslv_sdu_regmap_t *regmap, uint8_t *data_addr, uint16_t data_len)
199 {
200     sdioslv_sdu_regmap_t *sdu_fsr = NULL;
201 
202     if ((regmap == NULL) || (data_addr == NULL) || (data_len == 0U))
203     {
204         return (status_t)kStatus_InvalidArgument;
205     }
206 
207     sdu_fsr                    = regmap;
208     sdu_fsr->CmdPortSqReadBase = (uint32_t)data_addr;
209     sdu_fsr->CmdPortRdLen      = data_len;
210     sdu_fsr->CardToHostEvent   = (uint16_t)SDIO_CCR_CS_CmdUpLdRdy;
211     return (status_t)kStatus_Success;
212 }
213 
214 /*!
215  * @brief SDIOSLV provide command buffer.
216  *
217  * Call this API to provide receive command buffer to SDU driver.
218  *
219  * @param regmap FN FSR pointer.
220  * @param data_addr Data Address.
221  * @param data_len Data Length.
222  * @retval #kStatus_Success buffer refill sucessfully.
223  * @retval #kStatus_Fail fail to refill buffer.
224  */
SDIOSLV_RefillCmdBuffer(sdioslv_sdu_regmap_t * regmap,uint8_t * data_addr)225 status_t SDIOSLV_RefillCmdBuffer(sdioslv_sdu_regmap_t *regmap, uint8_t *data_addr)
226 {
227     sdioslv_sdu_regmap_t *sdu_fsr = NULL;
228 
229     if ((regmap == NULL) || (data_addr == NULL))
230     {
231         return kStatus_Fail;
232     }
233 
234     sdu_fsr                     = regmap;
235     sdu_fsr->CmdPortSqWriteBase = (uint32_t)data_addr;
236     sdu_fsr->CardToHostEvent    = (uint16_t)SDIO_CCR_CS_CmdDnLdRdy;
237     return kStatus_Success;
238 }
239 
240 /*!
241  * @brief SDIOSLV send data transfer.
242  *
243  * Call this API to send data to host driver.
244  * The callback is always invoked from theinterrupt context.
245  *
246  * @param regmap FN FSR pointer.
247  * @param port Data Port.
248  * @param data_addr Data Address.
249  * @param data_len Data Length.
250  * @retval #kStatus_Success buffer is added to data slot with problem.
251  * @retval #kStatus_InvalidArgument Invalid argument.
252  * @retval #kStatus_SDIOSLV_SendFull all data slots are occupied, application
253  */
SDIOSLV_SendDataNonBlocking(sdioslv_sdu_regmap_t * regmap,sdioslv_port_t tx_port,uint8_t * data_addr,uint16_t data_len)254 status_t SDIOSLV_SendDataNonBlocking(sdioslv_sdu_regmap_t *regmap, sdioslv_port_t tx_port, uint8_t *data_addr, uint16_t data_len)
255 {
256     sdioslv_sdu_regmap_t *sdu_fsr = NULL;
257 
258     if ((regmap == NULL) || (data_addr == NULL) || (data_len == 0U))
259     {
260         return (status_t)kStatus_InvalidArgument;
261     }
262 
263     if ((uint8_t)tx_port >= (uint8_t)SDU_USED_PORT_NUM)
264     {
265         return (status_t)kStatus_SDIOSLV_SendFull;
266     }
267 
268     sdu_fsr                  = regmap;
269     sdu_fsr->RdIdx           = (uint8_t)tx_port;
270     sdu_fsr->SqReadBase      = (uint32_t)data_addr;
271     sdu_fsr->RdLen[tx_port]  = data_len;
272     sdu_fsr->RdBitMap        = ((uint32_t)1U << (uint32_t)tx_port);
273     sdu_fsr->CardToHostEvent = (uint16_t)SDIO_CCR_CS_UpLdRdy;
274     return (status_t)kStatus_Success;
275 }
276 
277 /*!
278  * @brief SDIOSLV provide receive data buffer.
279  *
280  * Call this API to provide receive data buffer to SDU driver.
281  *
282  * @param regmap FN FSR pointer.
283  * @param port Data Port.
284  * @param data_addr Data Address.
285  * @param data_len Data Length.
286  * @retval #kStatus_Success refill buffer sucessfully.
287  * @retval #kStatus_Fail fail to refill buffer.
288  */
SDIOSLV_RefillDataBuffer(sdioslv_sdu_regmap_t * regmap,sdioslv_port_t port,uint8_t * data_addr)289 status_t SDIOSLV_RefillDataBuffer(sdioslv_sdu_regmap_t *regmap, sdioslv_port_t port, uint8_t *data_addr)
290 {
291     sdioslv_sdu_regmap_t *sdu_fsr = NULL;
292 
293     if ((regmap == NULL) || (data_addr == NULL))
294     {
295         return kStatus_Fail;
296     }
297 
298     if ((uint32_t)port >= (uint32_t)SDU_USED_PORT_NUM)
299     {
300         return kStatus_Fail;
301     }
302 
303     sdu_fsr                  = regmap;
304     sdu_fsr->WrIdx           = (uint8_t)port;
305     sdu_fsr->SqWriteBase     = (uint32_t)data_addr;
306     sdu_fsr->WrBitMap        = (uint32_t)((uint32_t)1U << (uint32_t)port);
307     sdu_fsr->CardToHostEvent = (uint16_t)SDIO_CCR_CS_DnLdRdy;
308     return kStatus_Success;
309 }
310 
311 /*!
312  * @brief Get SDIO bus speed selection.
313  *
314  * Call this API to get current bus speed selected for SDIO.
315  *
316  * @param void None.
317  * @retval sdioslv_bus_speed_t Bus speed selected for SDIO.
318  */
SDIOSLV_GetBusSpeed(void)319 sdioslv_bus_speed_t SDIOSLV_GetBusSpeed(void)
320 {
321     uint8_t reg_val = 0;
322 
323     reg_val = SDU_REGS8(SDIO_FUNC0_BSS);
324     reg_val = (reg_val & (uint8_t)SDIO_FUNC0_BSS_MODE_MASK) >> SDIO_FUNC0_BSS_MODE_BIT;
325 
326     return (sdioslv_bus_speed_t)reg_val;
327 }
328 
329 /*!
330  * @brief Get SDIO the block size in FBR.
331  *
332  * For block mode, block size equals to block size in FBR.
333  *
334  * @param handle Created by SDIOSLV_CreateHanle().
335  * @retval the block size in FBR.
336  */
SDIOSLV_GetBlockSize(uint8_t fn_num)337 uint32_t SDIOSLV_GetBlockSize(uint8_t fn_num)
338 {
339     uint32_t fun_num;
340     uint32_t block_size = 0U;
341 
342     fun_num  = (uint32_t)(fn_num + 1U);
343     block_size = SDU_REGS8(SDU_SDIO_CFG_BASE + 0x8U + (fun_num << 4U));
344     block_size |= SDU_REGS8(SDU_SDIO_CFG_BASE + 0x9U + (((fun_num << 4U) & 0x01U) << 8U));
345 
346     if (block_size == 0U)
347     {
348         block_size = 512U;
349     }
350 
351     return block_size;
352 }
353 
354 /*!
355  * @brief SDIOSLV read scratch register of SDU.
356  *
357  * Call this API to read scratch register of SDU (based on group and offset).
358  *
359  * @param fun_num Specify which function.
360  * @param group Specify which scratch group.
361  * @param offset Specify offset of the scratch group.
362  * @param value Value read from the register.
363  * @retval #kStatus_Success read sucessfully.
364  * @retval #kStatus_Fail fail to read.
365  */
SDIOSLV_ReadScratchRegister(sdioslv_func_t fun_num,sdioslv_scratch_group_t group,sdioslv_scratch_offset_t offset,uint8_t * value)366 status_t SDIOSLV_ReadScratchRegister(sdioslv_func_t fun_num,
367                                      sdioslv_scratch_group_t group,
368                                      sdioslv_scratch_offset_t offset,
369                                      uint8_t *value)
370 {
371     status_t ret;
372     uint32_t reg_addr;
373 
374     ret = SDIOSLV_GetScratchRegisterAddr(fun_num, group, offset, &reg_addr);
375 
376     if (ret == kStatus_Success)
377     {
378         SDU_READ_REGS8(reg_addr, *value);
379     }
380 
381     return ret;
382 }
383 
384 /*!
385  * @brief SDIOSLV write value to scratch register of SDU.
386  *
387  * Call this API to write value to scratch register of SDU (based on group and offset).
388  *
389  * @param fun_num Specify which function.
390  * @param group Specify which scratch group.
391  * @param offset Specify offset of the scratch group.
392  * @param value Value write to the register.
393  * @retval #kStatus_Success write sucessfully.
394  * @retval #kStatus_Fail fail to write.
395  */
SDIOSLV_WriteScratchRegister(sdioslv_func_t fun_num,sdioslv_scratch_group_t group,sdioslv_scratch_offset_t offset,uint8_t value)396 status_t SDIOSLV_WriteScratchRegister(sdioslv_func_t fun_num,
397                                       sdioslv_scratch_group_t group,
398                                       sdioslv_scratch_offset_t offset,
399                                       uint8_t value)
400 {
401     status_t ret;
402     uint32_t reg_addr;
403 
404     ret = SDIOSLV_GetScratchRegisterAddr(fun_num, group, offset, &reg_addr);
405 
406     if (ret == kStatus_Success)
407     {
408         SDU_WRITE_REGS8(reg_addr, value);
409     }
410 
411     return ret;
412 }
413 
414 /*******************************************************************************
415  * Private Functions
416  ******************************************************************************/
417 
418 /*!
419  * @brief SDIOSLV get scratch register address.
420  *
421  * @param fun_num Specify which function.
422  * @param group Specify which scratch group.
423  * @param offset Specify offset of the scratch group.
424  * @param reg_addr Returned scratch register address.
425  * @retval #kStatus_Success get scratch register address sucessfully.
426  * @retval #kStatus_Fail fail to get scratch register address.
427  */
SDIOSLV_GetScratchRegisterAddr(sdioslv_func_t fun_num,sdioslv_scratch_group_t group,sdioslv_scratch_offset_t offset,uint32_t * reg_addr)428 static inline status_t SDIOSLV_GetScratchRegisterAddr(sdioslv_func_t fun_num,
429                                                       sdioslv_scratch_group_t group,
430                                                       sdioslv_scratch_offset_t offset,
431                                                       uint32_t *reg_addr)
432 {
433     status_t status = kStatus_Success;
434     switch (group)
435     {
436         case kSDIOSLV_ScratchGroup0:
437             if (offset == kSDIOSLV_ScratchOffset0)
438             {
439                 *reg_addr = 0xD4U;
440             }
441             else if (offset == kSDIOSLV_ScratchOffset1)
442             {
443                 *reg_addr = 0xD5U;
444             }
445             else
446             {
447                 status = kStatus_Fail;
448             }
449             break;
450         case kSDIOSLV_ScratchGroup1:
451             if (offset == kSDIOSLV_ScratchOffset0)
452             {
453                 *reg_addr = 0xB0U;
454             }
455             else if (offset == kSDIOSLV_ScratchOffset1)
456             {
457                 *reg_addr = 0xB1U;
458             }
459             else
460             {
461                 status = kStatus_Fail;
462             }
463             break;
464         case kSDIOSLV_ScratchGroup2:
465         case kSDIOSLV_ScratchGroup3:
466         case kSDIOSLV_ScratchGroup4:
467         case kSDIOSLV_ScratchGroup5:
468         case kSDIOSLV_ScratchGroup6:
469         case kSDIOSLV_ScratchGroup7:
470             *reg_addr = (uint32_t)SDU_SCRATCH2_OFFSET0_ADDR + (uint32_t)(((uint32_t)group - 2U) * 4U) + (uint32_t)offset;
471             break;
472         default:
473             status = kStatus_Fail;
474             break;
475     }
476 
477     if(status != kStatus_Success)
478     {
479         return status;
480     }
481 
482     switch (fun_num)
483     {
484         case kSDIOSLV_FunctionNum1:
485         case kSDIOSLV_FunctionNum2:
486         case kSDIOSLV_FunctionNum3:
487         case kSDIOSLV_FunctionNum4:
488         case kSDIOSLV_FunctionNum5:
489         case kSDIOSLV_FunctionNum6:
490         case kSDIOSLV_FunctionNum7:
491             *reg_addr |= ((uint32_t)fun_num << 8U);
492             break;
493         default:
494             status = kStatus_Fail;
495             break;
496     }
497 
498     if(status != kStatus_Success)
499     {
500         return status;
501     }
502 
503     *reg_addr += SDU_SDIO_CFG_BASE;
504 
505     return status;
506 }
507