|
| 1 | +/* |
| 2 | + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <stdbool.h> |
| 8 | +#include <stddef.h> |
| 9 | +#include <string.h> |
| 10 | +#include <sys/param.h> |
| 11 | +#include "esp_log.h" |
| 12 | +#include "esp_check.h" |
| 13 | +#include "driver/sdmmc_host.h" |
| 14 | +#include "driver/sd_host_sdmmc.h" |
| 15 | +#include "driver/sd_host.h" |
| 16 | +#include "sdmmc_internal.h" |
| 17 | + |
| 18 | +#define SLOT_CHECK(slot_num) \ |
| 19 | +if (slot_num < 0 || slot_num >= SOC_SDMMC_NUM_SLOTS) { \ |
| 20 | + return ESP_ERR_INVALID_ARG; \ |
| 21 | +} |
| 22 | + |
| 23 | +#define SDMMC_EVENT_QUEUE_LENGTH 32 |
| 24 | + |
| 25 | +static const char *TAG = "sdmmc_periph"; |
| 26 | +static sd_host_ctlr_handle_t s_ctlr = NULL; |
| 27 | +static sd_host_slot_handle_t s_slot0 = NULL; |
| 28 | +static sd_host_slot_handle_t s_slot1 = NULL; |
| 29 | + |
| 30 | +esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz) |
| 31 | +{ |
| 32 | + SLOT_CHECK(slot); |
| 33 | + |
| 34 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 35 | + sd_host_slot_cfg_t cfg = { |
| 36 | + .freq.freq_hz = freq_khz * 1000, |
| 37 | + .freq.override = true, |
| 38 | + }; |
| 39 | + ESP_RETURN_ON_ERROR(sd_host_slot_configure(hdl, &cfg), TAG, "failed to configure slot freq"); |
| 40 | + |
| 41 | + return ESP_OK; |
| 42 | +} |
| 43 | + |
| 44 | +esp_err_t sdmmc_host_get_real_freq(int slot, int *real_freq_khz) |
| 45 | +{ |
| 46 | + SLOT_CHECK(slot); |
| 47 | + |
| 48 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 49 | + sd_host_sdmmc_slot_t *slot_ctx = __containerof(hdl, sd_host_sdmmc_slot_t, drv); |
| 50 | + ESP_RETURN_ON_ERROR(sd_host_slot_get_calc_real_freq(slot_ctx, real_freq_khz), TAG, "failed to get slot freq"); |
| 51 | + |
| 52 | + return ESP_OK; |
| 53 | +} |
| 54 | + |
| 55 | +esp_err_t sdmmc_host_set_input_delay(int slot, sdmmc_delay_phase_t delay_phase) |
| 56 | +{ |
| 57 | + SLOT_CHECK(slot); |
| 58 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 59 | + sd_host_slot_cfg_t cfg = { |
| 60 | + .delay_phase.delayphase = delay_phase, |
| 61 | + .delay_phase.override = true, |
| 62 | + }; |
| 63 | + ESP_RETURN_ON_ERROR(sd_host_slot_configure(hdl, &cfg), TAG, "failed to configure slot delay phase"); |
| 64 | + |
| 65 | + return ESP_OK; |
| 66 | +} |
| 67 | + |
| 68 | +esp_err_t sdmmc_host_set_input_delayline(int slot, sdmmc_delay_line_t delay_line) |
| 69 | +{ |
| 70 | + SLOT_CHECK(slot); |
| 71 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 72 | + sd_host_slot_cfg_t cfg = { |
| 73 | + .delay_line.delayline = delay_line, |
| 74 | + .delay_line.override = true, |
| 75 | + }; |
| 76 | + ESP_RETURN_ON_ERROR(sd_host_slot_configure(hdl, &cfg), TAG, "failed to configure slot delay line"); |
| 77 | + |
| 78 | + return ESP_OK; |
| 79 | +} |
| 80 | + |
| 81 | +esp_err_t sdmmc_host_init(void) |
| 82 | +{ |
| 83 | + sd_host_sdmmc_cfg_t cfg = { |
| 84 | + .event_queue_items = SDMMC_EVENT_QUEUE_LENGTH, |
| 85 | + }; |
| 86 | + esp_err_t ret = sd_host_create_sdmmc_controller(&cfg, &s_ctlr); |
| 87 | + ESP_RETURN_ON_ERROR(ret, TAG, "failed to create new SD controller"); |
| 88 | + |
| 89 | + return ESP_OK; |
| 90 | +} |
| 91 | + |
| 92 | +sd_host_slot_handle_t sdmmc_get_slot_handle(int slot_id) |
| 93 | +{ |
| 94 | + return slot_id == 0 ? s_slot0 : s_slot1; |
| 95 | +} |
| 96 | + |
| 97 | +esp_err_t sdmmc_host_is_slot_set_to_uhs1(int slot, bool *is_uhs1) |
| 98 | +{ |
| 99 | + SLOT_CHECK(slot); |
| 100 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 101 | + |
| 102 | + sd_host_slot_info_t info = {}; |
| 103 | + ESP_RETURN_ON_ERROR(sd_host_slot_get_info(hdl, &info), TAG, "failed to get slot info"); |
| 104 | + if (info.sd_mode == SD_MODE_UHS1) { |
| 105 | + *is_uhs1 = true; |
| 106 | + } |
| 107 | + |
| 108 | + return ESP_OK; |
| 109 | +} |
| 110 | + |
| 111 | +esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config) |
| 112 | +{ |
| 113 | + esp_err_t ret = ESP_FAIL; |
| 114 | + |
| 115 | + bool internal_pullup = (slot_config->flags & SDMMC_SLOT_FLAG_INTERNAL_PULLUP); |
| 116 | + bool wp_active_high = (slot_config->flags & SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH); |
| 117 | + sd_host_slot_sdmmc_init_cfg_t cfg = { |
| 118 | + .slot_id = slot, |
| 119 | + .sd_mode = (slot_config->flags & SDMMC_SLOT_FLAG_UHS1) ? SD_MODE_UHS1 : SD_MODE_NORMAL, |
| 120 | + .io_config = { |
| 121 | + .width = slot_config->width, |
| 122 | + .clk_io = slot_config->clk, |
| 123 | + .cmd_io = slot_config->cmd, |
| 124 | + .cd_io = slot_config->cd, |
| 125 | + .wp_io = slot_config->wp, |
| 126 | + .d0_io = slot_config->d0, |
| 127 | + .d1_io = slot_config->d1, |
| 128 | + .d2_io = slot_config->d2, |
| 129 | + .d3_io = slot_config->d3, |
| 130 | + .d4_io = slot_config->d4, |
| 131 | + .d5_io = slot_config->d5, |
| 132 | + .d6_io = slot_config->d6, |
| 133 | + .d7_io = slot_config->d7, |
| 134 | + }, |
| 135 | + .slot_flags.internal_pullup = internal_pullup, |
| 136 | + .slot_flags.wp_active_high = wp_active_high, |
| 137 | + }; |
| 138 | + if (slot == 0) { |
| 139 | + ret = sd_host_sdmmc_controller_add_slot(s_ctlr, &cfg, &s_slot0); |
| 140 | + } else { |
| 141 | + ret = sd_host_sdmmc_controller_add_slot(s_ctlr, &cfg, &s_slot1); |
| 142 | + } |
| 143 | + ESP_RETURN_ON_ERROR(ret, TAG, "failed to add new SD slot"); |
| 144 | + |
| 145 | + return ESP_OK; |
| 146 | +} |
| 147 | + |
| 148 | +esp_err_t sdmmc_host_deinit_slot(int slot) |
| 149 | +{ |
| 150 | + esp_err_t ret = ESP_FAIL; |
| 151 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 152 | + ESP_RETURN_ON_ERROR(sd_host_remove_slot(hdl), TAG, "failed to remove slot"); |
| 153 | + |
| 154 | + ret = sd_host_del_controller(s_ctlr); |
| 155 | + //for backward compatibility, return ESP_OK when only slot is removed and host is still there |
| 156 | + if (ret == ESP_ERR_INVALID_STATE) { |
| 157 | + ret = ESP_OK; |
| 158 | + } |
| 159 | + |
| 160 | + return ret; |
| 161 | +} |
| 162 | + |
| 163 | +esp_err_t sdmmc_host_deinit(void) |
| 164 | +{ |
| 165 | + esp_err_t ret = ESP_FAIL; |
| 166 | + sd_host_slot_handle_t hdl[2] = {s_slot0, s_slot1}; |
| 167 | + for (int i = 0; i < 2; i++) { |
| 168 | + if (hdl[i]) { |
| 169 | + ret = sd_host_remove_slot(hdl[i]); |
| 170 | + ESP_RETURN_ON_ERROR(ret, TAG, "failed to remove slot%d", i); |
| 171 | + } |
| 172 | + } |
| 173 | + ESP_RETURN_ON_ERROR(sd_host_del_controller(s_ctlr), TAG, "failed to delete controller"); |
| 174 | + |
| 175 | + return ESP_OK; |
| 176 | +} |
| 177 | + |
| 178 | +esp_err_t sdmmc_host_set_bus_width(int slot, size_t width) |
| 179 | +{ |
| 180 | + SLOT_CHECK(slot); |
| 181 | + |
| 182 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 183 | + sd_host_slot_cfg_t cfg = { |
| 184 | + .width.override = true, |
| 185 | + }; |
| 186 | + ESP_RETURN_ON_ERROR(sd_host_slot_configure(hdl, &cfg), TAG, "failed to configure slot bus width"); |
| 187 | + |
| 188 | + return ESP_OK; |
| 189 | +} |
| 190 | + |
| 191 | +size_t sdmmc_host_get_slot_width(int slot) |
| 192 | +{ |
| 193 | + SLOT_CHECK(slot); |
| 194 | + |
| 195 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 196 | + sd_host_slot_info_t info = {}; |
| 197 | + esp_err_t ret = sd_host_slot_get_info(hdl, &info); |
| 198 | + assert(ret == ESP_OK); |
| 199 | + |
| 200 | + return info.width; |
| 201 | +} |
| 202 | + |
| 203 | +esp_err_t sdmmc_host_set_bus_ddr_mode(int slot, bool ddr_enabled) |
| 204 | +{ |
| 205 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 206 | + sd_host_slot_cfg_t cfg = { |
| 207 | + .sampling_mode.mode = ddr_enabled ? SD_SAMPLING_MODE_DDR : SD_SAMPLING_MODE_SDR, |
| 208 | + .sampling_mode.override = true, |
| 209 | + }; |
| 210 | + ESP_RETURN_ON_ERROR(sd_host_slot_configure(hdl, &cfg), TAG, "failed to configure slot ddr mode"); |
| 211 | + |
| 212 | + return ESP_OK; |
| 213 | +} |
| 214 | + |
| 215 | +esp_err_t sdmmc_host_set_cclk_always_on(int slot, bool cclk_always_on) |
| 216 | +{ |
| 217 | + SLOT_CHECK(slot); |
| 218 | + |
| 219 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 220 | + ESP_RETURN_ON_ERROR(sd_host_slot_set_cclk_always_on(hdl, cclk_always_on), TAG, "failed to configure slot cclk always on"); |
| 221 | + |
| 222 | + return ESP_OK; |
| 223 | +} |
| 224 | + |
| 225 | +esp_err_t sdmmc_host_io_int_enable(int slot) |
| 226 | +{ |
| 227 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 228 | + return sd_host_slot_enable_io_int(hdl); |
| 229 | +} |
| 230 | + |
| 231 | +esp_err_t sdmmc_host_io_int_wait(int slot, TickType_t timeout_ticks) |
| 232 | +{ |
| 233 | + assert(slot == 0 || slot == 1); |
| 234 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 235 | + ESP_RETURN_ON_ERROR(sd_host_slot_wait_io_int(hdl, timeout_ticks), TAG, "failed to wait io interrupt"); |
| 236 | + |
| 237 | + return ESP_OK; |
| 238 | +} |
| 239 | + |
| 240 | +esp_err_t sdmmc_host_get_dma_info(int slot, esp_dma_mem_info_t *dma_mem_info) |
| 241 | +{ |
| 242 | + SLOT_CHECK(slot); |
| 243 | + |
| 244 | + if (dma_mem_info == NULL) { |
| 245 | + return ESP_ERR_INVALID_ARG; |
| 246 | + } |
| 247 | + dma_mem_info->extra_heap_caps = MALLOC_CAP_DMA; |
| 248 | + dma_mem_info->dma_alignment_bytes = 4; |
| 249 | + return ESP_OK; |
| 250 | +} |
| 251 | + |
| 252 | +bool sdmmc_host_check_buffer_alignment(int slot, const void *buf, size_t size) |
| 253 | +{ |
| 254 | + assert(slot == 0 || slot == 1); |
| 255 | + sd_host_slot_handle_t hdl = sdmmc_get_slot_handle(slot); |
| 256 | + sd_host_sdmmc_slot_t *slot_ctx = __containerof(hdl, sd_host_sdmmc_slot_t, drv); |
| 257 | + |
| 258 | + return sd_host_check_buffer_alignment(slot_ctx, buf, size); |
| 259 | +} |
| 260 | + |
| 261 | +esp_err_t sdmmc_host_get_state(sdmmc_host_state_t* state) |
| 262 | +{ |
| 263 | + ESP_RETURN_ON_FALSE(state, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); |
| 264 | + |
| 265 | + if (s_ctlr) { |
| 266 | + state->host_initialized = true; |
| 267 | + sd_host_sdmmc_ctlr_t *ctlr_ctx = __containerof(s_ctlr, sd_host_sdmmc_ctlr_t, drv); |
| 268 | + state->num_of_init_slots = ctlr_ctx->registered_slot_nums; |
| 269 | + } |
| 270 | + |
| 271 | + return ESP_OK; |
| 272 | +} |
0 commit comments