1 /*
2 * Copyright (c) 2016, Freescale Semiconductor, Inc.
3 * Copyright 2016-2017 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_usdhc.h"
10 #if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
11 #include "fsl_cache.h"
12 #endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */
13
14 /*******************************************************************************
15 * Definitions
16 ******************************************************************************/
17 /*! @brief Clock setting */
18 /* Max SD clock divisor from base clock */
19 #define USDHC_MAX_DVS ((USDHC_SYS_CTRL_DVS_MASK >> USDHC_SYS_CTRL_DVS_SHIFT) + 1U)
20 #define USDHC_PREV_DVS(x) ((x) -= 1U)
21 #define USDHC_PREV_CLKFS(x, y) ((x) >>= (y))
22
23 /* Typedef for interrupt handler. */
24 typedef void (*usdhc_isr_t)(USDHC_Type *base, usdhc_handle_t *handle);
25
26 /*******************************************************************************
27 * Prototypes
28 ******************************************************************************/
29 /*!
30 * @brief Get the instance.
31 *
32 * @param base USDHC peripheral base address.
33 * @return Instance number.
34 */
35 static uint32_t USDHC_GetInstance(USDHC_Type *base);
36
37 /*!
38 * @brief Set transfer interrupt.
39 *
40 * @param base USDHC peripheral base address.
41 * @param usingInterruptSignal True to use IRQ signal.
42 */
43 static void USDHC_SetTransferInterrupt(USDHC_Type *base, bool usingInterruptSignal);
44
45 /*!
46 * @brief Start transfer according to current transfer state
47 *
48 * @param base USDHC peripheral base address.
49 * @param command Command to be sent.
50 * @param data Data to be transferred.
51 */
52 static status_t USDHC_SetTransferConfig(USDHC_Type *base, usdhc_command_t *command, usdhc_data_t *data);
53
54 /*!
55 * @brief Receive command response
56 *
57 * @param base USDHC peripheral base address.
58 * @param command Command to be sent.
59 */
60 static status_t USDHC_ReceiveCommandResponse(USDHC_Type *base, usdhc_command_t *command);
61
62 /*!
63 * @brief Read DATAPORT when buffer enable bit is set.
64 *
65 * @param base USDHC peripheral base address.
66 * @param data Data to be read.
67 * @param transferredWords The number of data words have been transferred last time transaction.
68 * @return The number of total data words have been transferred after this time transaction.
69 */
70 static uint32_t USDHC_ReadDataPort(USDHC_Type *base, usdhc_data_t *data, uint32_t transferredWords);
71
72 /*!
73 * @brief Read data by using DATAPORT polling way.
74 *
75 * @param base USDHC peripheral base address.
76 * @param data Data to be read.
77 * @retval kStatus_Fail Read DATAPORT failed.
78 * @retval kStatus_Success Operate successfully.
79 */
80 static status_t USDHC_ReadByDataPortBlocking(USDHC_Type *base, usdhc_data_t *data);
81
82 /*!
83 * @brief Write DATAPORT when buffer enable bit is set.
84 *
85 * @param base USDHC peripheral base address.
86 * @param data Data to be read.
87 * @param transferredWords The number of data words have been transferred last time.
88 * @return The number of total data words have been transferred after this time transaction.
89 */
90 static uint32_t USDHC_WriteDataPort(USDHC_Type *base, usdhc_data_t *data, uint32_t transferredWords);
91
92 /*!
93 * @brief Write data by using DATAPORT polling way.
94 *
95 * @param base USDHC peripheral base address.
96 * @param data Data to be transferred.
97 * @retval kStatus_Fail Write DATAPORT failed.
98 * @retval kStatus_Success Operate successfully.
99 */
100 static status_t USDHC_WriteByDataPortBlocking(USDHC_Type *base, usdhc_data_t *data);
101
102 /*!
103 * @brief Transfer data by polling way.
104 *
105 * @param base USDHC peripheral base address.
106 * @param data Data to be transferred.
107 * @param use DMA flag.
108 * @retval kStatus_Fail Transfer data failed.
109 * @retval kStatus_InvalidArgument Argument is invalid.
110 * @retval kStatus_Success Operate successfully.
111 */
112 static status_t USDHC_TransferDataBlocking(USDHC_Type *base, usdhc_data_t *data, bool enDMA);
113
114 /*!
115 * @brief Handle card detect interrupt.
116 *
117 * @param handle USDHC handle.
118 * @param interruptFlags Card detect related interrupt flags.
119 */
120 static void USDHC_TransferHandleCardDetect(usdhc_handle_t *handle, uint32_t interruptFlags);
121
122 /*!
123 * @brief Handle command interrupt.
124 *
125 * @param base USDHC peripheral base address.
126 * @param handle USDHC handle.
127 * @param interruptFlags Command related interrupt flags.
128 */
129 static void USDHC_TransferHandleCommand(USDHC_Type *base, usdhc_handle_t *handle, uint32_t interruptFlags);
130
131 /*!
132 * @brief Handle data interrupt.
133 *
134 * @param base USDHC peripheral base address.
135 * @param handle USDHC handle.
136 * @param interruptFlags Data related interrupt flags.
137 */
138 static void USDHC_TransferHandleData(USDHC_Type *base, usdhc_handle_t *handle, uint32_t interruptFlags);
139
140 /*!
141 * @brief Handle SDIO card interrupt signal.
142 *
143 * @param handle USDHC handle.
144 */
145 static void USDHC_TransferHandleSdioInterrupt(usdhc_handle_t *handle);
146
147 /*!
148 * @brief Handle SDIO block gap event.
149 *
150 * @param handle USDHC handle.
151 */
152 static void USDHC_TransferHandleSdioBlockGap(usdhc_handle_t *handle);
153
154 /*!
155 * @brief Handle retuning
156 *
157 * @param interrupt flags
158 */
159 static void USDHC_TransferHandleReTuning(USDHC_Type *base, usdhc_handle_t *handle, uint32_t interruptFlags);
160
161 /*!
162 * @brief wait command done
163 *
164 * @param base USDHC peripheral base address.
165 * @param command configuration
166 * @param execute tuning flag
167 */
168 static status_t USDHC_WaitCommandDone(USDHC_Type *base, usdhc_command_t *command, bool executeTuning);
169
170 /*******************************************************************************
171 * Variables
172 ******************************************************************************/
173 /*! @brief USDHC base pointer array */
174 static USDHC_Type *const s_usdhcBase[] = USDHC_BASE_PTRS;
175
176 /*! @brief USDHC internal handle pointer array */
177 static usdhc_handle_t *s_usdhcHandle[ARRAY_SIZE(s_usdhcBase)] = {NULL};
178
179 /*! @brief USDHC IRQ name array */
180 static const IRQn_Type s_usdhcIRQ[] = USDHC_IRQS;
181
182 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
183 /*! @brief USDHC clock array name */
184 static const clock_ip_name_t s_usdhcClock[] = USDHC_CLOCKS;
185 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
186
187 /* USDHC ISR for transactional APIs. */
188 static usdhc_isr_t s_usdhcIsr;
189
190 /*******************************************************************************
191 * Code
192 ******************************************************************************/
USDHC_GetInstance(USDHC_Type * base)193 static uint32_t USDHC_GetInstance(USDHC_Type *base)
194 {
195 uint8_t instance = 0;
196
197 while ((instance < ARRAY_SIZE(s_usdhcBase)) && (s_usdhcBase[instance] != base))
198 {
199 instance++;
200 }
201
202 assert(instance < ARRAY_SIZE(s_usdhcBase));
203
204 return instance;
205 }
206
USDHC_SetTransferInterrupt(USDHC_Type * base,bool usingInterruptSignal)207 static void USDHC_SetTransferInterrupt(USDHC_Type *base, bool usingInterruptSignal)
208 {
209 uint32_t interruptEnabled; /* The Interrupt status flags to be enabled */
210
211 /* Disable all interrupts */
212 USDHC_DisableInterruptStatus(base, (uint32_t)kUSDHC_AllInterruptFlags);
213 USDHC_DisableInterruptSignal(base, (uint32_t)kUSDHC_AllInterruptFlags);
214 DisableIRQ(s_usdhcIRQ[USDHC_GetInstance(base)]);
215
216 interruptEnabled = (kUSDHC_CommandFlag | kUSDHC_CardInsertionFlag | kUSDHC_DataFlag | kUSDHC_CardRemovalFlag |
217 kUSDHC_SDR104TuningFlag);
218
219 USDHC_EnableInterruptStatus(base, interruptEnabled);
220
221 if (usingInterruptSignal)
222 {
223 USDHC_EnableInterruptSignal(base, interruptEnabled);
224 }
225 }
226
USDHC_SetTransferConfig(USDHC_Type * base,usdhc_command_t * command,usdhc_data_t * data)227 static status_t USDHC_SetTransferConfig(USDHC_Type *base, usdhc_command_t *command, usdhc_data_t *data)
228 {
229 assert(NULL != command);
230
231 if ((data != NULL) && (data->blockCount > USDHC_MAX_BLOCK_COUNT))
232 {
233 return kStatus_InvalidArgument;
234 }
235
236 /* Define the flag corresponding to each response type. */
237 switch (command->responseType)
238 {
239 case kCARD_ResponseTypeNone:
240 break;
241 case kCARD_ResponseTypeR1: /* Response 1 */
242 case kCARD_ResponseTypeR5: /* Response 5 */
243 case kCARD_ResponseTypeR6: /* Response 6 */
244 case kCARD_ResponseTypeR7: /* Response 7 */
245
246 command->flags |= (kUSDHC_ResponseLength48Flag | kUSDHC_EnableCrcCheckFlag | kUSDHC_EnableIndexCheckFlag);
247 break;
248
249 case kCARD_ResponseTypeR1b: /* Response 1 with busy */
250 case kCARD_ResponseTypeR5b: /* Response 5 with busy */
251 command->flags |=
252 (kUSDHC_ResponseLength48BusyFlag | kUSDHC_EnableCrcCheckFlag | kUSDHC_EnableIndexCheckFlag);
253 break;
254
255 case kCARD_ResponseTypeR2: /* Response 2 */
256 command->flags |= (kUSDHC_ResponseLength136Flag | kUSDHC_EnableCrcCheckFlag);
257 break;
258
259 case kCARD_ResponseTypeR3: /* Response 3 */
260 case kCARD_ResponseTypeR4: /* Response 4 */
261 command->flags |= (kUSDHC_ResponseLength48Flag);
262 break;
263
264 default:
265 break;
266 }
267
268 if (command->type == kCARD_CommandTypeAbort)
269 {
270 command->flags |= kUSDHC_CommandTypeAbortFlag;
271 }
272
273 if (data)
274 {
275 command->flags |= kUSDHC_DataPresentFlag;
276
277 if (data->rxData)
278 {
279 command->flags |= kUSDHC_DataReadFlag;
280 }
281 if (data->blockCount > 1U)
282 {
283 command->flags |= (kUSDHC_MultipleBlockFlag | kUSDHC_EnableBlockCountFlag);
284 /* auto command 12 */
285 if (data->enableAutoCommand12)
286 {
287 /* Enable Auto command 12. */
288 command->flags |= kUSDHC_EnableAutoCommand12Flag;
289 }
290 /* auto command 23 */
291 if (data->enableAutoCommand23)
292 {
293 command->flags |= kUSDHC_EnableAutoCommand23Flag;
294 }
295 }
296 /* config data block size/block count */
297 base->BLK_ATT = ((base->BLK_ATT & ~(USDHC_BLK_ATT_BLKSIZE_MASK | USDHC_BLK_ATT_BLKCNT_MASK)) |
298 (USDHC_BLK_ATT_BLKSIZE(data->blockSize) | USDHC_BLK_ATT_BLKCNT(data->blockCount)));
299
300 /* auto command 23, auto send set block count cmd before multiple read/write */
301 if (((command->flags & kUSDHC_EnableAutoCommand23Flag) != 0U))
302 {
303 base->MIX_CTRL |= USDHC_MIX_CTRL_AC23EN_MASK;
304 base->VEND_SPEC2 |= USDHC_VEND_SPEC2_ACMD23_ARGU2_EN_MASK;
305 /* config the block count to DS_ADDR */
306 base->DS_ADDR = data->blockCount;
307 }
308 else
309 {
310 base->MIX_CTRL &= ~USDHC_MIX_CTRL_AC23EN_MASK;
311 base->VEND_SPEC2 &= ~USDHC_VEND_SPEC2_ACMD23_ARGU2_EN_MASK;
312 }
313 }
314
315 return kStatus_Success;
316 }
317
USDHC_ReceiveCommandResponse(USDHC_Type * base,usdhc_command_t * command)318 static status_t USDHC_ReceiveCommandResponse(USDHC_Type *base, usdhc_command_t *command)
319 {
320 uint32_t i;
321
322 if (command->responseType != kCARD_ResponseTypeNone)
323 {
324 command->response[0U] = base->CMD_RSP0;
325 if (command->responseType == kCARD_ResponseTypeR2)
326 {
327 command->response[1U] = base->CMD_RSP1;
328 command->response[2U] = base->CMD_RSP2;
329 command->response[3U] = base->CMD_RSP3;
330
331 i = 4U;
332 /* R3-R2-R1-R0(lowest 8 bit is invalid bit) has the same format as R2 format in SD specification document
333 after removed internal CRC7 and end bit. */
334 do
335 {
336 command->response[i - 1U] <<= 8U;
337 if (i > 1U)
338 {
339 command->response[i - 1U] |= ((command->response[i - 2U] & 0xFF000000U) >> 24U);
340 }
341 } while (i--);
342 }
343 }
344 /* check response error flag */
345 if ((command->responseErrorFlags != 0U) &&
346 ((command->responseType == kCARD_ResponseTypeR1) || (command->responseType == kCARD_ResponseTypeR1b) ||
347 (command->responseType == kCARD_ResponseTypeR6) || (command->responseType == kCARD_ResponseTypeR5)))
348 {
349 if (((command->responseErrorFlags) & (command->response[0U])) != 0U)
350 {
351 return kStatus_USDHC_SendCommandFailed;
352 }
353 }
354
355 return kStatus_Success;
356 }
357
USDHC_ReadDataPort(USDHC_Type * base,usdhc_data_t * data,uint32_t transferredWords)358 static uint32_t USDHC_ReadDataPort(USDHC_Type *base, usdhc_data_t *data, uint32_t transferredWords)
359 {
360 uint32_t i;
361 uint32_t totalWords;
362 uint32_t wordsCanBeRead; /* The words can be read at this time. */
363 uint32_t readWatermark = ((base->WTMK_LVL & USDHC_WTMK_LVL_RD_WML_MASK) >> USDHC_WTMK_LVL_RD_WML_SHIFT);
364
365 /*
366 * Add non aligned access support ,user need make sure your buffer size is big
367 * enough to hold the data,in other words,user need make sure the buffer size
368 * is 4 byte aligned
369 */
370 if (data->blockSize % sizeof(uint32_t) != 0U)
371 {
372 data->blockSize +=
373 sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
374 }
375
376 totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));
377
378 /* If watermark level is equal or bigger than totalWords, transfers totalWords data. */
379 if (readWatermark >= totalWords)
380 {
381 wordsCanBeRead = totalWords;
382 }
383 /* If watermark level is less than totalWords and left words to be sent is equal or bigger than readWatermark,
384 transfers watermark level words. */
385 else if ((readWatermark < totalWords) && ((totalWords - transferredWords) >= readWatermark))
386 {
387 wordsCanBeRead = readWatermark;
388 }
389 /* If watermark level is less than totalWords and left words to be sent is less than readWatermark, transfers left
390 words. */
391 else
392 {
393 wordsCanBeRead = (totalWords - transferredWords);
394 }
395
396 i = 0U;
397 while (i < wordsCanBeRead)
398 {
399 data->rxData[transferredWords++] = USDHC_ReadData(base);
400 i++;
401 }
402
403 return transferredWords;
404 }
405
USDHC_ReadByDataPortBlocking(USDHC_Type * base,usdhc_data_t * data)406 static status_t USDHC_ReadByDataPortBlocking(USDHC_Type *base, usdhc_data_t *data)
407 {
408 uint32_t totalWords;
409 uint32_t transferredWords = 0U, interruptStatus = 0U;
410 status_t error = kStatus_Success;
411
412 /*
413 * Add non aligned access support ,user need make sure your buffer size is big
414 * enough to hold the data,in other words,user need make sure the buffer size
415 * is 4 byte aligned
416 */
417 if (data->blockSize % sizeof(uint32_t) != 0U)
418 {
419 data->blockSize +=
420 sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
421 }
422
423 totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));
424
425 while ((error == kStatus_Success) && (transferredWords < totalWords))
426 {
427 while (!(USDHC_GetInterruptStatusFlags(base) &
428 (kUSDHC_BufferReadReadyFlag | kUSDHC_DataErrorFlag | kUSDHC_TuningErrorFlag)))
429 {
430 }
431
432 interruptStatus = USDHC_GetInterruptStatusFlags(base);
433 /* during std tuning process, software do not need to read data, but wait BRR is enough */
434 if ((data->executeTuning) && (interruptStatus & kUSDHC_BufferReadReadyFlag))
435 {
436 USDHC_ClearInterruptStatusFlags(base, kUSDHC_BufferReadReadyFlag | kUSDHC_TuningPassFlag);
437 return kStatus_Success;
438 }
439 else if ((interruptStatus & kUSDHC_TuningErrorFlag) != 0U)
440 {
441 USDHC_ClearInterruptStatusFlags(base, kUSDHC_TuningErrorFlag);
442 /* if tuning error occur ,return directly */
443 error = kStatus_USDHC_TuningError;
444 }
445 else if ((interruptStatus & kUSDHC_DataErrorFlag) != 0U)
446 {
447 if (!(data->enableIgnoreError))
448 {
449 error = kStatus_Fail;
450 }
451 /* clear data error flag */
452 USDHC_ClearInterruptStatusFlags(base, kUSDHC_DataErrorFlag);
453 }
454 else
455 {
456 }
457
458 if (error == kStatus_Success)
459 {
460 transferredWords = USDHC_ReadDataPort(base, data, transferredWords);
461 /* clear buffer read ready */
462 USDHC_ClearInterruptStatusFlags(base, kUSDHC_BufferReadReadyFlag);
463 }
464 }
465
466 /* Clear data complete flag after the last read operation. */
467 USDHC_ClearInterruptStatusFlags(base, kUSDHC_DataCompleteFlag);
468
469 return error;
470 }
471
USDHC_WriteDataPort(USDHC_Type * base,usdhc_data_t * data,uint32_t transferredWords)472 static uint32_t USDHC_WriteDataPort(USDHC_Type *base, usdhc_data_t *data, uint32_t transferredWords)
473 {
474 uint32_t i;
475 uint32_t totalWords;
476 uint32_t wordsCanBeWrote; /* Words can be wrote at this time. */
477 uint32_t writeWatermark = ((base->WTMK_LVL & USDHC_WTMK_LVL_WR_WML_MASK) >> USDHC_WTMK_LVL_WR_WML_SHIFT);
478
479 /*
480 * Add non aligned access support ,user need make sure your buffer size is big
481 * enough to hold the data,in other words,user need make sure the buffer size
482 * is 4 byte aligned
483 */
484 if (data->blockSize % sizeof(uint32_t) != 0U)
485 {
486 data->blockSize +=
487 sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
488 }
489
490 totalWords = ((data->blockCount * data->blockSize) / sizeof(uint32_t));
491
492 /* If watermark level is equal or bigger than totalWords, transfers totalWords data.*/
493 if (writeWatermark >= totalWords)
494 {
495 wordsCanBeWrote = totalWords;
496 }
497 /* If watermark level is less than totalWords and left words to be sent is equal or bigger than watermark,
498 transfers watermark level words. */
499 else if ((writeWatermark < totalWords) && ((totalWords - transferredWords) >= writeWatermark))
500 {
501 wordsCanBeWrote = writeWatermark;
502 }
503 /* If watermark level is less than totalWords and left words to be sent is less than watermark, transfers left
504 words. */
505 else
506 {
507 wordsCanBeWrote = (totalWords - transferredWords);
508 }
509
510 i = 0U;
511 while (i < wordsCanBeWrote)
512 {
513 USDHC_WriteData(base, data->txData[transferredWords++]);
514 i++;
515 }
516
517 return transferredWords;
518 }
519
USDHC_WriteByDataPortBlocking(USDHC_Type * base,usdhc_data_t * data)520 static status_t USDHC_WriteByDataPortBlocking(USDHC_Type *base, usdhc_data_t *data)
521 {
522 uint32_t totalWords;
523
524 uint32_t transferredWords = 0U, interruptStatus = 0U;
525 status_t error = kStatus_Success;
526
527 /*
528 * Add non aligned access support ,user need make sure your buffer size is big
529 * enough to hold the data,in other words,user need make sure the buffer size
530 * is 4 byte aligned
531 */
532 if (data->blockSize % sizeof(uint32_t) != 0U)
533 {
534 data->blockSize +=
535 sizeof(uint32_t) - (data->blockSize % sizeof(uint32_t)); /* make the block size as word-aligned */
536 }
537
538 totalWords = (data->blockCount * data->blockSize) / sizeof(uint32_t);
539
540 while ((error == kStatus_Success) && (transferredWords < totalWords))
541 {
542 while (!(USDHC_GetInterruptStatusFlags(base) &
543 (kUSDHC_BufferWriteReadyFlag | kUSDHC_DataErrorFlag | kUSDHC_TuningErrorFlag)))
544 {
545 }
546
547 interruptStatus = USDHC_GetInterruptStatusFlags(base);
548
549 if ((interruptStatus & kUSDHC_TuningErrorFlag) != 0U)
550 {
551 USDHC_ClearInterruptStatusFlags(base, kUSDHC_TuningErrorFlag);
552 /* if tuning error occur ,return directly */
553 return kStatus_USDHC_TuningError;
554 }
555 else if ((interruptStatus & kUSDHC_DataErrorFlag) != 0U)
556 {
557 if (!(data->enableIgnoreError))
558 {
559 error = kStatus_Fail;
560 }
561 /* clear data error flag */
562 USDHC_ClearInterruptStatusFlags(base, kUSDHC_DataErrorFlag);
563 }
564 else
565 {
566 }
567
568 if (error == kStatus_Success)
569 {
570 transferredWords = USDHC_WriteDataPort(base, data, transferredWords);
571 /* clear buffer write ready */
572 USDHC_ClearInterruptStatusFlags(base, kUSDHC_BufferWriteReadyFlag);
573 }
574 }
575
576 /* Wait write data complete or data transfer error after the last writing operation. */
577 while (!(USDHC_GetInterruptStatusFlags(base) & (kUSDHC_DataCompleteFlag | kUSDHC_DataErrorFlag)))
578 {
579 }
580
581 if ((USDHC_GetInterruptStatusFlags(base) & kUSDHC_DataErrorFlag) != 0U)
582 {
583 if (!(data->enableIgnoreError))
584 {
585 error = kStatus_Fail;
586 }
587 }
588 USDHC_ClearInterruptStatusFlags(base, (kUSDHC_DataCompleteFlag | kUSDHC_DataErrorFlag));
589
590 return error;
591 }
592
USDHC_SendCommand(USDHC_Type * base,usdhc_command_t * command)593 void USDHC_SendCommand(USDHC_Type *base, usdhc_command_t *command)
594 {
595 assert(NULL != command);
596
597 uint32_t mixCtrl, xferType;
598
599 mixCtrl = base->MIX_CTRL;
600 xferType = base->CMD_XFR_TYP;
601
602 /* config mix parameter */
603 mixCtrl &= ~(USDHC_MIX_CTRL_MSBSEL_MASK | USDHC_MIX_CTRL_BCEN_MASK | USDHC_MIX_CTRL_DTDSEL_MASK |
604 USDHC_MIX_CTRL_AC12EN_MASK);
605 mixCtrl |= ((command->flags) & (USDHC_MIX_CTRL_MSBSEL_MASK | USDHC_MIX_CTRL_BCEN_MASK | USDHC_MIX_CTRL_DTDSEL_MASK |
606 USDHC_MIX_CTRL_AC12EN_MASK));
607
608 /* config cmd index */
609 xferType &= ~(USDHC_CMD_XFR_TYP_CMDINX_MASK | USDHC_CMD_XFR_TYP_DPSEL_MASK | USDHC_CMD_XFR_TYP_CMDTYP_MASK |
610 USDHC_CMD_XFR_TYP_CICEN_MASK | USDHC_CMD_XFR_TYP_CCCEN_MASK | USDHC_CMD_XFR_TYP_RSPTYP_MASK);
611
612 xferType |= (((command->index << USDHC_CMD_XFR_TYP_CMDINX_SHIFT) & USDHC_CMD_XFR_TYP_CMDINX_MASK) |
613 ((command->flags) &
614 (USDHC_CMD_XFR_TYP_DPSEL_MASK | USDHC_CMD_XFR_TYP_CMDTYP_MASK | USDHC_CMD_XFR_TYP_CICEN_MASK |
615 USDHC_CMD_XFR_TYP_CCCEN_MASK | USDHC_CMD_XFR_TYP_RSPTYP_MASK)));
616 /* config the mix parameter */
617 base->MIX_CTRL = mixCtrl;
618 /* config the command xfertype and argument */
619 base->CMD_ARG = command->argument;
620 base->CMD_XFR_TYP = xferType;
621 }
622
USDHC_WaitCommandDone(USDHC_Type * base,usdhc_command_t * command,bool executeTuning)623 static status_t USDHC_WaitCommandDone(USDHC_Type *base, usdhc_command_t *command, bool executeTuning)
624 {
625 assert(NULL != command);
626
627 status_t error = kStatus_Success;
628 uint32_t interruptStatus = 0U;
629 /* tuning cmd do not need to wait command done */
630 if (!executeTuning)
631 {
632 /* Wait command complete or USDHC encounters error. */
633 while (!(USDHC_GetInterruptStatusFlags(base) & (kUSDHC_CommandCompleteFlag | kUSDHC_CommandErrorFlag)))
634 {
635 }
636
637 interruptStatus = USDHC_GetInterruptStatusFlags(base);
638
639 if ((interruptStatus & kUSDHC_TuningErrorFlag) != 0U)
640 {
641 error = kStatus_USDHC_TuningError;
642 }
643 else if ((interruptStatus & kUSDHC_CommandErrorFlag) != 0U)
644 {
645 error = kStatus_Fail;
646 }
647 else
648 {
649 }
650 /* Receive response when command completes successfully. */
651 if (error == kStatus_Success)
652 {
653 error = USDHC_ReceiveCommandResponse(base, command);
654 }
655
656 USDHC_ClearInterruptStatusFlags(
657 base, (kUSDHC_CommandCompleteFlag | kUSDHC_CommandErrorFlag | kUSDHC_TuningErrorFlag));
658 }
659
660 return error;
661 }
662
USDHC_TransferDataBlocking(USDHC_Type * base,usdhc_data_t * data,bool enDMA)663 static status_t USDHC_TransferDataBlocking(USDHC_Type *base, usdhc_data_t *data, bool enDMA)
664 {
665 status_t error = kStatus_Success;
666 uint32_t interruptStatus = 0U;
667
668 if (enDMA)
669 {
670 /* Wait data complete or USDHC encounters error. */
671 while (!(USDHC_GetInterruptStatusFlags(base) &
672 (kUSDHC_DataCompleteFlag | kUSDHC_DataErrorFlag | kUSDHC_DmaErrorFlag | kUSDHC_TuningErrorFlag)))
673 {
674 }
675
676 interruptStatus = USDHC_GetInterruptStatusFlags(base);
677
678 if ((interruptStatus & kUSDHC_TuningErrorFlag) != 0U)
679 {
680 error = kStatus_USDHC_TuningError;
681 }
682 else if ((interruptStatus & (kUSDHC_DataErrorFlag | kUSDHC_DmaErrorFlag)) != 0U)
683 {
684 if ((!(data->enableIgnoreError)) || (interruptStatus & kUSDHC_DataTimeoutFlag))
685 {
686 error = kStatus_Fail;
687 }
688 }
689 else
690 {
691 }
692
693 USDHC_ClearInterruptStatusFlags(base, (kUSDHC_DataCompleteFlag | kUSDHC_DataErrorFlag | kUSDHC_DmaErrorFlag |
694 kUSDHC_TuningPassFlag | kUSDHC_TuningErrorFlag));
695 }
696 else
697 {
698 if (data->rxData)
699 {
700 error = USDHC_ReadByDataPortBlocking(base, data);
701 }
702 else
703 {
704 error = USDHC_WriteByDataPortBlocking(base, data);
705 }
706 }
707
708 #if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
709 /* invalidate cache for read */
710 if ((data != NULL) && (data->rxData != NULL))
711 {
712 /* invalidate the DCACHE */
713 DCACHE_InvalidateByRange((uint32_t)data->rxData, (data->blockSize) * (data->blockCount));
714 }
715 #endif
716
717 return error;
718 }
719
USDHC_Init(USDHC_Type * base,const usdhc_config_t * config)720 void USDHC_Init(USDHC_Type *base, const usdhc_config_t *config)
721 {
722 assert(config);
723 assert((config->writeWatermarkLevel >= 1U) && (config->writeWatermarkLevel <= 128U));
724 assert((config->readWatermarkLevel >= 1U) && (config->readWatermarkLevel <= 128U));
725 assert(config->writeBurstLen <= 16U);
726
727 uint32_t proctl, sysctl, wml;
728
729 /* Enable USDHC clock. */
730 CLOCK_EnableClock(s_usdhcClock[USDHC_GetInstance(base)]);
731
732 /* Reset USDHC. */
733 USDHC_Reset(base, kUSDHC_ResetAll, 100U);
734
735 proctl = base->PROT_CTRL;
736 wml = base->WTMK_LVL;
737 sysctl = base->SYS_CTRL;
738
739 proctl &= ~(USDHC_PROT_CTRL_EMODE_MASK | USDHC_PROT_CTRL_DMASEL_MASK);
740 /* Endian mode*/
741 proctl |= USDHC_PROT_CTRL_EMODE(config->endianMode);
742
743 /* Watermark level */
744 wml &= ~(USDHC_WTMK_LVL_RD_WML_MASK | USDHC_WTMK_LVL_WR_WML_MASK | USDHC_WTMK_LVL_RD_BRST_LEN_MASK |
745 USDHC_WTMK_LVL_WR_BRST_LEN_MASK);
746 wml |= (USDHC_WTMK_LVL_RD_WML(config->readWatermarkLevel) | USDHC_WTMK_LVL_WR_WML(config->writeWatermarkLevel) |
747 USDHC_WTMK_LVL_RD_BRST_LEN(config->readBurstLen) | USDHC_WTMK_LVL_WR_BRST_LEN(config->writeBurstLen));
748
749 /* config the data timeout value */
750 sysctl &= ~USDHC_SYS_CTRL_DTOCV_MASK;
751 sysctl |= USDHC_SYS_CTRL_DTOCV(config->dataTimeout);
752
753 base->SYS_CTRL = sysctl;
754 base->WTMK_LVL = wml;
755 base->PROT_CTRL = proctl;
756
757 #if FSL_FEATURE_USDHC_HAS_EXT_DMA
758 /* disable external DMA */
759 base->VEND_SPEC &= ~USDHC_VEND_SPEC_EXT_DMA_EN_MASK;
760 #endif
761 /* disable internal DMA */
762 base->MIX_CTRL &= ~USDHC_MIX_CTRL_DMAEN_MASK;
763
764 /* Enable interrupt status but doesn't enable interrupt signal. */
765 USDHC_SetTransferInterrupt(base, false);
766 }
767
USDHC_Deinit(USDHC_Type * base)768 void USDHC_Deinit(USDHC_Type *base)
769 {
770 /* Disable clock. */
771 CLOCK_DisableClock(s_usdhcClock[USDHC_GetInstance(base)]);
772 }
773
USDHC_Reset(USDHC_Type * base,uint32_t mask,uint32_t timeout)774 bool USDHC_Reset(USDHC_Type *base, uint32_t mask, uint32_t timeout)
775 {
776 base->SYS_CTRL |= (mask & (USDHC_SYS_CTRL_RSTA_MASK | USDHC_SYS_CTRL_RSTC_MASK | USDHC_SYS_CTRL_RSTD_MASK));
777 /* Delay some time to wait reset success. */
778 while ((base->SYS_CTRL & mask) != 0U)
779 {
780 if (timeout == 0U)
781 {
782 break;
783 }
784 timeout--;
785 }
786
787 return ((!timeout) ? false : true);
788 }
789
USDHC_GetCapability(USDHC_Type * base,usdhc_capability_t * capability)790 void USDHC_GetCapability(USDHC_Type *base, usdhc_capability_t *capability)
791 {
792 assert(capability);
793
794 uint32_t htCapability;
795 uint32_t maxBlockLength;
796
797 htCapability = base->HOST_CTRL_CAP;
798
799 /* Get the capability of USDHC. */
800 maxBlockLength = ((htCapability & USDHC_HOST_CTRL_CAP_MBL_MASK) >> USDHC_HOST_CTRL_CAP_MBL_SHIFT);
801 capability->maxBlockLength = (512U << maxBlockLength);
802 /* Other attributes not in HTCAPBLT register. */
803 capability->maxBlockCount = USDHC_MAX_BLOCK_COUNT;
804 capability->flags = (htCapability & (kUSDHC_SupportAdmaFlag | kUSDHC_SupportHighSpeedFlag | kUSDHC_SupportDmaFlag |
805 kUSDHC_SupportSuspendResumeFlag | kUSDHC_SupportV330Flag));
806 capability->flags |= (htCapability & kUSDHC_SupportV300Flag);
807 capability->flags |= (htCapability & kUSDHC_SupportV180Flag);
808 capability->flags |=
809 (htCapability & (kUSDHC_SupportDDR50Flag | kUSDHC_SupportSDR104Flag | kUSDHC_SupportSDR50Flag));
810 /* USDHC support 4/8 bit data bus width. */
811 capability->flags |= (kUSDHC_Support4BitFlag | kUSDHC_Support8BitFlag);
812 }
813
USDHC_SetSdClock(USDHC_Type * base,uint32_t srcClock_Hz,uint32_t busClock_Hz)814 uint32_t USDHC_SetSdClock(USDHC_Type *base, uint32_t srcClock_Hz, uint32_t busClock_Hz)
815 {
816 assert(srcClock_Hz != 0U);
817 assert((busClock_Hz != 0U) && (busClock_Hz <= srcClock_Hz));
818
819 uint32_t totalDiv = 0U;
820 uint32_t divisor = 0U;
821 uint32_t prescaler = 0U;
822 uint32_t sysctl = 0U;
823 uint32_t nearestFrequency = 0U;
824 uint32_t maxClKFS = ((USDHC_SYS_CTRL_SDCLKFS_MASK >> USDHC_SYS_CTRL_SDCLKFS_SHIFT) + 1U);
825 bool enDDR = false;
826 /* DDR mode max clkfs can reach 512 */
827 if ((base->MIX_CTRL & USDHC_MIX_CTRL_DDR_EN_MASK) != 0U)
828 {
829 enDDR = true;
830 maxClKFS *= 2U;
831 }
832 /* calucate total divisor first */
833 totalDiv = srcClock_Hz / busClock_Hz;
834
835 if (totalDiv != 0U)
836 {
837 /* calucate the divisor (srcClock_Hz / divisor) <= busClock_Hz */
838 if ((srcClock_Hz / totalDiv) > busClock_Hz)
839 {
840 totalDiv++;
841 }
842
843 /* divide the total divisor to div and prescaler */
844 if (totalDiv > USDHC_MAX_DVS)
845 {
846 prescaler = totalDiv / USDHC_MAX_DVS;
847 /* prescaler must be a value which equal 2^n and smaller than SDHC_MAX_CLKFS */
848 while (((maxClKFS % prescaler) != 0U) || (prescaler == 1U))
849 {
850 prescaler++;
851 }
852 /* calucate the divisor */
853 divisor = totalDiv / prescaler;
854 /* fine tuning the divisor until divisor * prescaler >= totalDiv */
855 while ((divisor * prescaler) < totalDiv)
856 {
857 divisor++;
858 }
859 nearestFrequency = srcClock_Hz / divisor / prescaler;
860 }
861 else
862 {
863 /* in this situation , divsior and SDCLKFS can generate same clock
864 use SDCLKFS*/
865 if ((USDHC_MAX_DVS % totalDiv) == 0U)
866 {
867 divisor = 0U;
868 prescaler = totalDiv;
869 }
870 else
871 {
872 divisor = totalDiv;
873 prescaler = 0U;
874 }
875 nearestFrequency = srcClock_Hz / totalDiv;
876 }
877 }
878 /* in this condition , srcClock_Hz = busClock_Hz, */
879 else
880 {
881 /* in DDR mode , set SDCLKFS to 0, divisor = 0, actually the
882 totoal divider = 2U */
883 divisor = 0U;
884 prescaler = 0U;
885 nearestFrequency = srcClock_Hz;
886 }
887
888 /* calucate the value write to register */
889 if (divisor != 0U)
890 {
891 USDHC_PREV_DVS(divisor);
892 }
893 /* calucate the value write to register */
894 if (prescaler != 0U)
895 {
896 USDHC_PREV_CLKFS(prescaler, (enDDR ? 2U : 1U));
897 }
898
899 /* Set the SD clock frequency divisor, SD clock frequency select, data timeout counter value. */
900 sysctl = base->SYS_CTRL;
901 sysctl &= ~(USDHC_SYS_CTRL_DVS_MASK | USDHC_SYS_CTRL_SDCLKFS_MASK);
902 sysctl |= (USDHC_SYS_CTRL_DVS(divisor) | USDHC_SYS_CTRL_SDCLKFS(prescaler));
903 base->SYS_CTRL = sysctl;
904
905 /* Wait until the SD clock is stable. */
906 while (!(base->PRES_STATE & USDHC_PRES_STATE_SDSTB_MASK))
907 {
908 }
909
910 return nearestFrequency;
911 }
912
USDHC_SetCardActive(USDHC_Type * base,uint32_t timeout)913 bool USDHC_SetCardActive(USDHC_Type *base, uint32_t timeout)
914 {
915 base->SYS_CTRL |= USDHC_SYS_CTRL_INITA_MASK;
916 /* Delay some time to wait card become active state. */
917 while ((base->SYS_CTRL & USDHC_SYS_CTRL_INITA_MASK) == USDHC_SYS_CTRL_INITA_MASK)
918 {
919 if (!timeout)
920 {
921 break;
922 }
923 timeout--;
924 }
925
926 return ((!timeout) ? false : true);
927 }
928
USDHC_SetMmcBootConfig(USDHC_Type * base,const usdhc_boot_config_t * config)929 void USDHC_SetMmcBootConfig(USDHC_Type *base, const usdhc_boot_config_t *config)
930 {
931 assert(config);
932 assert(config->ackTimeoutCount <= (USDHC_MMC_BOOT_DTOCV_ACK_MASK >> USDHC_MMC_BOOT_DTOCV_ACK_SHIFT));
933 assert(config->blockCount <= (USDHC_MMC_BOOT_BOOT_BLK_CNT_MASK >> USDHC_MMC_BOOT_BOOT_BLK_CNT_SHIFT));
934
935 uint32_t mmcboot = 0U;
936
937 mmcboot = (USDHC_MMC_BOOT_DTOCV_ACK(config->ackTimeoutCount) | USDHC_MMC_BOOT_BOOT_MODE(config->bootMode) |
938 USDHC_MMC_BOOT_BOOT_BLK_CNT(config->blockCount));
939
940 if (config->enableBootAck)
941 {
942 mmcboot |= USDHC_MMC_BOOT_BOOT_ACK_MASK;
943 }
944 if (config->enableBoot)
945 {
946 mmcboot |= USDHC_MMC_BOOT_BOOT_EN_MASK;
947 }
948 if (config->enableAutoStopAtBlockGap)
949 {
950 mmcboot |= USDHC_MMC_BOOT_AUTO_SABG_EN_MASK;
951 }
952
953 base->MMC_BOOT = mmcboot;
954 }
955
USDHC_SetAdmaTableConfig(USDHC_Type * base,usdhc_adma_config_t * dmaConfig,usdhc_data_t * dataConfig,uint32_t flags)956 status_t USDHC_SetAdmaTableConfig(USDHC_Type *base,
957 usdhc_adma_config_t *dmaConfig,
958 usdhc_data_t *dataConfig,
959 uint32_t flags)
960 {
961 assert(NULL != dmaConfig);
962 assert(NULL != dmaConfig->admaTable);
963 assert(NULL != dataConfig);
964
965 const uint32_t *startAddress;
966 uint32_t entries;
967 uint32_t i, dmaBufferLen = 0U;
968 usdhc_adma1_descriptor_t *adma1EntryAddress;
969 usdhc_adma2_descriptor_t *adma2EntryAddress;
970 uint32_t dataBytes = dataConfig->blockSize * dataConfig->blockCount;
971 const uint32_t *data = (dataConfig->rxData == NULL) ? dataConfig->txData : dataConfig->rxData;
972
973 /* check DMA data buffer address align or not */
974 if (((dmaConfig->dmaMode == kUSDHC_DmaModeAdma1) && (((uint32_t)data % USDHC_ADMA1_ADDRESS_ALIGN) != 0U)) ||
975 ((dmaConfig->dmaMode == kUSDHC_DmaModeAdma2) && (((uint32_t)data % USDHC_ADMA2_ADDRESS_ALIGN) != 0U)) ||
976 ((dmaConfig->dmaMode == kUSDHC_DmaModeSimple) && (((uint32_t)data % USDHC_ADMA2_ADDRESS_ALIGN) != 0U)))
977 {
978 return kStatus_USDHC_DMADataAddrNotAlign;
979 }
980
981 /*
982 * Add non aligned access support ,user need make sure your buffer size is big
983 * enough to hold the data,in other words,user need make sure the buffer size
984 * is 4 byte aligned
985 */
986 if (dataBytes % sizeof(uint32_t) != 0U)
987 {
988 /* make the data length as word-aligned */
989 dataBytes += sizeof(uint32_t) - (dataBytes % sizeof(uint32_t));
990 }
991
992 startAddress = data;
993
994 switch (dmaConfig->dmaMode)
995 {
996 #if FSL_FEATURE_USDHC_HAS_EXT_DMA
997 case kUSDHC_ExternalDMA:
998 /* enable the external DMA */
999 base->VEND_SPEC |= USDHC_VEND_SPEC_EXT_DMA_EN_MASK;
1000 break;
1001 #endif
1002 case kUSDHC_DmaModeSimple:
1003 /* in simple DMA mode if use auto CMD23, address should load to ADMA addr,
1004 and block count should load to DS_ADDR*/
1005 if ((flags & kUSDHC_EnableAutoCommand23Flag) != 0U)
1006 {
1007 base->ADMA_SYS_ADDR = (uint32_t)data;
1008 }
1009 else
1010 {
1011 base->DS_ADDR = (uint32_t)data;
1012 }
1013
1014 break;
1015
1016 case kUSDHC_DmaModeAdma1:
1017
1018 /* Check if ADMA descriptor's number is enough. */
1019 if ((dataBytes % USDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY) == 0U)
1020 {
1021 entries = dataBytes / USDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY;
1022 }
1023 else
1024 {
1025 entries = ((dataBytes / USDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY) + 1U);
1026 }
1027
1028 /* ADMA1 needs two descriptors to finish a transfer */
1029 entries <<= 1U;
1030
1031 if (entries > ((dmaConfig->admaTableWords * sizeof(uint32_t)) / sizeof(usdhc_adma1_descriptor_t)))
1032 {
1033 return kStatus_OutOfRange;
1034 }
1035
1036 adma1EntryAddress = (usdhc_adma1_descriptor_t *)(dmaConfig->admaTable);
1037 for (i = 0U; i < entries; i += 2U)
1038 {
1039 if (dataBytes > USDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY)
1040 {
1041 dmaBufferLen = USDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY;
1042 dataBytes -= dmaBufferLen;
1043 }
1044 else
1045 {
1046 dmaBufferLen = dataBytes;
1047 }
1048
1049 adma1EntryAddress[i] = (dmaBufferLen << USDHC_ADMA1_DESCRIPTOR_LENGTH_SHIFT);
1050 adma1EntryAddress[i] |= kUSDHC_Adma1DescriptorTypeSetLength;
1051 adma1EntryAddress[i + 1U] = ((uint32_t)(startAddress) << USDHC_ADMA1_DESCRIPTOR_ADDRESS_SHIFT);
1052 adma1EntryAddress[i + 1U] |= kUSDHC_Adma1DescriptorTypeTransfer;
1053 startAddress += dmaBufferLen / sizeof(uint32_t);
1054 }
1055 /* the end of the descriptor */
1056 adma1EntryAddress[i - 1U] |= kUSDHC_Adma1DescriptorEndFlag;
1057 /* When use ADMA, disable simple DMA */
1058 base->DS_ADDR = 0U;
1059 base->ADMA_SYS_ADDR = (uint32_t)(dmaConfig->admaTable);
1060 break;
1061
1062 case kUSDHC_DmaModeAdma2:
1063 /* Check if ADMA descriptor's number is enough. */
1064 if ((dataBytes % USDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY) == 0U)
1065 {
1066 entries = dataBytes / USDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY;
1067 }
1068 else
1069 {
1070 entries = ((dataBytes / USDHC_ADMA1_DESCRIPTOR_MAX_LENGTH_PER_ENTRY) + 1U);
1071 }
1072
1073 if (entries > ((dmaConfig->admaTableWords * sizeof(uint32_t)) / sizeof(usdhc_adma2_descriptor_t)))
1074 {
1075 return kStatus_OutOfRange;
1076 }
1077
1078 adma2EntryAddress = (usdhc_adma2_descriptor_t *)(dmaConfig->admaTable);
1079 for (i = 0U; i < entries; i++)
1080 {
1081 if (dataBytes > USDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY)
1082 {
1083 dmaBufferLen = USDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY;
1084 dataBytes -= dmaBufferLen;
1085 }
1086 else
1087 {
1088 dmaBufferLen = dataBytes;
1089 }
1090
1091 /* Each descriptor for ADMA2 is 64-bit in length */
1092 adma2EntryAddress[i].address = startAddress;
1093 adma2EntryAddress[i].attribute = (dmaBufferLen << USDHC_ADMA2_DESCRIPTOR_LENGTH_SHIFT);
1094 adma2EntryAddress[i].attribute |= kUSDHC_Adma2DescriptorTypeTransfer;
1095 startAddress += (dmaBufferLen / sizeof(uint32_t));
1096 }
1097 /* set the end bit */
1098 adma2EntryAddress[i - 1U].attribute |= kUSDHC_Adma2DescriptorEndFlag;
1099 /* When use ADMA, disable simple DMA */
1100 base->DS_ADDR = 0U;
1101 base->ADMA_SYS_ADDR = (uint32_t)(dmaConfig->admaTable);
1102
1103 break;
1104 default:
1105 return kStatus_USDHC_PrepareAdmaDescriptorFailed;
1106 }
1107
1108 /* for external dma */
1109 if (dmaConfig->dmaMode != kUSDHC_ExternalDMA)
1110 {
1111 #if FSL_FEATURE_USDHC_HAS_EXT_DMA
1112 /* disable the external DMA if support */
1113 base->VEND_SPEC &= ~USDHC_VEND_SPEC_EXT_DMA_EN_MASK;
1114 #endif
1115 /* select DMA mode and config the burst length */
1116 base->PROT_CTRL &= ~(USDHC_PROT_CTRL_DMASEL_MASK | USDHC_PROT_CTRL_BURST_LEN_EN_MASK);
1117 base->PROT_CTRL |=
1118 USDHC_PROT_CTRL_DMASEL(dmaConfig->dmaMode) | USDHC_PROT_CTRL_BURST_LEN_EN(dmaConfig->burstLen);
1119 /* enable DMA */
1120 base->MIX_CTRL |= USDHC_MIX_CTRL_DMAEN_MASK;
1121 }
1122
1123 /* disable the interrupt signal for interrupt mode */
1124 USDHC_DisableInterruptSignal(base, kUSDHC_BufferReadReadyFlag | kUSDHC_BufferWriteReadyFlag);
1125
1126 return kStatus_Success;
1127 }
1128
USDHC_TransferBlocking(USDHC_Type * base,usdhc_adma_config_t * dmaConfig,usdhc_transfer_t * transfer)1129 status_t USDHC_TransferBlocking(USDHC_Type *base, usdhc_adma_config_t *dmaConfig, usdhc_transfer_t *transfer)
1130 {
1131 assert(transfer);
1132
1133 status_t error = kStatus_Success;
1134 usdhc_command_t *command = transfer->command;
1135 usdhc_data_t *data = transfer->data;
1136 bool enDMA = false;
1137
1138 /* Wait until command/data bus out of busy status. */
1139 while (USDHC_GetPresentStatusFlags(base) & kUSDHC_CommandInhibitFlag)
1140 {
1141 }
1142 while (data && (USDHC_GetPresentStatusFlags(base) & kUSDHC_DataInhibitFlag))
1143 {
1144 }
1145 /*check re-tuning request*/
1146 if ((USDHC_GetInterruptStatusFlags(base) & kUSDHC_ReTuningEventFlag) != 0U)
1147 {
1148 USDHC_ClearInterruptStatusFlags(base, kUSDHC_ReTuningEventFlag);
1149 return kStatus_USDHC_ReTuningRequest;
1150 }
1151
1152 #if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
1153 if ((data != NULL) && (!(data->executeTuning)))
1154 {
1155 if (data->txData != NULL)
1156 {
1157 /* clear the DCACHE */
1158 DCACHE_CleanByRange((uint32_t)data->txData, (data->blockSize) * (data->blockCount));
1159 }
1160 else
1161 {
1162 /* clear the DCACHE */
1163 DCACHE_CleanByRange((uint32_t)data->rxData, (data->blockSize) * (data->blockCount));
1164 }
1165 }
1166 #endif
1167
1168 /* config the transfer parameter */
1169 if (kStatus_Success != USDHC_SetTransferConfig(base, command, data))
1170 {
1171 return kStatus_InvalidArgument;
1172 }
1173
1174 /* Update ADMA descriptor table according to different DMA mode(no DMA, ADMA1, ADMA2).*/
1175 if ((data != NULL) && (dmaConfig != NULL) && (!data->executeTuning))
1176 {
1177 error = USDHC_SetAdmaTableConfig(base, dmaConfig, data, command->flags);
1178 /* if the target data buffer address is not align , we change the transfer mode
1179 * to polling automatically, other DMA config error will not cover by driver, user
1180 * should handle it
1181 */
1182 if ((error != kStatus_USDHC_DMADataAddrNotAlign) && (error != kStatus_Success))
1183 {
1184 return kStatus_USDHC_PrepareAdmaDescriptorFailed;
1185 }
1186 else if (error == kStatus_USDHC_DMADataAddrNotAlign)
1187 {
1188 enDMA = false;
1189 /* disable DMA, using polling mode in this situation */
1190 base->MIX_CTRL &= ~USDHC_MIX_CTRL_DMAEN_MASK;
1191 }
1192 else
1193 {
1194 enDMA = true;
1195 }
1196 }
1197 else
1198 {
1199 /* disable DMA */
1200 base->MIX_CTRL &= ~USDHC_MIX_CTRL_DMAEN_MASK;
1201 }
1202 /* send command */
1203 USDHC_SendCommand(base, command);
1204 /* wait command done */
1205 error = USDHC_WaitCommandDone(base, command, ((data == NULL) ? false : data->executeTuning));
1206 /* transfer data */
1207 if ((data != NULL) && (error == kStatus_Success))
1208 {
1209 return USDHC_TransferDataBlocking(base, data, enDMA);
1210 }
1211
1212 return error;
1213 }
1214
USDHC_TransferNonBlocking(USDHC_Type * base,usdhc_handle_t * handle,usdhc_adma_config_t * dmaConfig,usdhc_transfer_t * transfer)1215 status_t USDHC_TransferNonBlocking(USDHC_Type *base,
1216 usdhc_handle_t *handle,
1217 usdhc_adma_config_t *dmaConfig,
1218 usdhc_transfer_t *transfer)
1219 {
1220 assert(handle);
1221 assert(transfer);
1222
1223 status_t error = kStatus_Success;
1224 usdhc_command_t *command = transfer->command;
1225 usdhc_data_t *data = transfer->data;
1226
1227 /* Wait until command/data bus out of busy status. */
1228 if ((USDHC_GetPresentStatusFlags(base) & kUSDHC_CommandInhibitFlag) ||
1229 (data && (USDHC_GetPresentStatusFlags(base) & kUSDHC_DataInhibitFlag)))
1230 {
1231 return kStatus_USDHC_BusyTransferring;
1232 }
1233
1234 /*check re-tuning request*/
1235 if ((USDHC_GetInterruptStatusFlags(base) & (kUSDHC_ReTuningEventFlag)) != 0U)
1236 {
1237 USDHC_ClearInterruptStatusFlags(base, kUSDHC_ReTuningEventFlag);
1238 return kStatus_USDHC_ReTuningRequest;
1239 }
1240
1241 #if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
1242 if ((data != NULL) && (!(data->executeTuning)))
1243 {
1244 if (data->txData != NULL)
1245 {
1246 /* clear the DCACHE */
1247 DCACHE_CleanByRange((uint32_t)data->txData, (data->blockSize) * (data->blockCount));
1248 }
1249 else
1250 {
1251 /* clear the DCACHE */
1252 DCACHE_CleanByRange((uint32_t)data->rxData, (data->blockSize) * (data->blockCount));
1253 }
1254 }
1255 #endif
1256
1257 /* Save command and data into handle before transferring. */
1258 handle->command = command;
1259 handle->data = data;
1260 handle->interruptFlags = 0U;
1261 /* transferredWords will only be updated in ISR when transfer way is DATAPORT. */
1262 handle->transferredWords = 0U;
1263
1264 if (kStatus_Success != USDHC_SetTransferConfig(base, command, data))
1265 {
1266 return kStatus_InvalidArgument;
1267 }
1268
1269 /* Update ADMA descriptor table according to different DMA mode(no DMA, ADMA1, ADMA2).*/
1270 if ((data != NULL) && (dmaConfig != NULL) && (!data->executeTuning))
1271 {
1272 error = USDHC_SetAdmaTableConfig(base, dmaConfig, data, command->flags);
1273 /* if the target data buffer address is not align , we change the transfer mode
1274 * to polling automatically, other DMA config error will not cover by driver, user
1275 * should handle it
1276 */
1277 if ((error != kStatus_USDHC_DMADataAddrNotAlign) && (error != kStatus_Success))
1278 {
1279 return kStatus_USDHC_PrepareAdmaDescriptorFailed;
1280 }
1281 else if (error == kStatus_USDHC_DMADataAddrNotAlign)
1282 {
1283 /* disable DMA, using polling mode in this situation */
1284 base->MIX_CTRL &= ~USDHC_MIX_CTRL_DMAEN_MASK;
1285 /* enable the interrupt signal for interrupt mode */
1286 USDHC_EnableInterruptSignal(base, kUSDHC_BufferReadReadyFlag | kUSDHC_BufferWriteReadyFlag);
1287 }
1288 else
1289 {
1290 }
1291 }
1292 else
1293 {
1294 /* disable DMA */
1295 base->MIX_CTRL &= ~USDHC_MIX_CTRL_DMAEN_MASK;
1296 }
1297
1298 /* enable the buffer read ready for std tuning */
1299 if ((data != NULL) && (data->executeTuning))
1300 {
1301 USDHC_EnableInterruptSignal(base, kUSDHC_BufferReadReadyFlag);
1302 }
1303
1304 /* send command */
1305 USDHC_SendCommand(base, command);
1306
1307 return kStatus_Success;
1308 }
1309
1310 #if defined(FSL_FEATURE_USDHC_HAS_SDR50_MODE) && (!FSL_FEATURE_USDHC_HAS_SDR50_MODE)
1311 #else
USDHC_EnableManualTuning(USDHC_Type * base,bool enable)1312 void USDHC_EnableManualTuning(USDHC_Type *base, bool enable)
1313 {
1314 if (enable)
1315 {
1316 /* make sure std_tun_en bit is clear */
1317 base->TUNING_CTRL &= ~USDHC_TUNING_CTRL_STD_TUNING_EN_MASK;
1318 /* disable auto tuning here */
1319 base->MIX_CTRL &= ~USDHC_MIX_CTRL_AUTO_TUNE_EN_MASK;
1320 /* execute tuning for SDR104 mode */
1321 base->MIX_CTRL |=
1322 USDHC_MIX_CTRL_EXE_TUNE_MASK | USDHC_MIX_CTRL_SMP_CLK_SEL_MASK | USDHC_MIX_CTRL_FBCLK_SEL_MASK;
1323 }
1324 else
1325 { /* abort the tuning */
1326 base->MIX_CTRL &= ~(USDHC_MIX_CTRL_EXE_TUNE_MASK | USDHC_MIX_CTRL_SMP_CLK_SEL_MASK);
1327 }
1328 }
1329
USDHC_AdjustDelayForManualTuning(USDHC_Type * base,uint32_t delay)1330 status_t USDHC_AdjustDelayForManualTuning(USDHC_Type *base, uint32_t delay)
1331 {
1332 uint32_t clkTuneCtrl = 0U;
1333
1334 clkTuneCtrl = base->CLK_TUNE_CTRL_STATUS;
1335
1336 clkTuneCtrl &= ~USDHC_CLK_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK;
1337
1338 clkTuneCtrl |= USDHC_CLK_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE(delay);
1339
1340 /* load the delay setting */
1341 base->CLK_TUNE_CTRL_STATUS = clkTuneCtrl;
1342 /* check delat setting error */
1343 if (base->CLK_TUNE_CTRL_STATUS &
1344 (USDHC_CLK_TUNE_CTRL_STATUS_PRE_ERR_MASK | USDHC_CLK_TUNE_CTRL_STATUS_NXT_ERR_MASK))
1345 {
1346 return kStatus_Fail;
1347 }
1348
1349 return kStatus_Success;
1350 }
1351
USDHC_EnableStandardTuning(USDHC_Type * base,uint32_t tuningStartTap,uint32_t step,bool enable)1352 void USDHC_EnableStandardTuning(USDHC_Type *base, uint32_t tuningStartTap, uint32_t step, bool enable)
1353 {
1354 uint32_t tuningCtrl = 0U;
1355
1356 if (enable)
1357 {
1358 /* feedback clock */
1359 base->MIX_CTRL |= USDHC_MIX_CTRL_FBCLK_SEL_MASK;
1360 /* config tuning start and step */
1361 tuningCtrl = base->TUNING_CTRL;
1362 tuningCtrl &= ~(USDHC_TUNING_CTRL_TUNING_START_TAP_MASK | USDHC_TUNING_CTRL_TUNING_STEP_MASK);
1363 tuningCtrl |= (USDHC_TUNING_CTRL_TUNING_START_TAP(tuningStartTap) | USDHC_TUNING_CTRL_TUNING_STEP(step) |
1364 USDHC_TUNING_CTRL_STD_TUNING_EN_MASK);
1365 base->TUNING_CTRL = tuningCtrl;
1366
1367 /* excute tuning */
1368 base->AUTOCMD12_ERR_STATUS |=
1369 (USDHC_AUTOCMD12_ERR_STATUS_EXECUTE_TUNING_MASK | USDHC_AUTOCMD12_ERR_STATUS_SMP_CLK_SEL_MASK);
1370 }
1371 else
1372 {
1373 /* disable the standard tuning */
1374 base->TUNING_CTRL &= ~USDHC_TUNING_CTRL_STD_TUNING_EN_MASK;
1375 /* clear excute tuning */
1376 base->AUTOCMD12_ERR_STATUS &=
1377 ~(USDHC_AUTOCMD12_ERR_STATUS_EXECUTE_TUNING_MASK | USDHC_AUTOCMD12_ERR_STATUS_SMP_CLK_SEL_MASK);
1378 }
1379 }
1380
USDHC_EnableAutoTuningForCmdAndData(USDHC_Type * base)1381 void USDHC_EnableAutoTuningForCmdAndData(USDHC_Type *base)
1382 {
1383 uint32_t busWidth = 0U;
1384
1385 base->VEND_SPEC2 |= USDHC_VEND_SPEC2_TUNING_CMD_EN_MASK;
1386 busWidth = (base->PROT_CTRL & USDHC_PROT_CTRL_DTW_MASK) >> USDHC_PROT_CTRL_DTW_SHIFT;
1387 if (busWidth == kUSDHC_DataBusWidth1Bit)
1388 {
1389 base->VEND_SPEC2 &= ~USDHC_VEND_SPEC2_TUNING_8bit_EN_MASK;
1390 base->VEND_SPEC2 |= USDHC_VEND_SPEC2_TUNING_1bit_EN_MASK;
1391 }
1392 else if (busWidth == kUSDHC_DataBusWidth4Bit)
1393 {
1394 base->VEND_SPEC2 &= ~USDHC_VEND_SPEC2_TUNING_8bit_EN_MASK;
1395 base->VEND_SPEC2 &= ~USDHC_VEND_SPEC2_TUNING_1bit_EN_MASK;
1396 }
1397 else if (busWidth == kUSDHC_DataBusWidth8Bit)
1398 {
1399 base->VEND_SPEC2 |= USDHC_VEND_SPEC2_TUNING_8bit_EN_MASK;
1400 base->VEND_SPEC2 &= ~USDHC_VEND_SPEC2_TUNING_1bit_EN_MASK;
1401 }
1402 else
1403 {
1404 }
1405 }
1406 #endif
1407
USDHC_TransferHandleCardDetect(usdhc_handle_t * handle,uint32_t interruptFlags)1408 static void USDHC_TransferHandleCardDetect(usdhc_handle_t *handle, uint32_t interruptFlags)
1409 {
1410 if (interruptFlags & kUSDHC_CardInsertionFlag)
1411 {
1412 if (handle->callback.CardInserted)
1413 {
1414 handle->callback.CardInserted();
1415 }
1416 }
1417 else
1418 {
1419 if (handle->callback.CardRemoved)
1420 {
1421 handle->callback.CardRemoved();
1422 }
1423 }
1424 }
1425
USDHC_TransferHandleCommand(USDHC_Type * base,usdhc_handle_t * handle,uint32_t interruptFlags)1426 static void USDHC_TransferHandleCommand(USDHC_Type *base, usdhc_handle_t *handle, uint32_t interruptFlags)
1427 {
1428 assert(handle->command);
1429
1430 if ((interruptFlags & kUSDHC_CommandErrorFlag) && (!(handle->data)))
1431 {
1432 if (handle->callback.TransferComplete)
1433 {
1434 handle->callback.TransferComplete(base, handle, kStatus_USDHC_SendCommandFailed, handle->userData);
1435 }
1436 }
1437 else
1438 {
1439 /* Receive response */
1440 if (kStatus_Success != USDHC_ReceiveCommandResponse(base, handle->command))
1441 {
1442 if (handle->callback.TransferComplete)
1443 {
1444 handle->callback.TransferComplete(base, handle, kStatus_USDHC_SendCommandFailed, handle->userData);
1445 }
1446 }
1447 else if ((!(handle->data)) && (handle->callback.TransferComplete))
1448 {
1449 if (handle->callback.TransferComplete)
1450 {
1451 handle->callback.TransferComplete(base, handle, kStatus_Success, handle->userData);
1452 }
1453 }
1454 else
1455 {
1456 }
1457 }
1458 }
1459
USDHC_TransferHandleData(USDHC_Type * base,usdhc_handle_t * handle,uint32_t interruptFlags)1460 static void USDHC_TransferHandleData(USDHC_Type *base, usdhc_handle_t *handle, uint32_t interruptFlags)
1461 {
1462 assert(handle->data);
1463
1464 if (((!(handle->data->enableIgnoreError)) || (interruptFlags & kUSDHC_DataTimeoutFlag)) &&
1465 (interruptFlags & (kUSDHC_DataErrorFlag | kUSDHC_DmaErrorFlag)))
1466 {
1467 if (handle->callback.TransferComplete)
1468 {
1469 handle->callback.TransferComplete(base, handle, kStatus_USDHC_TransferDataFailed, handle->userData);
1470 }
1471 }
1472 else
1473 {
1474 if (interruptFlags & kUSDHC_BufferReadReadyFlag)
1475 {
1476 /* std tuning process only need to wait BRR */
1477 if (handle->data->executeTuning)
1478 {
1479 if (handle->callback.TransferComplete)
1480 {
1481 handle->callback.TransferComplete(base, handle, kStatus_Success, handle->userData);
1482 }
1483 }
1484 else
1485 {
1486 handle->transferredWords = USDHC_ReadDataPort(base, handle->data, handle->transferredWords);
1487 }
1488 }
1489 else if (interruptFlags & kUSDHC_BufferWriteReadyFlag)
1490 {
1491 handle->transferredWords = USDHC_WriteDataPort(base, handle->data, handle->transferredWords);
1492 }
1493 else if (interruptFlags & kUSDHC_DataCompleteFlag)
1494 {
1495 if (handle->callback.TransferComplete)
1496 {
1497 handle->callback.TransferComplete(base, handle, kStatus_Success, handle->userData);
1498 }
1499 }
1500 else
1501 {
1502 /* Do nothing when DMA complete flag is set. Wait until data complete flag is set. */
1503 }
1504 #if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
1505 /* invalidate cache for read */
1506 if ((handle->data != NULL) && (handle->data->rxData != NULL))
1507 {
1508 /* invalidate the DCACHE */
1509 DCACHE_InvalidateByRange((uint32_t)handle->data->rxData,
1510 (handle->data->blockSize) * (handle->data->blockCount));
1511 }
1512 #endif
1513 }
1514 }
1515
USDHC_TransferHandleSdioInterrupt(usdhc_handle_t * handle)1516 static void USDHC_TransferHandleSdioInterrupt(usdhc_handle_t *handle)
1517 {
1518 if (handle->callback.SdioInterrupt)
1519 {
1520 handle->callback.SdioInterrupt();
1521 }
1522 }
1523
USDHC_TransferHandleReTuning(USDHC_Type * base,usdhc_handle_t * handle,uint32_t interruptFlags)1524 static void USDHC_TransferHandleReTuning(USDHC_Type *base, usdhc_handle_t *handle, uint32_t interruptFlags)
1525 {
1526 assert(handle->callback.ReTuning);
1527 /* retuning request */
1528 if ((interruptFlags & kUSDHC_TuningErrorFlag) == kUSDHC_TuningErrorFlag)
1529 {
1530 handle->callback.ReTuning(); /* retuning fail */
1531 }
1532 }
1533
USDHC_TransferHandleSdioBlockGap(usdhc_handle_t * handle)1534 static void USDHC_TransferHandleSdioBlockGap(usdhc_handle_t *handle)
1535 {
1536 if (handle->callback.SdioBlockGap)
1537 {
1538 handle->callback.SdioBlockGap();
1539 }
1540 }
1541
USDHC_TransferCreateHandle(USDHC_Type * base,usdhc_handle_t * handle,const usdhc_transfer_callback_t * callback,void * userData)1542 void USDHC_TransferCreateHandle(USDHC_Type *base,
1543 usdhc_handle_t *handle,
1544 const usdhc_transfer_callback_t *callback,
1545 void *userData)
1546 {
1547 assert(handle);
1548 assert(callback);
1549
1550 /* Zero the handle. */
1551 memset(handle, 0, sizeof(*handle));
1552
1553 /* Set the callback. */
1554 handle->callback.CardInserted = callback->CardInserted;
1555 handle->callback.CardRemoved = callback->CardRemoved;
1556 handle->callback.SdioInterrupt = callback->SdioInterrupt;
1557 handle->callback.SdioBlockGap = callback->SdioBlockGap;
1558 handle->callback.TransferComplete = callback->TransferComplete;
1559 handle->callback.ReTuning = callback->ReTuning;
1560 handle->userData = userData;
1561
1562 /* Save the handle in global variables to support the double weak mechanism. */
1563 s_usdhcHandle[USDHC_GetInstance(base)] = handle;
1564
1565 /* Enable interrupt in NVIC. */
1566 USDHC_SetTransferInterrupt(base, true);
1567 /* disable the tuning pass interrupt */
1568 USDHC_DisableInterruptSignal(base, kUSDHC_TuningPassFlag | kUSDHC_ReTuningEventFlag);
1569 /* save IRQ handler */
1570 s_usdhcIsr = USDHC_TransferHandleIRQ;
1571
1572 EnableIRQ(s_usdhcIRQ[USDHC_GetInstance(base)]);
1573 }
1574
USDHC_TransferHandleIRQ(USDHC_Type * base,usdhc_handle_t * handle)1575 void USDHC_TransferHandleIRQ(USDHC_Type *base, usdhc_handle_t *handle)
1576 {
1577 assert(handle);
1578
1579 uint32_t interruptFlags;
1580
1581 interruptFlags = USDHC_GetInterruptStatusFlags(base);
1582 handle->interruptFlags = interruptFlags;
1583
1584 if (interruptFlags & kUSDHC_CardDetectFlag)
1585 {
1586 USDHC_TransferHandleCardDetect(handle, (interruptFlags & kUSDHC_CardDetectFlag));
1587 }
1588 if (interruptFlags & kUSDHC_CommandFlag)
1589 {
1590 USDHC_TransferHandleCommand(base, handle, (interruptFlags & kUSDHC_CommandFlag));
1591 }
1592 if (interruptFlags & kUSDHC_DataFlag)
1593 {
1594 USDHC_TransferHandleData(base, handle, (interruptFlags & kUSDHC_DataFlag));
1595 }
1596 if (interruptFlags & kUSDHC_CardInterruptFlag)
1597 {
1598 USDHC_TransferHandleSdioInterrupt(handle);
1599 }
1600 if (interruptFlags & kUSDHC_BlockGapEventFlag)
1601 {
1602 USDHC_TransferHandleSdioBlockGap(handle);
1603 }
1604 if (interruptFlags & kUSDHC_SDR104TuningFlag)
1605 {
1606 USDHC_TransferHandleReTuning(base, handle, (interruptFlags & kUSDHC_SDR104TuningFlag));
1607 }
1608
1609 USDHC_ClearInterruptStatusFlags(base, interruptFlags);
1610 }
1611
1612 #ifdef USDHC0
USDHC0_DriverIRQHandler(void)1613 void USDHC0_DriverIRQHandler(void)
1614 {
1615 s_usdhcIsr(s_usdhcBase[0U], s_usdhcHandle[0U]);
1616 }
1617 #endif
1618
1619 #ifdef USDHC1
USDHC1_DriverIRQHandler(void)1620 void USDHC1_DriverIRQHandler(void)
1621 {
1622 s_usdhcIsr(s_usdhcBase[1U], s_usdhcHandle[1U]);
1623 }
1624 #endif
1625
1626 #ifdef USDHC2
USDHC2_DriverIRQHandler(void)1627 void USDHC2_DriverIRQHandler(void)
1628 {
1629 s_usdhcIsr(s_usdhcBase[2U], s_usdhcHandle[2U]);
1630 }
1631
1632 #endif
1633