1 /*
2  * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #pragma once
8 
9 #include <stdint.h>
10 #include <stddef.h>
11 #include "esp_err.h"
12 #include "driver/sdmmc_types.h"
13 #include "driver/gpio.h"
14 #include "driver/spi_master.h"
15 
16 #ifdef __cplusplus
17 extern "C" {
18 #endif
19 
20 /// Handle representing an SD SPI device
21 typedef int sdspi_dev_handle_t;
22 
23 #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
24 #define SDSPI_DEFAULT_HOST HSPI_HOST
25 #define SDSPI_DEFAULT_DMA  SDSPI_DEFAULT_HOST
26 #else
27 #define SDSPI_DEFAULT_HOST SPI2_HOST
28 #define SDSPI_DEFAULT_DMA  SPI_DMA_CH_AUTO
29 #endif
30 
31 /**
32  * @brief Default sdmmc_host_t structure initializer for SD over SPI driver
33  *
34  * Uses SPI mode and max frequency set to 20MHz
35  *
36  * 'slot' should be set to an sdspi device initialized by `sdspi_host_init_device()`.
37  */
38 #define SDSPI_HOST_DEFAULT() {\
39     .flags = SDMMC_HOST_FLAG_SPI | SDMMC_HOST_FLAG_DEINIT_ARG, \
40     .slot = SDSPI_DEFAULT_HOST, \
41     .max_freq_khz = SDMMC_FREQ_DEFAULT, \
42     .io_voltage = 3.3f, \
43     .init = &sdspi_host_init, \
44     .set_bus_width = NULL, \
45     .get_bus_width = NULL, \
46     .set_bus_ddr_mode = NULL, \
47     .set_card_clk = &sdspi_host_set_card_clk, \
48     .set_cclk_always_on = NULL, \
49     .do_transaction = &sdspi_host_do_transaction, \
50     .deinit_p = &sdspi_host_remove_device, \
51     .io_int_enable = &sdspi_host_io_int_enable, \
52     .io_int_wait = &sdspi_host_io_int_wait, \
53     .command_timeout_ms = 0, \
54     .get_real_freq = &sdspi_host_get_real_freq \
55 }
56 
57 /**
58  * Extra configuration for SD SPI device.
59  */
60 typedef struct {
61     spi_host_device_t host_id; ///< SPI host to use, SPIx_HOST (see spi_types.h).
62     gpio_num_t gpio_cs;     ///< GPIO number of CS signal
63     gpio_num_t gpio_cd;     ///< GPIO number of card detect signal
64     gpio_num_t gpio_wp;     ///< GPIO number of write protect signal
65     gpio_num_t gpio_int;    ///< GPIO number of interrupt line (input) for SDIO card.
66 } sdspi_device_config_t;
67 
68 #define SDSPI_SLOT_NO_CS    GPIO_NUM_NC ///< indicates that card select line is not used
69 #define SDSPI_SLOT_NO_CD    GPIO_NUM_NC ///< indicates that card detect line is not used
70 #define SDSPI_SLOT_NO_WP    GPIO_NUM_NC ///< indicates that write protect line is not used
71 #define SDSPI_SLOT_NO_INT   GPIO_NUM_NC ///< indicates that interrupt line is not used
72 
73 /**
74  * Macro defining default configuration of SD SPI device.
75  */
76 #define SDSPI_DEVICE_CONFIG_DEFAULT() {\
77     .host_id   = SDSPI_DEFAULT_HOST, \
78     .gpio_cs   = GPIO_NUM_13, \
79     .gpio_cd   = SDSPI_SLOT_NO_CD, \
80     .gpio_wp   = SDSPI_SLOT_NO_WP, \
81     .gpio_int  = GPIO_NUM_NC, \
82 }
83 
84 /**
85  * @brief Initialize SD SPI driver
86  *
87  * @note This function is not thread safe
88  *
89  * @return
90  *      - ESP_OK on success
91  *      - other error codes may be returned in future versions
92  */
93 esp_err_t sdspi_host_init(void);
94 
95 /**
96 * @brief Attach and initialize an SD SPI device on the specific SPI bus
97 *
98 * @note This function is not thread safe
99 *
100 * @note Initialize the SPI bus by `spi_bus_initialize()` before calling this function.
101 *
102 * @note The SDIO over sdspi needs an extra interrupt line. Call ``gpio_install_isr_service()`` before this function.
103 *
104 * @param dev_config pointer to device configuration structure
105 * @param out_handle Output of the handle to the sdspi device.
106 
107 * @return
108 *      - ESP_OK on success
109 *      - ESP_ERR_INVALID_ARG if sdspi_host_init_device has invalid arguments
110 *      - ESP_ERR_NO_MEM if memory can not be allocated
111 *      - other errors from the underlying spi_master and gpio drivers
112 */
113 esp_err_t sdspi_host_init_device(const sdspi_device_config_t* dev_config, sdspi_dev_handle_t* out_handle);
114 
115 /**
116  * @brief Remove an SD SPI device
117  *
118  * @param handle Handle of the SD SPI device
119  * @return Always ESP_OK
120  */
121 esp_err_t sdspi_host_remove_device(sdspi_dev_handle_t handle);
122 
123 /**
124  * @brief Send command to the card and get response
125  *
126  * This function returns when command is sent and response is received,
127  * or data is transferred, or timeout occurs.
128  *
129  * @note This function is not thread safe w.r.t. init/deinit functions,
130  *       and bus width/clock speed configuration functions. Multiple tasks
131  *       can call sdspi_host_do_transaction as long as other sdspi_host_*
132  *       functions are not called.
133  *
134  * @param handle    Handle of the sdspi device
135  * @param cmdinfo   pointer to structure describing command and data to transfer
136  * @return
137  *      - ESP_OK on success
138  *      - ESP_ERR_TIMEOUT if response or data transfer has timed out
139  *      - ESP_ERR_INVALID_CRC if response or data transfer CRC check has failed
140  *      - ESP_ERR_INVALID_RESPONSE if the card has sent an invalid response
141  */
142 esp_err_t sdspi_host_do_transaction(sdspi_dev_handle_t handle, sdmmc_command_t *cmdinfo);
143 
144 /**
145  * @brief Set card clock frequency
146  *
147  * Currently only integer fractions of 40MHz clock can be used.
148  * For High Speed cards, 40MHz can be used.
149  * For Default Speed cards, 20MHz can be used.
150  *
151  * @note This function is not thread safe
152  *
153  * @param host    Handle of the sdspi device
154  * @param freq_khz  card clock frequency, in kHz
155  * @return
156  *      - ESP_OK on success
157  *      - other error codes may be returned in the future
158  */
159 esp_err_t sdspi_host_set_card_clk(sdspi_dev_handle_t host, uint32_t freq_khz);
160 
161 /**
162  * @brief Calculate working frequency for specific device
163  *
164  * @param handle SDSPI device handle
165  * @param[out] real_freq_khz output parameter to hold the calculated frequency (in kHz)
166  *
167  * @return
168  *      - ESP_ERR_INVALID_ARG : ``handle`` is NULL or invalid or ``real_freq_khz`` parameter is NULL
169  *      - ESP_OK : Success
170  */
171 esp_err_t sdspi_host_get_real_freq(sdspi_dev_handle_t handle, int* real_freq_khz);
172 
173 /**
174  * @brief Release resources allocated using sdspi_host_init
175  *
176  * @note This function is not thread safe
177  *
178  * @return
179  *      - ESP_OK on success
180  *      - ESP_ERR_INVALID_STATE if sdspi_host_init function has not been called
181  */
182 esp_err_t sdspi_host_deinit(void);
183 
184 /**
185  * @brief Enable SDIO interrupt.
186  *
187  * @param handle    Handle of the sdspi device
188  *
189  * @return
190  *      - ESP_OK on success
191  */
192 esp_err_t sdspi_host_io_int_enable(sdspi_dev_handle_t handle);
193 
194 /**
195  * @brief Wait for SDIO interrupt until timeout.
196  *
197  * @param handle    Handle of the sdspi device
198  * @param timeout_ticks Ticks to wait before timeout.
199  *
200  * @return
201  *      - ESP_OK on success
202  */
203 esp_err_t sdspi_host_io_int_wait(sdspi_dev_handle_t handle, TickType_t timeout_ticks);
204 
205 #ifdef __cplusplus
206 }
207 #endif
208