1 /*
2 * Copyright (c) 2016, Freescale Semiconductor, Inc.
3 * Copyright 2016-2020 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_sdhc.h"
10
11 /*******************************************************************************
12 * Definitions
13 ******************************************************************************/
14
15 /* Component ID definition, used by tools. */
16 #ifndef FSL_COMPONENT_ID
17 #define FSL_COMPONENT_ID "platform.drivers.sdhc"
18 #endif
19
20 /*! @brief Clock setting */
21 /* Max SD clock divisor from base clock */
22 #define SDHC_MAX_DVS ((SDHC_SYSCTL_DVS_MASK >> SDHC_SYSCTL_DVS_SHIFT) + 1U)
23 #define SDHC_PREV_DVS(x) ((x) -= 1U)
24 #define SDHC_MAX_CLKFS ((SDHC_SYSCTL_SDCLKFS_MASK >> SDHC_SYSCTL_SDCLKFS_SHIFT) + 1U)
25 #define SDHC_PREV_CLKFS(x) ((x) >>= 1U)
26
27 /* Typedef for interrupt handler. */
28 typedef void (*sdhc_isr_t)(SDHC_Type *base, sdhc_handle_t *handle);
29
30 /*! @brief ADMA table configuration */
31 typedef struct _sdhc_adma_table_config
32 {
33 uint32_t *admaTable; /*!< ADMA table address, can't be null if transfer way is ADMA1/ADMA2 */
34 uint32_t admaTableWords; /*!< ADMA table length united as words, can't be 0 if transfer way is ADMA1/ADMA2 */
35 } sdhc_adma_table_config_t;
36 /*! @brief check flag avalibility */
37 #define IS_SDHC_FLAG_SET(reg, flag) (((reg) & ((uint32_t)flag)) != 0UL)
38 /*******************************************************************************
39 * Prototypes
40 ******************************************************************************/
41 /*!
42 * @brief Get the instance.
43 *
44 * @param base SDHC peripheral base address.
45 * @return Instance number.
46 */
47 static uint32_t SDHC_GetInstance(SDHC_Type *base);
48
49 /*!
50 * @brief Start transfer according to current transfer state
51 *
52 * @param base SDHC peripheral base address.
53 * @param command Command to be sent.
54 * @param data Data to be transferred.
55 * @param DMA mode selection
56 */
57 static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_data_t *data, sdhc_dma_mode_t dmaMode);
58
59 /*!
60 * @brief Receive command response
61 *
62 * @param base SDHC peripheral base address.
63 * @param command Command to be sent.
64 */
65 static status_t SDHC_ReceiveCommandResponse(SDHC_Type *base, sdhc_command_t *command);
66
67 /*!
68 * @brief Read DATAPORT when buffer enable bit is set.
69 *
70 * @param base SDHC peripheral base address.
71 * @param data Data to be read.
72 * @param transferredWords The number of data words have been transferred last time transaction.
73 * @return The number of total data words have been transferred after this time transaction.
74 */
75 static uint32_t SDHC_ReadDataPort(SDHC_Type *base, sdhc_data_t *data, uint32_t transferredWords);
76
77 /*!
78 * @brief Read data by using DATAPORT polling way.
79 *
80 * @param base SDHC peripheral base address.
81 * @param data Data to be read.
82 * @retval kStatus_Fail Read DATAPORT failed.
83 * @retval kStatus_Success Operate successfully.
84 */
85 static status_t SDHC_ReadByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data);
86
87 /*!
88 * @brief Write DATAPORT when buffer enable bit is set.
89 *
90 * @param base SDHC peripheral base address.
91 * @param data Data to be read.
92 * @param transferredWords The number of data words have been transferred last time.
93 * @return The number of total data words have been transferred after this time transaction.
94 */
95 static uint32_t SDHC_WriteDataPort(SDHC_Type *base, sdhc_data_t *data, uint32_t transferredWords);
96
97 /*!
98 * @brief Write data by using DATAPORT polling way.
99 *
100 * @param base SDHC peripheral base address.
101 * @param data Data to be transferred.
102 * @retval kStatus_Fail Write DATAPORT failed.
103 * @retval kStatus_Success Operate successfully.
104 */
105 static status_t SDHC_WriteByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data);
106
107 /*!
108 * @brief Send command by using polling way.
109 *
110 * @param base SDHC peripheral base address.
111 * @param command Command to be sent.
112 * @retval kStatus_Fail Send command failed.
113 * @retval kStatus_Success Operate successfully.
114 */
115 static status_t SDHC_SendCommandBlocking(SDHC_Type *base, sdhc_command_t *command);
116
117 /*!
118 * @brief Transfer data by DATAPORT and polling way.
119 *
120 * @param base SDHC peripheral base address.
121 * @param data Data to be transferred.
122 * @retval kStatus_Fail Transfer data failed.
123 * @retval kStatus_Success Operate successfully.
124 */
125 static status_t SDHC_TransferByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data);
126
127 /*!
128 * @brief Transfer data by ADMA2 and polling way.
129 *
130 * @param base SDHC peripheral base address.
131 * @param data Data to be transferred.
132 * @retval kStatus_Fail Transfer data failed.
133 * @retval kStatus_Success Operate successfully.
134 */
135 static status_t SDHC_TransferByAdma2Blocking(SDHC_Type *base, sdhc_data_t *data);
136
137 /*!
138 * @brief Transfer data by polling way.
139 *
140 * @param dmaMode DMA mode.
141 * @param base SDHC peripheral base address.
142 * @param data Data to be transferred.
143 * @retval kStatus_Fail Transfer data failed.
144 * @retval kStatus_InvalidArgument Argument is invalid.
145 * @retval kStatus_Success Operate successfully.
146 */
147 static status_t SDHC_TransferDataBlocking(sdhc_dma_mode_t dmaMode, SDHC_Type *base, sdhc_data_t *data);
148
149 /*!
150 * @brief Handle card detect interrupt.
151 *
152 * @param base SDHC peripheral base address.
153 * @param handle SDHC handle.
154 * @param interruptFlags Card detect related interrupt flags.
155 */
156 static void SDHC_TransferHandleCardDetect(SDHC_Type *base, sdhc_handle_t *handle, uint32_t interruptFlags);
157
158 /*!
159 * @brief Handle command interrupt.
160 *
161 * @param base SDHC peripheral base address.
162 * @param handle SDHC handle.
163 * @param interruptFlags Command related interrupt flags.
164 */
165 static void SDHC_TransferHandleCommand(SDHC_Type *base, sdhc_handle_t *handle, uint32_t interruptFlags);
166
167 /*!
168 * @brief Handle data interrupt.
169 *
170 * @param base SDHC peripheral base address.
171 * @param handle SDHC handle.
172 * @param interruptFlags Data related interrupt flags.
173 */
174 static void SDHC_TransferHandleData(SDHC_Type *base, sdhc_handle_t *handle, uint32_t interruptFlags);
175
176 /*!
177 * @brief Handle SDIO card interrupt signal.
178 *
179 * @param base SDHC peripheral base address.
180 * @param handle SDHC handle.
181 */
182 static void SDHC_TransferHandleSdioInterrupt(SDHC_Type *base, sdhc_handle_t *handle);
183
184 /*!
185 * @brief Handle SDIO block gap event.
186 *
187 * @param base SDHC peripheral base address.
188 * @param handle SDHC handle.
189 */
190 static void SDHC_TransferHandleSdioBlockGap(SDHC_Type *base, sdhc_handle_t *handle);
191
192 /*******************************************************************************
193 * Variables
194 ******************************************************************************/
195 /*! @brief SDHC internal handle pointer array */
196 static sdhc_handle_t *s_sdhcHandle[FSL_FEATURE_SOC_SDHC_COUNT];
197
198 /*! @brief SDHC base pointer array */
199 static SDHC_Type *const s_sdhcBase[] = SDHC_BASE_PTRS;
200
201 /*! @brief SDHC IRQ name array */
202 static const IRQn_Type s_sdhcIRQ[] = SDHC_IRQS;
203
204 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
205 /*! @brief SDHC clock array name */
206 static const clock_ip_name_t s_sdhcClock[] = SDHC_CLOCKS;
207 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
208
209 /* SDHC ISR for transactional APIs. */
210 #if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
211 static sdhc_isr_t s_sdhcIsr = (sdhc_isr_t)DefaultISR;
212 #else
213 static sdhc_isr_t s_sdhcIsr;
214 #endif
215
216 /*******************************************************************************
217 * Code
218 ******************************************************************************/
SDHC_GetInstance(SDHC_Type * base)219 static uint32_t SDHC_GetInstance(SDHC_Type *base)
220 {
221 uint8_t instance = 0;
222
223 while ((instance < ARRAY_SIZE(s_sdhcBase)) && (s_sdhcBase[instance] != base))
224 {
225 instance++;
226 }
227
228 assert(instance < ARRAY_SIZE(s_sdhcBase));
229
230 return instance;
231 }
232
SDHC_StartTransfer(SDHC_Type * base,sdhc_command_t * command,sdhc_data_t * data,sdhc_dma_mode_t dmaMode)233 static void SDHC_StartTransfer(SDHC_Type *base, sdhc_command_t *command, sdhc_data_t *data, sdhc_dma_mode_t dmaMode)
234 {
235 uint32_t flags = 0U;
236 sdhc_transfer_config_t sdhcTransferConfig = {0};
237
238 /* Define the flag corresponding to each response type. */
239 if (command->responseType == kCARD_ResponseTypeR1) /* Response 1 */
240 {
241 flags |= ((uint32_t)kSDHC_ResponseLength48Flag | (uint32_t)kSDHC_EnableCrcCheckFlag |
242 (uint32_t)kSDHC_EnableIndexCheckFlag);
243 }
244 else if (command->responseType == kCARD_ResponseTypeR1b) /* Response 1 with busy */
245 {
246 flags |= ((uint32_t)kSDHC_ResponseLength48BusyFlag | (uint32_t)kSDHC_EnableCrcCheckFlag |
247 (uint32_t)kSDHC_EnableIndexCheckFlag);
248 }
249 else if (command->responseType == kCARD_ResponseTypeR2) /* Response 2 */
250 {
251 flags |= ((uint32_t)kSDHC_ResponseLength136Flag | (uint32_t)kSDHC_EnableCrcCheckFlag);
252 }
253 else if (command->responseType == kCARD_ResponseTypeR3) /* Response 3 */
254 {
255 flags |= ((uint32_t)kSDHC_ResponseLength48Flag);
256 }
257 else if (command->responseType == kCARD_ResponseTypeR4) /* Response 4 */
258 {
259 flags |= ((uint32_t)kSDHC_ResponseLength48Flag);
260 }
261 else if (command->responseType == kCARD_ResponseTypeR5) /* Response 5 */
262 {
263 flags |= ((uint32_t)kSDHC_ResponseLength48Flag | (uint32_t)kSDHC_EnableCrcCheckFlag |
264 (uint32_t)kSDHC_EnableIndexCheckFlag);
265 }
266 else if (command->responseType == kCARD_ResponseTypeR5b) /* Response 5 with busy */
267 {
268 flags |= ((uint32_t)kSDHC_ResponseLength48BusyFlag | (uint32_t)kSDHC_EnableCrcCheckFlag |
269 (uint32_t)kSDHC_EnableIndexCheckFlag);
270 }
271 else if (command->responseType == kCARD_ResponseTypeR6) /* Response 6 */
272 {
273 flags |= ((uint32_t)kSDHC_ResponseLength48Flag | (uint32_t)kSDHC_EnableCrcCheckFlag |
274 (uint32_t)kSDHC_EnableIndexCheckFlag);
275 }
276 else if (command->responseType == kCARD_ResponseTypeR7) /* Response 7 */
277 {
278 flags |= ((uint32_t)kSDHC_ResponseLength48Flag | (uint32_t)kSDHC_EnableCrcCheckFlag |
279 (uint32_t)kSDHC_EnableIndexCheckFlag);
280 }
281 else
282 {
283 /* Intentional empty for kCARD_ResponseTypeNone */
284 }
285
286 if (command->type == kCARD_CommandTypeAbort)
287 {
288 flags |= (uint32_t)kSDHC_CommandTypeAbortFlag;
289 }
290
291 if (data != NULL)
292 {
293 flags |= (uint32_t)kSDHC_DataPresentFlag;
294
295 if (dmaMode != kSDHC_DmaModeNo)
296 {
297 flags |= (uint32_t)kSDHC_EnableDmaFlag;
298 }
299
300 if (data->rxData != NULL)
301 {
302 flags |= (uint32_t)kSDHC_DataReadFlag;
303 }
304 if (data->blockCount > 1U)
305 {
306 flags |= ((uint32_t)kSDHC_MultipleBlockFlag | (uint32_t)kSDHC_EnableBlockCountFlag);
307 if (data->enableAutoCommand12)
308 {
309 /* Enable Auto command 12. */
310 flags |= (uint32_t)kSDHC_EnableAutoCommand12Flag;
311 }
312 }
313
314 sdhcTransferConfig.dataBlockSize = data->blockSize;
315 sdhcTransferConfig.dataBlockCount = data->blockCount;
316 }
317 else
318 {
319 sdhcTransferConfig.dataBlockSize = 0U;
320 sdhcTransferConfig.dataBlockCount = 0U;
321 }
322
323 sdhcTransferConfig.commandArgument = command->argument;
324 sdhcTransferConfig.commandIndex = command->index;
325 sdhcTransferConfig.flags = flags;
326 SDHC_SetTransferConfig(base, &sdhcTransferConfig);
327 }
328
SDHC_ReceiveCommandResponse(SDHC_Type * base,sdhc_command_t * command)329 static status_t SDHC_ReceiveCommandResponse(SDHC_Type *base, sdhc_command_t *command)
330 {
331 assert(command != NULL);
332
333 uint32_t response0 = base->CMDRSP[0];
334 uint32_t response1 = base->CMDRSP[1];
335 uint32_t response2 = base->CMDRSP[2];
336
337 if (command->responseType != kCARD_ResponseTypeNone)
338 {
339 command->response[0U] = response0;
340 if (command->responseType == kCARD_ResponseTypeR2)
341 {
342 /* R3-R2-R1-R0(lowest 8 bit is invalid bit) has the same format as R2 format in SD specification document
343 after removed internal CRC7 and end bit. */
344 command->response[0U] <<= 8U;
345 command->response[1U] = (response1 << 8U) | ((response0 & 0xFF000000U) >> 24U);
346 command->response[2U] = (response2 << 8U) | ((response1 & 0xFF000000U) >> 24U);
347 command->response[3U] = (base->CMDRSP[3] << 8U) | ((response2 & 0xFF000000U) >> 24U);
348 }
349 }
350
351 /* check response error flag */
352 if ((command->responseErrorFlags != 0U) &&
353 ((command->responseType == kCARD_ResponseTypeR1) || (command->responseType == kCARD_ResponseTypeR1b) ||
354 (command->responseType == kCARD_ResponseTypeR6) || (command->responseType == kCARD_ResponseTypeR5)))
355 {
356 if (((command->responseErrorFlags) & (command->response[0U])) != 0U)
357 {
358 return kStatus_SDHC_SendCommandFailed;
359 }
360 }
361
362 return kStatus_Success;
363 }
364
SDHC_ReadDataPort(SDHC_Type * base,sdhc_data_t * data,uint32_t transferredWords)365 static uint32_t SDHC_ReadDataPort(SDHC_Type *base, sdhc_data_t *data, uint32_t transferredWords)
366 {
367 uint32_t i;
368 uint32_t totalWords;
369 uint32_t wordsCanBeRead; /* The words can be read at this time. */
370 uint32_t readWatermark = ((base->WML & SDHC_WML_RDWML_MASK) >> SDHC_WML_RDWML_SHIFT);
371
372 /*
373 * Add non aligned access support ,user need make sure your buffer size is big
374 * enough to hold the data,in other words,user need make sure the buffer size
375 * is 4 byte aligned
376 */
377 if (data->blockSize % sizeof(uint32_t) != 0U)
378 {
379 data->blockSize +=
380 sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
381 }
382
383 totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));
384
385 /* If watermark level is equal or bigger than totalWords, transfers totalWords data. */
386 if (readWatermark >= totalWords)
387 {
388 wordsCanBeRead = totalWords;
389 }
390 /* If watermark level is less than totalWords and left words to be sent is equal or bigger than readWatermark,
391 transfers watermark level words. */
392 else if ((readWatermark < totalWords) && ((totalWords - transferredWords) >= readWatermark))
393 {
394 wordsCanBeRead = readWatermark;
395 }
396 /* If watermark level is less than totalWords and left words to be sent is less than readWatermark, transfers left
397 words. */
398 else
399 {
400 wordsCanBeRead = (totalWords - transferredWords);
401 }
402
403 i = 0U;
404 while (i < wordsCanBeRead)
405 {
406 data->rxData[transferredWords++] = SDHC_ReadData(base);
407 i++;
408 }
409
410 return transferredWords;
411 }
412
SDHC_ReadByDataPortBlocking(SDHC_Type * base,sdhc_data_t * data)413 static status_t SDHC_ReadByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data)
414 {
415 uint32_t totalWords;
416 uint32_t transferredWords = 0U;
417 status_t error = kStatus_Success;
418
419 /*
420 * Add non aligned access support ,user need make sure your buffer size is big
421 * enough to hold the data,in other words,user need make sure the buffer size
422 * is 4 byte aligned
423 */
424 if (data->blockSize % sizeof(uint32_t) != 0U)
425 {
426 data->blockSize +=
427 sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
428 }
429
430 totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));
431
432 while ((error == kStatus_Success) && (transferredWords < totalWords))
433 {
434 while (!(IS_SDHC_FLAG_SET(SDHC_GetInterruptStatusFlags(base),
435 ((uint32_t)kSDHC_BufferReadReadyFlag | (uint32_t)kSDHC_DataErrorFlag))))
436 {
437 }
438
439 if (IS_SDHC_FLAG_SET(SDHC_GetInterruptStatusFlags(base), kSDHC_DataErrorFlag))
440 {
441 if (!(data->enableIgnoreError))
442 {
443 error = kStatus_Fail;
444 }
445 }
446 if (error == kStatus_Success)
447 {
448 transferredWords = SDHC_ReadDataPort(base, data, transferredWords);
449 }
450 /* clear buffer ready and error */
451 SDHC_ClearInterruptStatusFlags(base, (uint32_t)kSDHC_BufferReadReadyFlag | (uint32_t)kSDHC_DataErrorFlag);
452 }
453
454 /* Clear data complete flag after the last read operation. */
455 SDHC_ClearInterruptStatusFlags(base, (uint32_t)kSDHC_DataCompleteFlag | (uint32_t)kSDHC_DataErrorFlag);
456
457 return error;
458 }
459
SDHC_WriteDataPort(SDHC_Type * base,sdhc_data_t * data,uint32_t transferredWords)460 static uint32_t SDHC_WriteDataPort(SDHC_Type *base, sdhc_data_t *data, uint32_t transferredWords)
461 {
462 uint32_t i;
463 uint32_t totalWords;
464 uint32_t wordsCanBeWrote; /* Words can be wrote at this time. */
465 uint32_t writeWatermark = ((base->WML & SDHC_WML_WRWML_MASK) >> SDHC_WML_WRWML_SHIFT);
466
467 /*
468 * Add non aligned access support ,user need make sure your buffer size is big
469 * enough to hold the data,in other words,user need make sure the buffer size
470 * is 4 byte aligned
471 */
472 if (data->blockSize % sizeof(uint32_t) != 0U)
473 {
474 data->blockSize +=
475 sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
476 }
477
478 totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));
479
480 /* If watermark level is equal or bigger than totalWords, transfers totalWords data.*/
481 if (writeWatermark >= totalWords)
482 {
483 wordsCanBeWrote = totalWords;
484 }
485 /* If watermark level is less than totalWords and left words to be sent is equal or bigger than watermark,
486 transfers watermark level words. */
487 else if ((writeWatermark < totalWords) && ((totalWords - transferredWords) >= writeWatermark))
488 {
489 wordsCanBeWrote = writeWatermark;
490 }
491 /* If watermark level is less than totalWords and left words to be sent is less than watermark, transfers left
492 words. */
493 else
494 {
495 wordsCanBeWrote = (totalWords - transferredWords);
496 }
497
498 i = 0U;
499 while (i < wordsCanBeWrote)
500 {
501 SDHC_WriteData(base, data->txData[transferredWords++]);
502 i++;
503 }
504
505 return transferredWords;
506 }
507
SDHC_WriteByDataPortBlocking(SDHC_Type * base,sdhc_data_t * data)508 static status_t SDHC_WriteByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data)
509 {
510 uint32_t totalWords;
511 uint32_t transferredWords = 0U;
512 status_t error = kStatus_Success;
513
514 /*
515 * Add non aligned access support ,user need make sure your buffer size is big
516 * enough to hold the data,in other words,user need make sure the buffer size
517 * is 4 byte aligned
518 */
519 if (data->blockSize % sizeof(uint32_t) != 0U)
520 {
521 data->blockSize +=
522 sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
523 }
524
525 totalWords = (data->blockCount * data->blockSize) / sizeof(uint32_t);
526
527 while ((error == kStatus_Success) && (transferredWords < totalWords))
528 {
529 while (!(IS_SDHC_FLAG_SET(SDHC_GetInterruptStatusFlags(base),
530 ((uint32_t)kSDHC_BufferWriteReadyFlag | (uint32_t)kSDHC_DataErrorFlag))))
531 {
532 }
533
534 if (IS_SDHC_FLAG_SET(SDHC_GetInterruptStatusFlags(base), kSDHC_DataErrorFlag))
535 {
536 if (!(data->enableIgnoreError))
537 {
538 error = kStatus_Fail;
539 }
540 }
541 if (error == kStatus_Success)
542 {
543 transferredWords = SDHC_WriteDataPort(base, data, transferredWords);
544 }
545
546 /* Clear buffer enable flag to trigger transfer. Clear error flag when SDHC encounter error. */
547 SDHC_ClearInterruptStatusFlags(base, ((uint32_t)kSDHC_BufferWriteReadyFlag | (uint32_t)kSDHC_DataErrorFlag));
548 }
549
550 /* Wait write data complete or data transfer error after the last writing operation. */
551 while (!(IS_SDHC_FLAG_SET(SDHC_GetInterruptStatusFlags(base),
552 ((uint32_t)kSDHC_DataCompleteFlag | (uint32_t)kSDHC_DataErrorFlag))))
553 {
554 }
555 if (IS_SDHC_FLAG_SET(SDHC_GetInterruptStatusFlags(base), kSDHC_DataErrorFlag))
556 {
557 if (!(data->enableIgnoreError))
558 {
559 error = kStatus_Fail;
560 }
561 }
562
563 SDHC_ClearInterruptStatusFlags(base, ((uint32_t)kSDHC_DataCompleteFlag | (uint32_t)kSDHC_DataErrorFlag));
564
565 return error;
566 }
567
SDHC_SendCommandBlocking(SDHC_Type * base,sdhc_command_t * command)568 static status_t SDHC_SendCommandBlocking(SDHC_Type *base, sdhc_command_t *command)
569 {
570 status_t error = kStatus_Success;
571
572 /* Wait command complete or SDHC encounters error. */
573 while (!(IS_SDHC_FLAG_SET(SDHC_GetInterruptStatusFlags(base),
574 ((uint32_t)kSDHC_CommandCompleteFlag | (uint32_t)kSDHC_CommandErrorFlag))))
575 {
576 }
577
578 if (IS_SDHC_FLAG_SET(SDHC_GetInterruptStatusFlags(base), kSDHC_CommandErrorFlag))
579 {
580 error = kStatus_Fail;
581 }
582 /* Receive response when command completes successfully. */
583 if (error == kStatus_Success)
584 {
585 error = SDHC_ReceiveCommandResponse(base, command);
586 }
587
588 SDHC_ClearInterruptStatusFlags(base, ((uint32_t)kSDHC_CommandCompleteFlag | (uint32_t)kSDHC_CommandErrorFlag));
589
590 return error;
591 }
592
SDHC_TransferByDataPortBlocking(SDHC_Type * base,sdhc_data_t * data)593 static status_t SDHC_TransferByDataPortBlocking(SDHC_Type *base, sdhc_data_t *data)
594 {
595 status_t error = kStatus_Success;
596
597 if (data->rxData != NULL)
598 {
599 error = SDHC_ReadByDataPortBlocking(base, data);
600 if (error != kStatus_Success)
601 {
602 return error;
603 }
604 }
605 else
606 {
607 error = SDHC_WriteByDataPortBlocking(base, data);
608 if (error != kStatus_Success)
609 {
610 return error;
611 }
612 }
613
614 return kStatus_Success;
615 }
616
SDHC_TransferByAdma2Blocking(SDHC_Type * base,sdhc_data_t * data)617 static status_t SDHC_TransferByAdma2Blocking(SDHC_Type *base, sdhc_data_t *data)
618 {
619 status_t error = kStatus_Success;
620
621 /* Wait data complete or SDHC encounters error. */
622 while (!(IS_SDHC_FLAG_SET(
623 SDHC_GetInterruptStatusFlags(base),
624 ((uint32_t)kSDHC_DataCompleteFlag | (uint32_t)kSDHC_DataErrorFlag | (uint32_t)kSDHC_DmaErrorFlag))))
625 {
626 }
627 if (IS_SDHC_FLAG_SET(SDHC_GetInterruptStatusFlags(base),
628 ((uint32_t)kSDHC_DataErrorFlag | (uint32_t)kSDHC_DmaErrorFlag)))
629 {
630 if (!(data->enableIgnoreError))
631 {
632 error = kStatus_Fail;
633 }
634 }
635 SDHC_ClearInterruptStatusFlags(base, ((uint32_t)kSDHC_DataCompleteFlag | (uint32_t)kSDHC_DmaCompleteFlag |
636 (uint32_t)kSDHC_DataErrorFlag | (uint32_t)kSDHC_DmaErrorFlag));
637
638 return error;
639 }
640
641 #if defined FSL_SDHC_ENABLE_ADMA1
642 #define SDHC_TransferByAdma1Blocking(base, data) SDHC_TransferByAdma2Blocking(base, data)
643 #endif /* FSL_SDHC_ENABLE_ADMA1 */
644
SDHC_TransferDataBlocking(sdhc_dma_mode_t dmaMode,SDHC_Type * base,sdhc_data_t * data)645 static status_t SDHC_TransferDataBlocking(sdhc_dma_mode_t dmaMode, SDHC_Type *base, sdhc_data_t *data)
646 {
647 status_t error = kStatus_Success;
648
649 if (dmaMode == kSDHC_DmaModeNo)
650 {
651 error = SDHC_TransferByDataPortBlocking(base, data);
652 if (error != kStatus_Success)
653 {
654 return error;
655 }
656 }
657 #if defined FSL_SDHC_ENABLE_ADMA1
658 else if (dmaMode == kSDHC_DmaModeAdma1)
659 {
660 error = SDHC_TransferByAdma1Blocking(base, data);
661 if (error != kStatus_Success)
662 {
663 return error;
664 }
665 }
666 #endif /* FSL_SDHC_ENABLE_ADMA1 */
667
668 /*kSDHC_DmaModeAdma2*/
669 else
670 {
671 error = SDHC_TransferByAdma2Blocking(base, data);
672 if (error != kStatus_Success)
673 {
674 return error;
675 }
676 }
677
678 return kStatus_Success;
679 }
680
SDHC_TransferHandleCardDetect(SDHC_Type * base,sdhc_handle_t * handle,uint32_t interruptFlags)681 static void SDHC_TransferHandleCardDetect(SDHC_Type *base, sdhc_handle_t *handle, uint32_t interruptFlags)
682 {
683 if (IS_SDHC_FLAG_SET(interruptFlags, kSDHC_CardInsertionFlag))
684 {
685 if (handle->callback.CardInserted != NULL)
686 {
687 handle->callback.CardInserted(base, handle->userData);
688 }
689 }
690 else
691 {
692 if (handle->callback.CardRemoved != NULL)
693 {
694 handle->callback.CardRemoved(base, handle->userData);
695 }
696 }
697 }
698
SDHC_TransferHandleCommand(SDHC_Type * base,sdhc_handle_t * handle,uint32_t interruptFlags)699 static void SDHC_TransferHandleCommand(SDHC_Type *base, sdhc_handle_t *handle, uint32_t interruptFlags)
700 {
701 assert(handle->command != NULL);
702
703 if ((IS_SDHC_FLAG_SET(interruptFlags, kSDHC_CommandErrorFlag)) && (handle->data == NULL) &&
704 (handle->callback.TransferComplete != NULL))
705 {
706 handle->callback.TransferComplete(base, handle, kStatus_SDHC_SendCommandFailed, handle->userData);
707 }
708 else
709 {
710 /* Receive response */
711 if (kStatus_Success != SDHC_ReceiveCommandResponse(base, handle->command))
712 {
713 if (handle->callback.TransferComplete != NULL)
714 {
715 handle->callback.TransferComplete(base, handle, kStatus_SDHC_SendCommandFailed, handle->userData);
716 }
717 }
718 else
719 {
720 if (handle->callback.TransferComplete != NULL)
721 {
722 handle->callback.TransferComplete(base, handle, kStatus_SDHC_TransferCommandComplete, handle->userData);
723 }
724 }
725 }
726
727 SDHC_DisableInterruptSignal(base, kSDHC_CommandFlag);
728 handle->command = NULL;
729 }
730
SDHC_TransferHandleData(SDHC_Type * base,sdhc_handle_t * handle,uint32_t interruptFlags)731 static void SDHC_TransferHandleData(SDHC_Type *base, sdhc_handle_t *handle, uint32_t interruptFlags)
732 {
733 assert(handle->data != NULL);
734
735 status_t transferStatus = kStatus_SDHC_BusyTransferring;
736 uint32_t transferredWords = handle->transferredWords;
737
738 if ((!(handle->data->enableIgnoreError)) &&
739 (IS_SDHC_FLAG_SET(interruptFlags, ((uint32_t)kSDHC_DataErrorFlag | (uint32_t)kSDHC_DmaErrorFlag))))
740 {
741 transferStatus = kStatus_SDHC_TransferDataFailed;
742 }
743 else
744 {
745 if (IS_SDHC_FLAG_SET(interruptFlags, kSDHC_BufferReadReadyFlag))
746 {
747 handle->transferredWords = SDHC_ReadDataPort(base, handle->data, transferredWords);
748 }
749 else if (IS_SDHC_FLAG_SET(interruptFlags, kSDHC_BufferWriteReadyFlag))
750 {
751 handle->transferredWords = SDHC_WriteDataPort(base, handle->data, transferredWords);
752 }
753 else
754 {
755 /* Intentional empty */
756 }
757
758 if (IS_SDHC_FLAG_SET(interruptFlags, kSDHC_DataCompleteFlag))
759 {
760 transferStatus = kStatus_SDHC_TransferDataComplete;
761 }
762 }
763
764 if ((transferStatus != kStatus_SDHC_BusyTransferring) && (handle->callback.TransferComplete != NULL))
765 {
766 handle->callback.TransferComplete(base, handle, transferStatus, handle->userData);
767 SDHC_DisableInterruptSignal(base, (uint32_t)kSDHC_DataFlag | (uint32_t)kSDHC_DataDMAFlag);
768 handle->data = NULL;
769 }
770 }
771
SDHC_TransferHandleSdioInterrupt(SDHC_Type * base,sdhc_handle_t * handle)772 static void SDHC_TransferHandleSdioInterrupt(SDHC_Type *base, sdhc_handle_t *handle)
773 {
774 if (handle->callback.SdioInterrupt != NULL)
775 {
776 handle->callback.SdioInterrupt(base, handle->userData);
777 }
778 }
779
SDHC_TransferHandleSdioBlockGap(SDHC_Type * base,sdhc_handle_t * handle)780 static void SDHC_TransferHandleSdioBlockGap(SDHC_Type *base, sdhc_handle_t *handle)
781 {
782 if (handle->callback.SdioBlockGap != NULL)
783 {
784 handle->callback.SdioBlockGap(base, handle->userData);
785 }
786 }
787
788 /*!
789 * brief SDHC module initialization function.
790 *
791 * Configures the SDHC according to the user configuration.
792 *
793 * Example:
794 code
795 sdhc_config_t config;
796 config.cardDetectDat3 = false;
797 config.endianMode = kSDHC_EndianModeLittle;
798 config.dmaMode = kSDHC_DmaModeAdma2;
799 config.readWatermarkLevel = 128U;
800 config.writeWatermarkLevel = 128U;
801 SDHC_Init(SDHC, &config);
802 endcode
803 *
804 * param base SDHC peripheral base address.
805 * param config SDHC configuration information.
806 * retval kStatus_Success Operate successfully.
807 */
SDHC_Init(SDHC_Type * base,const sdhc_config_t * config)808 void SDHC_Init(SDHC_Type *base, const sdhc_config_t *config)
809 {
810 assert(config != NULL);
811 #if !defined FSL_SDHC_ENABLE_ADMA1
812 assert(config->dmaMode != kSDHC_DmaModeAdma1);
813 #endif /* FSL_SDHC_ENABLE_ADMA1 */
814 assert((config->writeWatermarkLevel >= 1UL) && (config->writeWatermarkLevel <= 128UL));
815 assert((config->readWatermarkLevel >= 1UL) && (config->readWatermarkLevel <= 128UL));
816
817 uint32_t proctl;
818 uint32_t wml;
819
820 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
821 /* Enable SDHC clock. */
822 CLOCK_EnableClock(s_sdhcClock[SDHC_GetInstance(base)]);
823 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
824
825 /* Reset SDHC. */
826 base->SYSCTL |= SDHC_SYSCTL_RSTA_MASK | SDHC_SYSCTL_RSTC_MASK | SDHC_SYSCTL_RSTD_MASK;
827
828 proctl = base->PROCTL;
829 wml = base->WML;
830
831 proctl &= ~(SDHC_PROCTL_D3CD_MASK | SDHC_PROCTL_EMODE_MASK | SDHC_PROCTL_DMAS_MASK);
832 /* Set DAT3 as card detection pin */
833 if (config->cardDetectDat3)
834 {
835 proctl |= SDHC_PROCTL_D3CD_MASK;
836 }
837 /* Endian mode and DMA mode */
838 proctl |= (SDHC_PROCTL_EMODE(config->endianMode) | SDHC_PROCTL_DMAS(config->dmaMode));
839
840 /* Watermark level */
841 wml &= ~(SDHC_WML_RDWML_MASK | SDHC_WML_WRWML_MASK);
842 wml |= (SDHC_WML_RDWML(config->readWatermarkLevel) | SDHC_WML_WRWML(config->writeWatermarkLevel));
843
844 base->WML = wml;
845 base->PROCTL = proctl;
846
847 /* Disable all clock auto gated off feature because of DAT0 line logic(card buffer full status) can't be updated
848 correctly when clock auto gated off is enabled. */
849 base->SYSCTL |= (SDHC_SYSCTL_PEREN_MASK | SDHC_SYSCTL_HCKEN_MASK | SDHC_SYSCTL_IPGEN_MASK);
850
851 /* Enable interrupt status but doesn't enable interrupt signal, clear all the interrupt status */
852 base->IRQSTATEN = kSDHC_AllInterruptFlags;
853 base->IRQSIGEN = 0U;
854 base->IRQSTAT = kSDHC_AllInterruptFlags;
855 }
856
857 /*!
858 * brief Deinitializes the SDHC.
859 *
860 * param base SDHC peripheral base address.
861 */
SDHC_Deinit(SDHC_Type * base)862 void SDHC_Deinit(SDHC_Type *base)
863 {
864 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
865 /* Disable clock. */
866 CLOCK_DisableClock(s_sdhcClock[SDHC_GetInstance(base)]);
867 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
868 }
869
870 /*!
871 * brief Resets the SDHC.
872 *
873 * param base SDHC peripheral base address.
874 * param mask The reset type mask(_sdhc_reset).
875 * param timeout Timeout for reset.
876 * retval true Reset successfully.
877 * retval false Reset failed.
878 */
SDHC_Reset(SDHC_Type * base,uint32_t mask,uint32_t timeout)879 bool SDHC_Reset(SDHC_Type *base, uint32_t mask, uint32_t timeout)
880 {
881 base->SYSCTL |= (mask & (SDHC_SYSCTL_RSTA_MASK | SDHC_SYSCTL_RSTC_MASK | SDHC_SYSCTL_RSTD_MASK));
882 /* Delay some time to wait reset success. */
883 while (IS_SDHC_FLAG_SET(base->SYSCTL, mask))
884 {
885 if (timeout == 0UL)
886 {
887 break;
888 }
889 timeout--;
890 }
891
892 return (timeout == 0UL ? false : true);
893 }
894
895 /*!
896 * brief Gets the capability information.
897 *
898 * param base SDHC peripheral base address.
899 * param capability Structure to save capability information.
900 */
SDHC_GetCapability(SDHC_Type * base,sdhc_capability_t * capability)901 void SDHC_GetCapability(SDHC_Type *base, sdhc_capability_t *capability)
902 {
903 assert(capability != NULL);
904
905 uint32_t htCapability;
906 uint32_t hostVer;
907 uint32_t maxBlockLength;
908
909 hostVer = base->HOSTVER;
910 htCapability = base->HTCAPBLT;
911
912 /* Get the capability of SDHC. */
913 capability->specVersion = ((hostVer & SDHC_HOSTVER_SVN_MASK) >> SDHC_HOSTVER_SVN_SHIFT);
914 capability->vendorVersion = ((hostVer & SDHC_HOSTVER_VVN_MASK) >> SDHC_HOSTVER_VVN_SHIFT);
915 maxBlockLength = ((htCapability & SDHC_HTCAPBLT_MBL_MASK) >> SDHC_HTCAPBLT_MBL_SHIFT);
916 capability->maxBlockLength = (512UL << maxBlockLength);
917 /* Other attributes not in HTCAPBLT register. */
918 capability->maxBlockCount = SDHC_MAX_BLOCK_COUNT;
919 capability->flags = (htCapability & ((uint32_t)kSDHC_SupportAdmaFlag | (uint32_t)kSDHC_SupportHighSpeedFlag |
920 (uint32_t)kSDHC_SupportDmaFlag | (uint32_t)kSDHC_SupportSuspendResumeFlag |
921 (uint32_t)kSDHC_SupportV330Flag));
922 #if defined FSL_FEATURE_SDHC_HAS_V300_SUPPORT && FSL_FEATURE_SDHC_HAS_V300_SUPPORT
923 capability->flags |= (htCapability & (uint32_t)kSDHC_SupportV300Flag);
924 #endif
925 #if defined FSL_FEATURE_SDHC_HAS_V180_SUPPORT && FSL_FEATURE_SDHC_HAS_V180_SUPPORT
926 capability->flags |= (htCapability & (uint32_t)kSDHC_SupportV180Flag);
927 #endif
928 /* eSDHC on all kinetis boards will support 4/8 bit data bus width. */
929 capability->flags |= ((uint32_t)kSDHC_Support4BitFlag | (uint32_t)kSDHC_Support8BitFlag);
930 }
931
932 /*!
933 * brief Sets the SD bus clock frequency.
934 *
935 * param base SDHC peripheral base address.
936 * param srcClock_Hz SDHC source clock frequency united in Hz.
937 * param busClock_Hz SD bus clock frequency united in Hz.
938 *
939 * return The nearest frequency of busClock_Hz configured to SD bus.
940 */
SDHC_SetSdClock(SDHC_Type * base,uint32_t srcClock_Hz,uint32_t busClock_Hz)941 uint32_t SDHC_SetSdClock(SDHC_Type *base, uint32_t srcClock_Hz, uint32_t busClock_Hz)
942 {
943 assert(srcClock_Hz != 0U);
944 assert((busClock_Hz != 0U) && (busClock_Hz <= srcClock_Hz));
945
946 uint32_t totalDiv = 0U;
947 uint32_t divisor = 0U;
948 uint32_t prescaler = 0U;
949 uint32_t sysctl = 0U;
950 uint32_t nearestFrequency = 0U;
951
952 /* calucate total divisor first */
953 totalDiv = srcClock_Hz / busClock_Hz;
954
955 if (totalDiv > (SDHC_MAX_CLKFS * SDHC_MAX_DVS))
956 {
957 return 0UL;
958 }
959
960 if (totalDiv != 0U)
961 {
962 /* calucate the divisor (srcClock_Hz / divisor) <= busClock_Hz */
963 if ((srcClock_Hz / totalDiv) > busClock_Hz)
964 {
965 totalDiv++;
966 }
967
968 /* divide the total divisor to div and prescaler */
969 if (totalDiv > SDHC_MAX_DVS)
970 {
971 prescaler = totalDiv / SDHC_MAX_DVS;
972 /* prescaler must be a value which equal 2^n and smaller than SDHC_MAX_CLKFS */
973 while (((SDHC_MAX_CLKFS % prescaler) != 0U) || (prescaler == 1U))
974 {
975 prescaler++;
976 }
977 /* calucate the divisor */
978 divisor = totalDiv / prescaler;
979 /* fine tuning the divisor until divisor * prescaler >= totalDiv */
980 while ((divisor * prescaler) < totalDiv)
981 {
982 divisor++;
983 }
984
985 nearestFrequency = srcClock_Hz / (divisor == 0U ? 1U : divisor) / prescaler;
986 }
987 else
988 {
989 divisor = totalDiv;
990 prescaler = 0U;
991 nearestFrequency = srcClock_Hz / divisor;
992 }
993 }
994 /* in this condition , srcClock_Hz = busClock_Hz, */
995 else
996 {
997 /* total divider = 1U */
998 divisor = 0U;
999 prescaler = 0U;
1000 nearestFrequency = srcClock_Hz;
1001 }
1002
1003 /* calucate the value write to register */
1004 if (divisor != 0U)
1005 {
1006 SDHC_PREV_DVS(divisor);
1007 }
1008 /* calucate the value write to register */
1009 if (prescaler != 0U)
1010 {
1011 SDHC_PREV_CLKFS(prescaler);
1012 }
1013
1014 /* Disable SD clock. It should be disabled before changing the SD clock frequency.*/
1015 base->SYSCTL &= ~SDHC_SYSCTL_SDCLKEN_MASK;
1016
1017 /* Set the SD clock frequency divisor, SD clock frequency select, data timeout counter value. */
1018 sysctl = base->SYSCTL;
1019 sysctl &= ~(SDHC_SYSCTL_DVS_MASK | SDHC_SYSCTL_SDCLKFS_MASK | SDHC_SYSCTL_DTOCV_MASK);
1020 sysctl |= (SDHC_SYSCTL_DVS(divisor) | SDHC_SYSCTL_SDCLKFS(prescaler) | SDHC_SYSCTL_DTOCV(0xEU));
1021 base->SYSCTL = sysctl;
1022
1023 /* Wait until the SD clock is stable. */
1024 while (!(IS_SDHC_FLAG_SET(base->PRSSTAT, SDHC_PRSSTAT_SDSTB_MASK)))
1025 {
1026 }
1027 /* Enable the SD clock. */
1028 base->SYSCTL |= SDHC_SYSCTL_SDCLKEN_MASK;
1029
1030 return nearestFrequency;
1031 }
1032
1033 /*!
1034 * brief Sends 80 clocks to the card to set it to the active state.
1035 *
1036 * This function must be called each time the card is inserted to ensure that the card can receive the command
1037 * correctly.
1038 *
1039 * param base SDHC peripheral base address.
1040 * param timeout Timeout to initialize card.
1041 * retval true Set card active successfully.
1042 * retval false Set card active failed.
1043 */
SDHC_SetCardActive(SDHC_Type * base,uint32_t timeout)1044 bool SDHC_SetCardActive(SDHC_Type *base, uint32_t timeout)
1045 {
1046 base->SYSCTL |= SDHC_SYSCTL_INITA_MASK;
1047 /* Delay some time to wait card become active state. */
1048 while (IS_SDHC_FLAG_SET(base->SYSCTL, SDHC_SYSCTL_INITA_MASK))
1049 {
1050 if (timeout == 0UL)
1051 {
1052 break;
1053 }
1054 timeout--;
1055 }
1056
1057 return (timeout == 0UL ? false : true);
1058 }
1059
1060 /*!
1061 * brief Sets the card transfer-related configuration.
1062 *
1063 * This function fills the card transfer-related command argument/transfer flag/data size. The command and data are sent
1064 by
1065 * SDHC after calling this function.
1066 *
1067 * Example:
1068 code
1069 sdhc_transfer_config_t transferConfig;
1070 transferConfig.dataBlockSize = 512U;
1071 transferConfig.dataBlockCount = 2U;
1072 transferConfig.commandArgument = 0x01AAU;
1073 transferConfig.commandIndex = 8U;
1074 transferConfig.flags |= (kSDHC_EnableDmaFlag | kSDHC_EnableAutoCommand12Flag | kSDHC_MultipleBlockFlag);
1075 SDHC_SetTransferConfig(SDHC, &transferConfig);
1076 endcode
1077 *
1078 * param base SDHC peripheral base address.
1079 * param config Command configuration structure.
1080 */
SDHC_SetTransferConfig(SDHC_Type * base,const sdhc_transfer_config_t * config)1081 void SDHC_SetTransferConfig(SDHC_Type *base, const sdhc_transfer_config_t *config)
1082 {
1083 assert(config != NULL);
1084 assert(config->dataBlockSize <= (SDHC_BLKATTR_BLKSIZE_MASK >> SDHC_BLKATTR_BLKSIZE_SHIFT));
1085 assert(config->dataBlockCount <= (SDHC_BLKATTR_BLKCNT_MASK >> SDHC_BLKATTR_BLKCNT_SHIFT));
1086
1087 base->BLKATTR = ((base->BLKATTR & ~(SDHC_BLKATTR_BLKSIZE_MASK | SDHC_BLKATTR_BLKCNT_MASK)) |
1088 (SDHC_BLKATTR_BLKSIZE(config->dataBlockSize) | SDHC_BLKATTR_BLKCNT(config->dataBlockCount)));
1089 base->CMDARG = config->commandArgument;
1090 base->XFERTYP = (((config->commandIndex << SDHC_XFERTYP_CMDINX_SHIFT) & SDHC_XFERTYP_CMDINX_MASK) |
1091 (config->flags & (SDHC_XFERTYP_DMAEN_MASK | SDHC_XFERTYP_MSBSEL_MASK | SDHC_XFERTYP_DPSEL_MASK |
1092 SDHC_XFERTYP_CMDTYP_MASK | SDHC_XFERTYP_BCEN_MASK | SDHC_XFERTYP_CICEN_MASK |
1093 SDHC_XFERTYP_CCCEN_MASK | SDHC_XFERTYP_RSPTYP_MASK | SDHC_XFERTYP_DTDSEL_MASK |
1094 SDHC_XFERTYP_AC12EN_MASK)));
1095 }
1096
1097 /*!
1098 * brief Enables or disables the SDIO card control.
1099 *
1100 * param base SDHC peripheral base address.
1101 * param mask SDIO card control flags mask(_sdhc_sdio_control_flag).
1102 * param enable True to enable, false to disable.
1103 */
SDHC_EnableSdioControl(SDHC_Type * base,uint32_t mask,bool enable)1104 void SDHC_EnableSdioControl(SDHC_Type *base, uint32_t mask, bool enable)
1105 {
1106 uint32_t proctl = base->PROCTL;
1107 uint32_t vendor = base->VENDOR;
1108
1109 if (enable)
1110 {
1111 if (IS_SDHC_FLAG_SET(mask, kSDHC_StopAtBlockGapFlag))
1112 {
1113 proctl |= SDHC_PROCTL_SABGREQ_MASK;
1114 }
1115 if (IS_SDHC_FLAG_SET(mask, kSDHC_ReadWaitControlFlag))
1116 {
1117 proctl |= SDHC_PROCTL_RWCTL_MASK;
1118 }
1119 if (IS_SDHC_FLAG_SET(mask, kSDHC_InterruptAtBlockGapFlag))
1120 {
1121 proctl |= SDHC_PROCTL_IABG_MASK;
1122 }
1123 if (IS_SDHC_FLAG_SET(mask, kSDHC_ExactBlockNumberReadFlag))
1124 {
1125 vendor |= SDHC_VENDOR_EXBLKNU_MASK;
1126 }
1127 }
1128 else
1129 {
1130 if (IS_SDHC_FLAG_SET(mask, kSDHC_StopAtBlockGapFlag))
1131 {
1132 proctl &= ~SDHC_PROCTL_SABGREQ_MASK;
1133 }
1134 if (IS_SDHC_FLAG_SET(mask, kSDHC_ReadWaitControlFlag))
1135 {
1136 proctl &= ~SDHC_PROCTL_RWCTL_MASK;
1137 }
1138 if (IS_SDHC_FLAG_SET(mask, kSDHC_InterruptAtBlockGapFlag))
1139 {
1140 proctl &= ~SDHC_PROCTL_IABG_MASK;
1141 }
1142 if (IS_SDHC_FLAG_SET(mask, kSDHC_ExactBlockNumberReadFlag))
1143 {
1144 vendor &= ~SDHC_VENDOR_EXBLKNU_MASK;
1145 }
1146 }
1147
1148 base->PROCTL = proctl;
1149 base->VENDOR = vendor;
1150 }
1151
1152 /*!
1153 * brief Configures the MMC boot feature.
1154 *
1155 * Example:
1156 code
1157 sdhc_boot_config_t config;
1158 config.ackTimeoutCount = 4;
1159 config.bootMode = kSDHC_BootModeNormal;
1160 config.blockCount = 5;
1161 config.enableBootAck = true;
1162 config.enableBoot = true;
1163 config.enableAutoStopAtBlockGap = true;
1164 SDHC_SetMmcBootConfig(SDHC, &config);
1165 endcode
1166 *
1167 * param base SDHC peripheral base address.
1168 * param config The MMC boot configuration information.
1169 */
SDHC_SetMmcBootConfig(SDHC_Type * base,const sdhc_boot_config_t * config)1170 void SDHC_SetMmcBootConfig(SDHC_Type *base, const sdhc_boot_config_t *config)
1171 {
1172 assert(config != NULL);
1173 assert(config->ackTimeoutCount <= (SDHC_MMCBOOT_DTOCVACK_MASK >> SDHC_MMCBOOT_DTOCVACK_SHIFT));
1174 assert(config->blockCount <= (SDHC_MMCBOOT_BOOTBLKCNT_MASK >> SDHC_MMCBOOT_BOOTBLKCNT_SHIFT));
1175
1176 uint32_t mmcboot = 0U;
1177
1178 mmcboot = (SDHC_MMCBOOT_DTOCVACK(config->ackTimeoutCount) | SDHC_MMCBOOT_BOOTMODE(config->bootMode) |
1179 SDHC_MMCBOOT_BOOTBLKCNT(config->blockCount));
1180 if (config->enableBootAck)
1181 {
1182 mmcboot |= SDHC_MMCBOOT_BOOTACK_MASK;
1183 }
1184 if (config->enableBoot)
1185 {
1186 mmcboot |= SDHC_MMCBOOT_BOOTEN_MASK;
1187 }
1188 if (config->enableAutoStopAtBlockGap)
1189 {
1190 mmcboot |= SDHC_MMCBOOT_AUTOSABGEN_MASK;
1191 }
1192 base->MMCBOOT = mmcboot;
1193 }
1194
1195 /*!
1196 * brief Sets the ADMA descriptor table configuration.
1197 *
1198 * param base SDHC peripheral base address.
1199 * param dmaMode DMA mode.
1200 * param table ADMA table address.
1201 * param tableWords ADMA table buffer length united as Words.
1202 * param data Data buffer address.
1203 * param dataBytes Data length united as bytes.
1204 * retval kStatus_OutOfRange ADMA descriptor table length isn't enough to describe data.
1205 * retval kStatus_Success Operate successfully.
1206 */
SDHC_SetAdmaTableConfig(SDHC_Type * base,sdhc_dma_mode_t dmaMode,uint32_t * table,uint32_t tableWords,const uint32_t * data,uint32_t dataBytes)1207 status_t SDHC_SetAdmaTableConfig(SDHC_Type *base,
1208 sdhc_dma_mode_t dmaMode,
1209 uint32_t *table,
1210 uint32_t tableWords,
1211 const uint32_t *data,
1212 uint32_t dataBytes)
1213 {
1214 status_t error = kStatus_Success;
1215 const uint32_t *startAddress = data;
1216 uint32_t entries;
1217 uint32_t i;
1218 #if defined FSL_SDHC_ENABLE_ADMA1
1219 sdhc_adma1_descriptor_t *adma1EntryAddress;
1220 #endif
1221 sdhc_adma2_descriptor_t *adma2EntryAddress;
1222
1223 if ((((table == NULL) || (tableWords == 0UL)) &&
1224 ((dmaMode == kSDHC_DmaModeAdma1) || (dmaMode == kSDHC_DmaModeAdma2))) ||
1225 (data == NULL) || (dataBytes == 0UL)
1226 #if !defined FSL_SDHC_ENABLE_ADMA1
1227 || (dmaMode == kSDHC_DmaModeAdma1)
1228 #endif
1229 )
1230 {
1231 error = kStatus_InvalidArgument;
1232 }
1233 else if (((dmaMode == kSDHC_DmaModeAdma2) && (((uint32_t)startAddress % SDHC_ADMA2_LENGTH_ALIGN) != 0UL))
1234 #if defined FSL_SDHC_ENABLE_ADMA1
1235 || ((dmaMode == kSDHC_DmaModeAdma1) && (((uint32_t)startAddress % SDHC_ADMA1_LENGTH_ALIGN) != 0UL))
1236 #endif
1237 )
1238 {
1239 error = kStatus_SDHC_DMADataBufferAddrNotAlign;
1240 }
1241 else
1242 {
1243 #if defined FSL_SDHC_ENABLE_ADMA1
1244 if (dmaMode == kSDHC_DmaModeAdma1)
1245 {
1246 /*
1247 * Add non aligned access support ,user need make sure your buffer size is big
1248 * enough to hold the data,in other words,user need make sure the buffer size
1249 * is 4 byte aligned
1250 */
1251 if (dataBytes % sizeof(uint32_t) != 0U)
1252 {
1253 dataBytes +=
1254 sizeof(uint32_t) - (dataBytes % sizeof(uint32_t)); /* make the data length as word-aligned */
1255 }
1256
1257 /* Check if ADMA descriptor's number is enough. */
1258 entries = ((dataBytes / SDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY) + 1U);
1259 /* ADMA1 needs two descriptors to finish a transfer */
1260 entries <<= 1U;
1261 if (entries > ((tableWords * sizeof(uint32_t)) / sizeof(sdhc_adma1_descriptor_t)))
1262 {
1263 error = kStatus_OutOfRange;
1264 }
1265 else
1266 {
1267 adma1EntryAddress = (sdhc_adma1_descriptor_t *)(uint32_t)(table);
1268 for (i = 0U; i < entries; i += 2U)
1269 {
1270 /* Each descriptor for ADMA1 is 32-bit in length */
1271 if ((dataBytes - ((uint32_t)startAddress - (uint32_t)data)) <=
1272 SDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY)
1273 {
1274 /* The last piece of data, setting end flag in descriptor */
1275 adma1EntryAddress[i] = ((uint32_t)(dataBytes - ((uint32_t)startAddress - (uint32_t)data))
1276 << SDHC_ADMA1_DESCRIPTOR_LENGTH_SHIFT);
1277 adma1EntryAddress[i] |= (uint32_t)kSDHC_Adma1DescriptorTypeSetLength;
1278 adma1EntryAddress[i + 1U] = (uint32_t)(startAddress);
1279 adma1EntryAddress[i + 1U] |=
1280 ((uint32_t)kSDHC_Adma1DescriptorTypeTransfer | (uint32_t)kSDHC_Adma1DescriptorEndFlag);
1281 }
1282 else
1283 {
1284 adma1EntryAddress[i] = ((uint32_t)SDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY
1285 << SDHC_ADMA1_DESCRIPTOR_LENGTH_SHIFT);
1286 adma1EntryAddress[i] |= (uint32_t)kSDHC_Adma1DescriptorTypeSetLength;
1287 adma1EntryAddress[i + 1U] = ((uint32_t)(startAddress));
1288 adma1EntryAddress[i + 1U] |= (uint32_t)kSDHC_Adma1DescriptorTypeTransfer;
1289 startAddress += SDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY / sizeof(uint32_t);
1290 }
1291 }
1292
1293 /* When use ADMA, disable simple DMA */
1294 base->DSADDR = 0U;
1295 base->ADSADDR = (uint32_t)table;
1296 }
1297 }
1298 else
1299 #endif /* FSL_SDHC_ENABLE_ADMA1 */
1300 if (dmaMode == kSDHC_DmaModeAdma2)
1301 {
1302 /*
1303 * Add non aligned access support ,user need make sure your buffer size is big
1304 * enough to hold the data,in other words,user need make sure the buffer size
1305 * is 4 byte aligned
1306 */
1307 if (dataBytes % sizeof(uint32_t) != 0U)
1308 {
1309 dataBytes +=
1310 sizeof(uint32_t) - (dataBytes % sizeof(uint32_t)); /* make the data length as word-aligned */
1311 }
1312
1313 /* Check if ADMA descriptor's number is enough. */
1314 entries = ((dataBytes / SDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY) + 1U);
1315 if (entries > ((tableWords * sizeof(uint32_t)) / sizeof(sdhc_adma2_descriptor_t)))
1316 {
1317 error = kStatus_OutOfRange;
1318 }
1319 else
1320 {
1321 adma2EntryAddress = (sdhc_adma2_descriptor_t *)(uint32_t)(table);
1322 for (i = 0U; i < entries; i++)
1323 {
1324 /* Each descriptor for ADMA2 is 64-bit in length */
1325 if ((dataBytes - ((uint32_t)startAddress - (uint32_t)data)) <=
1326 SDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY)
1327 {
1328 /* The last piece of data, setting end flag in descriptor */
1329 adma2EntryAddress[i].address = startAddress;
1330 adma2EntryAddress[i].attribute = ((dataBytes - ((uint32_t)startAddress - (uint32_t)data))
1331 << SDHC_ADMA2_DESCRIPTOR_LENGTH_SHIFT);
1332 adma2EntryAddress[i].attribute |=
1333 ((uint32_t)kSDHC_Adma2DescriptorTypeTransfer | (uint32_t)kSDHC_Adma2DescriptorEndFlag);
1334 }
1335 else
1336 {
1337 adma2EntryAddress[i].address = startAddress;
1338 adma2EntryAddress[i].attribute =
1339 (((SDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY / sizeof(uint32_t)) * sizeof(uint32_t))
1340 << SDHC_ADMA2_DESCRIPTOR_LENGTH_SHIFT);
1341 adma2EntryAddress[i].attribute |= (uint32_t)kSDHC_Adma2DescriptorTypeTransfer;
1342 startAddress += (SDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY / sizeof(uint32_t));
1343 }
1344 }
1345
1346 /* When use ADMA, disable simple DMA */
1347 base->DSADDR = 0U;
1348 base->ADSADDR = (uint32_t)table;
1349 }
1350 }
1351 else
1352 {
1353 /* Intentional empty */
1354 }
1355 }
1356
1357 return error;
1358 }
1359
1360 /*!
1361 * brief Transfers the command/data using a blocking method.
1362 *
1363 * This function waits until the command response/data is received or the SDHC encounters an error by polling the status
1364 * flag.
1365 * This function support non word align data addr transfer support, if data buffer addr is not align in DMA mode,
1366 * the API will continue finish the transfer by polling IO directly
1367 * The application must not call this API in multiple threads at the same time. Because of that this API doesn't support
1368 * the re-entry mechanism.
1369 *
1370 * note There is no need to call the API 'SDHC_TransferCreateHandle' when calling this API.
1371 *
1372 * param base SDHC peripheral base address.
1373 * param admaTable ADMA table address, can't be null if transfer way is ADMA1/ADMA2.
1374 * param admaTableWords ADMA table length united as words, can't be 0 if transfer way is ADMA1/ADMA2.
1375 * param transfer Transfer content.
1376 * retval kStatus_InvalidArgument Argument is invalid.
1377 * retval kStatus_SDHC_PrepareAdmaDescriptorFailed Prepare ADMA descriptor failed.
1378 * retval kStatus_SDHC_SendCommandFailed Send command failed.
1379 * retval kStatus_SDHC_TransferDataFailed Transfer data failed.
1380 * retval kStatus_Success Operate successfully.
1381 */
SDHC_TransferBlocking(SDHC_Type * base,uint32_t * admaTable,uint32_t admaTableWords,sdhc_transfer_t * transfer)1382 status_t SDHC_TransferBlocking(SDHC_Type *base, uint32_t *admaTable, uint32_t admaTableWords, sdhc_transfer_t *transfer)
1383 {
1384 assert(transfer != NULL);
1385
1386 status_t error = kStatus_Success;
1387 sdhc_dma_mode_t dmaMode =
1388 (sdhc_dma_mode_t)(uint8_t)((base->PROCTL & SDHC_PROCTL_DMAS_MASK) >> SDHC_PROCTL_DMAS_SHIFT);
1389 sdhc_command_t *command = transfer->command;
1390 sdhc_data_t *data = transfer->data;
1391
1392 /* make sure the cmd/block count is valid */
1393 if ((command == NULL) || ((data != NULL) && (data->blockCount > SDHC_MAX_BLOCK_COUNT)))
1394 {
1395 return kStatus_InvalidArgument;
1396 }
1397
1398 /* Wait until command/data bus out of busy status. */
1399 while (IS_SDHC_FLAG_SET(SDHC_GetPresentStatusFlags(base), kSDHC_CommandInhibitFlag))
1400 {
1401 }
1402 while ((data != NULL) && (IS_SDHC_FLAG_SET(SDHC_GetPresentStatusFlags(base), kSDHC_DataInhibitFlag)))
1403 {
1404 }
1405
1406 /* Update ADMA descriptor table according to different DMA mode(no DMA, ADMA1, ADMA2).*/
1407 if ((data != NULL) && (NULL != admaTable))
1408 {
1409 error = SDHC_SetAdmaTableConfig(base, (sdhc_dma_mode_t)dmaMode, admaTable, admaTableWords,
1410 (data->rxData != NULL ? data->rxData : data->txData),
1411 (data->blockCount * data->blockSize));
1412 /* in this situation , we disable the DMA instead of polling transfer mode */
1413 if (error != kStatus_Success)
1414 {
1415 dmaMode = (sdhc_dma_mode_t)kSDHC_DmaModeNo;
1416 }
1417 }
1418
1419 if (data != NULL)
1420 {
1421 SDHC_ClearInterruptStatusFlags(
1422 base,
1423 (uint32_t)(dmaMode == kSDHC_DmaModeNo ? kSDHC_DataFlag : kSDHC_DataDMAFlag) | (uint32_t)kSDHC_CommandFlag);
1424 }
1425 else
1426 {
1427 SDHC_ClearInterruptStatusFlags(base, kSDHC_CommandFlag);
1428 }
1429
1430 /* Send command and receive data. */
1431 SDHC_StartTransfer(base, command, data, (sdhc_dma_mode_t)dmaMode);
1432
1433 error = SDHC_SendCommandBlocking(base, command);
1434 if (kStatus_Success != error)
1435 {
1436 return error;
1437 }
1438
1439 if (data != NULL)
1440 {
1441 error = SDHC_TransferDataBlocking((sdhc_dma_mode_t)dmaMode, base, data);
1442 if (kStatus_Success != error)
1443 {
1444 return error;
1445 }
1446 }
1447
1448 return kStatus_Success;
1449 }
1450
1451 /*!
1452 * brief Creates the SDHC handle.
1453 *
1454 * param base SDHC peripheral base address.
1455 * param handle SDHC handle pointer.
1456 * param callback Structure pointer to contain all callback functions.
1457 * param userData Callback function parameter.
1458 */
SDHC_TransferCreateHandle(SDHC_Type * base,sdhc_handle_t * handle,const sdhc_transfer_callback_t * callback,void * userData)1459 void SDHC_TransferCreateHandle(SDHC_Type *base,
1460 sdhc_handle_t *handle,
1461 const sdhc_transfer_callback_t *callback,
1462 void *userData)
1463 {
1464 assert(handle != NULL);
1465 assert(callback != NULL);
1466
1467 /* Zero the handle. */
1468 (void)memset(handle, 0, sizeof(*handle));
1469
1470 /* Set the callback. */
1471 handle->callback.CardInserted = callback->CardInserted;
1472 handle->callback.CardRemoved = callback->CardRemoved;
1473 handle->callback.SdioInterrupt = callback->SdioInterrupt;
1474 handle->callback.SdioBlockGap = callback->SdioBlockGap;
1475 handle->callback.TransferComplete = callback->TransferComplete;
1476 handle->userData = userData;
1477
1478 /* Save the handle in global variables to support the double weak mechanism. */
1479 s_sdhcHandle[SDHC_GetInstance(base)] = handle;
1480
1481 /* save IRQ handler */
1482 s_sdhcIsr = SDHC_TransferHandleIRQ;
1483
1484 (void)EnableIRQ(s_sdhcIRQ[SDHC_GetInstance(base)]);
1485 }
1486
1487 /*!
1488 * brief Transfers the command/data using an interrupt and an asynchronous method.
1489 *
1490 * This function sends a command and data and returns immediately. It doesn't wait the transfer complete or encounter an
1491 * error.
1492 * This function support non word align data addr transfer support, if data buffer addr is not align in DMA mode,
1493 * the API will continue finish the transfer by polling IO directly
1494 * The application must not call this API in multiple threads at the same time. Because of that this API doesn't support
1495 * the re-entry mechanism.
1496 *
1497 * note Call the API 'SDHC_TransferCreateHandle' when calling this API.
1498 *
1499 * param base SDHC peripheral base address.
1500 * param handle SDHC handle.
1501 * param admaTable ADMA table address, can't be null if transfer way is ADMA1/ADMA2.
1502 * param admaTableWords ADMA table length united as words, can't be 0 if transfer way is ADMA1/ADMA2.
1503 * param transfer Transfer content.
1504 * retval kStatus_InvalidArgument Argument is invalid.
1505 * retval kStatus_SDHC_BusyTransferring Busy transferring.
1506 * retval kStatus_SDHC_PrepareAdmaDescriptorFailed Prepare ADMA descriptor failed.
1507 * retval kStatus_Success Operate successfully.
1508 */
SDHC_TransferNonBlocking(SDHC_Type * base,sdhc_handle_t * handle,uint32_t * admaTable,uint32_t admaTableWords,sdhc_transfer_t * transfer)1509 status_t SDHC_TransferNonBlocking(
1510 SDHC_Type *base, sdhc_handle_t *handle, uint32_t *admaTable, uint32_t admaTableWords, sdhc_transfer_t *transfer)
1511 {
1512 assert(transfer != NULL);
1513
1514 sdhc_dma_mode_t dmaMode =
1515 (sdhc_dma_mode_t)(uint8_t)((base->PROCTL & SDHC_PROCTL_DMAS_MASK) >> SDHC_PROCTL_DMAS_SHIFT);
1516 status_t error = kStatus_Success;
1517 sdhc_command_t *command = transfer->command;
1518 sdhc_data_t *data = transfer->data;
1519
1520 /* make sure cmd/block count is valid */
1521 if ((command == NULL) || ((data != NULL) && (data->blockCount > SDHC_MAX_BLOCK_COUNT)))
1522 {
1523 return kStatus_InvalidArgument;
1524 }
1525
1526 /* Wait until command/data bus out of busy status. */
1527 if ((IS_SDHC_FLAG_SET(SDHC_GetPresentStatusFlags(base), kSDHC_CommandInhibitFlag)) ||
1528 ((data != NULL) && (IS_SDHC_FLAG_SET(SDHC_GetPresentStatusFlags(base), kSDHC_DataInhibitFlag))))
1529 {
1530 return kStatus_SDHC_BusyTransferring;
1531 }
1532
1533 /* Update ADMA descriptor table according to different DMA mode(no DMA, ADMA1, ADMA2).*/
1534 if ((data != NULL) && (NULL != admaTable))
1535 {
1536 error = SDHC_SetAdmaTableConfig(base, dmaMode, admaTable, admaTableWords,
1537 (data->rxData != NULL ? data->rxData : data->txData),
1538 (data->blockCount * data->blockSize));
1539 /* in this situation , we disable the DMA instead of polling transfer mode */
1540 if (error != kStatus_Success)
1541 {
1542 /* change to polling mode */
1543 dmaMode = kSDHC_DmaModeNo;
1544 }
1545 }
1546
1547 /* Save command and data into handle before transferring. */
1548 handle->command = command;
1549 handle->data = data;
1550 /* transferredWords will only be updated in ISR when transfer way is DATAPORT. */
1551 handle->transferredWords = 0U;
1552
1553 /* enable interrupt per transfer request */
1554 if (handle->data != NULL)
1555 {
1556 SDHC_ClearInterruptStatusFlags(
1557 base,
1558 (uint32_t)(dmaMode == kSDHC_DmaModeNo ? kSDHC_DataFlag : kSDHC_DataDMAFlag) | (uint32_t)kSDHC_CommandFlag);
1559 SDHC_EnableInterruptSignal(base, (uint32_t)(dmaMode == kSDHC_DmaModeNo ? kSDHC_DataFlag : kSDHC_DataDMAFlag) |
1560 (uint32_t)kSDHC_CommandFlag);
1561 }
1562 else
1563 {
1564 SDHC_ClearInterruptStatusFlags(base, kSDHC_CommandFlag);
1565 SDHC_EnableInterruptSignal(base, kSDHC_CommandFlag);
1566 }
1567
1568 SDHC_StartTransfer(base, command, data, dmaMode);
1569
1570 return kStatus_Success;
1571 }
1572
1573 /*!
1574 * brief IRQ handler for the SDHC.
1575 *
1576 * This function deals with the IRQs on the given host controller.
1577 *
1578 * param base SDHC peripheral base address.
1579 * param handle SDHC handle.
1580 */
SDHC_TransferHandleIRQ(SDHC_Type * base,sdhc_handle_t * handle)1581 void SDHC_TransferHandleIRQ(SDHC_Type *base, sdhc_handle_t *handle)
1582 {
1583 assert(handle != NULL);
1584
1585 uint32_t interruptFlags;
1586
1587 interruptFlags = SDHC_GetEnabledInterruptStatusFlags(base);
1588
1589 if (IS_SDHC_FLAG_SET(interruptFlags, kSDHC_CardDetectFlag))
1590 {
1591 SDHC_TransferHandleCardDetect(base, handle, interruptFlags);
1592 }
1593 if (IS_SDHC_FLAG_SET(interruptFlags, kSDHC_CommandFlag))
1594 {
1595 SDHC_TransferHandleCommand(base, handle, interruptFlags);
1596 }
1597 if (IS_SDHC_FLAG_SET(interruptFlags, kSDHC_DataFlag))
1598 {
1599 SDHC_TransferHandleData(base, handle, interruptFlags);
1600 }
1601 if (IS_SDHC_FLAG_SET(interruptFlags, kSDHC_CardInterruptFlag))
1602 {
1603 SDHC_TransferHandleSdioInterrupt(base, handle);
1604 }
1605 if (IS_SDHC_FLAG_SET(interruptFlags, kSDHC_BlockGapEventFlag))
1606 {
1607 SDHC_TransferHandleSdioBlockGap(base, handle);
1608 }
1609
1610 SDHC_ClearInterruptStatusFlags(base, interruptFlags);
1611 }
1612
1613 #if defined(SDHC)
1614 void SDHC_DriverIRQHandler(void);
SDHC_DriverIRQHandler(void)1615 void SDHC_DriverIRQHandler(void)
1616 {
1617 assert(s_sdhcHandle[0] != NULL);
1618
1619 s_sdhcIsr(SDHC, s_sdhcHandle[0]);
1620 SDK_ISR_EXIT_BARRIER;
1621 }
1622 #endif
1623