zephyr/drivers/sdhc/sdhc_spi.c
Daniel DeGrasse a2087bed16 drivers: sdhc: sdhc_spi: rework CMD12 failure logic
Rework CMD12 failure logic for SDHC SPI driver. Previously, the error
code of CMD12 was not checked, so even if CMD12 failed to send the
initial command would be retried. Change this behavior to retry CMD12
until it succeeds. If CMD12 fails, its error code will be propagated to
the caller. Otherwise, the return code from the command being sent by
the caller will be propagated.

Fixes #72365

Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2024-05-17 09:30:12 +02:00

815 lines
22 KiB
C

/*
* 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)