ChibiOS 21.11.5
hal_mmc_spi.c
Go to the documentation of this file.
1/*
2 ChibiOS - Copyright (C) 2006-2026 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 Parts of this file have been contributed by Matthias Blaicher.
18 */
19
20/**
21 * @file hal_mmc_spi.c
22 * @brief MMC over SPI driver code.
23 *
24 * @addtogroup MMC_SPI
25 * @{
26 */
27
28#include <string.h>
29
30#include "hal.h"
31
32#if (HAL_USE_MMC_SPI == TRUE) || defined(__DOXYGEN__)
33
34/*===========================================================================*/
35/* Driver local definitions. */
36/*===========================================================================*/
37
38/*===========================================================================*/
39/* Driver exported variables. */
40/*===========================================================================*/
41
42/*===========================================================================*/
43/* Driver local variables and types. */
44/*===========================================================================*/
45
46/* Forward declarations required by mmc_vmt.*/
47static bool mmc_is_card_inserted(void *instance);
48static bool mmc_is_write_protected(void *instance);
49static bool mmc_connect(void *instance);
50static bool mmc_disconnect(void *instance);
51static bool mmc_read(void *instance, uint32_t startblk,
52 uint8_t *buffer, uint32_t n);
53static bool mmc_write(void *instance, uint32_t startblk,
54 const uint8_t *buffer, uint32_t n);
55static bool mmc_sync(void *instance);
56static bool mmc_get_info(void *instance, BlockDeviceInfo *bdip);
57
58/**
59 * @brief Virtual methods table.
60 */
72
73/**
74 * @brief Lookup table for CRC-7 ( based on polynomial x^7 + x^3 + 1).
75 */
76static const uint8_t mmc_crc7_lookup_table[256] = {
77 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53,
78 0x6c, 0x65, 0x7e, 0x77, 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
79 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, 0x32, 0x3b, 0x20, 0x29,
80 0x16, 0x1f, 0x04, 0x0d, 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
81 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, 0x63, 0x6a, 0x71, 0x78,
82 0x47, 0x4e, 0x55, 0x5c, 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
83 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, 0x7d, 0x74, 0x6f, 0x66,
84 0x59, 0x50, 0x4b, 0x42, 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
85 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, 0x1e, 0x17, 0x0c, 0x05,
86 0x3a, 0x33, 0x28, 0x21, 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
87 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, 0x41, 0x48, 0x53, 0x5a,
88 0x65, 0x6c, 0x77, 0x7e, 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
89 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, 0x10, 0x19, 0x02, 0x0b,
90 0x34, 0x3d, 0x26, 0x2f, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
91 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x6a, 0x63, 0x78, 0x71,
92 0x4e, 0x47, 0x5c, 0x55, 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
93 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, 0x6d, 0x64, 0x7f, 0x76,
94 0x49, 0x40, 0x5b, 0x52, 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
95 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, 0x17, 0x1e, 0x05, 0x0c,
96 0x33, 0x3a, 0x21, 0x28, 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
97 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, 0x46, 0x4f, 0x54, 0x5d,
98 0x62, 0x6b, 0x70, 0x79
99};
100
101/*===========================================================================*/
102/* Driver local functions. */
103/*===========================================================================*/
104
105static bool mmc_is_card_inserted(void *instance) {
106 MMCDriver *mmcp = (MMCDriver *)instance;
107 bool err;
108
109 err = mmcIsCardInserted(mmcp);
110
111 return err;
112}
113
114static bool mmc_is_write_protected(void *instance) {
115 MMCDriver *mmcp = (MMCDriver *)instance;
116 bool err;
117
118 err = mmcIsWriteProtected(mmcp);
119
120 return err;
121}
122
123static bool mmc_connect(void *instance) {
124 MMCDriver *mmcp = (MMCDriver *)instance;
125 bool err;
126
127#if MMC_USE_MUTUAL_EXCLUSION == TRUE
128 spiAcquireBus(mmcp->config->spip);
129#endif
130
131 err = mmcConnect(mmcp);
132
133#if MMC_USE_MUTUAL_EXCLUSION == TRUE
134 spiReleaseBus(mmcp->config->spip);
135#endif
136
137 return err;
138}
139
140static bool mmc_disconnect(void *instance) {
141 MMCDriver *mmcp = (MMCDriver *)instance;
142 bool err;
143
144#if MMC_USE_MUTUAL_EXCLUSION == TRUE
145 spiAcquireBus(mmcp->config->spip);
146#endif
147
148 err = mmcDisconnect(mmcp);
149
150#if MMC_USE_MUTUAL_EXCLUSION == TRUE
151 spiReleaseBus(mmcp->config->spip);
152#endif
153
154 return err;
155}
156
157static bool mmc_read(void *instance, uint32_t startblk,
158 uint8_t *buffer, uint32_t n) {
159 MMCDriver *mmcp = (MMCDriver *)instance;
160 bool err = HAL_FAILED;
161
162#if MMC_USE_MUTUAL_EXCLUSION == TRUE
163 spiAcquireBus(mmcp->config->spip);
164#endif
165
166 do {
167 if (mmcStartSequentialRead(mmcp, startblk)) {
168 break;
169 }
170
171 while (n > 0U) {
172 if (mmcSequentialRead(mmcp, buffer)) {
173 break;
174 }
175 buffer += MMCSD_BLOCK_SIZE;
176 n--;
177 }
178
179 if (mmcStopSequentialRead(mmcp)) {
180 break;
181 }
182
183 err = HAL_SUCCESS;
184 } while (false);
185
186#if MMC_USE_MUTUAL_EXCLUSION == TRUE
187 spiReleaseBus(mmcp->config->spip);
188#endif
189
190 return err;
191}
192
193static bool mmc_write(void *instance, uint32_t startblk,
194 const uint8_t *buffer, uint32_t n) {
195 MMCDriver *mmcp = (MMCDriver *)instance;
196 bool err = HAL_FAILED;
197
198#if MMC_USE_MUTUAL_EXCLUSION == TRUE
199 spiAcquireBus(mmcp->config->spip);
200#endif
201
202 do {
203 if (mmcStartSequentialWrite(mmcp, startblk)) {
204 break;
205 }
206
207 while (n > 0U) {
208 if (mmcSequentialWrite(mmcp, buffer)) {
209 break;
210 }
211 buffer += MMCSD_BLOCK_SIZE;
212 n--;
213 }
214
215 if (mmcStopSequentialWrite(mmcp)) {
216 break;
217 }
218
219 err = HAL_SUCCESS;
220 } while (false);
221
222#if MMC_USE_MUTUAL_EXCLUSION == TRUE
223 spiReleaseBus(mmcp->config->spip);
224#endif
225
226 return err;
227}
228
229static bool mmc_sync(void *instance) {
230 MMCDriver *mmcp = (MMCDriver *)instance;
231 bool err;
232
233#if MMC_USE_MUTUAL_EXCLUSION == TRUE
234 spiAcquireBus(mmcp->config->spip);
235#endif
236
237 err = mmcSync(mmcp);
238
239#if MMC_USE_MUTUAL_EXCLUSION == TRUE
240 spiReleaseBus(mmcp->config->spip);
241#endif
242
243 return err;
244}
245
246static bool mmc_get_info(void *instance, BlockDeviceInfo *bdip) {
247 MMCDriver *mmcp = (MMCDriver *)instance;
248 bool err;
249
250 err = mmcGetInfo(mmcp, bdip);
251
252 return err;
253}
254
255/**
256 * @brief Calculate the MMC standard CRC-7 based on a lookup table.
257 *
258 * @param[in] crc start value for CRC
259 * @param[in] buffer pointer to data buffer
260 * @param[in] len length of data
261 * @return Calculated CRC
262 */
263static uint8_t mmc_crc7(uint8_t crc, const uint8_t *buffer, size_t len) {
264
265 while (len > 0U) {
266 crc = mmc_crc7_lookup_table[(crc << 1) ^ (*buffer++)];
267 len--;
268 }
269 return crc;
270}
271
272/**
273 * @brief Waits an idle condition.
274 *
275 * @param[in] mmcp pointer to the @p MMCDriver object
276 * @return The operation status.
277 * @retval HAL_SUCCESS if the operation succeeded.
278 * @retval HAL_FAILED if the operation failed.
279 *
280 * @notapi
281 */
282static bool mmc_wait_idle(MMCDriver *mmcp) {
283 unsigned i;
284
285 for (i = 0U; i < 16U; i++) {
286 (void) spiReceive(mmcp->config->spip, 1U, mmcp->buffer);
287 if (mmcp->buffer[0] == 0xFFU) {
288 return HAL_SUCCESS;
289 }
290 }
291
292 /* Looks like it is a long wait.*/
293 i = 0U;
294 do {
295 (void) spiReceive(mmcp->config->spip, 1U, mmcp->buffer);
296 if (mmcp->buffer[0] == 0xFFU) {
297 return HAL_SUCCESS;
298 }
299
300 /* Trying to be nice with the other threads.*/
302 } while (++i < (unsigned)MMC_IDLE_TIMEOUT_MS);
303
304 return HAL_FAILED;
305}
306
307/**
308 * @brief Sends a command header.
309 *
310 * @param[in] mmcp pointer to the @p MMCDriver object
311 * @param[in] cmd the command id
312 * @param[in] arg the command argument
313 * @return The operation status.
314 * @retval HAL_SUCCESS if the operation succeeded.
315 * @retval HAL_FAILED if the operation failed.
316 *
317 * @notapi
318 */
319static bool mmc_send_hdr(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
320
321 /* Wait for the bus to become idle if a write operation was in progress.*/
322 if (mmc_wait_idle(mmcp) == HAL_FAILED) {
323 return HAL_FAILED;
324 }
325
326 mmcp->buffer[0] = (uint8_t)0x40U | cmd;
327 mmcp->buffer[1] = (uint8_t)(arg >> 24U);
328 mmcp->buffer[2] = (uint8_t)(arg >> 16U);
329 mmcp->buffer[3] = (uint8_t)(arg >> 8U);
330 mmcp->buffer[4] = (uint8_t)arg;
331 /* Calculate CRC for command header, shift to right position, add stop bit.*/
332 mmcp->buffer[5] = ((mmc_crc7(0, mmcp->buffer, 5U) & 0x7FU) << 1U) | 0x01U;
333
334 (void) spiSend(mmcp->config->spip, 6, mmcp->buffer);
335
336 return HAL_SUCCESS;
337}
338
339/**
340 * @brief Receives a single byte response.
341 *
342 * @param[in] mmcp pointer to the @p MMCDriver object
343 * @param[out] r1p pointer to buffer for R1 answer
344 * @return Operation status.
345 * @retval HAL_SUCCESS if the operation succeeded.
346 * @retval HAL_FAILED if the operation failed.
347 *
348 * @notapi
349 */
350static bool mmc_recvr1(MMCDriver *mmcp, uint8_t *r1p) {
351 int i;
352
353 for (i = 0; i < 9; i++) {
354 (void) spiReceive(mmcp->config->spip, 1, mmcp->buffer);
355 *r1p = mmcp->buffer[0];
356 if (mmcp->buffer[0] != 0xFFU) {
357 return HAL_SUCCESS;
358 }
359 }
360 return HAL_FAILED;
361}
362
363/**
364 * @brief Receives a three byte response.
365 *
366 * @param[in] mmcp pointer to the @p MMCDriver object
367 * @param[out] r1p pointer to buffer for R1 answer
368 * @return Operation status.
369 * @retval HAL_SUCCESS if the operation succeeded.
370 * @retval HAL_FAILED if the operation failed.
371 *
372 * @notapi
373 */
374static bool mmc_recvr3(MMCDriver *mmcp, uint8_t *r1p) {
375 bool ret;
376
377 ret = mmc_recvr1(mmcp, r1p);
378 (void) spiReceive(mmcp->config->spip, 4, mmcp->buffer);
379
380 return ret;
381}
382
383/**
384 * @brief Sends a command an returns a single byte response.
385 *
386 * @param[in] mmcp pointer to the @p MMCDriver object
387 * @param[in] cmd the command id
388 * @param[in] arg the command argument
389 * @param[out] r1p pointer to buffer for R1 answer
390 * @return Operation status.
391 * @retval HAL_SUCCESS if the operation succeeded.
392 * @retval HAL_FAILED if the operation failed.
393 *
394 * @notapi
395 */
396static bool mmc_send_command_R1(MMCDriver *mmcp, uint8_t cmd,
397 uint32_t arg, uint8_t *r1p) {
398 bool result;
399
400 spiSelect(mmcp->config->spip);
401
402 result = mmc_send_hdr(mmcp, cmd, arg);
403 result = mmc_recvr1(mmcp, r1p) || result;
404
405 spiUnselect(mmcp->config->spip);
406
407 return result;
408}
409
410/**
411 * @brief Sends a command which returns a five bytes response (R3).
412 *
413 * @param[in] mmcp pointer to the @p MMCDriver object
414 * @param[in] cmd the command id
415 * @param[in] arg the command argument
416 * @param[out] r1p pointer to buffer for R1 answer
417 * @return The first byte of the response (R1) as an @p
418 * uint8_t value.
419 * @retval 0xFF timed out.
420 *
421 * @notapi
422 */
423static bool mmc_send_command_R3(MMCDriver *mmcp, uint8_t cmd, uint32_t arg,
424 uint8_t *r1p) {
425 bool result;
426
427 spiSelect(mmcp->config->spip);
428
429 result = mmc_send_hdr(mmcp, cmd, arg);
430 result = mmc_recvr3(mmcp, r1p) || result;
431
432 spiUnselect(mmcp->config->spip);
433
434 return result;
435}
436
437/**
438 * @brief Reads the CSD.
439 *
440 * @param[in] mmcp pointer to the @p MMCDriver object
441 * @param[out] cmd command
442 * @param[out] cxd pointer to the CSD/CID buffer
443 *
444 * @return The operation status.
445 * @retval HAL_SUCCESS if the operation succeeded.
446 * @retval HAL_FAILED if the operation failed.
447 *
448 * @notapi
449 */
450static bool mmc_read_CxD(MMCDriver *mmcp, uint8_t cmd, uint32_t cxd[4]) {
451 unsigned i;
452 uint8_t r1;
453 uint8_t *bp;
454 bool result;
455
456 spiSelect(mmcp->config->spip);
457
458 result = mmc_send_hdr(mmcp, cmd, 0);
459 result = mmc_recvr1(mmcp, &r1) || result;
460 if (result || (r1 != 0x00U)) {
461
462 spiUnselect(mmcp->config->spip);
463 return HAL_FAILED;
464 }
465
466 /* Wait for data availability.*/
467 for (i = 0U; i < MMC_WAIT_DATA; i++) {
468 (void) spiReceive(mmcp->config->spip, 1, mmcp->buffer);
469 if (mmcp->buffer[0] == 0xFEU) {
470 uint32_t *wp;
471
472 (void) spiReceive(mmcp->config->spip, 16, mmcp->buffer);
473 bp = mmcp->buffer;
474 for (wp = &cxd[3]; wp >= cxd; wp--) {
475 *wp = ((uint32_t)bp[0] << 24U) | ((uint32_t)bp[1] << 16U) |
476 ((uint32_t)bp[2] << 8U) | (uint32_t)bp[3];
477 bp += 4;
478 }
479
480 /* CRC ignored then end of transaction. */
481 (void) spiIgnore(mmcp->config->spip, 2);
482 spiUnselect(mmcp->config->spip);
483
484 return HAL_SUCCESS;
485 }
486 }
487
488 spiUnselect(mmcp->config->spip);
489
490 return HAL_FAILED;
491}
492
493/*===========================================================================*/
494/* Driver exported functions. */
495/*===========================================================================*/
496
497/**
498 * @brief MMC over SPI driver initialization.
499 * @note This function is implicitly invoked by @p halInit(), there is
500 * no need to explicitly initialize the driver.
501 *
502 * @init
503 */
504void mmcInit(void) {
505
506}
507
508/**
509 * @brief Initializes an instance.
510 *
511 * @param[out] mmcp pointer to the @p MMCDriver object
512 * @param[in] buffer operations buffer
513 *
514 * @init
515 */
516void mmcObjectInit(MMCDriver *mmcp, uint8_t *buffer) {
517
518 mmcp->vmt = &mmc_vmt;
519 mmcp->state = BLK_STOP;
520 mmcp->config = NULL;
521 mmcp->block_addresses = false;
522 mmcp->buffer = buffer;
523}
524
525/**
526 * @brief Configures and activates the MMC peripheral.
527 *
528 * @param[in] mmcp pointer to the @p MMCDriver object
529 * @param[in] config pointer to the @p MMCConfig object.
530 * @return The operation status.
531 *
532 * @api
533 */
534msg_t mmcStart(MMCDriver *mmcp, const MMCConfig *config) {
535
536 osalDbgCheck((mmcp != NULL) && (config != NULL));
537 osalDbgAssert((mmcp->state == BLK_STOP) || (mmcp->state == BLK_ACTIVE),
538 "invalid state");
539
540 mmcp->config = config;
541 mmcp->state = BLK_ACTIVE;
542
543 return HAL_RET_SUCCESS;
544}
545
546/**
547 * @brief Disables the MMC peripheral.
548 *
549 * @param[in] mmcp pointer to the @p MMCDriver object
550 *
551 * @api
552 */
553void mmcStop(MMCDriver *mmcp) {
554
555 osalDbgCheck(mmcp != NULL);
556 osalDbgAssert((mmcp->state == BLK_STOP) || (mmcp->state == BLK_ACTIVE),
557 "invalid state");
558
559 if (mmcp->config != NULL) {
560 spiStop(mmcp->config->spip);
561 }
562
563 mmcp->config = NULL;
564 mmcp->state = BLK_STOP;
565}
566
567/**
568 * @brief Performs the initialization procedure on the inserted card.
569 * @details This function should be invoked when a card is inserted and
570 * brings the driver in the @p MMC_READY state where it is possible
571 * to perform read and write operations.
572 * @note It is possible to invoke this function from the insertion event
573 * handler.
574 *
575 * @param[in] mmcp pointer to the @p MMCDriver object
576 *
577 * @return The operation status.
578 * @retval HAL_SUCCESS if the operation succeeded.
579 * @retval HAL_FAILED if the operation failed.
580 *
581 * @api
582 */
584 unsigned i;
585 uint8_t r1;
586
587 osalDbgCheck(mmcp != NULL);
588
589 osalDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
590 "invalid state");
591
592 /* Connection procedure in progress.*/
593 mmcp->state = BLK_CONNECTING;
594 mmcp->block_addresses = false;
595
596 /* Slow clock mode and 128 clock pulses.*/
597 (void) spiStart(mmcp->config->spip, mmcp->config->lscfg);
598 (void) spiIgnore(mmcp->config->spip, 16);
599
600 /* SPI mode selection.*/
601 i = 0U;
602 while (true) {
604 (r1 == 0x01U)) {
605 break;
606 }
607
608 if (++i >= MMC_CMD0_RETRY) {
609 goto failed;
610 }
611
613 }
614
615 /* Try to detect if this is a high capacity card and switch to block
616 addresses if possible.
617 This method is based on "How to support SDC Ver2 and high capacity cards"
618 by ElmChan.*/
621 goto failed;
622 }
623 if (r1 != 0x05U) {
624
625 /* Switch to SDHC mode.*/
626 i = 0;
627 while (true) {
628 if (mmc_send_command_R1(mmcp, MMCSD_CMD_APP_CMD, 0, &r1) == HAL_SUCCESS) {
629 if (r1 <= 0x01U) {
630 if (mmc_send_command_R3(mmcp, MMCSD_CMD_APP_OP_COND, 0x400001AAU, &r1) == HAL_SUCCESS) {
631 if (r1 == 0x00U) {
632 break;
633 }
634 }
635 }
636 }
637
638 if (++i >= MMC_ACMD41_RETRY) {
639 goto failed;
640 }
642 }
643
644 /* Execute dedicated read on OCR register */
645 if (mmc_send_command_R3(mmcp, MMCSD_CMD_READ_OCR, 0, &r1) == HAL_FAILED) {
646 goto failed;
647 }
648
649 /* Check if CCS is set in response. Card operates in block mode if set.*/
650 if ((mmcp->buffer[0] & 0x40U) != 0U) {
651 mmcp->block_addresses = true;
652 }
653 }
654
655 /* Initialization.*/
656 i = 0;
657 while (true) {
658 (void) mmc_send_command_R1(mmcp, MMCSD_CMD_INIT, 0, &r1);
659 if (r1 == 0x00U) {
660 break;
661 }
662 if (r1 != 0x01U) {
663 goto failed;
664 }
665 if (++i >= MMC_CMD1_RETRY) {
666 goto failed;
667 }
669 }
670
671 /* Initialization complete, full speed.*/
672 (void) spiStart(mmcp->config->spip, mmcp->config->hscfg);
673
674 /* Setting block size.*/
676 MMCSD_BLOCK_SIZE, &r1) == HAL_FAILED) {
677 goto failed;
678 }
679 if (r1 != 0x00U) {
680 goto failed;
681 }
682
683 /* Determine capacity.*/
684 if (mmc_read_CxD(mmcp, MMCSD_CMD_SEND_CSD, mmcp->csd) == HAL_FAILED) {
685 goto failed;
686 }
687
688 mmcp->capacity = _mmcsd_get_capacity(mmcp->csd);
689 if (mmcp->capacity == 0U) {
690 goto failed;
691 }
692
693 if (mmc_read_CxD(mmcp, MMCSD_CMD_SEND_CID, mmcp->cid) == HAL_FAILED) {
694 goto failed;
695 }
696
697 mmcp->state = BLK_READY;
698
699 return HAL_SUCCESS;
700
701 /* Connection failed, state reset to BLK_ACTIVE.*/
702failed:
703 spiStop(mmcp->config->spip);
704
705 mmcp->state = BLK_ACTIVE;
706
707 return HAL_FAILED;
708}
709
710/**
711 * @brief Brings the driver in a state safe for card removal.
712 *
713 * @param[in] mmcp pointer to the @p MMCDriver object
714 * @return The operation status.
715 *
716 * @retval HAL_SUCCESS if the operation succeeded.
717 * @retval HAL_FAILED if the operation failed.
718 *
719 * @api
720 */
722 bool result;
723
724 osalDbgCheck(mmcp != NULL);
725
726 osalSysLock();
727
728 osalDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
729 "invalid state");
730
731 if (mmcp->state == BLK_ACTIVE) {
733 return HAL_SUCCESS;
734 }
735
736 mmcp->state = BLK_DISCONNECTING;
737
739
740 /* Wait for the pending write operations to complete.*/
741 (void) spiStart(mmcp->config->spip, mmcp->config->hscfg);
742 spiSelect(mmcp->config->spip);
743
744 result = mmc_wait_idle(mmcp);
745
746 spiUnselect(mmcp->config->spip);
747 spiStop(mmcp->config->spip);
748
749 mmcp->state = BLK_ACTIVE;
750
751 return result;
752}
753
754/**
755 * @brief Starts a sequential read.
756 *
757 * @param[in] mmcp pointer to the @p MMCDriver object
758 * @param[in] startblk first block to read
759 *
760 * @return The operation status.
761 * @retval HAL_SUCCESS if the operation succeeded.
762 * @retval HAL_FAILED if the operation failed.
763 *
764 * @api
765 */
766bool mmcStartSequentialRead(MMCDriver *mmcp, uint32_t startblk) {
767 uint8_t r1;
768 bool error;
769
770 osalDbgCheck(mmcp != NULL);
771 osalDbgAssert(mmcp->state == BLK_READY, "invalid state");
772
773 /* Read operation in progress.*/
774 mmcp->state = BLK_READING;
775
776 /* (Re)starting the SPI in case it has been reprogrammed externally, it can
777 happen if the SPI bus is shared among multiple peripherals.*/
778 (void) spiStart(mmcp->config->spip, mmcp->config->hscfg);
779 spiSelect(mmcp->config->spip);
780
781 if (mmcp->block_addresses) {
782 error = mmc_send_hdr(mmcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, startblk);
783 }
784 else {
786 startblk * MMCSD_BLOCK_SIZE);
787 }
788 if (error) {
789 goto failed;
790 }
791
792 if ((mmc_recvr1(mmcp, &r1)) ||
793 (r1 != 0x00U)) {
794 goto failed;
795 }
796
797 return HAL_SUCCESS;
798
799failed:
800 spiUnselect(mmcp->config->spip);
801
802 mmcp->state = BLK_READY;
803
804 return HAL_FAILED;
805}
806
807/**
808 * @brief Reads a block within a sequential read operation.
809 *
810 * @param[in] mmcp pointer to the @p MMCDriver object
811 * @param[out] buffer pointer to the read buffer
812 *
813 * @return The operation status.
814 * @retval HAL_SUCCESS if the operation succeeded.
815 * @retval HAL_FAILED if the operation failed.
816 *
817 * @api
818 */
819bool mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer) {
820 unsigned i;
821
822 osalDbgCheck((mmcp != NULL) && (buffer != NULL));
823
824 if (mmcp->state != BLK_READING) {
825 return HAL_FAILED;
826 }
827
828 for (i = 0; i < MMC_WAIT_DATA; i++) {
829 (void) spiReceive(mmcp->config->spip, 1, mmcp->buffer);
830 if (mmcp->buffer[0] == 0xFEU) {
831 (void) spiReceive(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);
832 /* CRC ignored. */
833 (void) spiIgnore(mmcp->config->spip, 2);
834 return HAL_SUCCESS;
835 }
836 }
837
838 /* Timeout.*/
839 spiUnselect(mmcp->config->spip);
840
841 mmcp->state = BLK_READY;
842
843 return HAL_FAILED;
844}
845
846/**
847 * @brief Stops a sequential read gracefully.
848 *
849 * @param[in] mmcp pointer to the @p MMCDriver object
850 *
851 * @return The operation status.
852 * @retval HAL_SUCCESS if the operation succeeded.
853 * @retval HAL_FAILED if the operation failed.
854 *
855 * @api
856 */
858 uint8_t r1;
859 static const uint8_t stopcmd[] = {
860 (uint8_t)(0x40U | MMCSD_CMD_STOP_TRANSMISSION), 0, 0, 0, 0, 1, 0xFF
861 };
862
863 osalDbgCheck(mmcp != NULL);
864
865 if (mmcp->state != BLK_READING) {
866 return HAL_FAILED;
867 }
868
869 (void) spiSend(mmcp->config->spip, sizeof(stopcmd), stopcmd);
870
871 /* TODO Ignoring R1 answer from the command. There is no action we could
872 do on error.*/
873 (void) mmc_recvr1(mmcp, &r1);
874
875 /* Read operation finished.*/
876 spiUnselect(mmcp->config->spip);
877
878 mmcp->state = BLK_READY;
879
880 return HAL_SUCCESS;
881}
882
883/**
884 * @brief Starts a sequential write.
885 *
886 * @param[in] mmcp pointer to the @p MMCDriver object
887 * @param[in] startblk first block to write
888 *
889 * @return The operation status.
890 * @retval HAL_SUCCESS if the operation succeeded.
891 * @retval HAL_FAILED if the operation failed.
892 *
893 * @api
894 */
895bool mmcStartSequentialWrite(MMCDriver *mmcp, uint32_t startblk) {
896 uint8_t r1;
897 bool error;
898
899 osalDbgCheck(mmcp != NULL);
900 osalDbgAssert(mmcp->state == BLK_READY, "invalid state");
901
902 /* Write operation in progress.*/
903 mmcp->state = BLK_WRITING;
904
905 (void) spiStart(mmcp->config->spip, mmcp->config->hscfg);
906 spiSelect(mmcp->config->spip);
907 if (mmcp->block_addresses) {
908 error = mmc_send_hdr(mmcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK, startblk);
909 }
910 else {
912 startblk * MMCSD_BLOCK_SIZE);
913 }
914 if (error) {
915 goto failed;
916 }
917
918 if (mmc_recvr1(mmcp, &r1) || (r1 != 0x00U)) {
919 goto failed;
920 }
921
922 return HAL_SUCCESS;
923
924failed:
925 spiUnselect(mmcp->config->spip);
926
927 mmcp->state = BLK_READY;
928
929 return HAL_FAILED;
930}
931
932/**
933 * @brief Writes a block within a sequential write operation.
934 *
935 * @param[in] mmcp pointer to the @p MMCDriver object
936 * @param[out] buffer pointer to the write buffer
937 *
938 * @return The operation status.
939 * @retval HAL_SUCCESS if the operation succeeded.
940 * @retval HAL_FAILED if the operation failed.
941 *
942 * @api
943 */
944bool mmcSequentialWrite(MMCDriver *mmcp, const uint8_t *buffer) {
945 static const uint8_t start[] = {0xFF, 0xFC};
946
947 osalDbgCheck((mmcp != NULL) && (buffer != NULL));
948
949 if (mmcp->state != BLK_WRITING) {
950 return HAL_FAILED;
951 }
952
953 (void) spiSend(mmcp->config->spip, sizeof(start), start); /* Data prologue. */
954 (void) spiSend(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);/* Data. */
955 (void) spiIgnore(mmcp->config->spip, 2); /* CRC ignored. */
956 (void) spiReceive(mmcp->config->spip, 1, mmcp->buffer);
957 if ((mmcp->buffer[0] & 0x1FU) == 0x05U) {
958 return mmc_wait_idle(mmcp);
959 }
960
961 /* Error.*/
962 spiUnselect(mmcp->config->spip);
963
964 mmcp->state = BLK_READY;
965
966 return HAL_FAILED;
967}
968
969/**
970 * @brief Stops a sequential write gracefully.
971 *
972 * @param[in] mmcp pointer to the @p MMCDriver object
973 *
974 * @return The operation status.
975 * @retval HAL_SUCCESS if the operation succeeded.
976 * @retval HAL_FAILED if the operation failed.
977 *
978 * @api
979 */
981 static const uint8_t stop[] = {0xFD, 0xFF};
982
983 osalDbgCheck(mmcp != NULL);
984
985 if (mmcp->state != BLK_WRITING) {
986 return HAL_FAILED;
987 }
988
989 (void) spiSend(mmcp->config->spip, sizeof(stop), stop);
990 spiUnselect(mmcp->config->spip);
991
992 /* Write operation finished.*/
993 mmcp->state = BLK_READY;
994
995 return HAL_SUCCESS;
996}
997
998/**
999 * @brief Waits for card idle condition.
1000 *
1001 * @param[in] mmcp pointer to the @p MMCDriver object
1002 *
1003 * @return The operation status.
1004 * @retval HAL_SUCCESS if the operation succeeded.
1005 * @retval HAL_FAILED if the operation failed.
1006 *
1007 * @api
1008 */
1009bool mmcSync(MMCDriver *mmcp) {
1010 bool result;
1011
1012 osalDbgCheck(mmcp != NULL);
1013
1014 if (mmcp->state != BLK_READY) {
1015 return HAL_FAILED;
1016 }
1017
1018 /* Synchronization operation in progress.*/
1019 mmcp->state = BLK_SYNCING;
1020
1021 (void) spiStart(mmcp->config->spip, mmcp->config->hscfg);
1022 spiSelect(mmcp->config->spip);
1023
1024 result = mmc_wait_idle(mmcp);
1025
1026 spiUnselect(mmcp->config->spip);
1027
1028 /* Synchronization operation finished.*/
1029 mmcp->state = BLK_READY;
1030
1031 return result;
1032}
1033
1034/**
1035 * @brief Returns the media info.
1036 *
1037 * @param[in] mmcp pointer to the @p MMCDriver object
1038 * @param[out] bdip pointer to a @p BlockDeviceInfo structure
1039 *
1040 * @return The operation status.
1041 * @retval HAL_SUCCESS if the operation succeeded.
1042 * @retval HAL_FAILED if the operation failed.
1043 *
1044 * @api
1045 */
1047
1048 osalDbgCheck((mmcp != NULL) && (bdip != NULL));
1049
1050 if (mmcp->state != BLK_READY) {
1051 return HAL_FAILED;
1052 }
1053
1054 bdip->blk_num = mmcp->capacity;
1055 bdip->blk_size = MMCSD_BLOCK_SIZE;
1056
1057 return HAL_SUCCESS;
1058}
1059
1060/**
1061 * @brief Erases blocks.
1062 *
1063 * @param[in] mmcp pointer to the @p MMCDriver object
1064 * @param[in] startblk starting block number
1065 * @param[in] endblk ending block number
1066 *
1067 * @return The operation status.
1068 * @retval HAL_SUCCESS if the operation succeeded.
1069 * @retval HAL_FAILED if the operation failed.
1070 *
1071 * @api
1072 */
1073bool mmcErase(MMCDriver *mmcp, uint32_t startblk, uint32_t endblk) {
1074 uint8_t r1;
1075
1076 osalDbgCheck((mmcp != NULL));
1077
1078 /* Erase operation in progress.*/
1079 mmcp->state = BLK_WRITING;
1080
1081 /* Handling command differences between HC and normal cards.*/
1082 if (!mmcp->block_addresses) {
1083 startblk *= MMCSD_BLOCK_SIZE;
1084 endblk *= MMCSD_BLOCK_SIZE;
1085 }
1086
1088 startblk, &r1) == HAL_FAILED) ||
1089 (r1 != 0x00U)) {
1090 goto failed;
1091 }
1092
1094 endblk, &r1) == HAL_FAILED) ||
1095 (r1 != 0x00U)) {
1096 goto failed;
1097 }
1098
1099 if ((mmc_send_command_R1(mmcp, MMCSD_CMD_ERASE, 0, &r1) == HAL_FAILED) ||
1100 (r1 != 0x00U)) {
1101 goto failed;
1102 }
1103
1104 mmcp->state = BLK_READY;
1105
1106 return HAL_SUCCESS;
1107
1108 /* Command failed, state reset to BLK_READY.*/
1109failed:
1110 mmcp->state = BLK_READY;
1111
1112 return HAL_FAILED;
1113}
1114
1115#endif /* HAL_USE_MMC_SPI == TRUE */
1116
1117/** @} */
#define HAL_RET_SUCCESS
Definition hal.h:93
#define HAL_SUCCESS
HAL operation success.
Definition hal.h:81
#define HAL_FAILED
HAL operation failed.
Definition hal.h:86
@ BLK_WRITING
Definition hal_ioblock.h:48
@ BLK_STOP
Definition hal_ioblock.h:42
@ BLK_CONNECTING
Definition hal_ioblock.h:44
@ BLK_READY
Definition hal_ioblock.h:46
@ BLK_DISCONNECTING
Definition hal_ioblock.h:45
@ BLK_READING
Definition hal_ioblock.h:47
@ BLK_SYNCING
Definition hal_ioblock.h:49
@ BLK_ACTIVE
Definition hal_ioblock.h:43
bool mmcStartSequentialWrite(MMCDriver *mmcp, uint32_t startblk)
Starts a sequential write.
static bool mmc_read(void *instance, uint32_t startblk, uint8_t *buffer, uint32_t n)
static bool mmc_write(void *instance, uint32_t startblk, const uint8_t *buffer, uint32_t n)
#define MMC_ACMD41_RETRY
Definition hal_mmc_spi.h:36
bool mmcDisconnect(MMCDriver *mmcp)
Brings the driver in a state safe for card removal.
void mmcStop(MMCDriver *mmcp)
Disables the MMC peripheral.
static bool mmc_recvr1(MMCDriver *mmcp, uint8_t *r1p)
Receives a single byte response.
static bool mmc_recvr3(MMCDriver *mmcp, uint8_t *r1p)
Receives a three byte response.
#define mmcIsWriteProtected(mmcp)
Returns the write protect status.
static const uint8_t mmc_crc7_lookup_table[256]
Lookup table for CRC-7 ( based on polynomial x^7 + x^3 + 1).
Definition hal_mmc_spi.c:76
bool mmcGetInfo(MMCDriver *mmcp, BlockDeviceInfo *bdip)
Returns the media info.
void mmcInit(void)
MMC over SPI driver initialization.
bool mmcStartSequentialRead(MMCDriver *mmcp, uint32_t startblk)
Starts a sequential read.
static uint8_t mmc_crc7(uint8_t crc, const uint8_t *buffer, size_t len)
Calculate the MMC standard CRC-7 based on a lookup table.
bool mmcSync(MMCDriver *mmcp)
Waits for card idle condition.
static bool mmc_wait_idle(MMCDriver *mmcp)
Waits an idle condition.
static bool mmc_is_write_protected(void *instance)
#define MMC_WAIT_DATA
Definition hal_mmc_spi.h:37
static bool mmc_get_info(void *instance, BlockDeviceInfo *bdip)
static bool mmc_sync(void *instance)
#define MMC_CMD1_RETRY
Definition hal_mmc_spi.h:35
#define mmcIsCardInserted(mmcp)
Returns the card insertion status.
static const struct mmc_spi_driver_vmt mmc_vmt
Virtual methods table.
Definition hal_mmc_spi.c:61
bool mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer)
Reads a block within a sequential read operation.
bool mmcSequentialWrite(MMCDriver *mmcp, const uint8_t *buffer)
Writes a block within a sequential write operation.
static bool mmc_send_command_R3(MMCDriver *mmcp, uint8_t cmd, uint32_t arg, uint8_t *r1p)
Sends a command which returns a five bytes response (R3).
static bool mmc_connect(void *instance)
msg_t mmcStart(MMCDriver *mmcp, const MMCConfig *config)
Configures and activates the MMC peripheral.
void mmcObjectInit(MMCDriver *mmcp, uint8_t *buffer)
Initializes an instance.
bool mmcStopSequentialRead(MMCDriver *mmcp)
Stops a sequential read gracefully.
mmc_spi_config_t MMCConfig
Legacy name for compatibility.
static bool mmc_send_command_R1(MMCDriver *mmcp, uint8_t cmd, uint32_t arg, uint8_t *r1p)
Sends a command an returns a single byte response.
#define MMC_IDLE_TIMEOUT_MS
Timeout before assuming a failure while waiting for card idle.
Definition hal_mmc_spi.h:59
mmc_spi_driver_t MMCDriver
Legacy name for compatibility.
bool mmcErase(MMCDriver *mmcp, uint32_t startblk, uint32_t endblk)
Erases blocks.
bool mmcStopSequentialWrite(MMCDriver *mmcp)
Stops a sequential write gracefully.
static bool mmc_send_hdr(MMCDriver *mmcp, uint8_t cmd, uint32_t arg)
Sends a command header.
static bool mmc_disconnect(void *instance)
#define MMC_CMD0_RETRY
Definition hal_mmc_spi.h:34
static bool mmc_is_card_inserted(void *instance)
static bool mmc_read_CxD(MMCDriver *mmcp, uint8_t cmd, uint32_t cxd[4])
Reads the CSD.
bool mmcConnect(MMCDriver *mmcp)
Performs the initialization procedure on the inserted card.
#define MMCSD_CMD_ERASE_RW_BLK_END
Definition hal_mmcsd.h:91
#define MMCSD_CMD_INIT
Definition hal_mmcsd.h:71
#define MMCSD_CMD_READ_OCR
Definition hal_mmcsd.h:96
#define MMCSD_CMD_WRITE_MULTIPLE_BLOCK
Definition hal_mmcsd.h:89
#define MMCSD_CMD_APP_OP_COND
Definition hal_mmcsd.h:93
#define MMCSD_CMD_STOP_TRANSMISSION
Definition hal_mmcsd.h:82
#define MMCSD_CMD_ERASE
Definition hal_mmcsd.h:92
#define MMCSD_CMD_APP_CMD
Definition hal_mmcsd.h:95
uint32_t _mmcsd_get_capacity(const uint32_t *csd)
Extract card capacity from a CSD.
Definition hal_mmcsd.c:93
#define MMCSD_CMD8_PATTERN
Fixed pattern for CMD8.
Definition hal_mmcsd.h:49
#define MMCSD_BLOCK_SIZE
Fixed block size for MMC/SD block devices.
Definition hal_mmcsd.h:39
#define MMCSD_CMD_SEND_CSD
Definition hal_mmcsd.h:80
#define MMCSD_CMD_SEND_IF_COND
Definition hal_mmcsd.h:78
#define MMCSD_CMD_SET_BLOCKLEN
Definition hal_mmcsd.h:84
#define MMCSD_CMD_GO_IDLE_STATE
Definition hal_mmcsd.h:70
#define MMCSD_CMD_SEND_CID
Definition hal_mmcsd.h:81
#define MMCSD_CMD_READ_MULTIPLE_BLOCK
Definition hal_mmcsd.h:86
#define MMCSD_CMD_ERASE_RW_BLK_START
Definition hal_mmcsd.h:90
static void osalSysLock(void)
Enters a critical zone from thread context.
Definition osal.h:601
static void osalSysUnlock(void)
Leaves a critical zone from thread context.
Definition osal.h:611
#define osalDbgAssert(c, remark)
Condition assertion.
Definition osal.h:264
#define osalDbgCheck(c)
Function parameters check.
Definition osal.h:284
#define osalThreadSleepMilliseconds(msecs)
Delays the invoking thread for the specified number of milliseconds.
Definition osal.h:522
void spiSelect(SPIDriver *spip)
Asserts the slave select signal and prepares for transfers.
void spiReleaseBus(SPIDriver *spip)
Releases exclusive access to the SPI bus.
void spiSend(SPIDriver *spip, size_t n, const void *txbuf)
Sends data over the SPI bus.
void spiIgnore(SPIDriver *spip, size_t n)
Ignores data on the SPI bus.
void spiAcquireBus(SPIDriver *spip)
Gains exclusive access to the SPI bus.
msg_t spiStart(SPIDriver *spip, const SPIConfig *config)
Configures and activates the SPI peripheral.
void spiStop(SPIDriver *spip)
Deactivates the SPI peripheral.
void spiReceive(SPIDriver *spip, size_t n, void *rxbuf)
Receives data from the SPI bus.
void spiUnselect(SPIDriver *spip)
Deasserts the slave select signal.
int32_t msg_t
Definition chearly.h:87
HAL subsystem header.
Block device info.
Definition hal_ioblock.h:55
uint32_t blk_num
Total number of blocks.
Definition hal_ioblock.h:57
uint32_t blk_size
Block size in bytes.
Definition hal_ioblock.h:56
const SPIConfig * lscfg
SPI low speed configuration used during initialization.
Definition hal_mmc_spi.h:97
const SPIConfig * hscfg
SPI high speed configuration used during transfers.
SPIDriver * spip
SPI driver associated to this MMC driver.
Definition hal_mmc_spi.h:93
uint8_t * buffer
Pointer to an un-cacheable buffer of size MMC_BUFFER_SIZE.
const struct mmc_spi_driver_vmt * vmt
Virtual Methods Table.
bool block_addresses
Addresses use blocks instead of bytes.
_mmcsd_block_device_data const mmc_spi_config_t * config
Current configuration data.
MMCDriver virtual methods table.