blob: b5c8fab1e913b12fc39431d5e9e20e4ce0a22fb1 [file] [log] [blame]
/*
* Copyright 2022 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;
};
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_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;
}
/* 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;
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;
for (i = 0; i < 16; i++) {
ret = sdhc_spi_rx(config->spi_dev, dev_data->spi_cfg,
response, 1);
if (ret < 0) {
return ret;
}
if (*response != 0xff) {
break;
}
}
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, 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 {
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)) {
/* CMD12 is required after multiple read, or
* to retry failed transfer
*/
sdhc_spi_send_cmd(dev,
(struct sdhc_command *)&stop_cmd,
false);
}
} while ((ret != 0) && (retries-- > 0));
}
if (ret) {
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 = 1000; /* SPI always needs 1ms power delay */
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;
if (!device_is_ready(cfg->spi_dev)) {
return -ENODEV;
}
data->power_mode = SDHC_POWER_OFF;
data->spi_cfg = &data->cfg_a;
data->spi_cfg->frequency = 0;
return 0;
}
static 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,
};
#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), \
}; \
\
IF_ENABLED(DT_INST_SPI_DEV_HAS_CS_GPIOS(n), \
(struct spi_cs_control sdhc_spi_cs_##n = { \
.gpio_dev = DEVICE_DT_GET(DT_INST_SPI_DEV_CS_GPIOS_CTLR(n)), \
.gpio_pin = DT_INST_SPI_DEV_CS_GPIOS_PIN(n), \
.gpio_dt_flags = DT_INST_SPI_DEV_CS_GPIOS_FLAGS(n), \
};)) \
struct sdhc_spi_data sdhc_spi_data_##n = { \
.cfg_a = { \
.operation = (SPI_LOCK_ON | \
SPI_HOLD_ON_CS | \
SPI_WORD_SET(8)), \
COND_CODE_1(DT_INST_SPI_DEV_HAS_CS_GPIOS(n), \
(.cs = &sdhc_spi_cs_##n), \
(.cs = NULL)) \
}, \
}; \
\
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)