/* * Copyright 2022 NXP * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include "sd_utils.h" #include "sdmmc_priv.h" LOG_MODULE_REGISTER(sd, CONFIG_SD_LOG_LEVEL); /* Idle all cards on bus. Can be used to clear errors on cards */ static inline int sd_idle(struct sd_card *card) { struct sdhc_command cmd = {0}; /* Reset card with CMD0 */ cmd.opcode = SD_GO_IDLE_STATE; cmd.response_type = (SD_RSP_TYPE_NONE | SD_SPI_RSP_TYPE_R1); cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; return sdhc_request(card->sdhc, &cmd, NULL); } /* Sends CMD8 during SD initialization */ static int sd_send_interface_condition(struct sd_card *card) { struct sdhc_command cmd = {0}; int ret; uint32_t resp; cmd.opcode = SD_SEND_IF_COND; cmd.arg = SD_IF_COND_VHS_3V3 | SD_IF_COND_CHECK; cmd.response_type = (SD_RSP_TYPE_R7 | SD_SPI_RSP_TYPE_R7); cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, NULL); if (ret) { LOG_DBG("SD CMD8 failed with error %d", ret); /* Retry */ return SD_RETRY; } if (card->host_props.is_spi) { resp = cmd.response[1]; } else { resp = cmd.response[0]; } if ((resp & 0xFF) != SD_IF_COND_CHECK) { LOG_INF("Legacy card detected, no CMD8 support"); /* Retry probe */ return SD_RETRY; } if ((resp & SD_IF_COND_VHS_MASK) != SD_IF_COND_VHS_3V3) { /* Card does not support 3.3V */ return -ENOTSUP; } LOG_DBG("Found SDHC with CMD8 support"); card->flags |= SD_SDHC_FLAG; return 0; } /* Sends CMD59 to enable CRC checking for SD card in SPI mode */ static int sd_enable_crc(struct sd_card *card) { struct sdhc_command cmd = {0}; /* CMD59 for CRC mode is only valid for SPI hosts */ __ASSERT_NO_MSG(card->host_props.is_spi); cmd.opcode = SD_SPI_CRC_ON_OFF; cmd.arg = 0x1; /* Enable CRC */ cmd.response_type = SD_SPI_RSP_TYPE_R1; cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; return sdhc_request(card->sdhc, &cmd, NULL); } /* * Perform init required for both SD and SDIO cards. * This function performs the following portions of SD initialization * - CMD0 (SD reset) * - CMD8 (SD voltage check) */ static int sd_common_init(struct sd_card *card) { int ret; /* Reset card with CMD0 */ ret = sd_idle(card); if (ret) { LOG_ERR("Card error on CMD0"); return ret; } /* Perform voltage check using SD CMD8 */ ret = sd_retry(sd_send_interface_condition, card, CONFIG_SD_RETRY_COUNT); if (ret == -ETIMEDOUT) { LOG_INF("Card does not support CMD8, assuming legacy card"); return sd_idle(card); } else if (ret) { LOG_ERR("Card error on CMD 8"); return ret; } if (card->host_props.is_spi && IS_ENABLED(CONFIG_SDHC_SUPPORTS_SPI_MODE)) { /* Enable CRC for spi commands using CMD59 */ ret = sd_enable_crc(card); } return ret; } static int sd_init_io(struct sd_card *card) { struct sdhc_io *bus_io = &card->bus_io; int ret; /* SD clock should start gated */ bus_io->clock = 0; /* SPI requires SDHC PUSH PULL, and open drain buses use more power */ bus_io->bus_mode = SDHC_BUSMODE_PUSHPULL; bus_io->power_mode = SDHC_POWER_ON; bus_io->bus_width = SDHC_BUS_WIDTH1BIT; /* Cards start with legacy timing and 3.3V signalling at power on */ bus_io->timing = SDHC_TIMING_LEGACY; bus_io->signal_voltage = SD_VOL_3_3_V; /* Toggle power to card to reset it */ LOG_DBG("Resetting power to card"); bus_io->power_mode = SDHC_POWER_OFF; ret = sdhc_set_io(card->sdhc, bus_io); if (ret) { LOG_ERR("Could not disable card power via SDHC"); return ret; } sd_delay(card->host_props.power_delay); bus_io->power_mode = SDHC_POWER_ON; ret = sdhc_set_io(card->sdhc, bus_io); if (ret) { LOG_ERR("Could not disable card power via SDHC"); return ret; } /* After reset or init, card voltage should be 3.3V */ card->card_voltage = SD_VOL_3_3_V; /* Reset card flags */ card->flags = 0U; /* Delay so card can power up */ sd_delay(card->host_props.power_delay); /* Start bus clock */ bus_io->clock = SDMMC_CLOCK_400KHZ; ret = sdhc_set_io(card->sdhc, bus_io); if (ret) { LOG_ERR("Could not start bus clock"); return ret; } return 0; } /* * Sends CMD5 to SD card, and uses response to determine if card * is SDIO or SDMMC card. Return 0 if SDIO card, positive value if not, or * negative errno on error */ int sd_test_sdio(struct sd_card *card) { struct sdhc_command cmd = {0}; int ret; cmd.opcode = SDIO_SEND_OP_COND; cmd.arg = 0; cmd.response_type = (SD_RSP_TYPE_R4 | SD_SPI_RSP_TYPE_R4); cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; ret = sdhc_request(card->sdhc, &cmd, NULL); if (ret) { /* * We are just probing card, and it is likely an SD. * return error */ card->type = CARD_SDMMC; return SD_NOT_SDIO; } /* Check the number of I/O functions */ card->num_io = ((cmd.response[0] & SDIO_OCR_IO_NUMBER) >> SDIO_OCR_IO_NUMBER_SHIFT); if ((card->num_io == 0) | ((cmd.response[0] & SDIO_IO_OCR_MASK) == 0)) { if (cmd.response[0] & SDIO_OCR_MEM_PRESENT_FLAG) { /* Card is not an SDIO card. */ card->type = CARD_SDMMC; return SD_NOT_SDIO; } /* Card is not a valid SD device. We do not support it */ return -ENOTSUP; } /* Since we got a valid OCR response, * we know this card is an SDIO card. */ card->type = CARD_SDIO; return 0; } /* * Check SD card type * Uses SDIO OCR response to determine what type of card is present. */ static int sd_check_card_type(struct sd_card *card) { int ret; /* Test if the card response to CMD5 (only SDIO cards will) */ /* Note that CMD5 can take many retries */ ret = sd_test_sdio(card); if ((ret == SD_NOT_SDIO) && card->type == CARD_SDMMC) { LOG_INF("Detected SD card"); return 0; } else if ((ret == 0) && card->type == CARD_SDIO) { LOG_INF("Detected SDIO card"); return 0; } LOG_ERR("No usable card type was found"); return -ENOTSUP; } /* * Performs init flow described in section 3.6 of SD specification. */ static int sd_command_init(struct sd_card *card) { int ret; /* * We must wait 74 clock cycles, per SD spec, to use card after power * on. At 400000KHz, this is a 185us delay. Wait 1ms to be safe. */ sd_delay(1); /* * Start card initialization and identification * flow described in section 3.6 of SD specification */ ret = sd_common_init(card); if (ret) { return ret; } /* Use CMD5 to determine card type */ ret = sd_check_card_type(card); if (ret) { LOG_ERR("Unusable card"); return -ENOTSUP; } if (card->type == CARD_SDMMC) { /* * Reset the card first- CMD5 sent to see if it is SDIO card * may have left it in error state */ ret = sd_common_init(card); if (ret) { LOG_ERR("Init after CMD5 failed"); return ret; } /* Perform memory card initialization */ ret = sdmmc_card_init(card); } else if (card->type == CARD_SDIO) { LOG_ERR("SDIO cards not currently supported"); return -ENOTSUP; } if (ret) { LOG_ERR("Card init failed"); return ret; } return 0; } /* Initializes SD/SDIO card */ int sd_init(const struct device *sdhc_dev, struct sd_card *card) { int ret; if (!sdhc_dev) { return -ENODEV; } card->sdhc = sdhc_dev; ret = sdhc_get_host_props(card->sdhc, &card->host_props); if (ret) { LOG_ERR("SD host controller returned invalid properties"); return ret; } /* init and lock card mutex */ ret = k_mutex_init(&card->lock); if (ret) { LOG_DBG("Could not init card mutex"); return ret; } ret = k_mutex_lock(&card->lock, K_MSEC(CONFIG_SD_INIT_TIMEOUT)); if (ret) { LOG_ERR("Timeout while trying to acquire card mutex"); return ret; } /* Initialize SDHC IO with defaults */ ret = sd_init_io(card); if (ret) { k_mutex_unlock(&card->lock); return ret; } /* * SD protocol is stateful, so we must account for the possibility * that the card is in a bad state. The return code SD_RESTART * indicates that the initialization left the card in a bad state. * In this case the subsystem takes the following steps: * - set card status to error * - re init host I/O (will also toggle power to the SD card) * - retry initialization once more * If initialization then fails, the sd_init routine will assume the * card is inaccessible */ ret = sd_command_init(card); if (ret == SD_RESTART) { /* Reset I/O, and retry sd initialization once more */ card->status = CARD_ERROR; /* Reset I/O to default */ ret = sd_init_io(card); if (ret) { LOG_ERR("Failed to reset SDHC I/O"); k_mutex_unlock(&card->lock); return ret; } ret = sd_command_init(card); if (ret) { LOG_ERR("Failed to init SD card after I/O reset"); k_mutex_unlock(&card->lock); return ret; } } else if (ret != 0) { /* Initialization failed */ k_mutex_unlock(&card->lock); card->status = CARD_ERROR; return ret; } /* Card initialization succeeded. */ card->status = CARD_INITIALIZED; /* Unlock card mutex */ ret = k_mutex_unlock(&card->lock); if (ret) { LOG_DBG("Could not unlock card mutex"); return ret; } return ret; } /* Return true if card is present, false otherwise */ bool sd_is_card_present(const struct device *sdhc_dev) { if (!sdhc_dev) { return false; } return sdhc_card_present(sdhc_dev) == 1; }