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, ®_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, ®_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