ChibiOS 21.11.4
hal_mmc_spi.c
Go to the documentation of this file.
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 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 spiStop(mmcp->config->spip);
560
561 mmcp->config = NULL;
562 mmcp->state = BLK_STOP;
563}
564
565/**
566 * @brief Performs the initialization procedure on the inserted card.
567 * @details This function should be invoked when a card is inserted and
568 * brings the driver in the @p MMC_READY state where it is possible
569 * to perform read and write operations.
570 * @note It is possible to invoke this function from the insertion event
571 * handler.
572 *
573 * @param[in] mmcp pointer to the @p MMCDriver object
574 *
575 * @return The operation status.
576 * @retval HAL_SUCCESS if the operation succeeded.
577 * @retval HAL_FAILED if the operation failed.
578 *
579 * @api
580 */
582 unsigned i;
583 uint8_t r1;
584
585 osalDbgCheck(mmcp != NULL);
586
587 osalDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
588 "invalid state");
589
590 /* Connection procedure in progress.*/
591 mmcp->state = BLK_CONNECTING;
592 mmcp->block_addresses = false;
593
594 /* Slow clock mode and 128 clock pulses.*/
595 (void) spiStart(mmcp->config->spip, mmcp->config->lscfg);
596 (void) spiIgnore(mmcp->config->spip, 16);
597
598 /* SPI mode selection.*/
599 i = 0U;
600 while (true) {
602 (r1 == 0x01U)) {
603 break;
604 }
605
606 if (++i >= MMC_CMD0_RETRY) {
607 goto failed;
608 }
609
611 }
612
613 /* Try to detect if this is a high capacity card and switch to block
614 addresses if possible.
615 This method is based on "How to support SDC Ver2 and high capacity cards"
616 by ElmChan.*/
619 goto failed;
620 }
621 if (r1 != 0x05U) {
622
623 /* Switch to SDHC mode.*/
624 i = 0;
625 while (true) {
626 if (mmc_send_command_R1(mmcp, MMCSD_CMD_APP_CMD, 0, &r1) == HAL_SUCCESS) {
627 if (r1 <= 0x01U) {
628 if (mmc_send_command_R3(mmcp, MMCSD_CMD_APP_OP_COND, 0x400001AAU, &r1) == HAL_SUCCESS) {
629 if (r1 == 0x00U) {
630 break;
631 }
632 }
633 }
634 }
635
636 if (++i >= MMC_ACMD41_RETRY) {
637 goto failed;
638 }
640 }
641
642 /* Execute dedicated read on OCR register */
643 if (mmc_send_command_R3(mmcp, MMCSD_CMD_READ_OCR, 0, &r1) == HAL_FAILED) {
644 goto failed;
645 }
646
647 /* Check if CCS is set in response. Card operates in block mode if set.*/
648 if ((mmcp->buffer[0] & 0x40U) != 0U) {
649 mmcp->block_addresses = true;
650 }
651 }
652
653 /* Initialization.*/
654 i = 0;
655 while (true) {
656 (void) mmc_send_command_R1(mmcp, MMCSD_CMD_INIT, 0, &r1);
657 if (r1 == 0x00U) {
658 break;
659 }
660 if (r1 != 0x01U) {
661 goto failed;
662 }
663 if (++i >= MMC_CMD1_RETRY) {
664 goto failed;
665 }
667 }
668
669 /* Initialization complete, full speed.*/
670 (void) spiStart(mmcp->config->spip, mmcp->config->hscfg);
671
672 /* Setting block size.*/
674 MMCSD_BLOCK_SIZE, &r1) == HAL_FAILED) {
675 goto failed;
676 }
677 if (r1 != 0x00U) {
678 goto failed;
679 }
680
681 /* Determine capacity.*/
682 if (mmc_read_CxD(mmcp, MMCSD_CMD_SEND_CSD, mmcp->csd) == HAL_FAILED) {
683 goto failed;
684 }
685
686 mmcp->capacity = _mmcsd_get_capacity(mmcp->csd);
687 if (mmcp->capacity == 0U) {
688 goto failed;
689 }
690
691 if (mmc_read_CxD(mmcp, MMCSD_CMD_SEND_CID, mmcp->cid) == HAL_FAILED) {
692 goto failed;
693 }
694
695 mmcp->state = BLK_READY;
696
697 return HAL_SUCCESS;
698
699 /* Connection failed, state reset to BLK_ACTIVE.*/
700failed:
701 spiStop(mmcp->config->spip);
702
703 mmcp->state = BLK_ACTIVE;
704
705 return HAL_FAILED;
706}
707
708/**
709 * @brief Brings the driver in a state safe for card removal.
710 *
711 * @param[in] mmcp pointer to the @p MMCDriver object
712 * @return The operation status.
713 *
714 * @retval HAL_SUCCESS if the operation succeeded.
715 * @retval HAL_FAILED if the operation failed.
716 *
717 * @api
718 */
720 bool result;
721
722 osalDbgCheck(mmcp != NULL);
723
724 osalSysLock();
725
726 osalDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
727 "invalid state");
728
729 if (mmcp->state == BLK_ACTIVE) {
731 return HAL_SUCCESS;
732 }
733
734 mmcp->state = BLK_DISCONNECTING;
735
737
738 /* Wait for the pending write operations to complete.*/
739 (void) spiStart(mmcp->config->spip, mmcp->config->hscfg);
740 spiSelect(mmcp->config->spip);
741
742 result = mmc_wait_idle(mmcp);
743
744 spiUnselect(mmcp->config->spip);
745 spiStop(mmcp->config->spip);
746
747 mmcp->state = BLK_ACTIVE;
748
749 return result;
750}
751
752/**
753 * @brief Starts a sequential read.
754 *
755 * @param[in] mmcp pointer to the @p MMCDriver object
756 * @param[in] startblk first block to read
757 *
758 * @return The operation status.
759 * @retval HAL_SUCCESS if the operation succeeded.
760 * @retval HAL_FAILED if the operation failed.
761 *
762 * @api
763 */
764bool mmcStartSequentialRead(MMCDriver *mmcp, uint32_t startblk) {
765 uint8_t r1;
766 bool error;
767
768 osalDbgCheck(mmcp != NULL);
769 osalDbgAssert(mmcp->state == BLK_READY, "invalid state");
770
771 /* Read operation in progress.*/
772 mmcp->state = BLK_READING;
773
774 /* (Re)starting the SPI in case it has been reprogrammed externally, it can
775 happen if the SPI bus is shared among multiple peripherals.*/
776 (void) spiStart(mmcp->config->spip, mmcp->config->hscfg);
777 spiSelect(mmcp->config->spip);
778
779 if (mmcp->block_addresses) {
780 error = mmc_send_hdr(mmcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, startblk);
781 }
782 else {
784 startblk * MMCSD_BLOCK_SIZE);
785 }
786 if (error) {
787 goto failed;
788 }
789
790 if ((mmc_recvr1(mmcp, &r1)) ||
791 (r1 != 0x00U)) {
792 goto failed;
793 }
794
795 return HAL_SUCCESS;
796
797failed:
798 spiUnselect(mmcp->config->spip);
799
800 mmcp->state = BLK_READY;
801
802 return HAL_FAILED;
803}
804
805/**
806 * @brief Reads a block within a sequential read operation.
807 *
808 * @param[in] mmcp pointer to the @p MMCDriver object
809 * @param[out] buffer pointer to the read buffer
810 *
811 * @return The operation status.
812 * @retval HAL_SUCCESS if the operation succeeded.
813 * @retval HAL_FAILED if the operation failed.
814 *
815 * @api
816 */
817bool mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer) {
818 unsigned i;
819
820 osalDbgCheck((mmcp != NULL) && (buffer != NULL));
821
822 if (mmcp->state != BLK_READING) {
823 return HAL_FAILED;
824 }
825
826 for (i = 0; i < MMC_WAIT_DATA; i++) {
827 (void) spiReceive(mmcp->config->spip, 1, mmcp->buffer);
828 if (mmcp->buffer[0] == 0xFEU) {
829 (void) spiReceive(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);
830 /* CRC ignored. */
831 (void) spiIgnore(mmcp->config->spip, 2);
832 return HAL_SUCCESS;
833 }
834 }
835
836 /* Timeout.*/
837 spiUnselect(mmcp->config->spip);
838
839 mmcp->state = BLK_READY;
840
841 return HAL_FAILED;
842}
843
844/**
845 * @brief Stops a sequential read gracefully.
846 *
847 * @param[in] mmcp pointer to the @p MMCDriver object
848 *
849 * @return The operation status.
850 * @retval HAL_SUCCESS if the operation succeeded.
851 * @retval HAL_FAILED if the operation failed.
852 *
853 * @api
854 */
856 uint8_t r1;
857 static const uint8_t stopcmd[] = {
858 (uint8_t)(0x40U | MMCSD_CMD_STOP_TRANSMISSION), 0, 0, 0, 0, 1, 0xFF
859 };
860
861 osalDbgCheck(mmcp != NULL);
862
863 if (mmcp->state != BLK_READING) {
864 return HAL_FAILED;
865 }
866
867 (void) spiSend(mmcp->config->spip, sizeof(stopcmd), stopcmd);
868
869 /* TODO Ignoring R1 answer from the command. There is no action we could
870 do on error.*/
871 (void) mmc_recvr1(mmcp, &r1);
872
873 /* Read operation finished.*/
874 spiUnselect(mmcp->config->spip);
875
876 mmcp->state = BLK_READY;
877
878 return HAL_SUCCESS;
879}
880
881/**
882 * @brief Starts a sequential write.
883 *
884 * @param[in] mmcp pointer to the @p MMCDriver object
885 * @param[in] startblk first block to write
886 *
887 * @return The operation status.
888 * @retval HAL_SUCCESS if the operation succeeded.
889 * @retval HAL_FAILED if the operation failed.
890 *
891 * @api
892 */
893bool mmcStartSequentialWrite(MMCDriver *mmcp, uint32_t startblk) {
894 uint8_t r1;
895 bool error;
896
897 osalDbgCheck(mmcp != NULL);
898 osalDbgAssert(mmcp->state == BLK_READY, "invalid state");
899
900 /* Write operation in progress.*/
901 mmcp->state = BLK_WRITING;
902
903 (void) spiStart(mmcp->config->spip, mmcp->config->hscfg);
904 spiSelect(mmcp->config->spip);
905 if (mmcp->block_addresses) {
906 error = mmc_send_hdr(mmcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK, startblk);
907 }
908 else {
910 startblk * MMCSD_BLOCK_SIZE);
911 }
912 if (error) {
913 goto failed;
914 }
915
916 if (mmc_recvr1(mmcp, &r1) || (r1 != 0x00U)) {
917 goto failed;
918 }
919
920 return HAL_SUCCESS;
921
922failed:
923 spiUnselect(mmcp->config->spip);
924
925 mmcp->state = BLK_READY;
926
927 return HAL_FAILED;
928}
929
930/**
931 * @brief Writes a block within a sequential write operation.
932 *
933 * @param[in] mmcp pointer to the @p MMCDriver object
934 * @param[out] buffer pointer to the write buffer
935 *
936 * @return The operation status.
937 * @retval HAL_SUCCESS if the operation succeeded.
938 * @retval HAL_FAILED if the operation failed.
939 *
940 * @api
941 */
942bool mmcSequentialWrite(MMCDriver *mmcp, const uint8_t *buffer) {
943 static const uint8_t start[] = {0xFF, 0xFC};
944
945 osalDbgCheck((mmcp != NULL) && (buffer != NULL));
946
947 if (mmcp->state != BLK_WRITING) {
948 return HAL_FAILED;
949 }
950
951 (void) spiSend(mmcp->config->spip, sizeof(start), start); /* Data prologue. */
952 (void) spiSend(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);/* Data. */
953 (void) spiIgnore(mmcp->config->spip, 2); /* CRC ignored. */
954 (void) spiReceive(mmcp->config->spip, 1, mmcp->buffer);
955 if ((mmcp->buffer[0] & 0x1FU) == 0x05U) {
956 return mmc_wait_idle(mmcp);
957 }
958
959 /* Error.*/
960 spiUnselect(mmcp->config->spip);
961
962 mmcp->state = BLK_READY;
963
964 return HAL_FAILED;
965}
966
967/**
968 * @brief Stops a sequential write gracefully.
969 *
970 * @param[in] mmcp pointer to the @p MMCDriver object
971 *
972 * @return The operation status.
973 * @retval HAL_SUCCESS if the operation succeeded.
974 * @retval HAL_FAILED if the operation failed.
975 *
976 * @api
977 */
979 static const uint8_t stop[] = {0xFD, 0xFF};
980
981 osalDbgCheck(mmcp != NULL);
982
983 if (mmcp->state != BLK_WRITING) {
984 return HAL_FAILED;
985 }
986
987 (void) spiSend(mmcp->config->spip, sizeof(stop), stop);
988 spiUnselect(mmcp->config->spip);
989
990 /* Write operation finished.*/
991 mmcp->state = BLK_READY;
992
993 return HAL_SUCCESS;
994}
995
996/**
997 * @brief Waits for card idle condition.
998 *
999 * @param[in] mmcp pointer to the @p MMCDriver object
1000 *
1001 * @return The operation status.
1002 * @retval HAL_SUCCESS if the operation succeeded.
1003 * @retval HAL_FAILED if the operation failed.
1004 *
1005 * @api
1006 */
1007bool mmcSync(MMCDriver *mmcp) {
1008 bool result;
1009
1010 osalDbgCheck(mmcp != NULL);
1011
1012 if (mmcp->state != BLK_READY) {
1013 return HAL_FAILED;
1014 }
1015
1016 /* Synchronization operation in progress.*/
1017 mmcp->state = BLK_SYNCING;
1018
1019 (void) spiStart(mmcp->config->spip, mmcp->config->hscfg);
1020 spiSelect(mmcp->config->spip);
1021
1022 result = mmc_wait_idle(mmcp);
1023
1024 spiUnselect(mmcp->config->spip);
1025
1026 /* Synchronization operation finished.*/
1027 mmcp->state = BLK_READY;
1028
1029 return result;
1030}
1031
1032/**
1033 * @brief Returns the media info.
1034 *
1035 * @param[in] mmcp pointer to the @p MMCDriver object
1036 * @param[out] bdip pointer to a @p BlockDeviceInfo structure
1037 *
1038 * @return The operation status.
1039 * @retval HAL_SUCCESS if the operation succeeded.
1040 * @retval HAL_FAILED if the operation failed.
1041 *
1042 * @api
1043 */
1045
1046 osalDbgCheck((mmcp != NULL) && (bdip != NULL));
1047
1048 if (mmcp->state != BLK_READY) {
1049 return HAL_FAILED;
1050 }
1051
1052 bdip->blk_num = mmcp->capacity;
1053 bdip->blk_size = MMCSD_BLOCK_SIZE;
1054
1055 return HAL_SUCCESS;
1056}
1057
1058/**
1059 * @brief Erases blocks.
1060 *
1061 * @param[in] mmcp pointer to the @p MMCDriver object
1062 * @param[in] startblk starting block number
1063 * @param[in] endblk ending block number
1064 *
1065 * @return The operation status.
1066 * @retval HAL_SUCCESS if the operation succeeded.
1067 * @retval HAL_FAILED if the operation failed.
1068 *
1069 * @api
1070 */
1071bool mmcErase(MMCDriver *mmcp, uint32_t startblk, uint32_t endblk) {
1072 uint8_t r1;
1073
1074 osalDbgCheck((mmcp != NULL));
1075
1076 /* Erase operation in progress.*/
1077 mmcp->state = BLK_WRITING;
1078
1079 /* Handling command differences between HC and normal cards.*/
1080 if (!mmcp->block_addresses) {
1081 startblk *= MMCSD_BLOCK_SIZE;
1082 endblk *= MMCSD_BLOCK_SIZE;
1083 }
1084
1086 startblk, &r1) == HAL_FAILED) ||
1087 (r1 != 0x00U)) {
1088 goto failed;
1089 }
1090
1092 endblk, &r1) == HAL_FAILED) ||
1093 (r1 != 0x00U)) {
1094 goto failed;
1095 }
1096
1097 if ((mmc_send_command_R1(mmcp, MMCSD_CMD_ERASE, 0, &r1) == HAL_FAILED) ||
1098 (r1 != 0x00U)) {
1099 goto failed;
1100 }
1101
1102 mmcp->state = BLK_READY;
1103
1104 return HAL_SUCCESS;
1105
1106 /* Command failed, state reset to BLK_READY.*/
1107failed:
1108 mmcp->state = BLK_READY;
1109
1110 return HAL_FAILED;
1111}
1112
1113#endif /* HAL_USE_MMC_SPI == TRUE */
1114
1115/** @} */
#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:88
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.