aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios/os/hal/src/hal_sdc.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios/os/hal/src/hal_sdc.c')
-rw-r--r--lib/chibios/os/hal/src/hal_sdc.c999
1 files changed, 999 insertions, 0 deletions
diff --git a/lib/chibios/os/hal/src/hal_sdc.c b/lib/chibios/os/hal/src/hal_sdc.c
new file mode 100644
index 000000000..0d93ce636
--- /dev/null
+++ b/lib/chibios/os/hal/src/hal_sdc.c
@@ -0,0 +1,999 @@
1/*
2 ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17/**
18 * @file hal_sdc.c
19 * @brief SDC Driver code.
20 *
21 * @addtogroup SDC
22 * @{
23 */
24
25#include <string.h>
26
27#include "hal.h"
28
29#if (HAL_USE_SDC == TRUE) || defined(__DOXYGEN__)
30
31/*===========================================================================*/
32/* Driver local definitions. */
33/*===========================================================================*/
34
35/**
36 * @brief MMC switch mode.
37 */
38typedef enum {
39 MMC_SWITCH_COMMAND_SET = 0,
40 MMC_SWITCH_SET_BITS = 1,
41 MMC_SWITCH_CLEAR_BITS = 2,
42 MMC_SWITCH_WRITE_BYTE = 3
43} mmc_switch_t;
44
45/**
46 * @brief SDC switch mode.
47 */
48typedef enum {
49 SD_SWITCH_CHECK = 0,
50 SD_SWITCH_SET = 1
51} sd_switch_t;
52
53/**
54 * @brief SDC switch function.
55 */
56typedef enum {
57 SD_SWITCH_FUNCTION_SPEED = 0,
58 SD_SWITCH_FUNCTION_CMD_SYSTEM = 1,
59 SD_SWITCH_FUNCTION_DRIVER_STRENGTH = 2,
60 SD_SWITCH_FUNCTION_CURRENT_LIMIT = 3
61} sd_switch_function_t;
62
63/*===========================================================================*/
64/* Driver exported variables. */
65/*===========================================================================*/
66
67/*===========================================================================*/
68/* Driver local variables and types. */
69/*===========================================================================*/
70
71/**
72 * @brief Virtual methods table.
73 */
74static const struct SDCDriverVMT sdc_vmt = {
75 (size_t)0,
76 (bool (*)(void *))sdc_lld_is_card_inserted,
77 (bool (*)(void *))sdc_lld_is_write_protected,
78 (bool (*)(void *))sdcConnect,
79 (bool (*)(void *))sdcDisconnect,
80 (bool (*)(void *, uint32_t, uint8_t *, uint32_t))sdcRead,
81 (bool (*)(void *, uint32_t, const uint8_t *, uint32_t))sdcWrite,
82 (bool (*)(void *))sdcSync,
83 (bool (*)(void *, BlockDeviceInfo *))sdcGetInfo
84};
85
86/*===========================================================================*/
87/* Driver local functions. */
88/*===========================================================================*/
89/**
90 * @brief Detects card mode.
91 *
92 * @param[in] sdcp pointer to the @p SDCDriver object
93 *
94 * @return The operation status.
95 * @retval HAL_SUCCESS operation succeeded.
96 * @retval HAL_FAILED operation failed.
97 *
98 * @notapi
99 */
100static bool mode_detect(SDCDriver *sdcp) {
101 uint32_t resp[1];
102
103 /* V2.0 cards detection.*/
104 if (!sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_IF_COND,
105 MMCSD_CMD8_PATTERN, resp)) {
106 sdcp->cardmode = SDC_MODE_CARDTYPE_SDV20;
107 /* Voltage verification.*/
108 if (((resp[0] >> 8U) & 0xFU) != 1U) {
109 return HAL_FAILED;
110 }
111 if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) ||
112 MMCSD_R1_ERROR(resp[0])) {
113 return HAL_FAILED;
114 }
115 }
116 else {
117 /* MMC or SD V1.1 detection.*/
118 if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) ||
119 MMCSD_R1_ERROR(resp[0])) {
120 sdcp->cardmode = SDC_MODE_CARDTYPE_MMC;
121 }
122 else {
123 sdcp->cardmode = SDC_MODE_CARDTYPE_SDV11;
124
125 /* Reset error flag illegal command.*/
126 sdc_lld_send_cmd_none(sdcp, MMCSD_CMD_GO_IDLE_STATE, 0);
127 }
128 }
129
130 return HAL_SUCCESS;
131}
132
133/**
134 * @brief Init procedure for MMC.
135 *
136 * @param[in] sdcp pointer to the @p SDCDriver object
137 *
138 * @return The operation status.
139 * @retval HAL_SUCCESS operation succeeded.
140 * @retval HAL_FAILED operation failed.
141 *
142 * @notapi
143 */
144static bool mmc_init(SDCDriver *sdcp) {
145 uint32_t ocr;
146 unsigned i;
147 uint32_t resp[1];
148
149 ocr = 0xC0FF8000U;
150 i = 0;
151 while (true) {
152 if (sdc_lld_send_cmd_short(sdcp, MMCSD_CMD_INIT, ocr, resp)) {
153 return HAL_FAILED;
154 }
155 if ((resp[0] & 0x80000000U) != 0U) {
156 if ((resp[0] & 0x40000000U) != 0U) {
157 sdcp->cardmode |= SDC_MODE_HIGH_CAPACITY;
158 }
159 break;
160 }
161 if (++i >= (unsigned)SDC_INIT_RETRY) {
162 return HAL_FAILED;
163 }
164 osalThreadSleepMilliseconds(10);
165 }
166
167 return HAL_SUCCESS;
168}
169
170/**
171 * @brief Init procedure for SDC.
172 *
173 * @param[in] sdcp pointer to the @p SDCDriver object
174 *
175 * @return The operation status.
176 * @retval HAL_SUCCESS operation succeeded.
177 * @retval HAL_FAILED operation failed.
178 *
179 * @notapi
180 */
181static bool sdc_init(SDCDriver *sdcp) {
182 unsigned i;
183 uint32_t ocr;
184 uint32_t resp[1];
185
186 if ((sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) == SDC_MODE_CARDTYPE_SDV20) {
187 ocr = SDC_INIT_OCR_V20;
188 }
189 else {
190 ocr = SDC_INIT_OCR;
191 }
192
193 i = 0;
194 while (true) {
195 if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) ||
196 MMCSD_R1_ERROR(resp[0])) {
197 return HAL_FAILED;
198 }
199 if (sdc_lld_send_cmd_short(sdcp, MMCSD_CMD_APP_OP_COND, ocr, resp)) {
200 return HAL_FAILED;
201 }
202 if ((resp[0] & 0x80000000U) != 0U) {
203 if ((resp[0] & 0x40000000U) != 0U) {
204 sdcp->cardmode |= SDC_MODE_HIGH_CAPACITY;
205 }
206 break;
207 }
208 if (++i >= (unsigned)SDC_INIT_RETRY) {
209 return HAL_FAILED;
210 }
211 osalThreadSleepMilliseconds(10);
212 }
213
214 return HAL_SUCCESS;
215}
216
217/**
218 * @brief Constructs CMD6 argument for MMC.
219 *
220 * @param[in] access EXT_CSD access mode
221 * @param[in] idx EXT_CSD byte number
222 * @param[in] value value to be written in target field
223 * @param[in] cmd_set switch current command set
224 *
225 * @return CMD6 argument.
226 *
227 * @notapi
228 */
229static uint32_t mmc_cmd6_construct(mmc_switch_t access, uint32_t idx,
230 uint32_t value, uint32_t cmd_set) {
231
232 osalDbgAssert(idx <= 191U, "This field is not writable");
233 osalDbgAssert(cmd_set < 8U, "This field has only 3 bits");
234
235 return ((uint32_t)access << 24U) | (idx << 16U) | (value << 8U) | cmd_set;
236}
237
238/**
239 * @brief Constructs CMD6 argument for SDC.
240 *
241 * @param[in] mode switch/test mode
242 * @param[in] function function number to be switched
243 * @param[in] value value to be written in target function
244 *
245 * @return CMD6 argument.
246 *
247 * @notapi
248 */
249static uint32_t sdc_cmd6_construct(sd_switch_t mode,
250 sd_switch_function_t function,
251 uint32_t value) {
252 uint32_t ret = 0xFFFFFF;
253
254 osalDbgAssert((value < 16U), "This field has only 4 bits");
255
256 ret &= ~((uint32_t)0xFU << ((uint32_t)function * 4U));
257 ret |= value << ((uint32_t)function * 4U);
258 return ret | ((uint32_t)mode << 31U);
259}
260
261/**
262 * @brief Extracts information from CMD6 answer.
263 *
264 * @param[in] function function number to be switched
265 * @param[in] buf buffer with answer
266 *
267 * @return extracted answer.
268 *
269 * @notapi
270 */
271static uint16_t sdc_cmd6_extract_info(sd_switch_function_t function,
272 const uint8_t *buf) {
273
274 unsigned start = 12U - ((unsigned)function * 2U);
275
276 return ((uint16_t)buf[start] << 8U) | (uint16_t)buf[start + 1U];
277}
278
279/**
280 * @brief Checks status after switching using CMD6.
281 *
282 * @param[in] function function number to be switched
283 * @param[in] buf buffer with answer
284 *
285 * @return The operation status.
286 * @retval HAL_SUCCESS operation succeeded.
287 * @retval HAL_FAILED operation failed.
288 *
289 * @notapi
290 */
291static bool sdc_cmd6_check_status(sd_switch_function_t function,
292 const uint8_t *buf) {
293
294 uint32_t tmp;
295 uint32_t status;
296
297 tmp = ((uint32_t)buf[14] << 16U) |
298 ((uint32_t)buf[15] << 8U) |
299 (uint32_t)buf[16];
300 status = (tmp >> ((uint32_t)function * 4U)) & 0xFU;
301 if (0xFU != status) {
302 return HAL_SUCCESS;
303 }
304 return HAL_FAILED;
305}
306
307/**
308 * @brief Reads supported bus clock and switch SDC to appropriate mode.
309 *
310 * @param[in] sdcp pointer to the @p SDCDriver object
311 * @param[out] clk pointer to clock enum
312 *
313 * @return The operation status.
314 * @retval HAL_SUCCESS operation succeeded.
315 * @retval HAL_FAILED operation failed.
316 *
317 * @notapi
318 */
319static bool sdc_detect_bus_clk(SDCDriver *sdcp, sdcbusclk_t *clk) {
320 uint32_t cmdarg;
321 const size_t N = 64;
322 uint8_t tmp[N];
323
324 /* Safe default.*/
325 *clk = SDC_CLK_25MHz;
326
327 /* Looks like only "high capacity" cards produce meaningful results during
328 this clock detection procedure.*/
329 if (0U == _mmcsd_get_slice(sdcp->csd, MMCSD_CSD_10_CSD_STRUCTURE_SLICE)) {
330 *clk = SDC_CLK_25MHz;
331 return HAL_SUCCESS;
332 }
333
334 /* Read switch functions' register.*/
335 if (sdc_lld_read_special(sdcp, tmp, N, MMCSD_CMD_SWITCH, 0)) {
336 return HAL_FAILED;
337 }
338
339 /* Check card capabilities parsing acquired data.*/
340 if ((sdc_cmd6_extract_info(SD_SWITCH_FUNCTION_SPEED, tmp) & 2U) == 2U) {
341 /* Construct command to set the bus speed.*/
342 cmdarg = sdc_cmd6_construct(SD_SWITCH_SET, SD_SWITCH_FUNCTION_SPEED, 1);
343
344 /* Write constructed command and read operation status in single call.*/
345 if (sdc_lld_read_special(sdcp, tmp, N, MMCSD_CMD_SWITCH, cmdarg)) {
346 return HAL_FAILED;
347 }
348
349 /* Check card answer for success status bits.*/
350 if (HAL_SUCCESS == sdc_cmd6_check_status(SD_SWITCH_FUNCTION_SPEED, tmp)) {
351 *clk = SDC_CLK_50MHz;
352 }
353 else {
354 *clk = SDC_CLK_25MHz;
355 }
356 }
357
358 return HAL_SUCCESS;
359}
360
361/**
362 * @brief Reads supported bus clock and switch MMC to appropriate mode.
363 *
364 * @param[in] sdcp pointer to the @p SDCDriver object
365 * @param[out] clk pointer to clock enum
366 *
367 * @return The operation status.
368 * @retval HAL_SUCCESS operation succeeded.
369 * @retval HAL_FAILED operation failed.
370 *
371 * @notapi
372 */
373static bool mmc_detect_bus_clk(SDCDriver *sdcp, sdcbusclk_t *clk) {
374 uint32_t cmdarg;
375 uint32_t resp[1];
376
377 /* Safe default.*/
378 *clk = SDC_CLK_25MHz;
379
380 cmdarg = mmc_cmd6_construct(MMC_SWITCH_WRITE_BYTE, 185, 1, 0);
381 if (!(sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SWITCH, cmdarg, resp) ||
382 MMCSD_R1_ERROR(resp[0]))) {
383 *clk = SDC_CLK_50MHz;
384 }
385
386 return HAL_SUCCESS;
387}
388
389/**
390 * @brief Reads supported bus clock and switch card to appropriate mode.
391 *
392 * @param[in] sdcp pointer to the @p SDCDriver object
393 * @param[out] clk pointer to clock enum
394 *
395 * @return The operation status.
396 * @retval HAL_SUCCESS operation succeeded.
397 * @retval HAL_FAILED operation failed.
398 *
399 * @notapi
400 */
401static bool detect_bus_clk(SDCDriver *sdcp, sdcbusclk_t *clk) {
402
403 if (SDC_MODE_CARDTYPE_MMC == (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK)) {
404 return mmc_detect_bus_clk(sdcp, clk);
405 }
406 return sdc_detect_bus_clk(sdcp, clk);
407}
408
409/**
410 * @brief Sets bus width for SDC.
411 *
412 * @param[in] sdcp pointer to the @p SDCDriver object
413 *
414 * @return The operation status.
415 * @retval HAL_SUCCESS operation succeeded.
416 * @retval HAL_FAILED operation failed.
417 *
418 * @notapi
419 */
420static bool sdc_set_bus_width(SDCDriver *sdcp) {
421 uint32_t resp[1];
422
423 if (SDC_MODE_1BIT == sdcp->config->bus_width) {
424 /* Nothing to do. Bus is already in 1bit mode.*/
425 return HAL_SUCCESS;
426 }
427 else if (SDC_MODE_4BIT == sdcp->config->bus_width) {
428 sdc_lld_set_bus_mode(sdcp, SDC_MODE_4BIT);
429 if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, sdcp->rca, resp) ||
430 MMCSD_R1_ERROR(resp[0])) {
431 return HAL_FAILED;
432 }
433
434 if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SET_BUS_WIDTH, 2, resp) ||
435 MMCSD_R1_ERROR(resp[0])) {
436 return HAL_FAILED;
437 }
438 }
439 else {
440 /* SD card does not support 8bit bus.*/
441 return HAL_FAILED;
442 }
443
444 return HAL_SUCCESS;
445}
446
447/**
448 * @brief Sets bus width for MMC.
449 *
450 * @param[in] sdcp pointer to the @p SDCDriver object
451 *
452 * @return The operation status.
453 * @retval HAL_SUCCESS operation succeeded.
454 * @retval HAL_FAILED operation failed.
455 *
456 * @notapi
457 */
458static bool mmc_set_bus_width(SDCDriver *sdcp) {
459 uint32_t resp[1];
460 uint32_t cmdarg = mmc_cmd6_construct(MMC_SWITCH_WRITE_BYTE, 183, 0, 0);
461
462 switch (sdcp->config->bus_width) {
463 case SDC_MODE_1BIT:
464 /* Nothing to do. Bus is already in 1bit mode.*/
465 return HAL_SUCCESS;
466 case SDC_MODE_4BIT:
467 cmdarg = mmc_cmd6_construct(MMC_SWITCH_WRITE_BYTE, 183, 1, 0);
468 break;
469 case SDC_MODE_8BIT:
470 cmdarg = mmc_cmd6_construct(MMC_SWITCH_WRITE_BYTE, 183, 2, 0);
471 break;
472 default:
473 osalDbgAssert(false, "unexpected case");
474 break;
475 }
476
477 sdc_lld_set_bus_mode(sdcp, sdcp->config->bus_width);
478 if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SWITCH, cmdarg, resp) ||
479 MMCSD_R1_ERROR(resp[0])) {
480 return HAL_FAILED;
481 }
482
483 return HAL_SUCCESS;
484}
485
486/**
487 * @brief Wait for the card to complete pending operations.
488 *
489 * @param[in] sdcp pointer to the @p SDCDriver object
490 *
491 * @return The operation status.
492 * @retval HAL_SUCCESS operation succeeded.
493 * @retval HAL_FAILED operation failed.
494 *
495 * @notapi
496 */
497bool _sdc_wait_for_transfer_state(SDCDriver *sdcp) {
498 uint32_t resp[1];
499
500 while (true) {
501 if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_STATUS,
502 sdcp->rca, resp) ||
503 MMCSD_R1_ERROR(resp[0])) {
504 return HAL_FAILED;
505 }
506
507 switch (MMCSD_R1_STS(resp[0])) {
508 case MMCSD_STS_TRAN:
509 return HAL_SUCCESS;
510 case MMCSD_STS_DATA:
511 case MMCSD_STS_RCV:
512 case MMCSD_STS_PRG:
513#if SDC_NICE_WAITING == TRUE
514 osalThreadSleepMilliseconds(1);
515#endif
516 continue;
517 default:
518 /* The card should have been initialized so any other state is not
519 valid and is reported as an error.*/
520 return HAL_FAILED;
521 }
522 }
523}
524
525/*===========================================================================*/
526/* Driver exported functions. */
527/*===========================================================================*/
528
529/**
530 * @brief SDC Driver initialization.
531 * @note This function is implicitly invoked by @p halInit(), there is
532 * no need to explicitly initialize the driver.
533 *
534 * @init
535 */
536void sdcInit(void) {
537
538 sdc_lld_init();
539}
540
541/**
542 * @brief Initializes the standard part of a @p SDCDriver structure.
543 *
544 * @param[out] sdcp pointer to the @p SDCDriver object
545 *
546 * @init
547 */
548void sdcObjectInit(SDCDriver *sdcp) {
549
550 sdcp->vmt = &sdc_vmt;
551 sdcp->state = BLK_STOP;
552 sdcp->errors = SDC_NO_ERROR;
553 sdcp->config = NULL;
554 sdcp->capacity = 0;
555}
556
557/**
558 * @brief Configures and activates the SDC peripheral.
559 *
560 * @param[in] sdcp pointer to the @p SDCDriver object
561 * @param[in] config pointer to the @p SDCConfig object, can be @p NULL if
562 * the driver supports a default configuration or
563 * requires no configuration
564 *
565 * @api
566 */
567void sdcStart(SDCDriver *sdcp, const SDCConfig *config) {
568
569 osalDbgCheck(sdcp != NULL);
570
571 osalSysLock();
572 osalDbgAssert((sdcp->state == BLK_STOP) || (sdcp->state == BLK_ACTIVE),
573 "invalid state");
574 sdcp->config = config;
575 sdc_lld_start(sdcp);
576 sdcp->state = BLK_ACTIVE;
577 osalSysUnlock();
578}
579
580/**
581 * @brief Deactivates the SDC peripheral.
582 *
583 * @param[in] sdcp pointer to the @p SDCDriver object
584 *
585 * @api
586 */
587void sdcStop(SDCDriver *sdcp) {
588
589 osalDbgCheck(sdcp != NULL);
590
591 osalSysLock();
592
593 osalDbgAssert((sdcp->state == BLK_STOP) || (sdcp->state == BLK_ACTIVE),
594 "invalid state");
595
596 sdc_lld_stop(sdcp);
597 sdcp->config = NULL;
598 sdcp->state = BLK_STOP;
599
600 osalSysUnlock();
601}
602
603/**
604 * @brief Performs the initialization procedure on the inserted card.
605 * @details This function should be invoked when a card is inserted and
606 * brings the driver in the @p BLK_READY state where it is possible
607 * to perform read and write operations.
608 *
609 * @param[in] sdcp pointer to the @p SDCDriver object
610 *
611 * @return The operation status.
612 * @retval HAL_SUCCESS operation succeeded.
613 * @retval HAL_FAILED operation failed.
614 *
615 * @api
616 */
617bool sdcConnect(SDCDriver *sdcp) {
618 uint32_t resp[1];
619 sdcbusclk_t clk = SDC_CLK_25MHz;
620
621 osalDbgCheck(sdcp != NULL);
622 osalDbgAssert((sdcp->state == BLK_ACTIVE) || (sdcp->state == BLK_READY),
623 "invalid state");
624
625 /* Connection procedure in progress.*/
626 sdcp->state = BLK_CONNECTING;
627
628 /* Card clock initialization.*/
629 sdc_lld_start_clk(sdcp);
630
631 /* Enforces the initial card state.*/
632 sdc_lld_send_cmd_none(sdcp, MMCSD_CMD_GO_IDLE_STATE, 0);
633
634 /* Detect card type.*/
635 if (HAL_FAILED == mode_detect(sdcp)) {
636 goto failed;
637 }
638
639 /* Perform specific initialization procedure.*/
640 if ((sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) == SDC_MODE_CARDTYPE_MMC) {
641 if (HAL_FAILED == mmc_init(sdcp)) {
642 goto failed;
643 }
644 }
645 else {
646 if (HAL_FAILED == sdc_init(sdcp)) {
647 goto failed;
648 }
649 }
650
651 /* Reads CID.*/
652 if (sdc_lld_send_cmd_long_crc(sdcp, MMCSD_CMD_ALL_SEND_CID, 0, sdcp->cid)) {
653 goto failed;
654 }
655
656 /* Asks for the RCA.*/
657 if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_RELATIVE_ADDR,
658 0, &sdcp->rca)) {
659 goto failed;
660 }
661
662 /* Reads CSD.*/
663 if (sdc_lld_send_cmd_long_crc(sdcp, MMCSD_CMD_SEND_CSD,
664 sdcp->rca, sdcp->csd)) {
665 goto failed;
666 }
667
668 /* Selects the card for operations.*/
669 if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEL_DESEL_CARD,
670 sdcp->rca, resp)) {
671 goto failed;
672 }
673
674 /* Switches to high speed.*/
675 if (HAL_SUCCESS != detect_bus_clk(sdcp, &clk)) {
676 goto failed;
677 }
678 sdc_lld_set_data_clk(sdcp, clk);
679
680 /* Reads extended CSD if needed and possible.*/
681 if (SDC_MODE_CARDTYPE_MMC == (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK)) {
682
683 /* The card is a MMC, checking if it is a large device.*/
684 if (_mmcsd_get_slice(sdcp->csd, MMCSD_CSD_MMC_CSD_STRUCTURE_SLICE) > 1U) {
685 uint8_t *ext_csd = sdcp->buf;
686
687 if (sdc_lld_read_special(sdcp, ext_csd, 512, MMCSD_CMD_SEND_EXT_CSD, 0)) {
688 goto failed;
689 }
690
691 /* Capacity from the EXT_CSD.*/
692 sdcp->capacity = _mmcsd_get_capacity_ext(ext_csd);
693 }
694 else {
695 /* Capacity from the normal CSD.*/
696 sdcp->capacity = _mmcsd_get_capacity(sdcp->csd);
697 }
698 }
699 else {
700 /* The card is an SDC, capacity from the normal CSD.*/
701 sdcp->capacity = _mmcsd_get_capacity(sdcp->csd);
702 }
703
704 /* Block length fixed at 512 bytes.*/
705 if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SET_BLOCKLEN,
706 MMCSD_BLOCK_SIZE, resp) ||
707 MMCSD_R1_ERROR(resp[0])) {
708 goto failed;
709 }
710
711 /* Switches to wide bus mode.*/
712 switch (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) {
713 case SDC_MODE_CARDTYPE_SDV11:
714 case SDC_MODE_CARDTYPE_SDV20:
715 if (HAL_FAILED == sdc_set_bus_width(sdcp)) {
716 goto failed;
717 }
718 break;
719 case SDC_MODE_CARDTYPE_MMC:
720 if (HAL_FAILED == mmc_set_bus_width(sdcp)) {
721 goto failed;
722 }
723 break;
724 default:
725 /* Unknown type.*/
726 goto failed;
727 }
728
729 /* Initialization complete.*/
730 sdcp->state = BLK_READY;
731 return HAL_SUCCESS;
732
733 /* Connection failed, state reset to BLK_ACTIVE.*/
734failed:
735 sdc_lld_stop_clk(sdcp);
736 sdcp->state = BLK_ACTIVE;
737 return HAL_FAILED;
738}
739
740/**
741 * @brief Brings the driver in a state safe for card removal.
742 *
743 * @param[in] sdcp pointer to the @p SDCDriver object
744 *
745 * @return The operation status.
746 * @retval HAL_SUCCESS operation succeeded.
747 * @retval HAL_FAILED operation failed.
748 *
749 * @api
750 */
751bool sdcDisconnect(SDCDriver *sdcp) {
752
753 osalDbgCheck(sdcp != NULL);
754
755 osalSysLock();
756 osalDbgAssert((sdcp->state == BLK_ACTIVE) || (sdcp->state == BLK_READY),
757 "invalid state");
758 if (sdcp->state == BLK_ACTIVE) {
759 osalSysUnlock();
760 return HAL_SUCCESS;
761 }
762 sdcp->state = BLK_DISCONNECTING;
763 osalSysUnlock();
764
765 /* Waits for eventual pending operations completion.*/
766 if (_sdc_wait_for_transfer_state(sdcp)) {
767 sdc_lld_stop_clk(sdcp);
768 sdcp->state = BLK_ACTIVE;
769 return HAL_FAILED;
770 }
771
772 /* Card clock stopped.*/
773 sdc_lld_stop_clk(sdcp);
774 sdcp->state = BLK_ACTIVE;
775 return HAL_SUCCESS;
776}
777
778/**
779 * @brief Reads one or more blocks.
780 * @pre The driver must be in the @p BLK_READY state after a successful
781 * sdcConnect() invocation.
782 *
783 * @param[in] sdcp pointer to the @p SDCDriver object
784 * @param[in] startblk first block to read
785 * @param[out] buf pointer to the read buffer
786 * @param[in] n number of blocks to read
787 *
788 * @return The operation status.
789 * @retval HAL_SUCCESS operation succeeded.
790 * @retval HAL_FAILED operation failed.
791 *
792 * @api
793 */
794bool sdcRead(SDCDriver *sdcp, uint32_t startblk, uint8_t *buf, uint32_t n) {
795 bool status;
796
797 osalDbgCheck((sdcp != NULL) && (buf != NULL) && (n > 0U));
798 osalDbgAssert(sdcp->state == BLK_READY, "invalid state");
799
800 if ((startblk + n - 1U) > sdcp->capacity) {
801 sdcp->errors |= SDC_OVERFLOW_ERROR;
802 return HAL_FAILED;
803 }
804
805 /* Read operation in progress.*/
806 sdcp->state = BLK_READING;
807
808 status = sdc_lld_read(sdcp, startblk, buf, n);
809
810 /* Read operation finished.*/
811 sdcp->state = BLK_READY;
812 return status;
813}
814
815/**
816 * @brief Writes one or more blocks.
817 * @pre The driver must be in the @p BLK_READY state after a successful
818 * sdcConnect() invocation.
819 *
820 * @param[in] sdcp pointer to the @p SDCDriver object
821 * @param[in] startblk first block to write
822 * @param[out] buf pointer to the write buffer
823 * @param[in] n number of blocks to write
824 *
825 * @return The operation status.
826 * @retval HAL_SUCCESS operation succeeded.
827 * @retval HAL_FAILED operation failed.
828 *
829 * @api
830 */
831bool sdcWrite(SDCDriver *sdcp, uint32_t startblk,
832 const uint8_t *buf, uint32_t n) {
833 bool status;
834
835 osalDbgCheck((sdcp != NULL) && (buf != NULL) && (n > 0U));
836 osalDbgAssert(sdcp->state == BLK_READY, "invalid state");
837
838 if ((startblk + n - 1U) > sdcp->capacity) {
839 sdcp->errors |= SDC_OVERFLOW_ERROR;
840 return HAL_FAILED;
841 }
842
843 /* Write operation in progress.*/
844 sdcp->state = BLK_WRITING;
845
846 status = sdc_lld_write(sdcp, startblk, buf, n);
847
848 /* Write operation finished.*/
849 sdcp->state = BLK_READY;
850 return status;
851}
852
853/**
854 * @brief Returns the errors mask associated to the previous operation.
855 *
856 * @param[in] sdcp pointer to the @p SDCDriver object
857 * @return The errors mask.
858 *
859 * @api
860 */
861sdcflags_t sdcGetAndClearErrors(SDCDriver *sdcp) {
862 sdcflags_t flags;
863
864 osalDbgCheck(sdcp != NULL);
865 osalDbgAssert(sdcp->state == BLK_READY, "invalid state");
866
867 osalSysLock();
868 flags = sdcp->errors;
869 sdcp->errors = SDC_NO_ERROR;
870 osalSysUnlock();
871 return flags;
872}
873
874/**
875 * @brief Waits for card idle condition.
876 *
877 * @param[in] sdcp pointer to the @p SDCDriver object
878 *
879 * @return The operation status.
880 * @retval HAL_SUCCESS the operation succeeded.
881 * @retval HAL_FAILED the operation failed.
882 *
883 * @api
884 */
885bool sdcSync(SDCDriver *sdcp) {
886 bool result;
887
888 osalDbgCheck(sdcp != NULL);
889
890 if (sdcp->state != BLK_READY) {
891 return HAL_FAILED;
892 }
893
894 /* Synchronization operation in progress.*/
895 sdcp->state = BLK_SYNCING;
896
897 result = sdc_lld_sync(sdcp);
898
899 /* Synchronization operation finished.*/
900 sdcp->state = BLK_READY;
901 return result;
902}
903
904/**
905 * @brief Returns the media info.
906 *
907 * @param[in] sdcp pointer to the @p SDCDriver object
908 * @param[out] bdip pointer to a @p BlockDeviceInfo structure
909 *
910 * @return The operation status.
911 * @retval HAL_SUCCESS the operation succeeded.
912 * @retval HAL_FAILED the operation failed.
913 *
914 * @api
915 */
916bool sdcGetInfo(SDCDriver *sdcp, BlockDeviceInfo *bdip) {
917
918 osalDbgCheck((sdcp != NULL) && (bdip != NULL));
919
920 if (sdcp->state != BLK_READY) {
921 return HAL_FAILED;
922 }
923
924 bdip->blk_num = sdcp->capacity;
925 bdip->blk_size = MMCSD_BLOCK_SIZE;
926
927 return HAL_SUCCESS;
928}
929
930/**
931 * @brief Erases the supplied blocks.
932 *
933 * @param[in] sdcp pointer to the @p SDCDriver object
934 * @param[in] startblk starting block number
935 * @param[in] endblk ending block number
936 *
937 * @return The operation status.
938 * @retval HAL_SUCCESS the operation succeeded.
939 * @retval HAL_FAILED the operation failed.
940 *
941 * @api
942 */
943bool sdcErase(SDCDriver *sdcp, uint32_t startblk, uint32_t endblk) {
944 uint32_t resp[1];
945
946 osalDbgCheck((sdcp != NULL));
947 osalDbgAssert(sdcp->state == BLK_READY, "invalid state");
948
949 /* Erase operation in progress.*/
950 sdcp->state = BLK_WRITING;
951
952 /* Handling command differences between HC and normal cards.*/
953 if ((sdcp->cardmode & SDC_MODE_HIGH_CAPACITY) != 0U) {
954 startblk *= MMCSD_BLOCK_SIZE;
955 endblk *= MMCSD_BLOCK_SIZE;
956 }
957
958 if (_sdc_wait_for_transfer_state(sdcp)) {
959 goto failed;
960 }
961
962 if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE_RW_BLK_START,
963 startblk, resp) != HAL_SUCCESS) ||
964 MMCSD_R1_ERROR(resp[0])) {
965 goto failed;
966 }
967
968 if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE_RW_BLK_END,
969 endblk, resp) != HAL_SUCCESS) ||
970 MMCSD_R1_ERROR(resp[0])) {
971 goto failed;
972 }
973
974 if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE,
975 0, resp) != HAL_SUCCESS) ||
976 MMCSD_R1_ERROR(resp[0])) {
977 goto failed;
978 }
979
980 /* Quick sleep to allow it to transition to programming or receiving state */
981 /* CHTODO: ??????????????????????????? */
982
983 /* Wait for it to return to transfer state to indicate it has finished erasing */
984 if (_sdc_wait_for_transfer_state(sdcp)) {
985 goto failed;
986 }
987
988 sdcp->state = BLK_READY;
989 return HAL_SUCCESS;
990
991failed:
992 sdcp->state = BLK_READY;
993 return HAL_FAILED;
994}
995
996#endif /* HAL_USE_SDC == TRUE */
997
998/** @} */
999