|  | /* | 
|  | * Copyright 2022,2024 NXP | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT zephyr_sdhc_spi_slot | 
|  |  | 
|  |  | 
|  |  | 
|  | #include <zephyr/drivers/sdhc.h> | 
|  | #include <zephyr/drivers/gpio.h> | 
|  | #include <zephyr/logging/log.h> | 
|  | #include <zephyr/sys/byteorder.h> | 
|  | #include <zephyr/drivers/spi.h> | 
|  | #include <zephyr/sys/crc.h> | 
|  |  | 
|  | LOG_MODULE_REGISTER(sdhc_spi, CONFIG_SDHC_LOG_LEVEL); | 
|  |  | 
|  | #define MAX_CMD_READ 21 | 
|  | #define SPI_R1B_TIMEOUT_MS 3000 | 
|  | #define SD_SPI_SKIP_RETRIES 1000000 | 
|  |  | 
|  | /* The SD protocol requires sending ones while reading but Zephyr | 
|  | * defaults to writing zeros. This block of 512 bytes is used for writing | 
|  | * 0xff while we read data blocks. Should remain const so this buffer is | 
|  | * stored in flash. | 
|  | */ | 
|  | static const uint8_t sdhc_ones[] = { | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 
|  | }; | 
|  |  | 
|  | BUILD_ASSERT(sizeof(sdhc_ones) == 512, "0xFF array for SDHC must be 512 bytes"); | 
|  |  | 
|  | struct sdhc_spi_config { | 
|  | const struct device *spi_dev; | 
|  | const struct gpio_dt_spec pwr_gpio; | 
|  | const uint32_t spi_max_freq; | 
|  | uint32_t power_delay_ms; | 
|  | }; | 
|  |  | 
|  | struct sdhc_spi_data { | 
|  | enum sdhc_power power_mode; | 
|  | struct spi_config *spi_cfg; | 
|  | struct spi_config cfg_a; | 
|  | struct spi_config cfg_b; | 
|  | uint8_t scratch[MAX_CMD_READ]; | 
|  | }; | 
|  |  | 
|  | /* Receives a block of bytes */ | 
|  | static int sdhc_spi_rx(const struct device *spi_dev, struct spi_config *spi_cfg, | 
|  | uint8_t *buf, int len) | 
|  | { | 
|  | struct spi_buf tx_bufs[] = { | 
|  | { | 
|  | .buf = (uint8_t *)sdhc_ones, | 
|  | .len = len | 
|  | } | 
|  | }; | 
|  |  | 
|  | const struct spi_buf_set tx = { | 
|  | .buffers = tx_bufs, | 
|  | .count = 1, | 
|  | }; | 
|  |  | 
|  | struct spi_buf rx_bufs[] = { | 
|  | { | 
|  | .buf = buf, | 
|  | .len = len | 
|  | } | 
|  | }; | 
|  |  | 
|  | const struct spi_buf_set rx = { | 
|  | .buffers = rx_bufs, | 
|  | .count = 1, | 
|  | }; | 
|  |  | 
|  | return spi_transceive(spi_dev, spi_cfg, &tx, &rx); | 
|  | } | 
|  |  | 
|  | static int sdhc_spi_init_card(const struct device *dev) | 
|  | { | 
|  | /* SD spec requires at least 74 clocks be send to SD to start it. | 
|  | * for SPI protocol, this will be performed by sending 10 0xff values | 
|  | * to the card (this should result in 80 SCK cycles) | 
|  | */ | 
|  | const struct sdhc_spi_config *config = dev->config; | 
|  | struct sdhc_spi_data *data = dev->data; | 
|  | struct spi_config *spi_cfg = data->spi_cfg; | 
|  | int ret; | 
|  |  | 
|  | if (spi_cfg->frequency == 0) { | 
|  | /* Use default 400KHZ frequency */ | 
|  | spi_cfg->frequency = SDMMC_CLOCK_400KHZ; | 
|  | } | 
|  | /* the initial 74 clocks must be sent while CS is high */ | 
|  | spi_cfg->operation |= SPI_CS_ACTIVE_HIGH; | 
|  | ret = sdhc_spi_rx(config->spi_dev, spi_cfg, data->scratch, 10); | 
|  | if (ret != 0) { | 
|  | spi_release(config->spi_dev, spi_cfg); | 
|  | spi_cfg->operation &= ~SPI_CS_ACTIVE_HIGH; | 
|  | return ret; | 
|  | } | 
|  | /* Release lock on SPI bus */ | 
|  | ret = spi_release(config->spi_dev, spi_cfg); | 
|  | spi_cfg->operation &= ~SPI_CS_ACTIVE_HIGH; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Checks if SPI SD card is sending busy signal */ | 
|  | static int sdhc_spi_card_busy(const struct device *dev) | 
|  | { | 
|  | const struct sdhc_spi_config *config = dev->config; | 
|  | struct sdhc_spi_data *data = dev->data; | 
|  | int ret; | 
|  | uint8_t response; | 
|  |  | 
|  |  | 
|  | ret = sdhc_spi_rx(config->spi_dev, data->spi_cfg, &response, 1); | 
|  | if (ret) { | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | if (response == 0xFF) { | 
|  | return 0; | 
|  | } else | 
|  | return 1; | 
|  |  | 
|  | } | 
|  |  | 
|  | /* Waits for SPI SD card to stop sending busy signal */ | 
|  | static int sdhc_spi_wait_unbusy(const struct device *dev, | 
|  | int timeout_ms, | 
|  | int interval_ticks) | 
|  | { | 
|  | const struct sdhc_spi_config *config = dev->config; | 
|  | struct sdhc_spi_data *data = dev->data; | 
|  | int ret; | 
|  | uint8_t response; | 
|  |  | 
|  | while (timeout_ms > 0) { | 
|  | ret = sdhc_spi_rx(config->spi_dev, data->spi_cfg, &response, 1); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | if (response == 0xFF) { | 
|  | return 0; | 
|  | } | 
|  | k_msleep(k_ticks_to_ms_floor32(interval_ticks)); | 
|  | timeout_ms -= k_ticks_to_ms_floor32(interval_ticks); | 
|  | } | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Read SD command from SPI response */ | 
|  | static int sdhc_spi_response_get(const struct device *dev, struct sdhc_command *cmd, | 
|  | int rx_len) | 
|  | { | 
|  | const struct sdhc_spi_config *config = dev->config; | 
|  | struct sdhc_spi_data *dev_data = dev->data; | 
|  | uint8_t *response = dev_data->scratch; | 
|  | uint8_t *end = response + rx_len; | 
|  | int ret, timeout = cmd->timeout_ms; | 
|  | uint8_t value, i; | 
|  |  | 
|  | /* First step is finding the first valid byte of the response. | 
|  | * All SPI responses start with R1, which will have MSB of zero. | 
|  | * we know we can ignore the first 7 bytes, which hold the command and | 
|  | * initial "card ready" byte. | 
|  | */ | 
|  | response += 8; | 
|  | while (response < end && ((*response & SD_SPI_START) == SD_SPI_START)) { | 
|  | response++; | 
|  | } | 
|  | if (response == end) { | 
|  | /* Some cards are slow, and need more time to respond. Continue | 
|  | * with single byte reads until the card responds. | 
|  | */ | 
|  | response = dev_data->scratch; | 
|  | end = response + 1; | 
|  | while (timeout > 0) { | 
|  | ret = sdhc_spi_rx(config->spi_dev, dev_data->spi_cfg, | 
|  | response, 1); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | if (*response != 0xff) { | 
|  | break; | 
|  | } | 
|  | /* Delay for a bit, and poll the card again */ | 
|  | k_msleep(10); | 
|  | timeout -= 10; | 
|  | } | 
|  | if (*response == 0xff) { | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  | } | 
|  | /* Record R1 response */ | 
|  | cmd->response[0] = *response++; | 
|  | /* Check response for error */ | 
|  | if (cmd->response[0] != 0) { | 
|  | if (cmd->response[0] &	(SD_SPI_R1PARAMETER_ERR | SD_SPI_R1ADDRESS_ERR)) { | 
|  | return -EFAULT; /* Bad address */ | 
|  | } else if (cmd->response[0] & (SD_SPI_R1ILLEGAL_CMD_ERR)) { | 
|  | return -EINVAL; /* Invalid command */ | 
|  | } else if (cmd->response[0] & (SD_SPI_R1CMD_CRC_ERR)) { | 
|  | return -EILSEQ; /* Illegal byte sequence */ | 
|  | } else if (cmd->response[0] & (SD_SPI_R1ERASE_SEQ_ERR | SD_SPI_R1ERASE_RESET)) { | 
|  | return -EIO; | 
|  | } | 
|  | /* else IDLE_STATE bit is set, which is not an error, card is just resetting */ | 
|  | } | 
|  | switch ((cmd->response_type & SDHC_SPI_RESPONSE_TYPE_MASK)) { | 
|  | case SD_SPI_RSP_TYPE_R1: | 
|  | /* R1 response - one byte*/ | 
|  | break; | 
|  | case SD_SPI_RSP_TYPE_R1b: | 
|  | /* R1b response - one byte plus busy signal */ | 
|  | /* Read remaining bytes to see if card is still busy. | 
|  | * card will be ready when it stops driving data out | 
|  | * low. | 
|  | */ | 
|  | while (response < end && (*response == 0x0)) { | 
|  | response++; | 
|  | } | 
|  | if (response == end) { | 
|  | value = cmd->timeout_ms; | 
|  | response--; | 
|  | /* Periodically check busy line */ | 
|  | ret = sdhc_spi_wait_unbusy(dev, | 
|  | SPI_R1B_TIMEOUT_MS, 1000); | 
|  | } | 
|  | break; | 
|  | case SD_SPI_RSP_TYPE_R2: | 
|  | case SD_SPI_RSP_TYPE_R5: | 
|  | /* R2/R5 response - R1 response + 1 byte*/ | 
|  | if (response == end) { | 
|  | response = dev_data->scratch; | 
|  | end = response + 1; | 
|  | /* Read the next byte */ | 
|  | ret = sdhc_spi_rx(config->spi_dev, | 
|  | dev_data->spi_cfg, | 
|  | response, 1); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | cmd->response[0] = (*response) << 8; | 
|  | break; | 
|  | case SD_SPI_RSP_TYPE_R3: | 
|  | case SD_SPI_RSP_TYPE_R4: | 
|  | case SD_SPI_RSP_TYPE_R7: | 
|  | /* R3/R4/R7 response - R1 response + 4 bytes */ | 
|  | cmd->response[1] = 0; | 
|  | for (i = 0; i < 4; i++) { | 
|  | cmd->response[1] <<= 8; | 
|  | /* Read bytes of response */ | 
|  | if (response == end) { | 
|  | response = dev_data->scratch; | 
|  | end = response + 1; | 
|  | /* Read the next byte */ | 
|  | ret = sdhc_spi_rx(config->spi_dev, | 
|  | dev_data->spi_cfg, | 
|  | response, 1); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | cmd->response[1] |= *response++; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | /* Other RSP types not supported */ | 
|  | return -ENOTSUP; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Send SD command using SPI */ | 
|  | static int sdhc_spi_send_cmd(const struct device *dev, struct sdhc_command *cmd, | 
|  | bool data_present) | 
|  | { | 
|  | const struct sdhc_spi_config *config = dev->config; | 
|  | struct sdhc_spi_data *dev_data = dev->data; | 
|  | int err; | 
|  | uint8_t *cmd_buf; | 
|  | /* To reduce overhead, we will send entire command in one SPI | 
|  | * transaction. The packet takes the following format: | 
|  | * - all ones byte to ensure card is ready | 
|  | * - opcode byte (which includes start and transmission bits) | 
|  | * - 4 bytes for argument | 
|  | * - crc7 byte (with end bit) | 
|  | * The SD card can take up to 8 bytes worth of SCLK cycles to respond. | 
|  | * therefore, we provide 8 bytes of all ones, to read data from the card. | 
|  | * the maximum spi response length is 5 bytes, so we provide an | 
|  | * additional 5 bytes of data, leaving us with 13 bytes of 0xff. | 
|  | * Finally, we send a padding byte of all 0xff, to ensure that | 
|  | * the card recives at least one 0xff byte before next command. | 
|  | */ | 
|  |  | 
|  | /* Note: we can discard CMD data as we send it, | 
|  | * so resuse the TX buf as RX | 
|  | */ | 
|  | struct spi_buf bufs[] = { | 
|  | { | 
|  | .buf = dev_data->scratch, | 
|  | .len = sizeof(dev_data->scratch), | 
|  | }, | 
|  | }; | 
|  |  | 
|  | const struct spi_buf_set buf_set = { | 
|  | .buffers = bufs, | 
|  | .count = 1, | 
|  | }; | 
|  |  | 
|  |  | 
|  | if (data_present) { | 
|  | /* We cannot send extra SCLK cycles with our command, | 
|  | * since we'll miss the data the card responds with. We | 
|  | * send one 0xff byte, six command bytes, two additional 0xff | 
|  | * bytes, since the min value of NCR (see SD SPI timing | 
|  | * diagrams) is one, and we know there will be an R1 response. | 
|  | */ | 
|  | bufs[0].len = SD_SPI_CMD_SIZE + 3; | 
|  | } | 
|  | memset(dev_data->scratch, 0xFF, sizeof(dev_data->scratch)); | 
|  | cmd_buf = dev_data->scratch + 1; | 
|  |  | 
|  | /* Command packet holds the following bits: | 
|  | * [47]: start bit, 0b0 | 
|  | * [46]: transmission bit, 0b1 | 
|  | * [45-40]: command index | 
|  | * [39-8]: argument | 
|  | * [7-1]: CRC | 
|  | * [0]: end bit, 0b1 | 
|  | * Note that packets are sent MSB first. | 
|  | */ | 
|  | /* Add start bit, tx bit, and cmd opcode */ | 
|  | cmd_buf[0] = (cmd->opcode & SD_SPI_CMD); | 
|  | cmd_buf[0] = ((cmd_buf[0] | SD_SPI_TX) & ~SD_SPI_START); | 
|  | /* Add argument */ | 
|  | sys_put_be32(cmd->arg, &cmd_buf[1]); | 
|  | /* Add CRC, and set LSB as the end bit */ | 
|  | cmd_buf[SD_SPI_CMD_BODY_SIZE] = crc7_be(0, cmd_buf, SD_SPI_CMD_BODY_SIZE) | 0x1; | 
|  | LOG_DBG("cmd%d arg 0x%x", cmd->opcode, cmd->arg); | 
|  | /* Set data, will lock SPI bus */ | 
|  | err = spi_transceive(config->spi_dev, dev_data->spi_cfg, &buf_set, &buf_set); | 
|  | if (err != 0) { | 
|  | return err; | 
|  | } | 
|  | /* Read command response */ | 
|  | return sdhc_spi_response_get(dev, cmd, bufs[0].len); | 
|  | } | 
|  |  | 
|  | /* Skips bytes in SDHC data stream. */ | 
|  | static int sdhc_skip(const struct device *dev, uint8_t skip_val) | 
|  | { | 
|  | const struct sdhc_spi_config *config = dev->config; | 
|  | struct sdhc_spi_data *data = dev->data; | 
|  | uint8_t buf; | 
|  | int ret; | 
|  | uint32_t retries = SD_SPI_SKIP_RETRIES; | 
|  |  | 
|  | do { | 
|  | ret = sdhc_spi_rx(config->spi_dev, data->spi_cfg, | 
|  | &buf, sizeof(buf)); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | } while (buf == skip_val && retries--); | 
|  | if (retries == 0) { | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  | /* Return first non-skipped value */ | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | /* Handles reading data from SD SPI device */ | 
|  | static int sdhc_spi_read_data(const struct device *dev, struct sdhc_data *data) | 
|  | { | 
|  | const struct sdhc_spi_config *config = dev->config; | 
|  | struct sdhc_spi_data *dev_data = dev->data; | 
|  | uint8_t *read_location = data->data; | 
|  | uint32_t remaining = data->blocks; | 
|  | int ret; | 
|  | uint8_t crc[SD_SPI_CRC16_SIZE + 1]; | 
|  |  | 
|  | /* The SPI API defaults to sending 0x00 when no TX buffer is | 
|  | * provided, so we are limited to 512 byte reads | 
|  | * (unless we increase the size of SDHC buffer) | 
|  | */ | 
|  | const struct spi_buf tx_bufs[] = { | 
|  | { | 
|  | .buf = (uint8_t *)sdhc_ones, | 
|  | .len = data->block_size, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | const struct spi_buf_set tx = { | 
|  | .buffers = tx_bufs, | 
|  | .count = 1, | 
|  | }; | 
|  |  | 
|  | struct spi_buf rx_bufs[] = { | 
|  | { | 
|  | .buf = read_location, | 
|  | .len = data->block_size, | 
|  | } | 
|  | }; | 
|  |  | 
|  | const struct spi_buf_set rx = { | 
|  | .buffers = rx_bufs, | 
|  | .count = 1, | 
|  | }; | 
|  |  | 
|  | if (data->block_size > 512) { | 
|  | /* SPI max BLKLEN is 512 */ | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | /* Read bytes until data stream starts. SD will send 0xff until | 
|  | * data is available | 
|  | */ | 
|  | ret = sdhc_skip(dev, 0xff); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | /* Check token */ | 
|  | if (ret != SD_SPI_TOKEN_SINGLE)	{ | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | /* Read blocks until we are out of data */ | 
|  | while (remaining--) { | 
|  | ret = spi_transceive(config->spi_dev, | 
|  | dev_data->spi_cfg, &tx, &rx); | 
|  | if (ret) { | 
|  | LOG_ERR("Data write failed"); | 
|  | return ret; | 
|  | } | 
|  | /* Read CRC16 plus one end byte */ | 
|  | ret = sdhc_spi_rx(config->spi_dev, dev_data->spi_cfg, | 
|  | crc, sizeof(crc)); | 
|  | if (crc16_itu_t(0, read_location, data->block_size) != | 
|  | sys_get_be16(crc)) { | 
|  | /* Bad CRC */ | 
|  | LOG_ERR("Bad data CRC"); | 
|  | return -EILSEQ; | 
|  | } | 
|  | /* Advance read location */ | 
|  | read_location += data->block_size; | 
|  | rx_bufs[0].buf = read_location; | 
|  | if (remaining) { | 
|  | /* Check next data token */ | 
|  | ret = sdhc_skip(dev, 0xff); | 
|  | if (ret != SD_SPI_TOKEN_SINGLE) { | 
|  | LOG_ERR("Bad token"); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Handles writing data to SD SPI device */ | 
|  | static int sdhc_spi_write_data(const struct device *dev, struct sdhc_data *data) | 
|  | { | 
|  | const struct sdhc_spi_config *config = dev->config; | 
|  | struct sdhc_spi_data *dev_data = dev->data; | 
|  | int ret; | 
|  | uint8_t token, resp; | 
|  | uint8_t *write_location = data->data, crc[SD_SPI_CRC16_SIZE]; | 
|  | uint32_t remaining = data->blocks; | 
|  |  | 
|  | struct spi_buf tx_bufs[] = { | 
|  | { | 
|  | .buf = &token, | 
|  | .len = sizeof(uint8_t), | 
|  | }, | 
|  | { | 
|  | .buf = write_location, | 
|  | .len = data->block_size, | 
|  | }, | 
|  | { | 
|  | .buf = crc, | 
|  | .len = sizeof(crc), | 
|  | }, | 
|  | }; | 
|  |  | 
|  | struct spi_buf_set tx = { | 
|  | .buffers = tx_bufs, | 
|  | .count = 3, | 
|  | }; | 
|  |  | 
|  | /* Set the token- single block reads use different token | 
|  | * than multibock | 
|  | */ | 
|  | if (remaining > 1) { | 
|  | token = SD_SPI_TOKEN_MULTI_WRITE; | 
|  | } else { | 
|  | token = SD_SPI_TOKEN_SINGLE; | 
|  | } | 
|  |  | 
|  | while (remaining--) { | 
|  | /* Build the CRC for this data block */ | 
|  | sys_put_be16(crc16_itu_t(0, write_location, data->block_size), | 
|  | crc); | 
|  | ret = spi_write(config->spi_dev, dev_data->spi_cfg, &tx); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | /* Read back the data response token from the card */ | 
|  | ret = sdhc_spi_rx(config->spi_dev, dev_data->spi_cfg, | 
|  | &resp, sizeof(resp)); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | /* Check response token */ | 
|  | if ((resp & 0xF) != SD_SPI_RESPONSE_ACCEPTED) { | 
|  | if ((resp & 0xF) == SD_SPI_RESPONSE_CRC_ERR) { | 
|  | return -EILSEQ; | 
|  | } else if ((resp & 0xF) == SD_SPI_RESPONSE_WRITE_ERR) { | 
|  | return -EIO; | 
|  | } | 
|  | LOG_DBG("Unknown write response token 0x%x", resp); | 
|  | return -EIO; | 
|  | } | 
|  | /* Advance write location */ | 
|  | write_location += data->block_size; | 
|  | tx_bufs[1].buf = write_location; | 
|  | /* Wait for card to stop being busy */ | 
|  | ret = sdhc_spi_wait_unbusy(dev, data->timeout_ms, 0); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | if (data->blocks > 1) { | 
|  | /* Write stop transfer token to card */ | 
|  | token = SD_SPI_TOKEN_STOP_TRAN; | 
|  | tx.count = 1; | 
|  | ret = spi_write(config->spi_dev, dev_data->spi_cfg, &tx); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | /* Wait for card to stop being busy */ | 
|  | ret = sdhc_spi_wait_unbusy(dev, data->timeout_ms, 0); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int sdhc_spi_request(const struct device *dev, | 
|  | struct sdhc_command *cmd, | 
|  | struct sdhc_data *data) | 
|  | { | 
|  | const struct sdhc_spi_config *config = dev->config; | 
|  | struct sdhc_spi_data *dev_data = dev->data; | 
|  | int ret, stop_ret, retries = cmd->retries; | 
|  | const struct sdhc_command stop_cmd = { | 
|  | .opcode = SD_STOP_TRANSMISSION, | 
|  | .arg = 0, | 
|  | .response_type = SD_SPI_RSP_TYPE_R1b, | 
|  | .timeout_ms = 1000, | 
|  | .retries = 1, | 
|  | }; | 
|  | if (data == NULL) { | 
|  | do { | 
|  | ret = sdhc_spi_send_cmd(dev, cmd, false); | 
|  | } while ((ret != 0) && (retries-- > 0)); | 
|  | } else { | 
|  | do { | 
|  | retries--; | 
|  | ret = sdhc_spi_send_cmd(dev, cmd, true); | 
|  | if (ret) { | 
|  | continue; | 
|  | } | 
|  | if ((cmd->opcode == SD_WRITE_SINGLE_BLOCK) || | 
|  | (cmd->opcode == SD_WRITE_MULTIPLE_BLOCK)) { | 
|  | ret = sdhc_spi_write_data(dev, data); | 
|  | } else { | 
|  | ret = sdhc_spi_read_data(dev, data); | 
|  | } | 
|  | if (ret || (cmd->opcode == SD_READ_MULTIPLE_BLOCK)) { | 
|  | int stop_retries = cmd->retries; | 
|  |  | 
|  | /* CMD12 is required after multiple read, or | 
|  | * to retry failed transfer | 
|  | */ | 
|  | stop_ret = sdhc_spi_send_cmd(dev, | 
|  | (struct sdhc_command *)&stop_cmd, | 
|  | false); | 
|  | while ((stop_ret != 0) && (stop_retries > 0)) { | 
|  | /* Retry stop command */ | 
|  | ret = stop_ret = sdhc_spi_send_cmd(dev, | 
|  | (struct sdhc_command *)&stop_cmd, | 
|  | false); | 
|  | stop_retries--; | 
|  | } | 
|  | } | 
|  | } while ((ret != 0) && (retries > 0)); | 
|  | } | 
|  | if (ret) { | 
|  | /* Release SPI bus */ | 
|  | spi_release(config->spi_dev, dev_data->spi_cfg); | 
|  | return ret; | 
|  | } | 
|  | /* Release SPI bus */ | 
|  | return spi_release(config->spi_dev, dev_data->spi_cfg); | 
|  | } | 
|  |  | 
|  | static int sdhc_spi_set_io(const struct device *dev, struct sdhc_io *ios) | 
|  | { | 
|  | const struct sdhc_spi_config *cfg = dev->config; | 
|  | struct sdhc_spi_data *data = dev->data; | 
|  |  | 
|  | if (ios->clock != data->spi_cfg->frequency) { | 
|  | if (ios->clock > cfg->spi_max_freq) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  | /* Because pointer comparision is used, we have to | 
|  | * swap to a new configuration structure to reconfigure SPI. | 
|  | */ | 
|  | if (ios->clock != 0) { | 
|  | if (data->spi_cfg == &data->cfg_a) { | 
|  | data->cfg_a.frequency = ios->clock; | 
|  | memcpy(&data->cfg_b, &data->cfg_a, | 
|  | sizeof(struct spi_config)); | 
|  | data->spi_cfg = &data->cfg_b; | 
|  | } else { | 
|  | data->cfg_b.frequency = ios->clock; | 
|  | memcpy(&data->cfg_a, &data->cfg_b, | 
|  | sizeof(struct spi_config)); | 
|  | data->spi_cfg = &data->cfg_a; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (ios->bus_mode != SDHC_BUSMODE_PUSHPULL) { | 
|  | /* SPI mode supports push pull */ | 
|  | return -ENOTSUP; | 
|  | } | 
|  | if (data->power_mode != ios->power_mode) { | 
|  | if (ios->power_mode == SDHC_POWER_ON) { | 
|  | /* Send 74 clock cycles to start card */ | 
|  | if (sdhc_spi_init_card(dev) != 0) { | 
|  | LOG_ERR("Card SCLK init sequence failed"); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  | if (cfg->pwr_gpio.port) { | 
|  | /* If power control GPIO is defined, toggle SD power */ | 
|  | if (ios->power_mode == SDHC_POWER_ON) { | 
|  | if (gpio_pin_set_dt(&cfg->pwr_gpio, 1)) { | 
|  | return -EIO; | 
|  | } | 
|  | } else { | 
|  | if (gpio_pin_set_dt(&cfg->pwr_gpio, 0)) { | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  | } | 
|  | data->power_mode = ios->power_mode; | 
|  | } | 
|  | if (ios->bus_width != SDHC_BUS_WIDTH1BIT) { | 
|  | /* SPI mode supports 1 bit bus */ | 
|  | return -ENOTSUP; | 
|  | } | 
|  | if (ios->signal_voltage != SD_VOL_3_3_V) { | 
|  | /* SPI mode does not support UHS voltages */ | 
|  | return -ENOTSUP; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int sdhc_spi_get_card_present(const struct device *dev) | 
|  | { | 
|  | /* SPI has no card presence method, assume card is in slot */ | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int sdhc_spi_get_host_props(const struct device *dev, | 
|  | struct sdhc_host_props *props) | 
|  | { | 
|  | const struct sdhc_spi_config *cfg = dev->config; | 
|  |  | 
|  | memset(props, 0, sizeof(struct sdhc_host_props)); | 
|  |  | 
|  | props->f_min = SDMMC_CLOCK_400KHZ; | 
|  | props->f_max = cfg->spi_max_freq; | 
|  | props->power_delay = cfg->power_delay_ms; | 
|  | props->host_caps.vol_330_support = true; | 
|  | props->is_spi = true; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int sdhc_spi_reset(const struct device *dev) | 
|  | { | 
|  | struct sdhc_spi_data *data = dev->data; | 
|  |  | 
|  | /* Reset host I/O */ | 
|  | data->spi_cfg->frequency = SDMMC_CLOCK_400KHZ; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int sdhc_spi_init(const struct device *dev) | 
|  | { | 
|  | const struct sdhc_spi_config *cfg = dev->config; | 
|  | struct sdhc_spi_data *data = dev->data; | 
|  | int ret = 0; | 
|  |  | 
|  | if (!device_is_ready(cfg->spi_dev)) { | 
|  | return -ENODEV; | 
|  | } | 
|  | if (cfg->pwr_gpio.port) { | 
|  | if (!gpio_is_ready_dt(&cfg->pwr_gpio)) { | 
|  | return -ENODEV; | 
|  | } | 
|  | ret = gpio_pin_configure_dt(&cfg->pwr_gpio, GPIO_OUTPUT_INACTIVE); | 
|  | if (ret != 0) { | 
|  | LOG_ERR("Could not configure power gpio (%d)", ret); | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | data->power_mode = SDHC_POWER_OFF; | 
|  | data->spi_cfg = &data->cfg_a; | 
|  | data->spi_cfg->frequency = 0; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static const struct sdhc_driver_api sdhc_spi_api = { | 
|  | .request = sdhc_spi_request, | 
|  | .set_io = sdhc_spi_set_io, | 
|  | .get_host_props = sdhc_spi_get_host_props, | 
|  | .get_card_present = sdhc_spi_get_card_present, | 
|  | .reset = sdhc_spi_reset, | 
|  | .card_busy = sdhc_spi_card_busy, | 
|  | }; | 
|  |  | 
|  |  | 
|  | #define SDHC_SPI_INIT(n)							\ | 
|  | const struct sdhc_spi_config sdhc_spi_config_##n = {			\ | 
|  | .spi_dev = DEVICE_DT_GET(DT_INST_PARENT(n)),			\ | 
|  | .pwr_gpio = GPIO_DT_SPEC_INST_GET_OR(n, pwr_gpios, {0}),	\ | 
|  | .spi_max_freq = DT_INST_PROP(n, spi_max_frequency),		\ | 
|  | .power_delay_ms = DT_INST_PROP(n, power_delay_ms),		\ | 
|  | };									\ | 
|  | \ | 
|  | struct sdhc_spi_data sdhc_spi_data_##n = {				\ | 
|  | .cfg_a = SPI_CONFIG_DT_INST(n,					\ | 
|  | (SPI_LOCK_ON | SPI_HOLD_ON_CS | SPI_WORD_SET(8) \ | 
|  | | (DT_INST_PROP(n, spi_clock_mode_cpol) ? SPI_MODE_CPOL : 0) \ | 
|  | | (DT_INST_PROP(n, spi_clock_mode_cpha) ? SPI_MODE_CPHA : 0) \ | 
|  | ),\ | 
|  | 0),						\ | 
|  | };									\ | 
|  | \ | 
|  | DEVICE_DT_INST_DEFINE(n,						\ | 
|  | &sdhc_spi_init,						\ | 
|  | NULL,							\ | 
|  | &sdhc_spi_data_##n,					\ | 
|  | &sdhc_spi_config_##n,					\ | 
|  | POST_KERNEL,						\ | 
|  | CONFIG_SDHC_INIT_PRIORITY,				\ | 
|  | &sdhc_spi_api); | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(SDHC_SPI_INIT) |