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:
parent
8f13b7860e
commit
03101e75d8
15 changed files with 175 additions and 151 deletions
|
@ -1,8 +1,3 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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_SPI_SDHC disk_access_spi_sdhc.c)
|
||||
zephyr_sources_ifdef(CONFIG_DISK_ACCESS_STM32_SDMMC disk_access_stm32_sdmmc.c)
|
||||
zephyr_sources_ifdef(CONFIG_DISK_ACCESS_USDHC disk_access_usdhc.c)
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
# Copyright (c) 2016 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
DT_COMPAT_ST_STM32_SDMMC := st,stm32-sdmmc
|
||||
|
||||
menuconfig DISK_ACCESS
|
||||
bool "Disk Interface"
|
||||
select DISK_DRIVERS
|
||||
help
|
||||
Enable disk access over a supported media backend like FLASH or RAM
|
||||
|
||||
|
@ -14,148 +13,4 @@ module = DISK
|
|||
module-str = disk
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
config DISK_ACCESS_RAM
|
||||
bool "RAM Disk"
|
||||
help
|
||||
RAM buffer used to emulate storage disk.
|
||||
This option can be used to test the file
|
||||
system.
|
||||
|
||||
if DISK_ACCESS_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.
|
||||
|
||||
endif # DISK_ACCESS_RAM
|
||||
|
||||
config DISK_ACCESS_FLASH
|
||||
bool "Flash"
|
||||
select FLASH
|
||||
help
|
||||
Flash device is used for the file system.
|
||||
|
||||
if DISK_ACCESS_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.
|
||||
|
||||
endif # DISK_ACCESS_FLASH
|
||||
|
||||
config DISK_ACCESS_SDHC
|
||||
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.
|
||||
|
||||
config DISK_ACCESS_USDHC
|
||||
bool "NXP i.MXRT USDHC driver"
|
||||
depends on (HAS_MCUX_USDHC1 || HAS_MCUX_USDHC2)
|
||||
help
|
||||
File system on a SDHC card accessed over
|
||||
NXP USDHC.
|
||||
|
||||
if DISK_ACCESS_USDHC
|
||||
|
||||
config DISK_ACCESS_USDHC1
|
||||
bool "NXP i.MXRT USDHC instance 1"
|
||||
depends on HAS_MCUX_USDHC1
|
||||
help
|
||||
File system on a SDHC card accessed over
|
||||
USDHC instance 1.
|
||||
|
||||
config DISK_ACCESS_USDHC2
|
||||
bool "NXP i.MXRT USDHC instance 2"
|
||||
depends on HAS_MCUX_USDHC2
|
||||
help
|
||||
File system on a SDHC card accessed over
|
||||
USDHC instance 2.
|
||||
|
||||
endif # DISK_ACCESS_USDHC
|
||||
|
||||
config DISK_SDHC_VOLUME_NAME
|
||||
string "SDHC Disk mount point or drive name"
|
||||
default "SD" if FAT_FILESYSTEM_ELM
|
||||
default "SDHC"
|
||||
help
|
||||
Disk name as per file system naming guidelines.
|
||||
|
||||
endif # DISK_ACCESS_SDHC
|
||||
|
||||
config DISK_ACCESS_STM32_SDMMC
|
||||
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.
|
||||
|
||||
config DISK_STM32_SDMMC_VOLUME_NAME
|
||||
string "SDMMC Disk mount point or drive name"
|
||||
depends on DISK_ACCESS_STM32_SDMMC
|
||||
default "SD" if FAT_FILESYSTEM_ELM
|
||||
default "SDMMC"
|
||||
help
|
||||
Disk name as per file system naming guidelines.
|
||||
|
||||
endif # DISK_ACCESS
|
||||
|
|
|
@ -1,299 +0,0 @@
|
|||
/*
|
||||
* 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);
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* 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);
|
|
@ -1,748 +0,0 @@
|
|||
/* 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_*/
|
|
@ -1,985 +0,0 @@
|
|||
/*
|
||||
* 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
|
|
@ -1,435 +0,0 @@
|
|||
/*
|
||||
* 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
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue