ChibiOS/HAL  6.1.0
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.*/
47 static bool mmc_read(void *instance, uint32_t startblk,
48  uint8_t *buffer, uint32_t n);
49 static bool mmc_write(void *instance, uint32_t startblk,
50  const uint8_t *buffer, uint32_t n);
51 
52 /**
53  * @brief Virtual methods table.
54  */
55 static const struct MMCDriverVMT mmc_vmt = {
56  (size_t)0,
57  (bool (*)(void *))mmc_lld_is_card_inserted,
58  (bool (*)(void *))mmc_lld_is_write_protected,
59  (bool (*)(void *))mmcConnect,
60  (bool (*)(void *))mmcDisconnect,
61  mmc_read,
62  mmc_write,
63  (bool (*)(void *))mmcSync,
64  (bool (*)(void *, BlockDeviceInfo *))mmcGetInfo
65 };
66 
67 /**
68  * @brief Lookup table for CRC-7 ( based on polynomial x^7 + x^3 + 1).
69  */
70 static const uint8_t crc7_lookup_table[256] = {
71  0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53,
72  0x6c, 0x65, 0x7e, 0x77, 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
73  0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, 0x32, 0x3b, 0x20, 0x29,
74  0x16, 0x1f, 0x04, 0x0d, 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
75  0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, 0x63, 0x6a, 0x71, 0x78,
76  0x47, 0x4e, 0x55, 0x5c, 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
77  0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, 0x7d, 0x74, 0x6f, 0x66,
78  0x59, 0x50, 0x4b, 0x42, 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
79  0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, 0x1e, 0x17, 0x0c, 0x05,
80  0x3a, 0x33, 0x28, 0x21, 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
81  0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, 0x41, 0x48, 0x53, 0x5a,
82  0x65, 0x6c, 0x77, 0x7e, 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
83  0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, 0x10, 0x19, 0x02, 0x0b,
84  0x34, 0x3d, 0x26, 0x2f, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
85  0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x6a, 0x63, 0x78, 0x71,
86  0x4e, 0x47, 0x5c, 0x55, 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
87  0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, 0x6d, 0x64, 0x7f, 0x76,
88  0x49, 0x40, 0x5b, 0x52, 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
89  0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, 0x17, 0x1e, 0x05, 0x0c,
90  0x33, 0x3a, 0x21, 0x28, 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
91  0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, 0x46, 0x4f, 0x54, 0x5d,
92  0x62, 0x6b, 0x70, 0x79
93 };
94 
95 /*===========================================================================*/
96 /* Driver local functions. */
97 /*===========================================================================*/
98 
99 static bool mmc_read(void *instance, uint32_t startblk,
100  uint8_t *buffer, uint32_t n) {
101 
102  if (mmcStartSequentialRead((MMCDriver *)instance, startblk)) {
103  return HAL_FAILED;
104  }
105 
106  while (n > 0U) {
107  if (mmcSequentialRead((MMCDriver *)instance, buffer)) {
108  return HAL_FAILED;
109  }
110  buffer += MMCSD_BLOCK_SIZE;
111  n--;
112  }
113 
114  if (mmcStopSequentialRead((MMCDriver *)instance)) {
115  return HAL_FAILED;
116  }
117  return HAL_SUCCESS;
118 }
119 
120 static bool mmc_write(void *instance, uint32_t startblk,
121  const uint8_t *buffer, uint32_t n) {
122 
123  if (mmcStartSequentialWrite((MMCDriver *)instance, startblk)) {
124  return HAL_FAILED;
125  }
126 
127  while (n > 0U) {
128  if (mmcSequentialWrite((MMCDriver *)instance, buffer)) {
129  return HAL_FAILED;
130  }
131  buffer += MMCSD_BLOCK_SIZE;
132  n--;
133  }
134 
135  if (mmcStopSequentialWrite((MMCDriver *)instance)) {
136  return HAL_FAILED;
137  }
138  return HAL_SUCCESS;
139 }
140 
141 /**
142  * @brief Calculate the MMC standard CRC-7 based on a lookup table.
143  *
144  * @param[in] crc start value for CRC
145  * @param[in] buffer pointer to data buffer
146  * @param[in] len length of data
147  * @return Calculated CRC
148  */
149 static uint8_t crc7(uint8_t crc, const uint8_t *buffer, size_t len) {
150 
151  while (len > 0U) {
152  crc = crc7_lookup_table[(crc << 1) ^ (*buffer++)];
153  len--;
154  }
155  return crc;
156 }
157 
158 /**
159  * @brief Waits an idle condition.
160  *
161  * @param[in] mmcp pointer to the @p MMCDriver object
162  *
163  * @notapi
164  */
165 static void wait(MMCDriver *mmcp) {
166  int i;
167  uint8_t buf[4];
168 
169  for (i = 0; i < 16; i++) {
170  spiReceive(mmcp->config->spip, 1, buf);
171  if (buf[0] == 0xFFU) {
172  return;
173  }
174  }
175  /* Looks like it is a long wait.*/
176  while (true) {
177  spiReceive(mmcp->config->spip, 1, buf);
178  if (buf[0] == 0xFFU) {
179  break;
180  }
181 #if MMC_NICE_WAITING == TRUE
182  /* Trying to be nice with the other threads.*/
184 #endif
185  }
186 }
187 
188 /**
189  * @brief Sends a command header.
190  *
191  * @param[in] mmcp pointer to the @p MMCDriver object
192  * @param[in] cmd the command id
193  * @param[in] arg the command argument
194  *
195  * @notapi
196  */
197 static void send_hdr(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
198  uint8_t buf[6];
199 
200  /* Wait for the bus to become idle if a write operation was in progress.*/
201  wait(mmcp);
202 
203  buf[0] = (uint8_t)0x40U | cmd;
204  buf[1] = (uint8_t)(arg >> 24U);
205  buf[2] = (uint8_t)(arg >> 16U);
206  buf[3] = (uint8_t)(arg >> 8U);
207  buf[4] = (uint8_t)arg;
208  /* Calculate CRC for command header, shift to right position, add stop bit.*/
209  buf[5] = ((crc7(0, buf, 5U) & 0x7FU) << 1U) | 0x01U;
210 
211  spiSend(mmcp->config->spip, 6, buf);
212 }
213 
214 /**
215  * @brief Receives a single byte response.
216  *
217  * @param[in] mmcp pointer to the @p MMCDriver object
218  * @return The response as an @p uint8_t value.
219  * @retval 0xFF timed out.
220  *
221  * @notapi
222  */
223 static uint8_t recvr1(MMCDriver *mmcp) {
224  int i;
225  uint8_t r1[1];
226 
227  for (i = 0; i < 9; i++) {
228  spiReceive(mmcp->config->spip, 1, r1);
229  if (r1[0] != 0xFFU) {
230  return r1[0];
231  }
232  }
233  return 0xFFU;
234 }
235 
236 /**
237  * @brief Receives a three byte response.
238  *
239  * @param[in] mmcp pointer to the @p MMCDriver object
240  * @param[out] buffer pointer to four bytes wide buffer
241  * @return First response byte as an @p uint8_t value.
242  * @retval 0xFF timed out.
243  *
244  * @notapi
245  */
246 static uint8_t recvr3(MMCDriver *mmcp, uint8_t* buffer) {
247  uint8_t r1;
248 
249  r1 = recvr1(mmcp);
250  spiReceive(mmcp->config->spip, 4, buffer);
251 
252  return r1;
253 }
254 
255 /**
256  * @brief Sends a command an returns a single byte response.
257  *
258  * @param[in] mmcp pointer to the @p MMCDriver object
259  * @param[in] cmd the command id
260  * @param[in] arg the command argument
261  * @return The response as an @p uint8_t value.
262  * @retval 0xFF timed out.
263  *
264  * @notapi
265  */
266 static uint8_t send_command_R1(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
267  uint8_t r1;
268 
269  spiSelect(mmcp->config->spip);
270  send_hdr(mmcp, cmd, arg);
271  r1 = recvr1(mmcp);
272  spiUnselect(mmcp->config->spip);
273  return r1;
274 }
275 
276 /**
277  * @brief Sends a command which returns a five bytes response (R3).
278  *
279  * @param[in] mmcp pointer to the @p MMCDriver object
280  * @param[in] cmd the command id
281  * @param[in] arg the command argument
282  * @param[out] response pointer to four bytes wide uint8_t buffer
283  * @return The first byte of the response (R1) as an @p
284  * uint8_t value.
285  * @retval 0xFF timed out.
286  *
287  * @notapi
288  */
289 static uint8_t send_command_R3(MMCDriver *mmcp, uint8_t cmd, uint32_t arg,
290  uint8_t *response) {
291  uint8_t r1;
292 
293  spiSelect(mmcp->config->spip);
294  send_hdr(mmcp, cmd, arg);
295  r1 = recvr3(mmcp, response);
296  spiUnselect(mmcp->config->spip);
297  return r1;
298 }
299 
300 /**
301  * @brief Reads the CSD.
302  *
303  * @param[in] mmcp pointer to the @p MMCDriver object
304  * @param[out] cmd command
305  * @param[out] cxd pointer to the CSD/CID buffer
306  *
307  * @return The operation status.
308  * @retval HAL_SUCCESS the operation succeeded.
309  * @retval HAL_FAILED the operation failed.
310  *
311  * @notapi
312  */
313 static bool read_CxD(MMCDriver *mmcp, uint8_t cmd, uint32_t cxd[4]) {
314  unsigned i;
315  uint8_t *bp, buf[16];
316 
317  spiSelect(mmcp->config->spip);
318  send_hdr(mmcp, cmd, 0);
319  if (recvr1(mmcp) != 0x00U) {
320  spiUnselect(mmcp->config->spip);
321  return HAL_FAILED;
322  }
323 
324  /* Wait for data availability.*/
325  for (i = 0U; i < MMC_WAIT_DATA; i++) {
326  spiReceive(mmcp->config->spip, 1, buf);
327  if (buf[0] == 0xFEU) {
328  uint32_t *wp;
329 
330  spiReceive(mmcp->config->spip, 16, buf);
331  bp = buf;
332  for (wp = &cxd[3]; wp >= cxd; wp--) {
333  *wp = ((uint32_t)bp[0] << 24U) | ((uint32_t)bp[1] << 16U) |
334  ((uint32_t)bp[2] << 8U) | (uint32_t)bp[3];
335  bp += 4;
336  }
337 
338  /* CRC ignored then end of transaction. */
339  spiIgnore(mmcp->config->spip, 2);
340  spiUnselect(mmcp->config->spip);
341 
342  return HAL_SUCCESS;
343  }
344  }
345  return HAL_FAILED;
346 }
347 
348 /**
349  * @brief Waits that the card reaches an idle state.
350  *
351  * @param[in] mmcp pointer to the @p MMCDriver object
352  *
353  * @notapi
354  */
355 static void sync(MMCDriver *mmcp) {
356  uint8_t buf[1];
357 
358  spiSelect(mmcp->config->spip);
359  while (true) {
360  spiReceive(mmcp->config->spip, 1, buf);
361  if (buf[0] == 0xFFU) {
362  break;
363  }
364 #if MMC_NICE_WAITING == TRUE
365  /* Trying to be nice with the other threads.*/
367 #endif
368  }
369  spiUnselect(mmcp->config->spip);
370 }
371 
372 /*===========================================================================*/
373 /* Driver exported functions. */
374 /*===========================================================================*/
375 
376 /**
377  * @brief MMC over SPI driver initialization.
378  * @note This function is implicitly invoked by @p halInit(), there is
379  * no need to explicitly initialize the driver.
380  *
381  * @init
382  */
383 void mmcInit(void) {
384 
385 }
386 
387 /**
388  * @brief Initializes an instance.
389  *
390  * @param[out] mmcp pointer to the @p MMCDriver object
391  *
392  * @init
393  */
395 
396  mmcp->vmt = &mmc_vmt;
397  mmcp->state = BLK_STOP;
398  mmcp->config = NULL;
399  mmcp->block_addresses = false;
400 }
401 
402 /**
403  * @brief Configures and activates the MMC peripheral.
404  *
405  * @param[in] mmcp pointer to the @p MMCDriver object
406  * @param[in] config pointer to the @p MMCConfig object.
407  *
408  * @api
409  */
410 void mmcStart(MMCDriver *mmcp, const MMCConfig *config) {
411 
412  osalDbgCheck((mmcp != NULL) && (config != NULL));
413  osalDbgAssert((mmcp->state == BLK_STOP) || (mmcp->state == BLK_ACTIVE),
414  "invalid state");
415 
416  mmcp->config = config;
417  mmcp->state = BLK_ACTIVE;
418 }
419 
420 /**
421  * @brief Disables the MMC peripheral.
422  *
423  * @param[in] mmcp pointer to the @p MMCDriver object
424  *
425  * @api
426  */
427 void mmcStop(MMCDriver *mmcp) {
428 
429  osalDbgCheck(mmcp != NULL);
430  osalDbgAssert((mmcp->state == BLK_STOP) || (mmcp->state == BLK_ACTIVE),
431  "invalid state");
432 
433  spiStop(mmcp->config->spip);
434  mmcp->config = NULL;
435  mmcp->state = BLK_STOP;
436 }
437 
438 /**
439  * @brief Performs the initialization procedure on the inserted card.
440  * @details This function should be invoked when a card is inserted and
441  * brings the driver in the @p MMC_READY state where it is possible
442  * to perform read and write operations.
443  * @note It is possible to invoke this function from the insertion event
444  * handler.
445  *
446  * @param[in] mmcp pointer to the @p MMCDriver object
447  *
448  * @return The operation status.
449  * @retval HAL_SUCCESS the operation succeeded and the driver is now
450  * in the @p MMC_READY state.
451  * @retval HAL_FAILED the operation failed.
452  *
453  * @api
454  */
455 bool mmcConnect(MMCDriver *mmcp) {
456  unsigned i;
457  uint8_t r3[4];
458 
459  osalDbgCheck(mmcp != NULL);
460 
461  osalDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
462  "invalid state");
463 
464  /* Connection procedure in progress.*/
465  mmcp->state = BLK_CONNECTING;
466  mmcp->block_addresses = false;
467 
468  /* Slow clock mode and 128 clock pulses.*/
469  spiStart(mmcp->config->spip, mmcp->config->lscfg);
470  spiIgnore(mmcp->config->spip, 16);
471 
472  /* SPI mode selection.*/
473  i = 0;
474  while (true) {
475  if (send_command_R1(mmcp, MMCSD_CMD_GO_IDLE_STATE, 0) == 0x01U) {
476  break;
477  }
478  if (++i >= MMC_CMD0_RETRY) {
479  goto failed;
480  }
482  }
483 
484  /* Try to detect if this is a high capacity card and switch to block
485  addresses if possible.
486  This method is based on "How to support SDC Ver2 and high capacity cards"
487  by ElmChan.*/
488  if (send_command_R3(mmcp, MMCSD_CMD_SEND_IF_COND,
489  MMCSD_CMD8_PATTERN, r3) != 0x05U) {
490 
491  /* Switch to SDHC mode.*/
492  i = 0;
493  while (true) {
494  /*lint -save -e9007 [13.5] Side effect unimportant.*/
495  if ((send_command_R1(mmcp, MMCSD_CMD_APP_CMD, 0) <= 0x01U) &&
496  (send_command_R3(mmcp, MMCSD_CMD_APP_OP_COND, 0x400001AAU, r3) == 0x00U)) {
497  /*lint -restore*/
498  break;
499  }
500 
501  if (++i >= MMC_ACMD41_RETRY) {
502  goto failed;
503  }
505  }
506 
507  /* Execute dedicated read on OCR register */
508  (void) send_command_R3(mmcp, MMCSD_CMD_READ_OCR, 0, r3);
509 
510  /* Check if CCS is set in response. Card operates in block mode if set.*/
511  if ((r3[0] & 0x40U) != 0U) {
512  mmcp->block_addresses = true;
513  }
514  }
515 
516  /* Initialization.*/
517  i = 0;
518  while (true) {
519  uint8_t b = send_command_R1(mmcp, MMCSD_CMD_INIT, 0);
520  if (b == 0x00U) {
521  break;
522  }
523  if (b != 0x01U) {
524  goto failed;
525  }
526  if (++i >= MMC_CMD1_RETRY) {
527  goto failed;
528  }
530  }
531 
532  /* Initialization complete, full speed.*/
533  spiStart(mmcp->config->spip, mmcp->config->hscfg);
534 
535  /* Setting block size.*/
536  if (send_command_R1(mmcp, MMCSD_CMD_SET_BLOCKLEN,
537  MMCSD_BLOCK_SIZE) != 0x00U) {
538  goto failed;
539  }
540 
541  /* Determine capacity.*/
542  if (read_CxD(mmcp, MMCSD_CMD_SEND_CSD, mmcp->csd)) {
543  goto failed;
544  }
545 
546  mmcp->capacity = _mmcsd_get_capacity(mmcp->csd);
547  if (mmcp->capacity == 0U) {
548  goto failed;
549  }
550 
551  if (read_CxD(mmcp, MMCSD_CMD_SEND_CID, mmcp->cid)) {
552  goto failed;
553  }
554 
555  mmcp->state = BLK_READY;
556  return HAL_SUCCESS;
557 
558  /* Connection failed, state reset to BLK_ACTIVE.*/
559 failed:
560  spiStop(mmcp->config->spip);
561  mmcp->state = BLK_ACTIVE;
562  return HAL_FAILED;
563 }
564 
565 /**
566  * @brief Brings the driver in a state safe for card removal.
567  *
568  * @param[in] mmcp pointer to the @p MMCDriver object
569  * @return The operation status.
570  *
571  * @retval HAL_SUCCESS the operation succeeded and the driver is now
572  * in the @p MMC_INSERTED state.
573  * @retval HAL_FAILED the operation failed.
574  *
575  * @api
576  */
578 
579  osalDbgCheck(mmcp != NULL);
580 
581  osalSysLock();
582  osalDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
583  "invalid state");
584  if (mmcp->state == BLK_ACTIVE) {
585  osalSysUnlock();
586  return HAL_SUCCESS;
587  }
588  mmcp->state = BLK_DISCONNECTING;
589  osalSysUnlock();
590 
591  /* Wait for the pending write operations to complete.*/
592  spiStart(mmcp->config->spip, mmcp->config->hscfg);
593  sync(mmcp);
594 
595  spiStop(mmcp->config->spip);
596  mmcp->state = BLK_ACTIVE;
597  return HAL_SUCCESS;
598 }
599 
600 /**
601  * @brief Starts a sequential read.
602  *
603  * @param[in] mmcp pointer to the @p MMCDriver object
604  * @param[in] startblk first block to read
605  *
606  * @return The operation status.
607  * @retval HAL_SUCCESS the operation succeeded.
608  * @retval HAL_FAILED the operation failed.
609  *
610  * @api
611  */
612 bool mmcStartSequentialRead(MMCDriver *mmcp, uint32_t startblk) {
613 
614  osalDbgCheck(mmcp != NULL);
615  osalDbgAssert(mmcp->state == BLK_READY, "invalid state");
616 
617  /* Read operation in progress.*/
618  mmcp->state = BLK_READING;
619 
620  /* (Re)starting the SPI in case it has been reprogrammed externally, it can
621  happen if the SPI bus is shared among multiple peripherals.*/
622  spiStart(mmcp->config->spip, mmcp->config->hscfg);
623  spiSelect(mmcp->config->spip);
624 
625  if (mmcp->block_addresses) {
626  send_hdr(mmcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, startblk);
627  }
628  else {
629  send_hdr(mmcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, startblk * MMCSD_BLOCK_SIZE);
630  }
631 
632  if (recvr1(mmcp) != 0x00U) {
633  spiStop(mmcp->config->spip);
634  mmcp->state = BLK_READY;
635  return HAL_FAILED;
636  }
637  return HAL_SUCCESS;
638 }
639 
640 /**
641  * @brief Reads a block within a sequential read operation.
642  *
643  * @param[in] mmcp pointer to the @p MMCDriver object
644  * @param[out] buffer pointer to the read buffer
645  *
646  * @return The operation status.
647  * @retval HAL_SUCCESS the operation succeeded.
648  * @retval HAL_FAILED the operation failed.
649  *
650  * @api
651  */
652 bool mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer) {
653  unsigned i;
654 
655  osalDbgCheck((mmcp != NULL) && (buffer != NULL));
656 
657  if (mmcp->state != BLK_READING) {
658  return HAL_FAILED;
659  }
660 
661  for (i = 0; i < MMC_WAIT_DATA; i++) {
662  spiReceive(mmcp->config->spip, 1, buffer);
663  if (buffer[0] == 0xFEU) {
664  spiReceive(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);
665  /* CRC ignored. */
666  spiIgnore(mmcp->config->spip, 2);
667  return HAL_SUCCESS;
668  }
669  }
670  /* Timeout.*/
671  spiUnselect(mmcp->config->spip);
672  spiStop(mmcp->config->spip);
673  mmcp->state = BLK_READY;
674  return HAL_FAILED;
675 }
676 
677 /**
678  * @brief Stops a sequential read gracefully.
679  *
680  * @param[in] mmcp pointer to the @p MMCDriver object
681  *
682  * @return The operation status.
683  * @retval HAL_SUCCESS the operation succeeded.
684  * @retval HAL_FAILED the operation failed.
685  *
686  * @api
687  */
689  static const uint8_t stopcmd[] = {
690  (uint8_t)(0x40U | MMCSD_CMD_STOP_TRANSMISSION), 0, 0, 0, 0, 1, 0xFF
691  };
692 
693  osalDbgCheck(mmcp != NULL);
694 
695  if (mmcp->state != BLK_READING) {
696  return HAL_FAILED;
697  }
698 
699  spiSend(mmcp->config->spip, sizeof(stopcmd), stopcmd);
700 /* result = recvr1(mmcp) != 0x00U;*/
701  /* Note, ignored r1 response, it can be not zero, unknown issue.*/
702  (void) recvr1(mmcp);
703 
704  /* Read operation finished.*/
705  spiUnselect(mmcp->config->spip);
706  mmcp->state = BLK_READY;
707  return HAL_SUCCESS;
708 }
709 
710 /**
711  * @brief Starts a sequential write.
712  *
713  * @param[in] mmcp pointer to the @p MMCDriver object
714  * @param[in] startblk first block to write
715  *
716  * @return The operation status.
717  * @retval HAL_SUCCESS the operation succeeded.
718  * @retval HAL_FAILED the operation failed.
719  *
720  * @api
721  */
722 bool mmcStartSequentialWrite(MMCDriver *mmcp, uint32_t startblk) {
723 
724  osalDbgCheck(mmcp != NULL);
725  osalDbgAssert(mmcp->state == BLK_READY, "invalid state");
726 
727  /* Write operation in progress.*/
728  mmcp->state = BLK_WRITING;
729 
730  spiStart(mmcp->config->spip, mmcp->config->hscfg);
731  spiSelect(mmcp->config->spip);
732  if (mmcp->block_addresses) {
733  send_hdr(mmcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK, startblk);
734  }
735  else {
736  send_hdr(mmcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK,
737  startblk * MMCSD_BLOCK_SIZE);
738  }
739 
740  if (recvr1(mmcp) != 0x00U) {
741  spiStop(mmcp->config->spip);
742  mmcp->state = BLK_READY;
743  return HAL_FAILED;
744  }
745  return HAL_SUCCESS;
746 }
747 
748 /**
749  * @brief Writes a block within a sequential write operation.
750  *
751  * @param[in] mmcp pointer to the @p MMCDriver object
752  * @param[out] buffer pointer to the write buffer
753  *
754  * @return The operation status.
755  * @retval HAL_SUCCESS the operation succeeded.
756  * @retval HAL_FAILED the operation failed.
757  *
758  * @api
759  */
760 bool mmcSequentialWrite(MMCDriver *mmcp, const uint8_t *buffer) {
761  static const uint8_t start[] = {0xFF, 0xFC};
762  uint8_t b[1];
763 
764  osalDbgCheck((mmcp != NULL) && (buffer != NULL));
765 
766  if (mmcp->state != BLK_WRITING) {
767  return HAL_FAILED;
768  }
769 
770  spiSend(mmcp->config->spip, sizeof(start), start); /* Data prologue. */
771  spiSend(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);/* Data. */
772  spiIgnore(mmcp->config->spip, 2); /* CRC ignored. */
773  spiReceive(mmcp->config->spip, 1, b);
774  if ((b[0] & 0x1FU) == 0x05U) {
775  wait(mmcp);
776  return HAL_SUCCESS;
777  }
778 
779  /* Error.*/
780  spiUnselect(mmcp->config->spip);
781  spiStop(mmcp->config->spip);
782  mmcp->state = BLK_READY;
783  return HAL_FAILED;
784 }
785 
786 /**
787  * @brief Stops a sequential write gracefully.
788  *
789  * @param[in] mmcp pointer to the @p MMCDriver object
790  *
791  * @return The operation status.
792  * @retval HAL_SUCCESS the operation succeeded.
793  * @retval HAL_FAILED the operation failed.
794  *
795  * @api
796  */
798  static const uint8_t stop[] = {0xFD, 0xFF};
799 
800  osalDbgCheck(mmcp != NULL);
801 
802  if (mmcp->state != BLK_WRITING) {
803  return HAL_FAILED;
804  }
805 
806  spiSend(mmcp->config->spip, sizeof(stop), stop);
807  spiUnselect(mmcp->config->spip);
808 
809  /* Write operation finished.*/
810  mmcp->state = BLK_READY;
811  return HAL_SUCCESS;
812 }
813 
814 /**
815  * @brief Waits for card idle condition.
816  *
817  * @param[in] mmcp pointer to the @p MMCDriver object
818  *
819  * @return The operation status.
820  * @retval HAL_SUCCESS the operation succeeded.
821  * @retval HAL_FAILED the operation failed.
822  *
823  * @api
824  */
825 bool mmcSync(MMCDriver *mmcp) {
826 
827  osalDbgCheck(mmcp != NULL);
828 
829  if (mmcp->state != BLK_READY) {
830  return HAL_FAILED;
831  }
832 
833  /* Synchronization operation in progress.*/
834  mmcp->state = BLK_SYNCING;
835 
836  spiStart(mmcp->config->spip, mmcp->config->hscfg);
837  sync(mmcp);
838 
839  /* Synchronization operation finished.*/
840  mmcp->state = BLK_READY;
841  return HAL_SUCCESS;
842 }
843 
844 /**
845  * @brief Returns the media info.
846  *
847  * @param[in] mmcp pointer to the @p MMCDriver object
848  * @param[out] bdip pointer to a @p BlockDeviceInfo structure
849  *
850  * @return The operation status.
851  * @retval HAL_SUCCESS the operation succeeded.
852  * @retval HAL_FAILED the operation failed.
853  *
854  * @api
855  */
857 
858  osalDbgCheck((mmcp != NULL) && (bdip != NULL));
859 
860  if (mmcp->state != BLK_READY) {
861  return HAL_FAILED;
862  }
863 
864  bdip->blk_num = mmcp->capacity;
865  bdip->blk_size = MMCSD_BLOCK_SIZE;
866 
867  return HAL_SUCCESS;
868 }
869 
870 /**
871  * @brief Erases blocks.
872  *
873  * @param[in] mmcp pointer to the @p MMCDriver object
874  * @param[in] startblk starting block number
875  * @param[in] endblk ending block number
876  *
877  * @return The operation status.
878  * @retval HAL_SUCCESS the operation succeeded.
879  * @retval HAL_FAILED the operation failed.
880  *
881  * @api
882  */
883 bool mmcErase(MMCDriver *mmcp, uint32_t startblk, uint32_t endblk) {
884 
885  osalDbgCheck((mmcp != NULL));
886 
887  /* Erase operation in progress.*/
888  mmcp->state = BLK_WRITING;
889 
890  /* Handling command differences between HC and normal cards.*/
891  if (!mmcp->block_addresses) {
892  startblk *= MMCSD_BLOCK_SIZE;
893  endblk *= MMCSD_BLOCK_SIZE;
894  }
895 
896  if (send_command_R1(mmcp, MMCSD_CMD_ERASE_RW_BLK_START, startblk) != 0x00U) {
897  goto failed;
898  }
899 
900  if (send_command_R1(mmcp, MMCSD_CMD_ERASE_RW_BLK_END, endblk) != 0x00U) {
901  goto failed;
902  }
903 
904  if (send_command_R1(mmcp, MMCSD_CMD_ERASE, 0) != 0x00U) {
905  goto failed;
906  }
907 
908  mmcp->state = BLK_READY;
909  return HAL_SUCCESS;
910 
911  /* Command failed, state reset to BLK_ACTIVE.*/
912 failed:
913  spiStop(mmcp->config->spip);
914  mmcp->state = BLK_READY;
915  return HAL_FAILED;
916 }
917 
918 #endif /* HAL_USE_MMC_SPI == TRUE */
919 
920 /** @} */
Structure representing a MMC/SD over SPI driver.
Definition: hal_mmc_spi.h:110
bool mmcStartSequentialRead(MMCDriver *mmcp, uint32_t startblk)
Starts a sequential read.
Definition: hal_mmc_spi.c:612
void spiUnselect(SPIDriver *spip)
Deasserts the slave select signal.
Definition: hal_spi.c:152
static void send_hdr(MMCDriver *mmcp, uint8_t cmd, uint32_t arg)
Sends a command header.
Definition: hal_mmc_spi.c:197
void spiSelect(SPIDriver *spip)
Asserts the slave select signal and prepares for transfers.
Definition: hal_spi.c:134
void spiReceive(SPIDriver *spip, size_t n, void *rxbuf)
Receives data from the SPI bus.
Definition: hal_spi.c:416
bool mmcStopSequentialWrite(MMCDriver *mmcp)
Stops a sequential write gracefully.
Definition: hal_mmc_spi.c:797
void spiStart(SPIDriver *spip, const SPIConfig *config)
Configures and activates the SPI peripheral.
Definition: hal_spi.c:91
bool mmcConnect(MMCDriver *mmcp)
Performs the initialization procedure on the inserted card.
Definition: hal_mmc_spi.c:455
HAL subsystem header.
#define MMCSD_BLOCK_SIZE
Fixed block size for MMC/SD block devices.
Definition: hal_mmcsd.h:39
void mmcStart(MMCDriver *mmcp, const MMCConfig *config)
Configures and activates the MMC peripheral.
Definition: hal_mmc_spi.c:410
MMC/SD over SPI driver configuration structure.
Definition: hal_mmc_spi.h:75
bool mmcStopSequentialRead(MMCDriver *mmcp)
Stops a sequential read gracefully.
Definition: hal_mmc_spi.c:688
static void osalSysUnlock(void)
Leaves a critical zone from thread context.
Definition: osal.h:540
void mmcStop(MMCDriver *mmcp)
Disables the MMC peripheral.
Definition: hal_mmc_spi.c:427
const SPIConfig * lscfg
SPI low speed configuration used during initialization.
Definition: hal_mmc_spi.h:83
#define MMCSD_CMD8_PATTERN
Fixed pattern for CMD8.
Definition: hal_mmcsd.h:49
static uint8_t crc7(uint8_t crc, const uint8_t *buffer, size_t len)
Calculate the MMC standard CRC-7 based on a lookup table.
Definition: hal_mmc_spi.c:149
bool mmcGetInfo(MMCDriver *mmcp, BlockDeviceInfo *bdip)
Returns the media info.
Definition: hal_mmc_spi.c:856
bool mmcErase(MMCDriver *mmcp, uint32_t startblk, uint32_t endblk)
Erases blocks.
Definition: hal_mmc_spi.c:883
static const struct MMCDriverVMT mmc_vmt
Virtual methods table.
Definition: hal_mmc_spi.c:55
bool mmcSync(MMCDriver *mmcp)
Waits for card idle condition.
Definition: hal_mmc_spi.c:825
#define osalThreadSleepMilliseconds(msecs)
Delays the invoking thread for the specified number of milliseconds.
Definition: osal.h:451
static bool read_CxD(MMCDriver *mmcp, uint8_t cmd, uint32_t cxd[4])
Reads the CSD.
Definition: hal_mmc_spi.c:313
_mmcsd_block_device_data const MMCConfig * config
Current configuration data.
Definition: hal_mmc_spi.h:119
const struct MMCDriverVMT * vmt
Virtual Methods Table.
Definition: hal_mmc_spi.h:114
bool mmcStartSequentialWrite(MMCDriver *mmcp, uint32_t startblk)
Starts a sequential write.
Definition: hal_mmc_spi.c:722
Block device info.
Definition: hal_ioblock.h:55
uint32_t blk_num
Total number of blocks.
Definition: hal_ioblock.h:57
void mmcObjectInit(MMCDriver *mmcp)
Initializes an instance.
Definition: hal_mmc_spi.c:394
bool mmcSequentialWrite(MMCDriver *mmcp, const uint8_t *buffer)
Writes a block within a sequential write operation.
Definition: hal_mmc_spi.c:760
#define osalDbgCheck(c)
Function parameters check.
Definition: osal.h:278
static void sync(MMCDriver *mmcp)
Waits that the card reaches an idle state.
Definition: hal_mmc_spi.c:355
SPIDriver * spip
SPI driver associated to this MMC driver.
Definition: hal_mmc_spi.h:79
static void wait(MMCDriver *mmcp)
Waits an idle condition.
Definition: hal_mmc_spi.c:165
MMCDriver virtual methods table.
Definition: hal_mmc_spi.h:101
void spiIgnore(SPIDriver *spip, size_t n)
Ignores data on the SPI bus.
Definition: hal_spi.c:322
void mmcInit(void)
MMC over SPI driver initialization.
Definition: hal_mmc_spi.c:383
static void osalSysLock(void)
Enters a critical zone from thread context.
Definition: osal.h:530
static uint8_t recvr1(MMCDriver *mmcp)
Receives a single byte response.
Definition: hal_mmc_spi.c:223
static uint8_t send_command_R3(MMCDriver *mmcp, uint8_t cmd, uint32_t arg, uint8_t *response)
Sends a command which returns a five bytes response (R3).
Definition: hal_mmc_spi.c:289
void spiSend(SPIDriver *spip, size_t n, const void *txbuf)
Sends data over the SPI bus.
Definition: hal_spi.c:386
uint32_t blk_size
Block size in bytes.
Definition: hal_ioblock.h:56
#define osalDbgAssert(c, remark)
Condition assertion.
Definition: osal.h:258
uint32_t _mmcsd_get_capacity(const uint32_t *csd)
Extract card capacity from a CSD.
Definition: hal_mmcsd.c:93
static uint8_t recvr3(MMCDriver *mmcp, uint8_t *buffer)
Receives a three byte response.
Definition: hal_mmc_spi.c:246
bool mmcDisconnect(MMCDriver *mmcp)
Brings the driver in a state safe for card removal.
Definition: hal_mmc_spi.c:577
void spiStop(SPIDriver *spip)
Deactivates the SPI peripheral.
Definition: hal_spi.c:111
const SPIConfig * hscfg
SPI high speed configuration used during transfers.
Definition: hal_mmc_spi.h:87
static const uint8_t crc7_lookup_table[256]
Lookup table for CRC-7 ( based on polynomial x^7 + x^3 + 1).
Definition: hal_mmc_spi.c:70
static uint8_t send_command_R1(MMCDriver *mmcp, uint8_t cmd, uint32_t arg)
Sends a command an returns a single byte response.
Definition: hal_mmc_spi.c:266
bool mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer)
Reads a block within a sequential read operation.
Definition: hal_mmc_spi.c:652