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