sdhc: rename disk_access_sdhc.c

The name disk_access_sdhc.c is ambiguous,

actually this driver depends on SPI,

rename this file.

In addition, move the generic sdhc stuff from C file

to head file for other sdhc drivers to use.

1) disk_access_sdhc.c->disk_access_spi_sdhc.c.

2) create .h and move sdhc specifications from .c to .h.

Signed-off-by: Jun Yang <jun.yang@nxp.com>
This commit is contained in:
Jun Yang 2019-07-03 21:01:36 -07:00 committed by Maureen Helm
commit c7e625f2b3
8 changed files with 1603 additions and 1037 deletions

View file

@ -326,6 +326,8 @@
/subsys/bluetooth/ @joerchan @jhedberg @Vudentz
/subsys/bluetooth/controller/ @carlescufi @cvinayak @thoh-ot
/subsys/debug/ @nashif
/subsys/disk/disk_access_spi_sdhc.c @JunYangNXP
/subsys/disk/disk_access_sdhc.h @JunYangNXP
/subsys/fs/ @nashif
/subsys/fs/fcb/ @nvlsianpu
/subsys/fs/fuse_fs_access.c @vanwinkeljan

View file

@ -50,6 +50,9 @@ struct disk_info {
sys_dnode_t node;
char *name;
const struct disk_operations *ops;
/* Disk device associated to this disk.
*/
struct device *dev;
};
struct disk_operations {

View file

@ -2,8 +2,9 @@ CONFIG_SPI=y
CONFIG_SPI_1=y
CONFIG_DISK_ACCESS=y
CONFIG_DISK_ACCESS_SDHC=y
CONFIG_DISK_ACCESS_SPI_SDHC=y
CONFIG_DISK_SDHC_VOLUME_NAME="SD"
CONFIG_LOG=y
CONFIG_FILE_SYSTEM=y
CONFIG_FAT_FILESYSTEM_ELM=y
CONFIG_PRINTK=y
CONFIG_PRINTK=y

View file

@ -3,4 +3,4 @@
zephyr_sources_ifdef(CONFIG_DISK_ACCESS disk_access.c)
zephyr_sources_ifdef(CONFIG_DISK_ACCESS_FLASH disk_access_flash.c)
zephyr_sources_ifdef(CONFIG_DISK_ACCESS_RAM disk_access_ram.c)
zephyr_sources_ifdef(CONFIG_DISK_ACCESS_SDHC disk_access_sdhc.c)
zephyr_sources_ifdef(CONFIG_DISK_ACCESS_SPI_SDHC disk_access_spi_sdhc.c)

View file

@ -94,9 +94,16 @@ config DISK_VOLUME_SIZE
endif # DISK_ACCESS_FLASH
config DISK_ACCESS_SDHC
bool "SDHC card over SPI"
select SPI
bool "SDHC card access"
select FLASH
help
File system on a SDHC card.
if DISK_ACCESS_SDHC
config DISK_ACCESS_SPI_SDHC
bool "SDHC access over SPI"
depends on SPI
help
File system on a SDHC card accessed over SPI.
@ -107,4 +114,5 @@ config DISK_SDHC_VOLUME_NAME
help
Disk name as per file system naming guidelines.
endif # DISK_ACCESS_SDHC
endif # DISK_ACCESS

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,746 @@
/* disc_access_sdhc.h - header SDHC*/
/*
* Copyright (c) 2019 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DISK_ACCESS_SDHC_H_
#define ZEPHYR_INCLUDE_DISK_ACCESS_SDHC_H_
#include <spi.h>
#define SDMMC_CLOCK_400KHZ (400000U)
#define SD_CLOCK_25MHZ (25000000U)
#define SD_CLOCK_50MHZ (50000000U)
#define SD_CLOCK_100MHZ (100000000U)
#define SD_CLOCK_208MHZ (208000000U)
#define MMC_CLOCK_26MHZ (26000000U)
#define MMC_CLOCK_52MHZ (52000000U)
#define MMC_CLOCK_DDR52 (52000000U)
#define MMC_CLOCK_HS200 (200000000U)
#define MMC_CLOCK_HS400 (400000000U)
/* Command IDs */
#define SDHC_GO_IDLE_STATE 0
#define SDHC_ALL_SEND_CID 2
#define SDHC_SEND_RELATIVE_ADDR 3
#define SDHC_SWITCH 6
#define SDHC_SELECT_CARD 7
#define SDHC_SEND_IF_COND 8
#define SDHC_SEND_CSD 9
#define SDHC_SEND_CID 10
#define SDHC_VOL_SWITCH 11
#define SDHC_STOP_TRANSMISSION 12
#define SDHC_SEND_STATUS 13
#define SDHC_GO_INACTIVE_STATE 15
#define SDHC_SET_BLOCK_SIZE 16
#define SDHC_READ_SINGLE_BLOCK 17
#define SDHC_READ_MULTIPLE_BLOCK 18
#define SDHC_SEND_TUNING_BLOCK 19
#define SDHC_SET_BLOCK_COUNT 23
#define SDHC_WRITE_BLOCK 24
#define SDHC_WRITE_MULTIPLE_BLOCK 25
#define SDHC_ERASE_BLOCK_START 32
#define SDHC_ERASE_BLOCK_END 33
#define SDHC_ERASE_BLOCK_OPERATION 38
#define SDHC_APP_CMD 55
#define SDHC_READ_OCR 58
#define SDHC_CRC_ON_OFF 59
enum sdhc_app_ext_cmd {
SDHC_APP_SET_BUS_WIDTH = 6,
SDHC_APP_SEND_STATUS = 13,
SDHC_APP_SEND_NUM_WRITTEN_BLK = 22,
SDHC_APP_SET_WRITE_BLK_ERASE_CNT = 23,
SDHC_APP_SEND_OP_COND = 41,
SDHC_APP_CLEAR_CARD_DETECT = 42,
SDHC_APP_SEND_SCR = 51
};
#define SDHC_SEND_OP_COND SDHC_APP_SEND_OP_COND
/* R1 response status */
#define SDHC_R1_IDLE 0x01
#define SDHC_R1_ERASE_RESET 0x02
#define SDHC_R1_ILLEGAL_COMMAND 0x04
#define SDHC_R1_COM_CRC 0x08
#define SDHC_R1_ERASE_SEQ 0x10
#define SDHC_R1_ADDRESS 0x20
#define SDHC_R1_PARAMETER 0x40
#define SDHC_CMD_SIZE 6
#define SDHC_CMD_BODY_SIZE (SDHC_CMD_SIZE - 1)
#define SDHC_CRC16_SIZE 2
/* Command flags */
#define SDHC_START 0x80
#define SDHC_TX 0x40
/* Fields in various card registers */
#define SDHC_HCS (1 << 30)
#define SDHC_CCS (1 << 30)
#define SDHC_VHS_MASK (0x0F << 8)
#define SDHC_VHS_3V3 (1 << 8)
#define SDHC_CHECK 0xAA
#define SDHC_CSD_SIZE 16
#define SDHC_CSD_V2 1
/* Data block tokens */
#define SDHC_TOKEN_SINGLE 0xFE
#define SDHC_TOKEN_MULTI_WRITE 0xFC
#define SDHC_TOKEN_STOP_TRAN 0xFD
/* Data block responses */
#define SDHC_RESPONSE_ACCEPTED 0x05
#define SDHC_RESPONSE_CRC_ERR 0x0B
#define SDHC_RESPONSE_WRITE_ERR 0x0E
#define SDHC_MIN_TRIES 20
#define SDHC_RETRY_DELAY K_MSEC(20)
/* Time to wait for the card to initialise */
#define SDHC_INIT_TIMEOUT K_MSEC(5000)
/* Time to wait for the card to respond or come ready */
#define SDHC_READY_TIMEOUT K_MSEC(500)
enum sdhc_rsp_type {
SDHC_RSP_TYPE_NONE = 0U,
SDHC_RSP_TYPE_R1 = 1U,
SDHC_RSP_TYPE_R1b = 2U,
SDHC_RSP_TYPE_R2 = 3U,
SDHC_RSP_TYPE_R3 = 4U,
SDHC_RSP_TYPE_R4 = 5U,
SDHC_RSP_TYPE_R5 = 6U,
SDHC_RSP_TYPE_R5b = 7U,
SDHC_RSP_TYPE_R6 = 8U,
SDHC_RSP_TYPE_R7 = 9U,
};
enum sdhc_bus_width {
SDHC_BUS_WIDTH1BIT = 0U,
SDHC_BUS_WIDTH4BIT = 1U,
};
enum sdhc_flag {
SDHC_HIGH_CAPACITY_FLAG = (1U << 1U),
SDHC_4BITS_WIDTH = (1U << 2U),
SDHC_SDHC_FLAG = (1U << 3U),
SDHC_SDXC_FLAG = (1U << 4U),
SDHC_1800MV_FLAG = (1U << 5U),
SDHC_CMD23_FLAG = (1U << 6U),
SDHC_SPEED_CLASS_CONTROL_FLAG = (1U << 7U),
};
enum sdhc_r1_error_flag {
SDHC_R1OUTOF_RANGE_ERR = (1U << 31U),
SDHC_R1ADDRESS_ERR = (1U << 30U),
SDHC_R1BLK_LEN_ERR = (1U << 29U),
SDHC_R1ERASE_SEQ_ERR = (1U << 28U),
SDHC_R1ERASE_PARAMETER_ERR = (1U << 27U),
SDHC_R1WRITE_PROTECTION_ERR = (1U << 26U),
SDHC_R1CARD_LOCKED_ERR = (1U << 25U),
SDHC_R1LOCK_UNLOCK_ERR = (1U << 24U),
SDHC_R1CMD_CRC_ERR = (1U << 23U),
SDHC_R1ILLEGAL_CMD_ERR = (1U << 22U),
SDHC_R1ECC_ERR = (1U << 21U),
SDHC_R1CARD_CONTROL_ERR = (1U << 20U),
SDHC_R1ERR = (1U << 19U),
SDHC_R1CID_CSD_OVERWRITE_ERR = (1U << 16U),
SDHC_R1WRITE_PROTECTION_ERASE_SKIP = (1U << 15U),
SDHC_R1CARD_ECC_DISABLED = (1U << 14U),
SDHC_R1ERASE_RESET = (1U << 13U),
SDHC_R1READY_FOR_DATA = (1U << 8U),
SDHC_R1SWITCH_ERR = (1U << 7U),
SDHC_R1APP_CMD = (1U << 5U),
SDHC_R1AUTH_SEQ_ERR = (1U << 3U),
SDHC_R1ERR_All_FLAG =
(SDHC_R1OUTOF_RANGE_ERR |
SDHC_R1ADDRESS_ERR |
SDHC_R1BLK_LEN_ERR |
SDHC_R1ERASE_SEQ_ERR |
SDHC_R1ERASE_PARAMETER_ERR |
SDHC_R1WRITE_PROTECTION_ERR |
SDHC_R1CARD_LOCKED_ERR |
SDHC_R1LOCK_UNLOCK_ERR |
SDHC_R1CMD_CRC_ERR |
SDHC_R1ILLEGAL_CMD_ERR |
SDHC_R1ECC_ERR |
SDHC_R1CARD_CONTROL_ERR |
SDHC_R1ERR |
SDHC_R1CID_CSD_OVERWRITE_ERR |
SDHC_R1AUTH_SEQ_ERR),
SDHC_R1ERR_NONE = 0,
};
#define SD_R1_CURRENT_STATE(x) (((x)&0x00001E00U) >> 9U)
enum sd_r1_current_state {
SDMMC_R1_IDLE = 0U,
SDMMC_R1_READY = 1U,
SDMMC_R1_IDENTIFY = 2U,
SDMMC_R1_STANDBY = 3U,
SDMMC_R1_TRANSFER = 4U,
SDMMC_R1_SEND_DATA = 5U,
SDMMC_R1_RECIVE_DATA = 6U,
SDMMC_R1_PROGRAM = 7U,
SDMMC_R1_DISCONNECT = 8U,
};
enum sd_ocr_flag {
SD_OCR_PWR_BUSY_FLAG = (1U << 31U),
/*!< Power up busy status */
SD_OCR_HOST_CAP_FLAG = (1U << 30U),
/*!< Card capacity status */
SD_OCR_CARD_CAP_FLAG = SD_OCR_HOST_CAP_FLAG,
/*!< Card capacity status */
SD_OCR_SWITCH_18_REQ_FLAG = (1U << 24U),
/*!< Switch to 1.8V request */
SD_OCR_SWITCH_18_ACCEPT_FLAG = SD_OCR_SWITCH_18_REQ_FLAG,
/*!< Switch to 1.8V accepted */
SD_OCR_VDD27_28FLAG = (1U << 15U),
/*!< VDD 2.7-2.8 */
SD_OCR_VDD28_29FLAG = (1U << 16U),
/*!< VDD 2.8-2.9 */
SD_OCR_VDD29_30FLAG = (1U << 17U),
/*!< VDD 2.9-3.0 */
SD_OCR_VDD30_31FLAG = (1U << 18U),
/*!< VDD 2.9-3.0 */
SD_OCR_VDD31_32FLAG = (1U << 19U),
/*!< VDD 3.0-3.1 */
SD_OCR_VDD32_33FLAG = (1U << 20U),
/*!< VDD 3.1-3.2 */
SD_OCR_VDD33_34FLAG = (1U << 21U),
/*!< VDD 3.2-3.3 */
SD_OCR_VDD34_35FLAG = (1U << 22U),
/*!< VDD 3.3-3.4 */
SD_OCR_VDD35_36FLAG = (1U << 23U),
/*!< VDD 3.4-3.5 */
};
#define SD_PRODUCT_NAME_BYTES (5U)
struct sd_cid {
u8_t manufacturer;
/*!< Manufacturer ID [127:120] */
u16_t application;
/*!< OEM/Application ID [119:104] */
u8_t name[SD_PRODUCT_NAME_BYTES];
/*!< Product name [103:64] */
u8_t version;
/*!< Product revision [63:56] */
u32_t ser_num;
/*!< Product serial number [55:24] */
u16_t date;
/*!< Manufacturing date [19:8] */
};
struct sd_csd {
u8_t csd_structure;
/*!< CSD structure [127:126] */
u8_t read_time1;
/*!< Data read access-time-1 [119:112] */
u8_t read_time2;
/*!< Data read access-time-2 in clock cycles (NSAC*100) [111:104] */
u8_t xfer_rate;
/*!< Maximum data transfer rate [103:96] */
u16_t cmd_class;
/*!< Card command classes [95:84] */
u8_t read_blk_len;
/*!< Maximum read data block length [83:80] */
u16_t flags;
/*!< Flags in _sd_csd_flag */
u32_t device_size;
/*!< Device size [73:62] */
u8_t read_current_min;
/*!< Maximum read current at VDD min [61:59] */
u8_t read_current_max;
/*!< Maximum read current at VDD max [58:56] */
u8_t write_current_min;
/*!< Maximum write current at VDD min [55:53] */
u8_t write_current_max;
/*!< Maximum write current at VDD max [52:50] */
u8_t dev_size_mul;
/*!< Device size multiplier [49:47] */
u8_t erase_size;
/*!< Erase sector size [45:39] */
u8_t write_prtect_size;
/*!< Write protect group size [38:32] */
u8_t write_speed_factor;
/*!< Write speed factor [28:26] */
u8_t write_blk_len;
/*!< Maximum write data block length [25:22] */
u8_t file_fmt;
/*!< File format [11:10] */
};
struct sd_scr {
u8_t scr_structure;
/*!< SCR Structure [63:60] */
u8_t sd_spec;
/*!< SD memory card specification version [59:56] */
u16_t flags;
/*!< SCR flags in _sd_scr_flag */
u8_t sd_sec;
/*!< Security specification supported [54:52] */
u8_t sd_width;
/*!< Data bus widths supported [51:48] */
u8_t sd_ext_sec;
/*!< Extended security support [46:43] */
u8_t cmd_support;
/*!< Command support bits [33:32] 33-support CMD23, 32-support cmd20*/
u32_t rsvd;
/*!< reserved for manufacturer usage [31:0] */
};
enum sd_timing_mode {
SD_TIMING_SDR12_DFT_MODE = 0U,
/*!< Identification mode & SDR12 */
SD_TIMING_SDR25_HIGH_SPEED_MODE = 1U,
/*!< High speed mode & SDR25 */
SD_TIMING_SDR50_MODE = 2U,
/*!< SDR50 mode*/
SD_TIMING_SDR104_MODE = 3U,
/*!< SDR104 mode */
SD_TIMING_DDR50_MODE = 4U,
/*!< DDR50 mode */
};
/*! @brief SD card current limit */
enum sd_max_current {
SD_MAX_CURRENT_200MA = 0U,
/*!< default current limit */
SD_MAX_CURRENT_400MA = 1U,
/*!< current limit to 400MA */
SD_MAX_CURRENT_600MA = 2U,
/*!< current limit to 600MA */
SD_MAX_CURRENT_800MA = 3U,
/*!< current limit to 800MA */
};
enum sd_voltage {
SD_VOL_NONE = 0U,
/*!< indicate current voltage setting is not setting bu suser*/
SD_VOL_3_3_V = 1U,
/*!< card operation voltage around 3.3v */
SD_VOL_3_0_V = 2U,
/*!< card operation voltage around 3.0v */
SD_VOL_1_8_V = 3U,
/*!< card operation voltage around 31.8v */
};
#define SDMMC_DEFAULT_BLOCK_SIZE (512U)
struct sd_data_op {
u32_t start_block;
u32_t block_size;
u32_t block_count;
u32_t *buf;
};
enum sd_switch_arg {
SD_SWITCH_CHECK = 0U,
/*!< SD switch mode 0: check function */
SD_SWITCH_SET = 1U,
/*!< SD switch mode 1: set function */
};
enum sd_group_num {
SD_GRP_TIMING_MODE = 0U,
/*!< access mode group*/
SD_GRP_CMD_SYS_MODE = 1U,
/*!< command system group*/
SD_GRP_DRIVER_STRENGTH_MODE = 2U,
/*!< driver strength group*/
SD_GRP_CURRENT_LIMIT_MODE = 3U,
/*!< current limit group*/
};
enum sd_driver_strength {
SD_DRV_STRENGTH_TYPEB = 0U,
/*!< default driver strength*/
SD_DRV_STRENGTH_TYPEA = 1U,
/*!< driver strength TYPE A */
SD_DRV_STRENGTH_TYPEC = 2U,
/*!< driver strength TYPE C */
SD_DRV_STRENGTH_TYPED = 3U,
/*!< driver strength TYPE D */
};
enum sd_csd_flag {
SD_CSD_READ_BLK_PARTIAL_FLAG = (1U << 0U),
/*!< Partial blocks for read allowed [79:79] */
SD_CSD_WRITE_BLK_MISALIGN_FLAG = (1U << 1U),
/*!< Write block misalignment [78:78] */
SD_CSD_READ_BLK_MISALIGN_FLAG = (1U << 2U),
/*!< Read block misalignment [77:77] */
SD_CSD_DSR_IMPLEMENTED_FLAG = (1U << 3U),
/*!< DSR implemented [76:76] */
SD_CSD_ERASE_BLK_EN_FLAG = (1U << 4U),
/*!< Erase single block enabled [46:46] */
SD_CSD_WRITE_PROTECT_GRP_EN_FLAG = (1U << 5U),
/*!< Write protect group enabled [31:31] */
SD_CSD_WRITE_BLK_PARTIAL_FLAG = (1U << 6U),
/*!< Partial blocks for write allowed [21:21] */
SD_CSD_FILE_FMT_GRP_FLAG = (1U << 7U),
/*!< File format group [15:15] */
SD_CSD_COPY_FLAG = (1U << 8U),
/*!< Copy flag [14:14] */
SD_CSD_PERMANENT_WRITE_PROTECT_FLAG = (1U << 9U),
/*!< Permanent write protection [13:13] */
SD_CSD_TMP_WRITE_PROTECT_FLAG = (1U << 10U),
/*!< Temporary write protection [12:12] */
};
enum sd_scr_flag {
SD_SCR_DATA_STATUS_AFTER_ERASE = (1U << 0U),
/*!< Data status after erases [55:55] */
SD_SCR_SPEC3 = (1U << 1U),
/*!< Specification version 3.00 or higher [47:47]*/
};
enum sd_spec_version {
SD_SPEC_VER1_0 = (1U << 0U),
/*!< SD card version 1.0-1.01 */
SD_SPEC_VER1_1 = (1U << 1U),
/*!< SD card version 1.10 */
SD_SPEC_VER2_0 = (1U << 2U),
/*!< SD card version 2.00 */
SD_SPEC_VER3_0 = (1U << 3U),
/*!< SD card version 3.0 */
};
enum sd_command_class {
SD_CMD_CLASS_BASIC = (1U << 0U),
/*!< Card command class 0 */
SD_CMD_CLASS_BLOCK_READ = (1U << 2U),
/*!< Card command class 2 */
SD_CMD_CLASS_BLOCK_WRITE = (1U << 4U),
/*!< Card command class 4 */
SD_CMD_CLASS_ERASE = (1U << 5U),
/*!< Card command class 5 */
SD_CMD_CLASS_WRITE_PROTECT = (1U << 6U),
/*!< Card command class 6 */
SD_CMD_CLASS_LOCKCARD = (1U << 7U),
/*!< Card command class 7 */
SD_CMD_CLASS_APP_SPECIFIC = (1U << 8U),
/*!< Card command class 8 */
SD_CMD_CLASS_IO_MODE = (1U << 9U),
/*!< Card command class 9 */
SD_CMD_CLASS_SWITCH = (1U << 10U),
/*!< Card command class 10 */
};
struct sdhc_retry {
u32_t end;
s16_t tries;
u16_t sleep;
};
struct sdhc_flag_map {
u8_t mask;
u8_t err;
};
/* The SD protocol requires sending ones while reading but Zephyr
* defaults to writing zeros.
*/
static const u8_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,
};
BUILD_ASSERT(sizeof(sdhc_ones) % SDHC_CSD_SIZE == 0);
BUILD_ASSERT(SDMMC_DEFAULT_BLOCK_SIZE % sizeof(sdhc_ones) == 0);
/* Maps R1 response flags to error codes */
static const struct sdhc_flag_map sdhc_r1_flags[] = {
{SDHC_R1_PARAMETER, EFAULT}, {SDHC_R1_ADDRESS, EFAULT},
{SDHC_R1_ILLEGAL_COMMAND, EINVAL}, {SDHC_R1_COM_CRC, EILSEQ},
{SDHC_R1_ERASE_SEQ, EIO}, {SDHC_R1_ERASE_RESET, EIO},
{SDHC_R1_IDLE, ECONNRESET}, {0, 0},
};
/* Maps disk status flags to error codes */
static const struct sdhc_flag_map sdhc_disk_status_flags[] = {
{DISK_STATUS_UNINIT, ENODEV},
{DISK_STATUS_NOMEDIA, ENOENT},
{DISK_STATUS_WR_PROTECT, EROFS},
{0, 0},
};
/* Maps data block flags to error codes */
static const struct sdhc_flag_map sdhc_data_response_flags[] = {
{SDHC_RESPONSE_WRITE_ERR, EIO},
{SDHC_RESPONSE_CRC_ERR, EILSEQ},
{SDHC_RESPONSE_ACCEPTED, 0},
/* Unrecognised value */
{0, EPROTO},
};
/* Returns true if an error code is retryable at the disk layer */
static inline bool sdhc_is_retryable(int err)
{
switch (err) {
case 0:
return false;
case -EILSEQ:
case -EIO:
case -ETIMEDOUT:
return true;
default:
return false;
}
}
/* Maps a flag based error code into a Zephyr errno */
static inline int sdhc_map_flags(const struct sdhc_flag_map *map, int flags)
{
if (flags < 0) {
return flags;
}
for (; map->mask != 0U; map++) {
if ((flags & map->mask) == map->mask) {
return -map->err;
}
}
return -map->err;
}
/* Converts disk status into an error code */
static inline int sdhc_map_disk_status(int status)
{
return sdhc_map_flags(sdhc_disk_status_flags, status);
}
/* Converts the R1 response flags into an error code */
static inline int sdhc_map_r1_status(int status)
{
return sdhc_map_flags(sdhc_r1_flags, status);
}
/* Converts an eary stage idle mode R1 code into an error code */
static inline int sdhc_map_r1_idle_status(int status)
{
if (status < 0) {
return status;
}
if (status == SDHC_R1_IDLE) {
return 0;
}
return sdhc_map_r1_status(status);
}
/* Converts the data block response flags into an error code */
static inline int sdhc_map_data_status(int status)
{
return sdhc_map_flags(sdhc_data_response_flags, status);
}
/* Initialises a retry helper */
static inline void sdhc_retry_init(struct sdhc_retry *retry, u32_t timeout,
u16_t sleep)
{
retry->end = k_uptime_get_32() + timeout;
retry->tries = 0;
retry->sleep = sleep;
}
/* Called at the end of a retry loop. Returns if the minimum try
* count and timeout has passed. Delays/yields on retry.
*/
static inline bool sdhc_retry_ok(struct sdhc_retry *retry)
{
s32_t remain = retry->end - k_uptime_get_32();
if (retry->tries < SDHC_MIN_TRIES) {
retry->tries++;
if (retry->sleep != 0U) {
k_sleep(retry->sleep);
}
return true;
}
if (remain >= 0) {
if (retry->sleep > 0) {
k_sleep(retry->sleep);
} else {
k_yield();
}
return true;
}
return false;
}
static inline void sdhc_decode_csd(struct sd_csd *csd,
u32_t *raw_csd, u32_t *blk_cout, u32_t *blk_size)
{
u32_t tmp_blk_cout, tmp_blk_size;
csd->csd_structure = (u8_t)((raw_csd[3U] &
0xC0000000U) >> 30U);
csd->read_time1 = (u8_t)((raw_csd[3U] &
0xFF0000U) >> 16U);
csd->read_time2 = (u8_t)((raw_csd[3U] &
0xFF00U) >> 8U);
csd->xfer_rate = (u8_t)(raw_csd[3U] &
0xFFU);
csd->cmd_class = (u16_t)((raw_csd[2U] &
0xFFF00000U) >> 20U);
csd->read_blk_len = (u8_t)((raw_csd[2U] &
0xF0000U) >> 16U);
if (raw_csd[2U] & 0x8000U)
csd->flags |= SD_CSD_READ_BLK_PARTIAL_FLAG;
if (raw_csd[2U] & 0x4000U)
csd->flags |= SD_CSD_READ_BLK_PARTIAL_FLAG;
if (raw_csd[2U] & 0x2000U)
csd->flags |= SD_CSD_READ_BLK_MISALIGN_FLAG;
if (raw_csd[2U] & 0x1000U)
csd->flags |= SD_CSD_DSR_IMPLEMENTED_FLAG;
switch (csd->csd_structure) {
case 0:
csd->device_size = (u32_t)((raw_csd[2U] &
0x3FFU) << 2U);
csd->device_size |= (u32_t)((raw_csd[1U] &
0xC0000000U) >> 30U);
csd->read_current_min = (u8_t)((raw_csd[1U] &
0x38000000U) >> 27U);
csd->read_current_max = (u8_t)((raw_csd[1U] &
0x7000000U) >> 24U);
csd->write_current_min = (u8_t)((raw_csd[1U] &
0xE00000U) >> 20U);
csd->write_current_max = (u8_t)((raw_csd[1U] &
0x1C0000U) >> 18U);
csd->dev_size_mul = (u8_t)((raw_csd[1U] &
0x38000U) >> 15U);
/* Get card total block count and block size. */
tmp_blk_cout = ((csd->device_size + 1U) <<
(csd->dev_size_mul + 2U));
tmp_blk_size = (1U << (csd->read_blk_len));
if (tmp_blk_size != SDMMC_DEFAULT_BLOCK_SIZE) {
tmp_blk_cout = (tmp_blk_cout * tmp_blk_size);
tmp_blk_size = SDMMC_DEFAULT_BLOCK_SIZE;
tmp_blk_cout = (tmp_blk_cout / tmp_blk_size);
}
if (blk_cout)
*blk_cout = tmp_blk_cout;
if (blk_size)
*blk_size = tmp_blk_size;
break;
case 1:
tmp_blk_size = SDMMC_DEFAULT_BLOCK_SIZE;
csd->device_size = (u32_t)((raw_csd[2U] &
0x3FU) << 16U);
csd->device_size |= (u32_t)((raw_csd[1U] &
0xFFFF0000U) >> 16U);
tmp_blk_cout = ((csd->device_size + 1U) * 1024U);
if (blk_cout)
*blk_cout = tmp_blk_cout;
if (blk_size)
*blk_size = tmp_blk_size;
break;
default:
break;
}
if ((u8_t)((raw_csd[1U] & 0x4000U) >> 14U))
csd->flags |= SD_CSD_ERASE_BLK_EN_FLAG;
csd->erase_size = (u8_t)((raw_csd[1U] &
0x3F80U) >> 7U);
csd->write_prtect_size = (u8_t)(raw_csd[1U] &
0x7FU);
csd->write_speed_factor = (u8_t)((raw_csd[0U] &
0x1C000000U) >> 26U);
csd->write_blk_len = (u8_t)((raw_csd[0U] &
0x3C00000U) >> 22U);
if ((u8_t)((raw_csd[0U] & 0x200000U) >> 21U))
csd->flags |= SD_CSD_WRITE_BLK_PARTIAL_FLAG;
if ((u8_t)((raw_csd[0U] & 0x8000U) >> 15U))
csd->flags |= SD_CSD_FILE_FMT_GRP_FLAG;
if ((u8_t)((raw_csd[0U] & 0x4000U) >> 14U))
csd->flags |= SD_CSD_COPY_FLAG;
if ((u8_t)((raw_csd[0U] & 0x2000U) >> 13U))
csd->flags |=
SD_CSD_PERMANENT_WRITE_PROTECT_FLAG;
if ((u8_t)((raw_csd[0U] & 0x1000U) >> 12U))
csd->flags |=
SD_CSD_TMP_WRITE_PROTECT_FLAG;
csd->file_fmt = (u8_t)((raw_csd[0U] & 0xC00U) >> 10U);
}
static inline void sdhc_decode_scr(struct sd_scr *scr,
u32_t *raw_scr, u32_t *version)
{
u32_t tmp_version = 0;
scr->scr_structure = (u8_t)((raw_scr[0U] & 0xF0000000U) >> 28U);
scr->sd_spec = (u8_t)((raw_scr[0U] & 0xF000000U) >> 24U);
if ((u8_t)((raw_scr[0U] & 0x800000U) >> 23U))
scr->flags |= SD_SCR_DATA_STATUS_AFTER_ERASE;
scr->sd_sec = (u8_t)((raw_scr[0U] & 0x700000U) >> 20U);
scr->sd_width = (u8_t)((raw_scr[0U] & 0xF0000U) >> 16U);
if ((u8_t)((raw_scr[0U] & 0x8000U) >> 15U))
scr->flags |= SD_SCR_SPEC3;
scr->sd_ext_sec = (u8_t)((raw_scr[0U] & 0x7800U) >> 10U);
scr->cmd_support = (u8_t)(raw_scr[0U] & 0x3U);
scr->rsvd = raw_scr[1U];
/* Get specification version. */
switch (scr->sd_spec) {
case 0U:
tmp_version = SD_SPEC_VER1_0;
break;
case 1U:
tmp_version = SD_SPEC_VER1_1;
break;
case 2U:
tmp_version = SD_SPEC_VER2_0;
if (scr->flags & SD_SCR_SPEC3) {
tmp_version = SD_SPEC_VER3_0;
}
break;
default:
break;
}
if (version && tmp_version)
*version = tmp_version;
}
static inline void sdhc_decode_cid(struct sd_cid *cid,
u32_t *raw_cid)
{
cid->manufacturer = (u8_t)((raw_cid[3U] & 0xFF000000U) >> 24U);
cid->application = (u16_t)((raw_cid[3U] & 0xFFFF00U) >> 8U);
cid->name[0U] = (u8_t)((raw_cid[3U] & 0xFFU));
cid->name[1U] = (u8_t)((raw_cid[2U] & 0xFF000000U) >> 24U);
cid->name[2U] = (u8_t)((raw_cid[2U] & 0xFF0000U) >> 16U);
cid->name[3U] = (u8_t)((raw_cid[2U] & 0xFF00U) >> 8U);
cid->name[4U] = (u8_t)((raw_cid[2U] & 0xFFU));
cid->version = (u8_t)((raw_cid[1U] & 0xFF000000U) >> 24U);
cid->ser_num = (u32_t)((raw_cid[1U] & 0xFFFFFFU) << 8U);
cid->ser_num |= (u32_t)((raw_cid[0U] & 0xFF000000U) >> 24U);
cid->date = (u16_t)((raw_cid[0U] & 0xFFF00U) >> 8U);
}
#endif /*ZEPHYR_INCLUDE_DISK_ACCESS_SDHC_H_*/

View file

@ -0,0 +1,839 @@
/*
* Copyright (c) 2017 Google LLC.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(sdhc_spi, CONFIG_DISK_LOG_LEVEL);
#include <disk/disk_access.h>
#include <drivers/gpio.h>
#include <sys/byteorder.h>
#include <drivers/spi.h>
#include <sys/crc.h>
#include "disk_access_sdhc.h"
/* Clock speed used during initialisation */
#define SDHC_SPI_INITIAL_SPEED 400000
/* Clock speed used after initialisation */
#define SDHC_SPI_SPEED 4000000
#ifndef DT_INST_0_ZEPHYR_MMC_SPI_SLOT_LABEL
#warning NO SDHC slot specified on board
#else
struct sdhc_spi_data {
struct device *spi;
struct spi_config cfg;
struct device *cs;
u32_t pin;
u32_t sector_count;
u8_t status;
int trace_dir;
};
DEVICE_DECLARE(sdhc_spi_0);
/* Traces card traffic for LOG_LEVEL_DBG */
static int sdhc_spi_trace(struct sdhc_spi_data *data, int dir, int err,
const u8_t *buf, int len)
{
#if LOG_LEVEL >= LOG_LEVEL_DBG
if (err != 0) {
printk("(err=%d)", err);
data->trace_dir = 0;
}
if (dir != data->trace_dir) {
data->trace_dir = dir;
printk("\n");
if (dir == 1) {
printk(">>");
} else if (dir == -1) {
printk("<<");
}
}
for (; len != 0; len--) {
printk(" %x", *buf++);
}
#endif
return err;
}
/* Asserts or deasserts chip select */
static void sdhc_spi_set_cs(struct sdhc_spi_data *data, int value)
{
gpio_pin_write(data->cs, data->pin, value);
}
/* Receives a fixed number of bytes */
static int sdhc_spi_rx_bytes(struct sdhc_spi_data *data, u8_t *buf, int len)
{
struct spi_buf tx_bufs[] = {
{
.buf = (u8_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 sdhc_spi_trace(data, -1,
spi_transceive(data->spi, &data->cfg, &tx, &rx),
buf, len);
}
/* Receives and returns a single byte */
static int sdhc_spi_rx_u8(struct sdhc_spi_data *data)
{
u8_t buf[1];
int err = sdhc_spi_rx_bytes(data, buf, sizeof(buf));
if (err != 0) {
return err;
}
return buf[0];
}
/* Transmits a block of bytes */
static int sdhc_spi_tx(struct sdhc_spi_data *data, const u8_t *buf, int len)
{
struct spi_buf spi_bufs[] = {
{
.buf = (u8_t *)buf,
.len = len
}
};
const struct spi_buf_set tx = {
.buffers = spi_bufs,
.count = 1
};
return sdhc_spi_trace(data, 1,
spi_write(data->spi, &data->cfg, &tx), buf,
len);
}
/* Transmits the command and payload */
static int sdhc_spi_tx_cmd(struct sdhc_spi_data *data, u8_t cmd, u32_t payload)
{
u8_t buf[SDHC_CMD_SIZE];
LOG_DBG("cmd%d payload=%u", cmd, payload);
sdhc_spi_trace(data, 0, 0, NULL, 0);
/* Encode the command */
buf[0] = SDHC_TX | (cmd & ~SDHC_START);
sys_put_be32(payload, &buf[1]);
buf[SDHC_CMD_BODY_SIZE] = crc7_be(0, buf, SDHC_CMD_BODY_SIZE);
return sdhc_spi_tx(data, buf, sizeof(buf));
}
/* Reads until anything but `discard` is received */
static int sdhc_spi_skip(struct sdhc_spi_data *data, int discard)
{
int err;
struct sdhc_retry retry;
sdhc_retry_init(&retry, SDHC_READY_TIMEOUT, 0);
do {
err = sdhc_spi_rx_u8(data);
if (err != discard) {
return err;
}
} while (sdhc_retry_ok(&retry));
LOG_WRN("Timeout while waiting for !%d", discard);
return -ETIMEDOUT;
}
/* Reads until the first byte in a response is received */
static int sdhc_spi_skip_until_start(struct sdhc_spi_data *data)
{
struct sdhc_retry retry;
int status;
sdhc_retry_init(&retry, SDHC_READY_TIMEOUT, 0);
do {
status = sdhc_spi_rx_u8(data);
if (status < 0) {
return status;
}
if ((status & SDHC_START) == 0) {
return status;
}
} while (sdhc_retry_ok(&retry));
return -ETIMEDOUT;
}
/* Reads until the bus goes high */
static int sdhc_spi_skip_until_ready(struct sdhc_spi_data *data)
{
struct sdhc_retry retry;
int status;
sdhc_retry_init(&retry, SDHC_READY_TIMEOUT, 0);
do {
status = sdhc_spi_rx_u8(data);
if (status < 0) {
return status;
}
if (status == 0) {
/* Card is still busy */
continue;
}
if (status == 0xFF) {
return 0;
}
/* Got something else. Some cards release MISO part
* way through the transfer. Read another and see if
* MISO went high.
*/
status = sdhc_spi_rx_u8(data);
if (status < 0) {
return status;
}
if (status == 0xFF) {
return 0;
}
return -EPROTO;
} while (sdhc_retry_ok(&retry));
return -ETIMEDOUT;
}
/* Sends a command and returns the received R1 status code */
static int sdhc_spi_cmd_r1_raw(struct sdhc_spi_data *data,
u8_t cmd, u32_t payload)
{
int err;
err = sdhc_spi_tx_cmd(data, cmd, payload);
if (err != 0) {
return err;
}
err = sdhc_spi_skip_until_start(data);
/* Ensure there's a idle byte between commands */
sdhc_spi_rx_u8(data);
return err;
}
/* Sends a command and returns the mapped error code */
static int sdhc_spi_cmd_r1(struct sdhc_spi_data *data,
u8_t cmd, uint32_t payload)
{
return sdhc_map_r1_status(sdhc_spi_cmd_r1_raw(data, cmd, payload));
}
/* Sends a command in idle mode returns the mapped error code */
static int sdhc_spi_cmd_r1_idle(struct sdhc_spi_data *data, u8_t cmd,
uint32_t payload)
{
return sdhc_map_r1_idle_status(sdhc_spi_cmd_r1_raw(data, cmd, payload));
}
/* Sends a command and returns the received multi-byte R2 status code */
static int sdhc_spi_cmd_r2(struct sdhc_spi_data *data,
u8_t cmd, uint32_t payload)
{
int err;
int r1;
int r2;
err = sdhc_spi_tx_cmd(data, cmd, payload);
if (err != 0) {
return err;
}
r1 = sdhc_map_r1_status(sdhc_spi_skip_until_start(data));
/* Always read the rest of the reply */
r2 = sdhc_spi_rx_u8(data);
/* Ensure there's a idle byte between commands */
sdhc_spi_rx_u8(data);
if (r1 < 0) {
return r1;
}
return r2;
}
/* Sends a command and returns the received multi-byte status code */
static int sdhc_spi_cmd_r37_raw(struct sdhc_spi_data *data,
u8_t cmd, u32_t payload, u32_t *reply)
{
int err;
int status;
u8_t buf[sizeof(*reply)];
err = sdhc_spi_tx_cmd(data, cmd, payload);
if (err != 0) {
return err;
}
status = sdhc_spi_skip_until_start(data);
/* Always read the rest of the reply */
err = sdhc_spi_rx_bytes(data, buf, sizeof(buf));
*reply = sys_get_be32(buf);
/* Ensure there's a idle byte between commands */
sdhc_spi_rx_u8(data);
if (err != 0) {
return err;
}
return status;
}
/* Sends a command in idle mode returns the mapped error code */
static int sdhc_spi_cmd_r7_idle(struct sdhc_spi_data *data,
u8_t cmd, u32_t payload, u32_t *reply)
{
return sdhc_map_r1_idle_status(
sdhc_spi_cmd_r37_raw(data, cmd, payload, reply));
}
/* Sends a command and returns the received multi-byte R3 error code */
static int sdhc_spi_cmd_r3(struct sdhc_spi_data *data,
u8_t cmd, uint32_t payload, u32_t *reply)
{
return sdhc_map_r1_status(
sdhc_spi_cmd_r37_raw(data, cmd, payload, reply));
}
/* Receives a SDHC data block */
static int sdhc_spi_rx_block(struct sdhc_spi_data *data,
u8_t *buf, int len)
{
int err;
int token;
int i;
/* Note the one extra byte to ensure there's an idle byte
* between commands.
*/
u8_t crc[SDHC_CRC16_SIZE + 1];
token = sdhc_spi_skip(data, 0xFF);
if (token < 0) {
return token;
}
if (token != SDHC_TOKEN_SINGLE) {
/* No start token */
return -EIO;
}
/* Read the data in batches */
for (i = 0; i < len; i += sizeof(sdhc_ones)) {
int remain = MIN(sizeof(sdhc_ones), len - i);
struct spi_buf tx_bufs[] = {
{
.buf = (u8_t *)sdhc_ones,
.len = remain
}
};
const struct spi_buf_set tx = {
.buffers = tx_bufs,
.count = 1,
};
struct spi_buf rx_bufs[] = {
{
.buf = &buf[i],
.len = remain
}
};
const struct spi_buf_set rx = {
.buffers = rx_bufs,
.count = 1,
};
err = sdhc_spi_trace(data, -1,
spi_transceive(data->spi, &data->cfg,
&tx, &rx),
&buf[i], remain);
if (err != 0) {
return err;
}
}
err = sdhc_spi_rx_bytes(data, crc, sizeof(crc));
if (err != 0) {
return err;
}
if (sys_get_be16(crc) != crc16_itu_t(0, buf, len)) {
/* Bad CRC */
return -EILSEQ;
}
return 0;
}
/* Transmits a SDHC data block */
static int sdhc_spi_tx_block(struct sdhc_spi_data *data,
u8_t *send, int len)
{
u8_t buf[SDHC_CRC16_SIZE];
int err;
/* Start the block */
buf[0] = SDHC_TOKEN_SINGLE;
err = sdhc_spi_tx(data, buf, 1);
if (err != 0) {
return err;
}
/* Write the payload */
err = sdhc_spi_tx(data, send, len);
if (err != 0) {
return err;
}
/* Build and write the trailing CRC */
sys_put_be16(crc16_itu_t(0, send, len), buf);
err = sdhc_spi_tx(data, buf, sizeof(buf));
if (err != 0) {
return err;
}
return sdhc_map_data_status(sdhc_spi_rx_u8(data));
}
static int sdhc_spi_recover(struct sdhc_spi_data *data)
{
/* TODO(nzmichaelh): implement */
return sdhc_spi_cmd_r1(data, SDHC_SEND_STATUS, 0);
}
/* Attempts to return the card to idle mode */
static int sdhc_spi_go_idle(struct sdhc_spi_data *data)
{
sdhc_spi_set_cs(data, 1);
/* Write the initial >= 74 clocks */
sdhc_spi_tx(data, sdhc_ones, 10);
sdhc_spi_set_cs(data, 0);
return sdhc_spi_cmd_r1_idle(data, SDHC_GO_IDLE_STATE, 0);
}
/* Checks the supported host volatage and basic protocol */
static int sdhc_spi_check_card(struct sdhc_spi_data *data)
{
u32_t cond;
int err;
/* Check that the current voltage is supported */
err = sdhc_spi_cmd_r7_idle(data, SDHC_SEND_IF_COND,
SDHC_VHS_3V3 | SDHC_CHECK, &cond);
if (err != 0) {
return err;
}
if ((cond & 0xFF) != SDHC_CHECK) {
/* Card returned a different check pattern */
return -ENOENT;
}
if ((cond & SDHC_VHS_MASK) != SDHC_VHS_3V3) {
/* Card doesn't support this voltage */
return -ENOTSUP;
}
return 0;
}
/* Detect and initialise the card */
static int sdhc_spi_detect(struct sdhc_spi_data *data)
{
int err;
u32_t ocr;
struct sdhc_retry retry;
u8_t structure;
u32_t csize;
u8_t buf[SDHC_CSD_SIZE];
data->cfg.frequency = SDHC_SPI_INITIAL_SPEED;
data->status = DISK_STATUS_UNINIT;
sdhc_retry_init(&retry, SDHC_INIT_TIMEOUT, SDHC_RETRY_DELAY);
/* Synchronise with the card by sending it to idle */
do {
err = sdhc_spi_go_idle(data);
if (err == 0) {
err = sdhc_spi_check_card(data);
if (err == 0) {
break;
}
}
if (!sdhc_retry_ok(&retry)) {
return -ENOENT;
}
} while (true);
/* Enable CRC mode */
err = sdhc_spi_cmd_r1_idle(data, SDHC_CRC_ON_OFF, 1);
if (err != 0) {
return err;
}
/* Wait for the card to leave idle state */
do {
sdhc_spi_cmd_r1_raw(data, SDHC_APP_CMD, 0);
err = sdhc_spi_cmd_r1(data, SDHC_SEND_OP_COND, SDHC_HCS);
if (err == 0) {
break;
}
} while (sdhc_retry_ok(&retry));
if (err != 0) {
/* Card never exited idle */
return -ETIMEDOUT;
}
/* Read OCR and confirm this is a SDHC card */
err = sdhc_spi_cmd_r3(data, SDHC_READ_OCR, 0, &ocr);
if (err != 0) {
return err;
}
if ((ocr & SDHC_CCS) == 0U) {
/* A 'SDSC' card */
return -ENOTSUP;
}
/* Read the CSD */
err = sdhc_spi_cmd_r1(data, SDHC_SEND_CSD, 0);
if (err != 0) {
return err;
}
err = sdhc_spi_rx_block(data, buf, sizeof(buf));
if (err != 0) {
return err;
}
/* Bits 126..127 are the structure version */
structure = (buf[0] >> 6);
if (structure != SDHC_CSD_V2) {
/* Unsupported CSD format */
return -ENOTSUP;
}
/* Bits 48..69 are the capacity of the card in 512 KiB units, minus 1.
*/
csize = sys_get_be32(&buf[6]) & ((1 << 22) - 1);
if (csize < 4112) {
/* Invalid capacity according to section 5.3.3 */
return -ENOTSUP;
}
data->sector_count = (csize + 1) *
(512 * 1024 / SDMMC_DEFAULT_BLOCK_SIZE);
LOG_INF("Found a ~%u MiB SDHC card.",
data->sector_count / (1024 * 1024 / SDMMC_DEFAULT_BLOCK_SIZE));
/* Read the CID */
err = sdhc_spi_cmd_r1(data, SDHC_SEND_CID, 0);
if (err != 0) {
return err;
}
err = sdhc_spi_rx_block(data, buf, sizeof(buf));
if (err != 0) {
return err;
}
LOG_INF("Manufacturer ID=%d OEM='%c%c' Name='%c%c%c%c%c' "
"Revision=0x%x Serial=0x%x",
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
buf[7], buf[8], sys_get_be32(&buf[9]));
/* Initilisation complete */
data->cfg.frequency = SDHC_SPI_SPEED;
data->status = DISK_STATUS_OK;
return 0;
}
static int sdhc_spi_read(struct sdhc_spi_data *data,
u8_t *buf, u32_t sector, u32_t count)
{
int err;
err = sdhc_map_disk_status(data->status);
if (err != 0) {
return err;
}
sdhc_spi_set_cs(data, 0);
/* Send the start read command */
err = sdhc_spi_cmd_r1(data, SDHC_READ_MULTIPLE_BLOCK, sector);
if (err != 0) {
goto error;
}
/* Read the sectors */
for (; count != 0U; count--) {
err = sdhc_spi_rx_block(data, buf, SDMMC_DEFAULT_BLOCK_SIZE);
if (err != 0) {
goto error;
}
buf += SDMMC_DEFAULT_BLOCK_SIZE;
}
/* Ignore the error as STOP_TRANSMISSION always returns 0x7F */
sdhc_spi_cmd_r1(data, SDHC_STOP_TRANSMISSION, 0);
/* Wait until the card becomes ready */
err = sdhc_spi_skip_until_ready(data);
error:
sdhc_spi_set_cs(data, 1);
return err;
}
static int sdhc_spi_write(struct sdhc_spi_data *data,
const u8_t *buf, u32_t sector, u32_t count)
{
int err;
err = sdhc_map_disk_status(data->status);
if (err != 0) {
return err;
}
sdhc_spi_set_cs(data, 0);
/* Write the blocks one-by-one */
for (; count != 0U; count--) {
err = sdhc_spi_cmd_r1(data, SDHC_WRITE_BLOCK, sector);
if (err < 0) {
goto error;
}
err = sdhc_spi_tx_block(data, (u8_t *)buf,
SDMMC_DEFAULT_BLOCK_SIZE);
if (err != 0) {
goto error;
}
/* Wait for the card to finish programming */
err = sdhc_spi_skip_until_ready(data);
if (err != 0) {
goto error;
}
err = sdhc_spi_cmd_r2(data, SDHC_SEND_STATUS, 0);
if (err != 0) {
goto error;
}
buf += SDMMC_DEFAULT_BLOCK_SIZE;
sector++;
}
err = 0;
error:
sdhc_spi_set_cs(data, 1);
return err;
}
static int disk_spi_sdhc_init(struct device *dev);
static int sdhc_spi_init(struct device *dev)
{
struct sdhc_spi_data *data = dev->driver_data;
data->spi = device_get_binding(DT_INST_0_ZEPHYR_MMC_SPI_SLOT_BUS_NAME);
data->cfg.frequency = SDHC_SPI_INITIAL_SPEED;
data->cfg.operation = SPI_WORD_SET(8) | SPI_HOLD_ON_CS;
data->cfg.slave = DT_INST_0_ZEPHYR_MMC_SPI_SLOT_BASE_ADDRESS;
data->cs = device_get_binding(
DT_INST_0_ZEPHYR_MMC_SPI_SLOT_CS_GPIO_CONTROLLER);
__ASSERT_NO_MSG(data->cs != NULL);
data->pin = DT_INST_0_ZEPHYR_MMC_SPI_SLOT_CS_GPIO_PIN;
disk_spi_sdhc_init(dev);
return gpio_pin_configure(data->cs, data->pin, GPIO_DIR_OUT);
}
static int disk_spi_sdhc_access_status(struct disk_info *disk)
{
struct device *dev = disk->dev;
struct sdhc_spi_data *data = dev->driver_data;
return data->status;
}
static int disk_spi_sdhc_access_read(struct disk_info *disk,
u8_t *buf, u32_t sector, u32_t count)
{
struct device *dev = disk->dev;
struct sdhc_spi_data *data = dev->driver_data;
int err;
LOG_DBG("sector=%u count=%u", sector, count);
err = sdhc_spi_read(data, buf, sector, count);
if (err != 0 && sdhc_is_retryable(err)) {
sdhc_spi_recover(data);
err = sdhc_spi_read(data, buf, sector, count);
}
return err;
}
static int disk_spi_sdhc_access_write(struct disk_info *disk,
const u8_t *buf, u32_t sector, u32_t count)
{
struct device *dev = disk->dev;
struct sdhc_spi_data *data = dev->driver_data;
int err;
LOG_DBG("sector=%u count=%u", sector, count);
err = sdhc_spi_write(data, buf, sector, count);
if (err != 0 && sdhc_is_retryable(err)) {
sdhc_spi_recover(data);
err = sdhc_spi_write(data, buf, sector, count);
}
return err;
}
static int disk_spi_sdhc_access_ioctl(struct disk_info *disk,
u8_t cmd, void *buf)
{
struct device *dev = disk->dev;
struct sdhc_spi_data *data = dev->driver_data;
int err;
err = sdhc_map_disk_status(data->status);
if (err != 0) {
return err;
}
switch (cmd) {
case DISK_IOCTL_CTRL_SYNC:
break;
case DISK_IOCTL_GET_SECTOR_COUNT:
*(u32_t *)buf = data->sector_count;
break;
case DISK_IOCTL_GET_SECTOR_SIZE:
*(u32_t *)buf = SDMMC_DEFAULT_BLOCK_SIZE;
break;
case DISK_IOCTL_GET_ERASE_BLOCK_SZ:
*(u32_t *)buf = SDMMC_DEFAULT_BLOCK_SIZE;
break;
default:
return -EINVAL;
}
return 0;
}
static int disk_spi_sdhc_access_init(struct disk_info *disk)
{
struct device *dev = disk->dev;
struct sdhc_spi_data *data = dev->driver_data;
int err;
if (data->status == DISK_STATUS_OK) {
/* Called twice, don't re-init. */
return 0;
}
err = sdhc_spi_detect(data);
sdhc_spi_set_cs(data, 1);
return err;
}
static const struct disk_operations spi_sdhc_disk_ops = {
.init = disk_spi_sdhc_access_init,
.status = disk_spi_sdhc_access_status,
.read = disk_spi_sdhc_access_read,
.write = disk_spi_sdhc_access_write,
.ioctl = disk_spi_sdhc_access_ioctl,
};
static struct disk_info spi_sdhc_disk = {
.name = CONFIG_DISK_SDHC_VOLUME_NAME,
.ops = &spi_sdhc_disk_ops,
};
static int disk_spi_sdhc_init(struct device *dev)
{
struct sdhc_spi_data *data = dev->driver_data;
data->status = DISK_STATUS_UNINIT;
spi_sdhc_disk.dev = dev;
return disk_access_register(&spi_sdhc_disk);
}
static struct sdhc_spi_data sdhc_spi_data_0;
DEVICE_AND_API_INIT(sdhc_spi_0,
DT_INST_0_ZEPHYR_MMC_SPI_SLOT_LABEL,
sdhc_spi_init, &sdhc_spi_data_0, NULL,
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL);
#endif