Added support for write protection polarity for SDCard

Closes https://github.com/espressif/esp-idf/issues/11208
This commit is contained in:
sonika.rathi
2023-06-15 09:22:07 +02:00
parent 52071f4e44
commit b8c3a3aeee
5 changed files with 51 additions and 21 deletions

View File

@@ -82,6 +82,11 @@ typedef struct {
are insufficient however, please make sure external pullups are are insufficient however, please make sure external pullups are
connected on the bus. This is for debug / example purpose only. connected on the bus. This is for debug / example purpose only.
*/ */
#define SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH BIT(1)
/**< GPIO write protect polarity.
* 0 means "active low", i.e. card is protected when the GPIO is low;
* 1 means "active high", i.e. card is protected when GPIO is high.
*/
} sdmmc_slot_config_t; } sdmmc_slot_config_t;
#define SDMMC_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used #define SDMMC_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used

View File

@@ -558,6 +558,7 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)
} }
int gpio_cd = slot_config->cd; int gpio_cd = slot_config->cd;
int gpio_wp = slot_config->wp; int gpio_wp = slot_config->wp;
bool gpio_wp_polarity = slot_config->flags & SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH;
uint8_t slot_width = slot_config->width; uint8_t slot_width = slot_config->width;
// Configure pins // Configure pins
@@ -649,9 +650,10 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)
// if not set, default to WP high (not write protected) // if not set, default to WP high (not write protected)
matrix_in_wp = GPIO_MATRIX_CONST_ONE_INPUT; matrix_in_wp = GPIO_MATRIX_CONST_ONE_INPUT;
} }
// WP signal is normally active low, but hardware expects // As hardware expects an active-high signal,
// an active-high signal, so invert it in GPIO matrix // if WP signal is active low, then invert it in GPIO matrix,
esp_rom_gpio_connect_in_signal(matrix_in_wp, slot_info->write_protect, true); // else keep it in its default state
esp_rom_gpio_connect_in_signal(matrix_in_wp, slot_info->write_protect, (gpio_wp_polarity? false : true));
// By default, set probing frequency (400kHz) and 1-bit bus // By default, set probing frequency (400kHz) and 1-bit bus
esp_err_t ret = sdmmc_host_set_card_clk(slot, 400); esp_err_t ret = sdmmc_host_set_card_clk(slot, 400);

View File

@@ -61,16 +61,20 @@ typedef int sdspi_dev_handle_t;
*/ */
typedef struct { typedef struct {
spi_host_device_t host_id; ///< SPI host to use, SPIx_HOST (see spi_types.h). spi_host_device_t host_id; ///< SPI host to use, SPIx_HOST (see spi_types.h).
gpio_num_t gpio_cs; ///< GPIO number of CS signal gpio_num_t gpio_cs; ///< GPIO number of CS signal
gpio_num_t gpio_cd; ///< GPIO number of card detect signal gpio_num_t gpio_cd; ///< GPIO number of card detect signal
gpio_num_t gpio_wp; ///< GPIO number of write protect signal gpio_num_t gpio_wp; ///< GPIO number of write protect signal
gpio_num_t gpio_int; ///< GPIO number of interrupt line (input) for SDIO card. gpio_num_t gpio_int; ///< GPIO number of interrupt line (input) for SDIO card.
bool gpio_wp_polarity; ///< GPIO write protect polarity
/// 0 means "active low", i.e. card is protected when the GPIO is low;
/// 1 means "active high", i.e. card is protected when GPIO is high.
} sdspi_device_config_t; } sdspi_device_config_t;
#define SDSPI_SLOT_NO_CS GPIO_NUM_NC ///< indicates that card select line is not used #define SDSPI_SLOT_NO_CS GPIO_NUM_NC ///< indicates that card select line is not used
#define SDSPI_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used #define SDSPI_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used
#define SDSPI_SLOT_NO_WP GPIO_NUM_NC ///< indicates that write protect line is not used #define SDSPI_SLOT_NO_WP GPIO_NUM_NC ///< indicates that write protect line is not used
#define SDSPI_SLOT_NO_INT GPIO_NUM_NC ///< indicates that interrupt line is not used #define SDSPI_SLOT_NO_INT GPIO_NUM_NC ///< indicates that interrupt line is not used
#define SDSPI_IO_ACTIVE_LOW 0
/** /**
* Macro defining default configuration of SD SPI device. * Macro defining default configuration of SD SPI device.
@@ -81,6 +85,7 @@ typedef struct {
.gpio_cd = SDSPI_SLOT_NO_CD, \ .gpio_cd = SDSPI_SLOT_NO_CD, \
.gpio_wp = SDSPI_SLOT_NO_WP, \ .gpio_wp = SDSPI_SLOT_NO_WP, \
.gpio_int = GPIO_NUM_NC, \ .gpio_int = GPIO_NUM_NC, \
.gpio_wp_polarity = SDSPI_IO_ACTIVE_LOW, \
} }
/** /**

View File

@@ -46,7 +46,11 @@ typedef struct {
uint8_t gpio_cs; //!< CS GPIO, or GPIO_UNUSED uint8_t gpio_cs; //!< CS GPIO, or GPIO_UNUSED
uint8_t gpio_cd; //!< Card detect GPIO, or GPIO_UNUSED uint8_t gpio_cd; //!< Card detect GPIO, or GPIO_UNUSED
uint8_t gpio_wp; //!< Write protect GPIO, or GPIO_UNUSED uint8_t gpio_wp; //!< Write protect GPIO, or GPIO_UNUSED
uint8_t gpio_int; //!< Write protect GPIO, or GPIO_UNUSED uint8_t gpio_int; //!< Write protect GPIO, or GPIO_UNUSED
/// GPIO write protect polarity.
/// 0 means "active low", i.e. card is protected when the GPIO is low;
/// 1 means "active high", i.e. card is protected when GPIO is high.
uint8_t gpio_wp_polarity : 1;
/// Set to 1 if the higher layer has asked the card to enable CRC checks /// Set to 1 if the higher layer has asked the card to enable CRC checks
uint8_t data_crc_enabled : 1; uint8_t data_crc_enabled : 1;
/// Intermediate buffer used when application buffer is not in DMA memory; /// Intermediate buffer used when application buffer is not in DMA memory;
@@ -133,13 +137,13 @@ static void cs_low(slot_info_t *slot)
} }
} }
/// Return true if WP pin is configured and is low /// Return true if WP pin is configured and is set as per its polarity
static bool card_write_protected(slot_info_t *slot) static bool card_write_protected(slot_info_t *slot)
{ {
if (slot->gpio_wp == GPIO_UNUSED) { if (slot->gpio_wp == GPIO_UNUSED) {
return false; return false;
} }
return gpio_get_level(slot->gpio_wp) == 0; return gpio_get_level(slot->gpio_wp) == (slot->gpio_wp_polarity ? 1 : 0);
} }
/// Return true if CD pin is configured and is high /// Return true if CD pin is configured and is high
@@ -322,9 +326,9 @@ static void gpio_intr(void* arg)
esp_err_t sdspi_host_init_device(const sdspi_device_config_t* slot_config, sdspi_dev_handle_t* out_handle) esp_err_t sdspi_host_init_device(const sdspi_device_config_t* slot_config, sdspi_dev_handle_t* out_handle)
{ {
ESP_LOGD(TAG, "%s: SPI%d cs=%d cd=%d wp=%d", ESP_LOGD(TAG, "%s: SPI%d cs=%d cd=%d wp=%d wp_polarity:%d",
__func__, slot_config->host_id + 1, slot_config->gpio_cs, __func__, slot_config->host_id + 1, slot_config->gpio_cs,
slot_config->gpio_cd, slot_config->gpio_wp); slot_config->gpio_cd, slot_config->gpio_wp, slot_config->gpio_wp_polarity);
slot_info_t* slot = (slot_info_t*)malloc(sizeof(slot_info_t)); slot_info_t* slot = (slot_info_t*)malloc(sizeof(slot_info_t));
if (slot == NULL) { if (slot == NULL) {
@@ -380,6 +384,11 @@ esp_err_t sdspi_host_init_device(const sdspi_device_config_t* slot_config, sdspi
if (slot_config->gpio_wp != SDSPI_SLOT_NO_WP) { if (slot_config->gpio_wp != SDSPI_SLOT_NO_WP) {
io_conf.pin_bit_mask |= (1ULL << slot_config->gpio_wp); io_conf.pin_bit_mask |= (1ULL << slot_config->gpio_wp);
slot->gpio_wp = slot_config->gpio_wp; slot->gpio_wp = slot_config->gpio_wp;
slot->gpio_wp_polarity = slot_config->gpio_wp_polarity;
if (slot->gpio_wp_polarity) {
io_conf.pull_down_en = true;
io_conf.pull_up_en = false;
}
} else { } else {
slot->gpio_wp = GPIO_UNUSED; slot->gpio_wp = GPIO_UNUSED;
} }

View File

@@ -69,6 +69,8 @@
#define SDSPI_TEST_CS_PIN GPIO_NUM_13 #define SDSPI_TEST_CS_PIN GPIO_NUM_13
#endif #endif
#define GPIO_ACTIVE_LOW 0
#define GPIO_ACTIVE_HIGH 1
TEST_CASE("MMC_RSP_BITS", "[sd]") TEST_CASE("MMC_RSP_BITS", "[sd]")
{ {
@@ -595,7 +597,7 @@ static void test_cd_input(int gpio_cd_num, const sdmmc_host_t* config)
free(card); free(card);
} }
static void test_wp_input(int gpio_wp_num, const sdmmc_host_t* config) static void test_wp_input(int gpio_wp_num, bool gpio_wp_polarity, const sdmmc_host_t* config)
{ {
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t)); sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card); TEST_ASSERT_NOT_NULL(card);
@@ -613,12 +615,12 @@ static void test_wp_input(int gpio_wp_num, const sdmmc_host_t* config)
uint32_t* data = heap_caps_calloc(1, 512, MALLOC_CAP_DMA); uint32_t* data = heap_caps_calloc(1, 512, MALLOC_CAP_DMA);
// Check that card write succeeds if WP is high // Check that card write succeeds if WP is high
REG_WRITE(GPIO_OUT_W1TS_REG, BIT(gpio_wp_num)); REG_WRITE((gpio_wp_polarity? GPIO_OUT_W1TC_REG : GPIO_OUT_W1TS_REG), BIT(gpio_wp_num));
usleep(1000); usleep(1000);
TEST_ESP_OK(sdmmc_write_sectors(card, &data, 0, 1)); TEST_ESP_OK(sdmmc_write_sectors(card, &data, 0, 1));
// Check that write fails if WP is low // Check that write fails if WP is low
REG_WRITE(GPIO_OUT_W1TC_REG, BIT(gpio_wp_num)); REG_WRITE((gpio_wp_polarity? GPIO_OUT_W1TS_REG : GPIO_OUT_W1TC_REG), BIT(gpio_wp_num));
usleep(1000); usleep(1000);
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, sdmmc_write_sectors(card, &data, 0, 1)); TEST_ESP_ERR(ESP_ERR_INVALID_STATE, sdmmc_write_sectors(card, &data, 0, 1));
// ...but reads still work // ...but reads still work
@@ -652,11 +654,17 @@ TEST_CASE("WP input works in SD mode", "[sd][test_env=UT_T1_SDMODE]")
sdmmc_host_t config = SDMMC_HOST_DEFAULT(); sdmmc_host_t config = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
slot_config.gpio_wp = CD_WP_TEST_GPIO; slot_config.gpio_wp = CD_WP_TEST_GPIO;
bool gpio_wp_polarity = GPIO_ACTIVE_LOW;
if (gpio_wp_polarity) {
slot_config.flags |= SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH;
} else {
slot_config.flags &= ~(SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH);
}
TEST_ESP_OK(sdmmc_host_init()); TEST_ESP_OK(sdmmc_host_init());
usleep(10000); usleep(10000);
TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config)); TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
test_wp_input(CD_WP_TEST_GPIO, &config); test_wp_input(slot_config.gpio_wp, gpio_wp_polarity, &config);
TEST_ESP_OK(sdmmc_host_deinit()); TEST_ESP_OK(sdmmc_host_deinit());
sd_test_board_power_off(); sd_test_board_power_off();
@@ -697,6 +705,7 @@ TEST_CASE("WP input works in SPI mode", "[sd][test_env=UT_T1_SPIMODE]")
dev_config.host_id = config.slot; dev_config.host_id = config.slot;
dev_config.gpio_cs = SDSPI_TEST_CS_PIN; dev_config.gpio_cs = SDSPI_TEST_CS_PIN;
dev_config.gpio_wp = CD_WP_TEST_GPIO; dev_config.gpio_wp = CD_WP_TEST_GPIO;
dev_config.gpio_wp_polarity = GPIO_ACTIVE_LOW;
test_sdspi_init_bus(dev_config.host_id, SDSPI_TEST_MOSI_PIN, SDSPI_TEST_MISO_PIN, SDSPI_TEST_SCLK_PIN, SPI_DMA_CH_AUTO); test_sdspi_init_bus(dev_config.host_id, SDSPI_TEST_MOSI_PIN, SDSPI_TEST_MISO_PIN, SDSPI_TEST_SCLK_PIN, SPI_DMA_CH_AUTO);
TEST_ESP_OK(sdspi_host_init()); TEST_ESP_OK(sdspi_host_init());
@@ -704,7 +713,7 @@ TEST_CASE("WP input works in SPI mode", "[sd][test_env=UT_T1_SPIMODE]")
config.slot = handle; config.slot = handle;
test_wp_input(CD_WP_TEST_GPIO, &config); test_wp_input(dev_config.gpio_wp, dev_config.gpio_wp_polarity, &config);
TEST_ESP_OK(sdspi_host_deinit()); TEST_ESP_OK(sdspi_host_deinit());
test_sdspi_deinit_bus(dev_config.host_id); test_sdspi_deinit_bus(dev_config.host_id);