disk: move disk and sdmmc controller drivers to drivers/disk

The files disk_access_usdhc.c, disk_access_spi_sdhc.c,
disk_access_stm32_sdmmc.c, disk_access_ram.c and
disk_access_flash.c are actually drivers for block devices and SD/MMC
controllers. This patch moves this drivers to drivers/disk and
reworks the configuration so that the drivers are selected when
the corresponding node is enabled.

Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
This commit is contained in:
Johann Fischer 2021-02-24 00:41:46 +01:00 committed by Carles Cufí
commit 03101e75d8
15 changed files with 175 additions and 151 deletions

View file

@ -0,0 +1,8 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
zephyr_sources_ifdef(CONFIG_DISK_DRIVER_RAM ramdisk.c)
zephyr_sources_ifdef(CONFIG_DISK_DRIVER_FLASH flashdisk.c)
zephyr_sources_ifdef(CONFIG_SDMMC_OVER_SPI sdmmc_spi.c)
zephyr_sources_ifdef(CONFIG_SDMMC_USDHC usdhc.c)
zephyr_sources_ifdef(CONFIG_SDMMC_STM32 sdmmc_stm32.c)

15
drivers/disk/Kconfig Normal file
View file

@ -0,0 +1,15 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
menuconfig DISK_DRIVERS
bool "Disk Drivers"
help
Disk Driver configuration
if DISK_DRIVERS
source "drivers/disk/Kconfig.ram"
source "drivers/disk/Kconfig.flash"
source "drivers/disk/Kconfig.sdmmc"
endif # DISK_DRIVERS

View file

@ -0,0 +1,61 @@
# Copyright (c) 2016 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
config DISK_DRIVER_FLASH
bool "Flash"
select FLASH
help
Flash device is used for the file system.
if DISK_DRIVER_FLASH
config DISK_FLASH_VOLUME_NAME
string "Flash mount point or drive name"
default "NAND"
help
Disk name as per file system naming guidelines.
config DISK_FLASH_DEV_NAME
string "Flash device name to be used as storage backend"
config DISK_FLASH_START
hex "Flash device start address in hex"
help
This is start address of the flash to be used as storage backend.
config DISK_FLASH_MAX_RW_SIZE
int "Flash device max read-write size in decimal"
help
This is the maximum number of bytes that the
flash_write API can accept per invocation.
API.
config DISK_FLASH_ERASE_ALIGNMENT
hex "Flash device erase alignment in hex"
help
This is the start address alignment required by
the flash component.
config DISK_ERASE_BLOCK_SIZE
hex "Flash device erasable block size in hex"
help
This is typically the minimum block size that
is erased at one time in flash storage.
Typically it is equal to the flash memory page size.
config DISK_FLASH_SECTOR_SIZE
int "Flash device sector size"
default 512
help
This is the file system sector size in bytes.
config DISK_VOLUME_SIZE
hex "Flash device volume size in hex"
help
This is the file system volume size in bytes.
module = FLASHDISK
module-str = flashdisk
source "subsys/logging/Kconfig.template.log_config"
endif # DISK_DRIVER_FLASH

29
drivers/disk/Kconfig.ram Normal file
View file

@ -0,0 +1,29 @@
# Copyright (c) 2016 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
config DISK_DRIVER_RAM
bool "RAM Disk"
help
RAM buffer used to emulate storage disk.
This option can be used to test the file
system.
if DISK_DRIVER_RAM
config DISK_RAM_VOLUME_SIZE
int "RAM Disk size in kilobytes"
default 96
help
Size of the RAM Disk.
config DISK_RAM_VOLUME_NAME
string "RAM Disk mount point or drive name"
default "RAM"
help
Disk name as per file system naming guidelines.
module = RAMDISK
module-str = ramdisk
source "subsys/logging/Kconfig.template.log_config"
endif # DISK_DRIVER_RAM

View file

@ -0,0 +1,58 @@
# Copyright (c) 2016 Intel Corporation
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
DT_COMPAT_ZEPHYR_MMC_SPI_SLOT := zephyr,mmc-spi-slot
DT_COMPAT_NXP_USDHC := nxp,imx-usdhc
DT_COMPAT_ST_STM32_SDMMC := st,stm32-sdmmc
config DISK_DRIVER_SDMMC
bool "SDMMC card driver"
help
SDMMC card driver.
if DISK_DRIVER_SDMMC
config SDMMC_INIT_PRIORITY
int "Init priority"
default 90
help
SDMMC controller driver initialization priority.
config SDMMC_VOLUME_NAME
string "SDMMC Disk mount point or drive name"
default "SD" if FAT_FILESYSTEM_ELM
default "SDMMC"
help
Disk name as per file system naming guidelines.
config SDMMC_OVER_SPI
bool "SDHC access over SPI"
select SPI
default $(dt_compat_enabled,$(DT_COMPAT_ZEPHYR_MMC_SPI_SLOT))
help
File system on a SDHC card accessed over SPI.
config SDMMC_USDHC
bool "NXP i.MXRT USDHC driver"
depends on (HAS_MCUX_USDHC1 || HAS_MCUX_USDHC2)
default $(dt_compat_enabled,$(DT_COMPAT_NXP_USDHC))
help
File system on a SDHC card accessed over
NXP USDHC.
config SDMMC_STM32
bool "STM32 SDMMC driver"
depends on HAS_STM32CUBE
select USE_STM32_HAL_SD
select USE_STM32_HAL_SD_EX if SOC_SERIES_STM32L4X
select USE_STM32_LL_SDMMC
default $(dt_compat_enabled,$(DT_COMPAT_ST_STM32_SDMMC))
help
File system on sdmmc accessed through stm32 sdmmc.
module = SDMMC
module-str = sdmmc
source "subsys/logging/Kconfig.template.log_config"
endif # DISK_DRIVER_SDMMC

299
drivers/disk/flashdisk.c Normal file
View file

@ -0,0 +1,299 @@
/*
* Copyright (c) 2016 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/types.h>
#include <sys/__assert.h>
#include <sys/util.h>
#include <disk/disk_access.h>
#include <errno.h>
#include <init.h>
#include <device.h>
#include <drivers/flash.h>
#define SECTOR_SIZE CONFIG_DISK_FLASH_SECTOR_SIZE
static const struct device *flash_dev;
/* flash read-copy-erase-write operation */
static uint8_t __aligned(4) read_copy_buf[CONFIG_DISK_ERASE_BLOCK_SIZE];
static uint8_t *fs_buff = read_copy_buf;
/* calculate number of blocks required for a given size */
#define GET_NUM_BLOCK(total_size, block_size) \
((total_size + block_size - 1) / block_size)
#define GET_SIZE_TO_BOUNDARY(start, block_size) \
(block_size - (start & (block_size - 1)))
static off_t lba_to_address(uint32_t sector_num)
{
off_t flash_addr;
flash_addr = CONFIG_DISK_FLASH_START + sector_num * SECTOR_SIZE;
__ASSERT(flash_addr < (CONFIG_DISK_FLASH_START +
CONFIG_DISK_VOLUME_SIZE), "FS bound error");
return flash_addr;
}
static int disk_flash_access_status(struct disk_info *disk)
{
if (!flash_dev) {
return DISK_STATUS_NOMEDIA;
}
return DISK_STATUS_OK;
}
static int disk_flash_access_init(struct disk_info *disk)
{
if (flash_dev) {
return 0;
}
flash_dev = device_get_binding(CONFIG_DISK_FLASH_DEV_NAME);
if (!flash_dev) {
return -ENODEV;
}
return 0;
}
static int disk_flash_access_read(struct disk_info *disk, uint8_t *buff,
uint32_t start_sector, uint32_t sector_count)
{
off_t fl_addr;
uint32_t remaining;
uint32_t len;
uint32_t num_read;
fl_addr = lba_to_address(start_sector);
remaining = (sector_count * SECTOR_SIZE);
len = CONFIG_DISK_FLASH_MAX_RW_SIZE;
num_read = GET_NUM_BLOCK(remaining, CONFIG_DISK_FLASH_MAX_RW_SIZE);
for (uint32_t i = 0; i < num_read; i++) {
if (remaining < CONFIG_DISK_FLASH_MAX_RW_SIZE) {
len = remaining;
}
if (flash_read(flash_dev, fl_addr, buff, len) != 0) {
return -EIO;
}
fl_addr += len;
buff += len;
remaining -= len;
}
return 0;
}
/* This performs read-copy into an output buffer */
static int read_copy_flash_block(off_t start_addr, uint32_t size,
const void *src_buff,
uint8_t *dest_buff)
{
off_t fl_addr;
uint32_t num_read;
uint32_t offset = 0U;
/* adjust offset if starting address is not erase-aligned address */
if (start_addr & (CONFIG_DISK_FLASH_ERASE_ALIGNMENT - 1)) {
offset = start_addr & (CONFIG_DISK_FLASH_ERASE_ALIGNMENT - 1);
}
/* align starting address to an aligned address for flash erase-write */
fl_addr = ROUND_DOWN(start_addr, CONFIG_DISK_FLASH_ERASE_ALIGNMENT);
num_read = GET_NUM_BLOCK(CONFIG_DISK_ERASE_BLOCK_SIZE,
CONFIG_DISK_FLASH_MAX_RW_SIZE);
/* read one block from flash */
for (uint32_t i = 0; i < num_read; i++) {
int rc;
rc = flash_read(flash_dev,
fl_addr + (CONFIG_DISK_FLASH_MAX_RW_SIZE * i),
dest_buff + (CONFIG_DISK_FLASH_MAX_RW_SIZE * i),
CONFIG_DISK_FLASH_MAX_RW_SIZE);
if (rc != 0) {
return -EIO;
}
}
/* overwrite with user data */
memcpy(dest_buff + offset, src_buff, size);
return 0;
}
/* input size is either less or equal to a block size,
* CONFIG_DISK_ERASE_BLOCK_SIZE.
*/
static int update_flash_block(off_t start_addr, uint32_t size, const void *buff)
{
off_t fl_addr;
uint8_t *src = (uint8_t *)buff;
uint32_t num_write;
/* if size is a partial block, perform read-copy with user data */
if (size < CONFIG_DISK_ERASE_BLOCK_SIZE) {
int rc;
rc = read_copy_flash_block(start_addr, size, buff, fs_buff);
if (rc != 0) {
return -EIO;
}
/* now use the local buffer as the source */
src = (uint8_t *)fs_buff;
}
/* always align starting address for flash write operation */
fl_addr = ROUND_DOWN(start_addr, CONFIG_DISK_FLASH_ERASE_ALIGNMENT);
/* disable write-protection first before erase */
flash_write_protection_set(flash_dev, false);
if (flash_erase(flash_dev, fl_addr, CONFIG_DISK_ERASE_BLOCK_SIZE)
!= 0) {
return -EIO;
}
/* write data to flash */
num_write = GET_NUM_BLOCK(CONFIG_DISK_ERASE_BLOCK_SIZE,
CONFIG_DISK_FLASH_MAX_RW_SIZE);
for (uint32_t i = 0; i < num_write; i++) {
/* flash_write reenabled write-protection so disable it again */
flash_write_protection_set(flash_dev, false);
if (flash_write(flash_dev, fl_addr, src,
CONFIG_DISK_FLASH_MAX_RW_SIZE) != 0) {
return -EIO;
}
fl_addr += CONFIG_DISK_FLASH_MAX_RW_SIZE;
src += CONFIG_DISK_FLASH_MAX_RW_SIZE;
}
return 0;
}
static int disk_flash_access_write(struct disk_info *disk, const uint8_t *buff,
uint32_t start_sector, uint32_t sector_count)
{
off_t fl_addr;
uint32_t remaining;
uint32_t size;
fl_addr = lba_to_address(start_sector);
remaining = (sector_count * SECTOR_SIZE);
/* check if start address is erased-aligned address */
if (fl_addr & (CONFIG_DISK_FLASH_ERASE_ALIGNMENT - 1)) {
off_t block_bnd;
/* not aligned */
/* check if the size goes over flash block boundary */
block_bnd = fl_addr + CONFIG_DISK_ERASE_BLOCK_SIZE;
block_bnd = block_bnd & ~(CONFIG_DISK_ERASE_BLOCK_SIZE - 1);
if ((fl_addr + remaining) < block_bnd) {
/* not over block boundary (a partial block also) */
if (update_flash_block(fl_addr, remaining, buff) != 0) {
return -EIO;
}
return 0;
}
/* write goes over block boundary */
size = GET_SIZE_TO_BOUNDARY(fl_addr,
CONFIG_DISK_ERASE_BLOCK_SIZE);
/* write first partial block */
if (update_flash_block(fl_addr, size, buff) != 0) {
return -EIO;
}
fl_addr += size;
remaining -= size;
buff += size;
}
/* start is an erase-aligned address */
while (remaining) {
int rc;
if (remaining < CONFIG_DISK_ERASE_BLOCK_SIZE) {
break;
}
rc = update_flash_block(fl_addr, CONFIG_DISK_ERASE_BLOCK_SIZE,
buff);
if (rc != 0) {
return -EIO;
}
fl_addr += CONFIG_DISK_ERASE_BLOCK_SIZE;
remaining -= CONFIG_DISK_ERASE_BLOCK_SIZE;
buff += CONFIG_DISK_ERASE_BLOCK_SIZE;
}
/* remaining partial block */
if (remaining) {
if (update_flash_block(fl_addr, remaining, buff) != 0) {
return -EIO;
}
}
return 0;
}
static int disk_flash_access_ioctl(struct disk_info *disk, uint8_t cmd, void *buff)
{
switch (cmd) {
case DISK_IOCTL_CTRL_SYNC:
return 0;
case DISK_IOCTL_GET_SECTOR_COUNT:
*(uint32_t *)buff = CONFIG_DISK_VOLUME_SIZE / SECTOR_SIZE;
return 0;
case DISK_IOCTL_GET_SECTOR_SIZE:
*(uint32_t *) buff = SECTOR_SIZE;
return 0;
case DISK_IOCTL_GET_ERASE_BLOCK_SZ: /* in sectors */
*(uint32_t *)buff = CONFIG_DISK_ERASE_BLOCK_SIZE / SECTOR_SIZE;
return 0;
default:
break;
}
return -EINVAL;
}
static const struct disk_operations flash_disk_ops = {
.init = disk_flash_access_init,
.status = disk_flash_access_status,
.read = disk_flash_access_read,
.write = disk_flash_access_write,
.ioctl = disk_flash_access_ioctl,
};
static struct disk_info flash_disk = {
.name = CONFIG_DISK_FLASH_VOLUME_NAME,
.ops = &flash_disk_ops,
};
static int disk_flash_init(const struct device *dev)
{
ARG_UNUSED(dev);
return disk_access_register(&flash_disk);
}
SYS_INIT(disk_flash_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

95
drivers/disk/ramdisk.c Normal file
View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2016 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/types.h>
#include <sys/__assert.h>
#include <disk/disk_access.h>
#include <errno.h>
#include <init.h>
#include <device.h>
#define RAMDISK_SECTOR_SIZE 512
#define RAMDISK_VOLUME_SIZE (CONFIG_DISK_RAM_VOLUME_SIZE * 1024)
static uint8_t ramdisk_buf[RAMDISK_VOLUME_SIZE];
static void *lba_to_address(uint32_t lba)
{
__ASSERT(((lba * RAMDISK_SECTOR_SIZE) < RAMDISK_VOLUME_SIZE),
"FS bound error");
return &ramdisk_buf[(lba * RAMDISK_SECTOR_SIZE)];
}
static int disk_ram_access_status(struct disk_info *disk)
{
return DISK_STATUS_OK;
}
static int disk_ram_access_init(struct disk_info *disk)
{
return 0;
}
static int disk_ram_access_read(struct disk_info *disk, uint8_t *buff,
uint32_t sector, uint32_t count)
{
memcpy(buff, lba_to_address(sector), count * RAMDISK_SECTOR_SIZE);
return 0;
}
static int disk_ram_access_write(struct disk_info *disk, const uint8_t *buff,
uint32_t sector, uint32_t count)
{
memcpy(lba_to_address(sector), buff, count * RAMDISK_SECTOR_SIZE);
return 0;
}
static int disk_ram_access_ioctl(struct disk_info *disk, uint8_t cmd, void *buff)
{
switch (cmd) {
case DISK_IOCTL_CTRL_SYNC:
break;
case DISK_IOCTL_GET_SECTOR_COUNT:
*(uint32_t *)buff = RAMDISK_VOLUME_SIZE / RAMDISK_SECTOR_SIZE;
break;
case DISK_IOCTL_GET_SECTOR_SIZE:
*(uint32_t *)buff = RAMDISK_SECTOR_SIZE;
break;
case DISK_IOCTL_GET_ERASE_BLOCK_SZ:
*(uint32_t *)buff = 1U;
break;
default:
return -EINVAL;
}
return 0;
}
static const struct disk_operations ram_disk_ops = {
.init = disk_ram_access_init,
.status = disk_ram_access_status,
.read = disk_ram_access_read,
.write = disk_ram_access_write,
.ioctl = disk_ram_access_ioctl,
};
static struct disk_info ram_disk = {
.name = CONFIG_DISK_RAM_VOLUME_NAME,
.ops = &ram_disk_ops,
};
static int disk_ram_init(const struct device *dev)
{
ARG_UNUSED(dev);
return disk_access_register(&ram_disk);
}
SYS_INIT(disk_ram_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

748
drivers/disk/sdmmc_sdhc.h Normal file
View file

@ -0,0 +1,748 @@
/* 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 <drivers/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_BUSY (1 << 31)
#define SDHC_VHS_MASK (0x0F << 8)
#define SDHC_VHS_3V3 (1 << 8)
#define SDHC_CHECK 0xAA
#define SDHC_CSD_SIZE 16
#define SDHC_CSD_V1 0
#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 20
/* Time to wait for the card to initialise */
#define SDHC_INIT_TIMEOUT 5000
/* Time to wait for the card to respond or come ready */
#define SDHC_READY_TIMEOUT 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 {
uint8_t manufacturer;
/*!< Manufacturer ID [127:120] */
uint16_t application;
/*!< OEM/Application ID [119:104] */
uint8_t name[SD_PRODUCT_NAME_BYTES];
/*!< Product name [103:64] */
uint8_t version;
/*!< Product revision [63:56] */
uint32_t ser_num;
/*!< Product serial number [55:24] */
uint16_t date;
/*!< Manufacturing date [19:8] */
};
struct sd_csd {
uint8_t csd_structure;
/*!< CSD structure [127:126] */
uint8_t read_time1;
/*!< Data read access-time-1 [119:112] */
uint8_t read_time2;
/*!< Data read access-time-2 in clock cycles (NSAC*100) [111:104] */
uint8_t xfer_rate;
/*!< Maximum data transfer rate [103:96] */
uint16_t cmd_class;
/*!< Card command classes [95:84] */
uint8_t read_blk_len;
/*!< Maximum read data block length [83:80] */
uint16_t flags;
/*!< Flags in _sd_csd_flag */
uint32_t device_size;
/*!< Device size [73:62] */
uint8_t read_current_min;
/*!< Maximum read current at VDD min [61:59] */
uint8_t read_current_max;
/*!< Maximum read current at VDD max [58:56] */
uint8_t write_current_min;
/*!< Maximum write current at VDD min [55:53] */
uint8_t write_current_max;
/*!< Maximum write current at VDD max [52:50] */
uint8_t dev_size_mul;
/*!< Device size multiplier [49:47] */
uint8_t erase_size;
/*!< Erase sector size [45:39] */
uint8_t write_prtect_size;
/*!< Write protect group size [38:32] */
uint8_t write_speed_factor;
/*!< Write speed factor [28:26] */
uint8_t write_blk_len;
/*!< Maximum write data block length [25:22] */
uint8_t file_fmt;
/*!< File format [11:10] */
};
struct sd_scr {
uint8_t scr_structure;
/*!< SCR Structure [63:60] */
uint8_t sd_spec;
/*!< SD memory card specification version [59:56] */
uint16_t flags;
/*!< SCR flags in _sd_scr_flag */
uint8_t sd_sec;
/*!< Security specification supported [54:52] */
uint8_t sd_width;
/*!< Data bus widths supported [51:48] */
uint8_t sd_ext_sec;
/*!< Extended security support [46:43] */
uint8_t cmd_support;
/*!< Command support bits [33:32] 33-support CMD23, 32-support cmd20*/
uint32_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 {
uint32_t start_block;
uint32_t block_size;
uint32_t block_count;
uint32_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 {
uint32_t end;
int16_t tries;
uint16_t sleep;
};
struct sdhc_flag_map {
uint8_t mask;
uint8_t err;
};
/* The SD protocol requires sending ones while reading but Zephyr
* defaults to writing zeros.
*/
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,
};
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, uint32_t timeout,
uint16_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)
{
int32_t remain = retry->end - k_uptime_get_32();
if (retry->tries < SDHC_MIN_TRIES) {
retry->tries++;
if (retry->sleep != 0U) {
k_msleep(retry->sleep);
}
return true;
}
if (remain >= 0) {
if (retry->sleep > 0) {
k_msleep(retry->sleep);
} else {
k_yield();
}
return true;
}
return false;
}
static inline void sdhc_decode_csd(struct sd_csd *csd,
uint32_t *raw_csd, uint32_t *blk_cout, uint32_t *blk_size)
{
uint32_t tmp_blk_cout, tmp_blk_size;
csd->csd_structure = (uint8_t)((raw_csd[3U] &
0xC0000000U) >> 30U);
csd->read_time1 = (uint8_t)((raw_csd[3U] &
0xFF0000U) >> 16U);
csd->read_time2 = (uint8_t)((raw_csd[3U] &
0xFF00U) >> 8U);
csd->xfer_rate = (uint8_t)(raw_csd[3U] &
0xFFU);
csd->cmd_class = (uint16_t)((raw_csd[2U] &
0xFFF00000U) >> 20U);
csd->read_blk_len = (uint8_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 = (uint32_t)((raw_csd[2U] &
0x3FFU) << 2U);
csd->device_size |= (uint32_t)((raw_csd[1U] &
0xC0000000U) >> 30U);
csd->read_current_min = (uint8_t)((raw_csd[1U] &
0x38000000U) >> 27U);
csd->read_current_max = (uint8_t)((raw_csd[1U] &
0x7000000U) >> 24U);
csd->write_current_min = (uint8_t)((raw_csd[1U] &
0xE00000U) >> 20U);
csd->write_current_max = (uint8_t)((raw_csd[1U] &
0x1C0000U) >> 18U);
csd->dev_size_mul = (uint8_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 = (uint32_t)((raw_csd[2U] &
0x3FU) << 16U);
csd->device_size |= (uint32_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 ((uint8_t)((raw_csd[1U] & 0x4000U) >> 14U))
csd->flags |= SD_CSD_ERASE_BLK_EN_FLAG;
csd->erase_size = (uint8_t)((raw_csd[1U] &
0x3F80U) >> 7U);
csd->write_prtect_size = (uint8_t)(raw_csd[1U] &
0x7FU);
csd->write_speed_factor = (uint8_t)((raw_csd[0U] &
0x1C000000U) >> 26U);
csd->write_blk_len = (uint8_t)((raw_csd[0U] &
0x3C00000U) >> 22U);
if ((uint8_t)((raw_csd[0U] & 0x200000U) >> 21U))
csd->flags |= SD_CSD_WRITE_BLK_PARTIAL_FLAG;
if ((uint8_t)((raw_csd[0U] & 0x8000U) >> 15U))
csd->flags |= SD_CSD_FILE_FMT_GRP_FLAG;
if ((uint8_t)((raw_csd[0U] & 0x4000U) >> 14U))
csd->flags |= SD_CSD_COPY_FLAG;
if ((uint8_t)((raw_csd[0U] & 0x2000U) >> 13U))
csd->flags |=
SD_CSD_PERMANENT_WRITE_PROTECT_FLAG;
if ((uint8_t)((raw_csd[0U] & 0x1000U) >> 12U))
csd->flags |=
SD_CSD_TMP_WRITE_PROTECT_FLAG;
csd->file_fmt = (uint8_t)((raw_csd[0U] & 0xC00U) >> 10U);
}
static inline void sdhc_decode_scr(struct sd_scr *scr,
uint32_t *raw_scr, uint32_t *version)
{
uint32_t tmp_version = 0;
scr->scr_structure = (uint8_t)((raw_scr[0U] & 0xF0000000U) >> 28U);
scr->sd_spec = (uint8_t)((raw_scr[0U] & 0xF000000U) >> 24U);
if ((uint8_t)((raw_scr[0U] & 0x800000U) >> 23U))
scr->flags |= SD_SCR_DATA_STATUS_AFTER_ERASE;
scr->sd_sec = (uint8_t)((raw_scr[0U] & 0x700000U) >> 20U);
scr->sd_width = (uint8_t)((raw_scr[0U] & 0xF0000U) >> 16U);
if ((uint8_t)((raw_scr[0U] & 0x8000U) >> 15U))
scr->flags |= SD_SCR_SPEC3;
scr->sd_ext_sec = (uint8_t)((raw_scr[0U] & 0x7800U) >> 10U);
scr->cmd_support = (uint8_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,
uint32_t *raw_cid)
{
cid->manufacturer = (uint8_t)((raw_cid[3U] & 0xFF000000U) >> 24U);
cid->application = (uint16_t)((raw_cid[3U] & 0xFFFF00U) >> 8U);
cid->name[0U] = (uint8_t)((raw_cid[3U] & 0xFFU));
cid->name[1U] = (uint8_t)((raw_cid[2U] & 0xFF000000U) >> 24U);
cid->name[2U] = (uint8_t)((raw_cid[2U] & 0xFF0000U) >> 16U);
cid->name[3U] = (uint8_t)((raw_cid[2U] & 0xFF00U) >> 8U);
cid->name[4U] = (uint8_t)((raw_cid[2U] & 0xFFU));
cid->version = (uint8_t)((raw_cid[1U] & 0xFF000000U) >> 24U);
cid->ser_num = (uint32_t)((raw_cid[1U] & 0xFFFFFFU) << 8U);
cid->ser_num |= (uint32_t)((raw_cid[0U] & 0xFF000000U) >> 24U);
cid->date = (uint16_t)((raw_cid[0U] & 0xFFF00U) >> 8U);
}
#endif /*ZEPHYR_INCLUDE_DISK_ACCESS_SDHC_H_*/

985
drivers/disk/sdmmc_spi.c Normal file
View file

@ -0,0 +1,985 @@
/*
* Copyright (c) 2017 Google LLC.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT zephyr_mmc_spi_slot
#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
#define SPI_SDHC_NODE DT_DRV_INST(0)
#if !DT_NODE_HAS_STATUS(SPI_SDHC_NODE, okay)
#warning NO SDHC slot specified on board
#else
struct sdhc_spi_data {
const struct device *spi;
struct spi_config cfg;
#if DT_SPI_DEV_HAS_CS_GPIOS(SPI_SDHC_NODE)
struct spi_cs_control cs;
#endif
bool high_capacity;
uint32_t sector_count;
uint8_t status;
#if LOG_LEVEL >= LOG_LEVEL_DBG
int trace_dir;
#endif
};
/* Traces card traffic for LOG_LEVEL_DBG */
static int sdhc_spi_trace(struct sdhc_spi_data *data, int dir, int err,
const uint8_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;
}
/* Receives a fixed number of bytes */
static int sdhc_spi_rx_bytes(struct sdhc_spi_data *data, 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 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)
{
uint8_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 uint8_t *buf, int len)
{
struct spi_buf spi_bufs[] = {
{
.buf = (uint8_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, uint8_t cmd, uint32_t payload)
{
uint8_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]);
/* Add CRC and set LSB as 'end bit' */
buf[SDHC_CMD_BODY_SIZE] = crc7_be(0, buf, SDHC_CMD_BODY_SIZE) | 0x01;
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,
uint8_t cmd, uint32_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 */
if (cmd != SDHC_SEND_CSD && cmd != SDHC_SEND_CID &&
cmd != SDHC_READ_SINGLE_BLOCK && cmd != SDHC_READ_MULTIPLE_BLOCK &&
cmd != SDHC_WRITE_BLOCK && cmd != SDHC_WRITE_MULTIPLE_BLOCK) {
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,
uint8_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, uint8_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,
uint8_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,
uint8_t cmd, uint32_t payload, uint32_t *reply)
{
int err;
int status;
uint8_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,
uint8_t cmd, uint32_t payload, uint32_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,
uint8_t cmd, uint32_t payload, uint32_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,
uint8_t *buf, int len)
{
int err;
int token;
int i;
/* Note the one extra byte to ensure there's an idle byte
* between commands.
*/
uint8_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 = (uint8_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,
uint8_t *send, int len)
{
uint8_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)
{
/* Write the initial >= 74 clocks */
sdhc_spi_tx(data, sdhc_ones, 10);
spi_release(data->spi, &data->cfg);
return sdhc_spi_cmd_r1_idle(data, SDHC_GO_IDLE_STATE, 0);
}
/* Checks the supported host voltage and basic protocol of a SDHC card */
static int sdhc_spi_check_interface(struct sdhc_spi_data *data)
{
uint32_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;
uint32_t ocr;
struct sdhc_retry retry;
uint8_t structure;
uint8_t readbllen;
uint32_t csize;
uint8_t csizemult;
uint8_t buf[SDHC_CSD_SIZE];
bool is_v2;
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_interface(data);
is_v2 = (err == 0) ? true : false;
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);
/* Set HCS only if card conforms to specification v2.00 (cf. 4.2.3) */
err = sdhc_spi_cmd_r1(data, SDHC_SEND_OP_COND, is_v2 ? SDHC_HCS : 0);
if (err == 0) {
break;
}
} while (sdhc_retry_ok(&retry));
if (err != 0) {
/* Card never exited idle */
return -ETIMEDOUT;
}
ocr = 0;
if (is_v2) {
do {
/* Read OCR to check if this is a SDSC or SDHC card.
* CCS bit is valid after BUSY bit is set.
*/
err = sdhc_spi_cmd_r3(data, SDHC_READ_OCR, 0, &ocr);
if (err != 0) {
return err;
}
if ((ocr & SDHC_BUSY) != 0U) {
break;
}
} while (sdhc_retry_ok(&retry));
}
if ((ocr & SDHC_CCS) != 0U) {
data->high_capacity = true;
} else {
/* A 'SDSC' card: Set block length to 512 bytes. */
data->high_capacity = false;
err = sdhc_spi_cmd_r1(data, SDHC_SET_BLOCK_SIZE, SDMMC_DEFAULT_BLOCK_SIZE);
if (err != 0) {
return err;
}
}
/* 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);
switch (structure) {
case SDHC_CSD_V1:
/* The maximum read data block length is given by bits 80..83 raised
* to the power of 2. Possible values are 9, 10 and 11 for 512, 1024
* and 2048 bytes, respectively. This driver does not make use of block
* lengths greater than 512 bytes, but forces 512 byte block transfers
* instead.
*/
readbllen = buf[5] & ((1 << 4) - 1);
if ((readbllen < 9) || (readbllen > 11)) {
/* Invalid maximum read data block length (cf. section 5.3.2) */
return -ENOTSUP;
}
/* The capacity of the card is given by bits 62..73 plus 1 multiplied
* by bits 47..49 plus 2 raised to the power of 2 in maximum read data
* blocks.
*/
csize = (sys_get_be32(&buf[6]) >> 14) & ((1 << 12) - 1);
csizemult = (uint8_t) ((sys_get_be16(&buf[9]) >> 7) & ((1 << 3) - 1));
data->sector_count = ((csize + 1) << (csizemult + 2 + readbllen - 9));
break;
case SDHC_CSD_V2:
/* 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 (cf. section 5.3.3) */
return -ENOTSUP;
}
data->sector_count = (csize + 1) *
(512 * 1024 / SDMMC_DEFAULT_BLOCK_SIZE);
break;
default:
/* Unsupported CSD format */
return -ENOTSUP;
}
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,
uint8_t *buf, uint32_t sector, uint32_t count)
{
int err;
uint32_t addr;
err = sdhc_map_disk_status(data->status);
if (err != 0) {
return err;
}
/* Translate sector number to data address.
* SDSC cards use byte addressing, SDHC cards use block addressing.
*/
if (data->high_capacity) {
addr = sector;
} else {
addr = sector * SDMMC_DEFAULT_BLOCK_SIZE;
}
/* Send the start read command */
err = sdhc_spi_cmd_r1(data, SDHC_READ_MULTIPLE_BLOCK, addr);
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:
spi_release(data->spi, &data->cfg);
return err;
}
static int sdhc_spi_write(struct sdhc_spi_data *data,
const uint8_t *buf, uint32_t sector, uint32_t count)
{
int err;
uint32_t addr;
err = sdhc_map_disk_status(data->status);
if (err != 0) {
return err;
}
/* Write the blocks one-by-one */
for (; count != 0U; count--) {
/* Translate sector number to data address.
* SDSC cards use byte addressing, SDHC cards use block addressing.
*/
if (data->high_capacity) {
addr = sector;
} else {
addr = sector * SDMMC_DEFAULT_BLOCK_SIZE;
}
err = sdhc_spi_cmd_r1(data, SDHC_WRITE_BLOCK, addr);
if (err < 0) {
goto error;
}
err = sdhc_spi_tx_block(data, (uint8_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:
spi_release(data->spi, &data->cfg);
return err;
}
/* this function is optimized to write multiple blocks */
static int sdhc_spi_write_multi(struct sdhc_spi_data *data,
const uint8_t *buf, uint32_t sector, uint32_t count)
{
int err;
uint32_t addr;
uint8_t block[SDHC_CRC16_SIZE];
err = sdhc_map_disk_status(data->status);
if (err != 0) {
return err;
}
if (data->high_capacity) {
addr = sector;
} else {
addr = sector * SDMMC_DEFAULT_BLOCK_SIZE;
}
err = sdhc_spi_cmd_r1(data, SDHC_WRITE_MULTIPLE_BLOCK, addr);
if (err < 0) {
goto exit;
}
/* Write the blocks */
for (; count != 0U; count--) {
/* Start the block */
block[0] = SDHC_TOKEN_MULTI_WRITE;
err = sdhc_spi_tx(data, block, 1);
if (err != 0) {
goto exit;
}
/* Write the payload */
err = sdhc_spi_tx(data, buf, SDMMC_DEFAULT_BLOCK_SIZE);
if (err != 0) {
goto exit;
}
/* Build and write the trailing CRC */
sys_put_be16(crc16_itu_t(0, buf, SDMMC_DEFAULT_BLOCK_SIZE),
block);
err = sdhc_spi_tx(data, block, sizeof(block));
if (err != 0) {
goto exit;
}
err = sdhc_map_data_status(sdhc_spi_rx_u8(data));
if (err != 0) {
goto exit;
}
/* Wait for the card to finish programming */
err = sdhc_spi_skip_until_ready(data);
if (err != 0) {
goto exit;
}
buf += SDMMC_DEFAULT_BLOCK_SIZE;
sector++;
}
/* Stop the transmission */
sdhc_spi_tx_cmd(data, SDHC_STOP_TRANSMISSION, 0);
/* Wait for the card to finish operation */
err = sdhc_spi_skip_until_ready(data);
if (err != 0) {
goto exit;
}
err = 0;
exit:
spi_release(data->spi, &data->cfg);
return err;
}
static int disk_spi_sdhc_init(const struct device *dev);
static int sdhc_spi_init(const struct device *dev)
{
struct sdhc_spi_data *data = dev->data;
data->spi = device_get_binding(DT_BUS_LABEL(SPI_SDHC_NODE));
data->cfg.frequency = SDHC_SPI_INITIAL_SPEED;
data->cfg.operation = SPI_WORD_SET(8) | SPI_HOLD_ON_CS;
data->cfg.slave = DT_REG_ADDR(SPI_SDHC_NODE);
#if DT_SPI_DEV_HAS_CS_GPIOS(SPI_SDHC_NODE)
data->cs.gpio_dev =
device_get_binding(DT_SPI_DEV_CS_GPIOS_LABEL(SPI_SDHC_NODE));
__ASSERT_NO_MSG(data->cs.gpio_dev != NULL);
data->cs.gpio_pin = DT_SPI_DEV_CS_GPIOS_PIN(SPI_SDHC_NODE);
data->cs.gpio_dt_flags = DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_SDHC_NODE);
data->cfg.cs = &data->cs;
#endif
disk_spi_sdhc_init(dev);
return 0;
}
static int disk_spi_sdhc_access_status(struct disk_info *disk)
{
const struct device *dev = disk->dev;
struct sdhc_spi_data *data = dev->data;
return data->status;
}
static int disk_spi_sdhc_access_read(struct disk_info *disk,
uint8_t *buf, uint32_t sector, uint32_t count)
{
const struct device *dev = disk->dev;
struct sdhc_spi_data *data = dev->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 uint8_t *buf, uint32_t sector, uint32_t count)
{
const struct device *dev = disk->dev;
struct sdhc_spi_data *data = dev->data;
int err;
/* for more than 2 blocks the multiple block is preferred */
if (count > 2) {
LOG_DBG("multi block sector=%u count=%u", sector, count);
err = sdhc_spi_write_multi(data, buf, sector, count);
if (err != 0 && sdhc_is_retryable(err)) {
sdhc_spi_recover(data);
err = sdhc_spi_write_multi(data, buf, sector, count);
}
} else {
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,
uint8_t cmd, void *buf)
{
const struct device *dev = disk->dev;
struct sdhc_spi_data *data = dev->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:
*(uint32_t *)buf = data->sector_count;
break;
case DISK_IOCTL_GET_SECTOR_SIZE:
*(uint32_t *)buf = SDMMC_DEFAULT_BLOCK_SIZE;
break;
case DISK_IOCTL_GET_ERASE_BLOCK_SZ:
*(uint32_t *)buf = SDMMC_DEFAULT_BLOCK_SIZE;
break;
default:
return -EINVAL;
}
return 0;
}
static int disk_spi_sdhc_access_init(struct disk_info *disk)
{
const struct device *dev = disk->dev;
struct sdhc_spi_data *data = dev->data;
int err;
err = sdhc_spi_detect(data);
spi_release(data->spi, &data->cfg);
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(const struct device *dev)
{
struct sdhc_spi_data *data = dev->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_DT_INST_DEFINE(0, sdhc_spi_init, device_pm_control_nop,
&sdhc_spi_data_0, NULL,
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL);
#endif

435
drivers/disk/sdmmc_stm32.c Normal file
View file

@ -0,0 +1,435 @@
/*
* Copyright (c) 2020 Amarula Solutions.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT st_stm32_sdmmc
#include <devicetree.h>
#include <disk/disk_access.h>
#include <drivers/clock_control.h>
#include <drivers/clock_control/stm32_clock_control.h>
#include <pinmux/stm32/pinmux_stm32.h>
#include <drivers/gpio.h>
#include <logging/log.h>
#include <soc.h>
#include <stm32_ll_rcc.h>
LOG_MODULE_REGISTER(stm32_sdmmc);
struct stm32_sdmmc_priv {
SD_HandleTypeDef hsd;
int status;
struct k_work work;
struct gpio_callback cd_cb;
struct {
const char *name;
const struct device *port;
int pin;
int flags;
} cd;
struct {
const char *name;
const struct device *port;
int pin;
int flags;
} pe;
struct stm32_pclken pclken;
struct {
const struct soc_gpio_pinctrl *list;
size_t len;
} pinctrl;
};
static int stm32_sdmmc_clock_enable(struct stm32_sdmmc_priv *priv)
{
const struct device *clock;
#if CONFIG_SOC_SERIES_STM32L4X
LL_RCC_PLLSAI1_Disable();
/* Configure PLLSA11 to enable 48M domain */
LL_RCC_PLLSAI1_ConfigDomain_48M(LL_RCC_PLLSOURCE_HSI,
LL_RCC_PLLM_DIV_1,
8, LL_RCC_PLLSAI1Q_DIV_8);
/* Enable PLLSA1 */
LL_RCC_PLLSAI1_Enable();
/* Enable PLLSAI1 output mapped on 48MHz domain clock */
LL_RCC_PLLSAI1_EnableDomain_48M();
/* Wait for PLLSA1 ready flag */
while (LL_RCC_PLLSAI1_IsReady() != 1)
;
LL_RCC_SetSDMMCClockSource(LL_RCC_SDMMC1_CLKSOURCE_PLLSAI1);
#endif
clock = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
/* Enable the APB clock for stm32_sdmmc */
return clock_control_on(clock, (clock_control_subsys_t *)&priv->pclken);
}
static int stm32_sdmmc_clock_disable(struct stm32_sdmmc_priv *priv)
{
const struct device *clock;
clock = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
return clock_control_off(clock,
(clock_control_subsys_t *)&priv->pclken);
}
static int stm32_sdmmc_access_init(struct disk_info *disk)
{
const struct device *dev = disk->dev;
struct stm32_sdmmc_priv *priv = dev->data;
int err;
if (priv->status == DISK_STATUS_OK) {
return 0;
}
if (priv->status == DISK_STATUS_NOMEDIA) {
return -ENODEV;
}
err = stm32_sdmmc_clock_enable(priv);
if (err) {
LOG_ERR("failed to init clocks");
return err;
}
err = HAL_SD_Init(&priv->hsd);
if (err != HAL_OK) {
LOG_ERR("failed to init stm32_sdmmc");
return -EIO;
}
priv->status = DISK_STATUS_OK;
return 0;
}
static void stm32_sdmmc_access_deinit(struct stm32_sdmmc_priv *priv)
{
HAL_SD_DeInit(&priv->hsd);
stm32_sdmmc_clock_disable(priv);
}
static int stm32_sdmmc_access_status(struct disk_info *disk)
{
const struct device *dev = disk->dev;
struct stm32_sdmmc_priv *priv = dev->data;
return priv->status;
}
static int stm32_sdmmc_access_read(struct disk_info *disk, uint8_t *data_buf,
uint32_t start_sector, uint32_t num_sector)
{
const struct device *dev = disk->dev;
struct stm32_sdmmc_priv *priv = dev->data;
int err;
err = HAL_SD_ReadBlocks(&priv->hsd, data_buf, start_sector,
num_sector, 30000);
if (err != HAL_OK) {
LOG_ERR("sd read block failed %d", err);
return -EIO;
}
while (HAL_SD_GetCardState(&priv->hsd) != HAL_SD_CARD_TRANSFER)
;
return 0;
}
static int stm32_sdmmc_access_write(struct disk_info *disk,
const uint8_t *data_buf,
uint32_t start_sector, uint32_t num_sector)
{
const struct device *dev = disk->dev;
struct stm32_sdmmc_priv *priv = dev->data;
int err;
err = HAL_SD_WriteBlocks(&priv->hsd, (uint8_t *)data_buf, start_sector,
num_sector, 30000);
if (err != HAL_OK) {
LOG_ERR("sd write block failed %d", err);
return -EIO;
}
while (HAL_SD_GetCardState(&priv->hsd) != HAL_SD_CARD_TRANSFER)
;
return 0;
}
static int stm32_sdmmc_access_ioctl(struct disk_info *disk, uint8_t cmd,
void *buff)
{
const struct device *dev = disk->dev;
struct stm32_sdmmc_priv *priv = dev->data;
HAL_SD_CardInfoTypeDef info;
int err;
switch (cmd) {
case DISK_IOCTL_GET_SECTOR_COUNT:
err = HAL_SD_GetCardInfo(&priv->hsd, &info);
if (err != HAL_OK) {
return -EIO;
}
*(uint32_t *)buff = info.LogBlockNbr;
break;
case DISK_IOCTL_GET_SECTOR_SIZE:
err = HAL_SD_GetCardInfo(&priv->hsd, &info);
if (err != HAL_OK) {
return -EIO;
}
*(uint32_t *)buff = info.LogBlockSize;
break;
case DISK_IOCTL_GET_ERASE_BLOCK_SZ:
*(uint32_t *)buff = 1;
break;
case DISK_IOCTL_CTRL_SYNC:
/* we use a blocking API, so nothing to do for sync */
break;
default:
return -EINVAL;
}
return 0;
}
static const struct disk_operations stm32_sdmmc_ops = {
.init = stm32_sdmmc_access_init,
.status = stm32_sdmmc_access_status,
.read = stm32_sdmmc_access_read,
.write = stm32_sdmmc_access_write,
.ioctl = stm32_sdmmc_access_ioctl,
};
static struct disk_info stm32_sdmmc_info = {
.name = CONFIG_DISK_STM32_SDMMC_VOLUME_NAME,
.ops = &stm32_sdmmc_ops,
};
/*
* Check if the card is present or not. If no card detect gpio is set, assume
* the card is present. If reading the gpio fails for some reason, assume the
* card is there.
*/
static bool stm32_sdmmc_card_present(struct stm32_sdmmc_priv *priv)
{
int err;
if (!priv->cd.name) {
return true;
}
err = gpio_pin_get(priv->cd.port, priv->cd.pin);
if (err < 0) {
LOG_WRN("reading card detect failed %d", err);
return true;
}
return err;
}
static void stm32_sdmmc_cd_handler(struct k_work *item)
{
struct stm32_sdmmc_priv *priv = CONTAINER_OF(item,
struct stm32_sdmmc_priv,
work);
if (stm32_sdmmc_card_present(priv)) {
LOG_DBG("card inserted");
priv->status = DISK_STATUS_UNINIT;
} else {
LOG_DBG("card removed");
stm32_sdmmc_access_deinit(priv);
priv->status = DISK_STATUS_NOMEDIA;
}
}
static void stm32_sdmmc_cd_callback(const struct device *gpiodev,
struct gpio_callback *cb,
uint32_t pin)
{
struct stm32_sdmmc_priv *priv = CONTAINER_OF(cb,
struct stm32_sdmmc_priv,
cd_cb);
k_work_submit(&priv->work);
}
static int stm32_sdmmc_card_detect_init(struct stm32_sdmmc_priv *priv)
{
int err;
if (!priv->cd.name) {
return 0;
}
priv->cd.port = device_get_binding(priv->cd.name);
if (!priv->cd.port) {
return -ENODEV;
}
gpio_init_callback(&priv->cd_cb, stm32_sdmmc_cd_callback,
1 << priv->cd.pin);
err = gpio_add_callback(priv->cd.port, &priv->cd_cb);
if (err) {
return err;
}
err = gpio_pin_configure(priv->cd.port, priv->cd.pin,
priv->cd.flags | GPIO_INPUT);
if (err) {
goto remove_callback;
}
err = gpio_pin_interrupt_configure(priv->cd.port, priv->cd.pin,
GPIO_INT_EDGE_BOTH);
if (err) {
goto unconfigure_pin;
}
return 0;
unconfigure_pin:
gpio_pin_configure(priv->cd.port, priv->cd.pin, GPIO_DISCONNECTED);
remove_callback:
gpio_remove_callback(priv->cd.port, &priv->cd_cb);
return err;
}
static int stm32_sdmmc_card_detect_uninit(struct stm32_sdmmc_priv *priv)
{
if (!priv->cd.name) {
return 0;
}
gpio_pin_interrupt_configure(priv->cd.port, priv->cd.pin,
GPIO_INT_MODE_DISABLED);
gpio_pin_configure(priv->cd.port, priv->cd.pin, GPIO_DISCONNECTED);
gpio_remove_callback(priv->cd.port, &priv->cd_cb);
return 0;
}
static int stm32_sdmmc_pwr_init(struct stm32_sdmmc_priv *priv)
{
int err;
if (!priv->pe.name) {
return 0;
}
priv->pe.port = device_get_binding(priv->pe.name);
if (!priv->pe.port) {
return -ENODEV;
}
err = gpio_pin_configure(priv->pe.port, priv->pe.pin,
priv->pe.flags | GPIO_OUTPUT_ACTIVE);
if (err) {
return err;
}
k_sleep(K_MSEC(50));
return 0;
}
static int stm32_sdmmc_pwr_uninit(struct stm32_sdmmc_priv *priv)
{
if (!priv->pe.name) {
return 0;
}
gpio_pin_configure(priv->pe.port, priv->pe.pin, GPIO_DISCONNECTED);
return 0;
}
static int disk_stm32_sdmmc_init(const struct device *dev)
{
struct stm32_sdmmc_priv *priv = dev->data;
int err;
k_work_init(&priv->work, stm32_sdmmc_cd_handler);
/* Configure dt provided device signals when available */
err = stm32_dt_pinctrl_configure(priv->pinctrl.list,
priv->pinctrl.len,
(uint32_t)priv->hsd.Instance);
if (err < 0) {
return err;
}
err = stm32_sdmmc_card_detect_init(priv);
if (err) {
return err;
}
err = stm32_sdmmc_pwr_init(priv);
if (err) {
goto err_card_detect;
}
if (stm32_sdmmc_card_present(priv)) {
priv->status = DISK_STATUS_UNINIT;
} else {
priv->status = DISK_STATUS_NOMEDIA;
}
stm32_sdmmc_info.dev = dev;
err = disk_access_register(&stm32_sdmmc_info);
if (err) {
goto err_pwr;
}
return 0;
err_pwr:
stm32_sdmmc_pwr_uninit(priv);
err_card_detect:
stm32_sdmmc_card_detect_uninit(priv);
return err;
}
#if DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay)
static const struct soc_gpio_pinctrl sdmmc_pins_1[] =
ST_STM32_DT_INST_PINCTRL(0, 0);
static struct stm32_sdmmc_priv stm32_sdmmc_priv_1 = {
.hsd = {
.Instance = (SDMMC_TypeDef *)DT_INST_REG_ADDR(0),
},
#if DT_INST_NODE_HAS_PROP(0, cd_gpios)
.cd = {
.name = DT_INST_GPIO_LABEL(0, cd_gpios),
.pin = DT_INST_GPIO_PIN(0, cd_gpios),
.flags = DT_INST_GPIO_FLAGS(0, cd_gpios),
},
#endif
#if DT_INST_NODE_HAS_PROP(0, pwr_gpios)
.pe = {
.name = DT_INST_GPIO_LABEL(0, pwr_gpios),
.pin = DT_INST_GPIO_PIN(0, pwr_gpios),
.flags = DT_INST_GPIO_FLAGS(0, pwr_gpios),
},
#endif
.pclken = {
.bus = DT_INST_CLOCKS_CELL(0, bus),
.enr = DT_INST_CLOCKS_CELL(0, bits),
},
.pinctrl = {
.list = sdmmc_pins_1,
.len = ARRAY_SIZE(sdmmc_pins_1)
}
};
DEVICE_DT_INST_DEFINE(0, disk_stm32_sdmmc_init, device_pm_control_nop,
&stm32_sdmmc_priv_1, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
NULL);
#endif

2851
drivers/disk/usdhc.c Normal file

File diff suppressed because it is too large Load diff