1 /*
2  * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
3  * Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include "sdmmc_common.h"
19 
20 static const char* TAG = "sdmmc_init";
21 
22 #define SDMMC_INIT_STEP(condition, function) \
23     do { \
24         if ((condition)) { \
25             esp_err_t err = (function)(card); \
26             if (err != ESP_OK) { \
27                 ESP_LOGD(TAG, "%s: %s returned 0x%x", __func__, #function, err); \
28                 return err; \
29             } \
30         } \
31     } while(0);
32 
33 
sdmmc_card_init(const sdmmc_host_t * config,sdmmc_card_t * card)34 esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
35 {
36     memset(card, 0, sizeof(*card));
37     memcpy(&card->host, config, sizeof(*config));
38     const bool is_spi = host_is_spi(card);
39     const bool always = true;
40     const bool io_supported = true;
41 
42     /* Check if host flags are compatible with slot configuration. */
43     SDMMC_INIT_STEP(!is_spi, sdmmc_fix_host_flags);
44 
45     /* Reset SDIO (CMD52, RES) before re-initializing IO (CMD5). */
46     SDMMC_INIT_STEP(io_supported, sdmmc_io_reset);
47 
48     /* GO_IDLE_STATE (CMD0) command resets the card */
49     SDMMC_INIT_STEP(always, sdmmc_send_cmd_go_idle_state);
50 
51     /* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards. */
52     SDMMC_INIT_STEP(always, sdmmc_init_sd_if_cond);
53 
54     /* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card. */
55     SDMMC_INIT_STEP(io_supported, sdmmc_init_io);
56 
57     const bool is_mem = card->is_mem;
58     const bool is_sdio = !is_mem;
59 
60     /* Enable CRC16 checks for data transfers in SPI mode */
61     SDMMC_INIT_STEP(is_spi, sdmmc_init_spi_crc);
62 
63     /* Use SEND_OP_COND to set up card OCR */
64     SDMMC_INIT_STEP(is_mem, sdmmc_init_ocr);
65 
66     const bool is_mmc = is_mem && card->is_mmc;
67     const bool is_sdmem = is_mem && !is_mmc;
68 
69     ESP_LOGD(TAG, "%s: card type is %s", __func__,
70             is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD");
71 
72     /* Read the contents of CID register*/
73     SDMMC_INIT_STEP(is_mem, sdmmc_init_cid);
74 
75     /* Assign RCA */
76     SDMMC_INIT_STEP(!is_spi, sdmmc_init_rca);
77 
78     /* Read and decode the contents of CSD register */
79     SDMMC_INIT_STEP(is_mem, sdmmc_init_csd);
80 
81     /* Decode the contents of mmc CID register */
82     SDMMC_INIT_STEP(is_mmc && !is_spi, sdmmc_init_mmc_decode_cid);
83 
84     /* Switch the card from stand-by mode to data transfer mode (not needed if
85      * SPI interface is used). This is needed to issue SET_BLOCKLEN and
86      * SEND_SCR commands.
87      */
88     SDMMC_INIT_STEP(!is_spi, sdmmc_init_select_card);
89 
90     /* SD memory cards:
91      * Set block len for SDSC cards to 512 bytes (same as SDHC)
92      * Read SCR
93      * Wait to enter data transfer state
94      */
95     SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_blocklen);
96     SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_scr);
97     SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_wait_data_ready);
98 
99     /* MMC cards: read CXD */
100     SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_read_ext_csd);
101 
102     /* Try to switch card to HS mode if the card supports it.
103      * Set card->max_freq_khz value accordingly.
104      */
105     SDMMC_INIT_STEP(always, sdmmc_init_card_hs_mode);
106 
107     /* Set bus width. One call for every kind of card, then one for the host */
108     if (!is_spi) {
109         SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_bus_width);
110         SDMMC_INIT_STEP(is_sdio, sdmmc_init_io_bus_width);
111         SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_bus_width);
112         SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width);
113     }
114 
115     /* Switch to the host to use card->max_freq_khz frequency. */
116     SDMMC_INIT_STEP(always, sdmmc_init_host_frequency);
117 
118     /* Sanity check after switching the bus mode and frequency */
119     SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
120     /* TODO: this is CMD line only, add data checks for eMMC */
121     SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_check_csd);
122     /* TODO: add similar checks for SDIO */
123 
124     return ESP_OK;
125 }
126