Files
2025-11-14 21:41:29 +08:00

657 lines
22 KiB
C

/**
* @file disk_mmm.c
*
* Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*
* Description:
*
* Revision History:
* -----------------
*/
#include <debug.h>
#include <disk.h>
#include <disk_mmc.h>
#include <param.h>
#include <stdlib.h>
#include <string.h>
#include <types.h>
/* Maximum value of a single transmission */
#define MMC_MAX_XFER_SIZE (0x1000000)
/* mmc erase value */
#define MMC_DEFAULT_FLAG(flag) \
((flag == DISK_ERASE_DEFAULT) || (flag == DISK_ERASE_ZERO))
/* MMC physical layer API */
static int mmc_dev_read(struct disk_mmc_info *mmc_info, uint8_t *dst,
block_t sector, block_count_t count)
{
if (mmc_info->config.mmc_read)
return mmc_info->config.mmc_read(mmc_info->config.mmc_dev, dst, sector,
count);
else
return -DISK_ERROR_READ_ACCESS;
}
static int mmc_dev_write(struct disk_mmc_info *mmc_info, const uint8_t *src,
block_t sector, block_count_t count)
{
if (mmc_info->config.mmc_write)
return mmc_info->config.mmc_write(mmc_info->config.mmc_dev, src, sector,
count);
else
return -DISK_ERROR_WRITE_ACCESS;
}
static int mmc_dev_erase(struct disk_mmc_info *mmc_info, block_t block,
block_count_t count)
{
if (mmc_info->config.mmc_erase)
return mmc_info->config.mmc_erase(mmc_info->config.mmc_dev, block,
count);
else
return -DISK_ERROR_ERASE_ACCESS;
}
/* MMC buff reads */
static int mmc_buff_read(struct disk_mmc_info *mmc_info, uint8_t *buff_dst,
block_t sector, block_count_t sector_count,
uint8_t *dst, disk_addr_t addr, disk_size_t size)
{
int ret = 0;
uint32_t sector_size = mmc_info->config.sector_size;
/* Protect the buffer */
xSemaphoreTake(mmc_info->disk_mmc_mutex, portMAX_DELAY);
ret = mmc_dev_read(mmc_info, buff_dst, sector, sector_count);
if (ret)
goto error;
memcpy(dst, buff_dst + addr - ROUNDDOWN(addr, sector_size), size);
xSemaphoreGive(mmc_info->disk_mmc_mutex);
return 0;
error:
xSemaphoreGive(mmc_info->disk_mmc_mutex);
return ret;
}
static int disk_mmc_read(struct disk_info *info, uint8_t *dst, disk_addr_t addr,
disk_size_t size)
{
int ret = 0;
struct disk_mmc_info *mmc_info = info->private_data;
uint8_t *buf = NULL;
disk_size_t remaining = size;
uint32_t sector_size = mmc_info->config.sector_size;
uint32_t buff_size = mmc_info->buff_size;
disk_addr_t sector_addr = 0;
disk_size_t sector_count = 0;
disk_size_t read_len = 0;
disk_size_t max_read_length = 0;
while (remaining) {
/* Process the unaligned portion of the address */
if (!IS_ALIGNED(addr, sector_size)) {
ssdk_printf(SSDK_DEBUG,
"mmc disk read %s address sector unaligned portion\n",
info->disk_name);
buf = mmc_info->buff_sector;
sector_addr = addr / sector_size;
sector_count = 1;
read_len = MIN(ROUNDUP(addr, sector_size) - addr, remaining);
ssdk_printf(SSDK_DEBUG, "mmc disk read addr:%llx read_len:%lld\n",
addr, read_len);
}
/* Address & size aligned portion of sector,Memory CACHE_LINE alignment
*/
else if ((remaining > sector_size) &&
IS_ALIGNED(dst, CONFIG_ARCH_CACHE_LINE)) {
ssdk_printf(SSDK_DEBUG,
"mmc disk read %s address sector aligned & dst "
"CACHE_LINE aligned portion\n",
info->disk_name);
buf = dst;
sector_addr = addr / sector_size;
max_read_length = MIN(mmc_info->mmc_max_xfer_size, remaining);
sector_count = max_read_length / sector_size;
read_len = sector_count * sector_size;
ssdk_printf(SSDK_DEBUG, "mmc disk read addr:%llx read_len:%lld\n",
addr, read_len);
}
/* Address & size aligned portion of sector,Memory CACHE_LINE is not
aligned */
else {
ssdk_printf(SSDK_DEBUG, "mmc disk read %s remaining portion\n",
info->disk_name);
buf = mmc_info->buff_sector;
sector_addr = addr / sector_size;
read_len = MIN(buff_size, remaining);
sector_count = ROUNDUP(read_len, sector_size) / sector_size;
ssdk_printf(SSDK_DEBUG, "mmc disk read addr:%llx read_len:%lld\n",
addr, read_len);
}
if (buf == dst) {
ret = mmc_dev_read(mmc_info, buf, sector_addr, sector_count);
if (ret)
return ret;
}
else {
ret = mmc_buff_read(mmc_info, buf, sector_addr, sector_count, dst,
addr, read_len);
if (ret)
return ret;
}
/* Address & size offset */
dst += read_len;
addr += read_len;
remaining -= read_len;
}
return 0;
}
static int mmc_buff_write(struct disk_mmc_info *mmc_info, uint8_t *buff_src,
block_t sector, block_count_t sector_count,
uint8_t *src, disk_addr_t addr, disk_size_t size)
{
int ret = 0;
uint32_t sector_size = mmc_info->config.sector_size;
/* Protect the buffer */
xSemaphoreTake(mmc_info->disk_mmc_mutex, portMAX_DELAY);
if (IS_ALIGNED(addr, sector_size) && IS_ALIGNED(size, sector_size)) {
memcpy(buff_src, src, size);
}
else {
ret = mmc_dev_read(mmc_info, buff_src, sector, sector_count);
if (ret)
goto error;
memcpy(buff_src + addr - ROUNDDOWN(addr, sector_size), src, size);
}
ret = mmc_dev_write(mmc_info, buff_src, sector, sector_count);
if (ret)
goto error;
xSemaphoreGive(mmc_info->disk_mmc_mutex);
return 0;
error:
xSemaphoreGive(mmc_info->disk_mmc_mutex);
return ret;
}
static int disk_mmc_write(struct disk_info *info, const uint8_t *src,
disk_addr_t addr, disk_size_t size)
{
int ret = 0;
struct disk_mmc_info *mmc_info = info->private_data;
uint8_t *buf = NULL;
disk_size_t remaining = size;
uint32_t sector_size = mmc_info->config.sector_size;
uint32_t buff_size = mmc_info->buff_size;
disk_addr_t sector_addr = 0;
disk_size_t sector_count = 0;
disk_size_t write_len = 0;
disk_size_t max_read_length = 0;
while (remaining) {
/* Process the unaligned portion of the address */
if (!IS_ALIGNED(addr, sector_size)) {
ssdk_printf(SSDK_DEBUG,
"mmc disk write %s address sector unaligned portion\n",
info->disk_name);
buf = mmc_info->buff_sector;
sector_addr = addr / sector_size;
sector_count = 1;
write_len = MIN(ROUNDUP(addr, sector_size) - addr, remaining);
ssdk_printf(SSDK_DEBUG, "mmc disk write addr:%llx write_len:%lld\n",
addr, write_len);
}
/* Address & size aligned portion of sector,Memory CACHE_LINE alignment
*/
else if ((remaining > sector_size) &&
IS_ALIGNED(src, CONFIG_ARCH_CACHE_LINE)) {
ssdk_printf(SSDK_DEBUG,
"mmc disk write %s address sector aligned & dst "
"CACHE_LINE aligned portion\n",
info->disk_name);
buf = (uint8_t *)src;
sector_addr = addr / sector_size;
max_read_length = MIN(mmc_info->mmc_max_xfer_size, remaining);
sector_count = max_read_length / sector_size;
write_len = sector_count * sector_size;
ssdk_printf(SSDK_DEBUG, "mmc disk write addr:%llx write_len:%lld\n",
addr, write_len);
}
else {
ssdk_printf(SSDK_DEBUG, "mmc disk write %s remaining portion\n",
info->disk_name);
buf = mmc_info->buff_sector;
sector_addr = addr / sector_size;
write_len = MIN(buff_size, remaining);
if (!IS_ALIGNED(write_len, sector_size) &&
(write_len > sector_size))
write_len = ROUNDDOWN(write_len, sector_size);
sector_count = ROUNDUP(write_len, sector_size) / sector_size;
ssdk_printf(SSDK_DEBUG, "mmc disk write addr:%llx write_len:%lld\n",
addr, write_len);
}
if (buf == src) {
ret = mmc_dev_write(mmc_info, buf, sector_addr, sector_count);
if (ret)
return ret;
}
else {
ret = mmc_buff_write(mmc_info, buf, sector_addr, sector_count,
(uint8_t *)src, addr, write_len);
if (ret)
return ret;
}
/* Address & size offset */
src += write_len;
addr += write_len;
remaining -= write_len;
}
return 0;
}
static int disk_mmc_read_block(struct disk_info *info, uint8_t *dst,
block_t block, block_count_t count,
block_size_t blk_sz)
{
struct disk_mmc_info *mmc_info = info->private_data;
uint32_t sector_size = mmc_info->config.sector_size;
uint32_t blk_to_sector = blk_sz / sector_size;
return mmc_dev_read(mmc_info, dst, block * blk_to_sector,
count * blk_to_sector);
}
static int disk_mmc_write_block(struct disk_info *info, const uint8_t *src,
block_t block, block_count_t count,
block_size_t blk_sz)
{
struct disk_mmc_info *mmc_info = info->private_data;
uint32_t sector_size = mmc_info->config.sector_size;
uint32_t blk_to_sector = blk_sz / sector_size;
return mmc_dev_write(mmc_info, src, block * blk_to_sector,
count * blk_to_sector);
}
static int mmc_buff_erase(struct disk_mmc_info *mmc_info, uint8_t *buff,
block_t sector, block_count_t sector_count,
disk_addr_t addr, disk_size_t size, char flag)
{
int ret = 0;
uint32_t sector_size = mmc_info->config.sector_size;
/* Protect the buff_sector buffer */
xSemaphoreTake(mmc_info->disk_mmc_mutex, portMAX_DELAY);
if (IS_ALIGNED(addr, sector_size) && IS_ALIGNED(size, sector_size)) {
memset(buff, flag, size);
}
else {
ret = mmc_dev_read(mmc_info, buff, sector, sector_count);
if (ret)
goto error;
memset(buff + addr - ROUNDDOWN(addr, sector_size), flag, size);
}
ret = mmc_dev_write(mmc_info, buff, sector, sector_count);
if (ret)
goto error;
xSemaphoreGive(mmc_info->disk_mmc_mutex);
return 0;
error:
xSemaphoreGive(mmc_info->disk_mmc_mutex);
return ret;
}
static int disk_mmc_erase(struct disk_info *info, disk_addr_t addr,
disk_size_t size, uint32_t flag)
{
int ret = 0;
struct disk_mmc_info *mmc_info = info->private_data;
uint8_t *buf = NULL;
disk_size_t remaining = size;
uint32_t sector_size = mmc_info->config.sector_size;
uint32_t buff_size = mmc_info->buff_size;
uint32_t scr_data_erase = mmc_info->config.scr_data_erase;
uint32_t erase_size = info->erase_size;
disk_addr_t sector_addr = 0;
disk_size_t sector_count = 0;
disk_size_t erase_count = 0;
disk_size_t erase_len = 0;
/* configure c_flag */
char c_flag = 0;
if (MMC_DEFAULT_FLAG(flag))
c_flag = 0x0;
else
c_flag = 0xff;
while (remaining) {
/* Process the sector unaligned portion of the address */
if (!IS_ALIGNED(addr, sector_size)) {
ssdk_printf(SSDK_DEBUG,
"mmc disk erase %s address sector unaligned portion\n",
info->disk_name);
buf = mmc_info->buff_sector;
sector_addr = addr / sector_size;
sector_count = 1;
erase_len = MIN(ROUNDUP(addr, sector_size) - addr, remaining);
ssdk_printf(SSDK_DEBUG, "mmc disk erase addr:%llx erase_len:%lld\n",
addr, erase_len);
}
/* Process the erase_size unaligned portion of the address */
else if (!IS_ALIGNED(addr, erase_size) && (remaining > erase_size)) {
ssdk_printf(
SSDK_DEBUG,
"mmc disk erase %s address erase_size unaligned portion\n",
info->disk_name);
buf = mmc_info->buff_sector;
sector_addr = addr / sector_size;
erase_len = MIN(buff_size, remaining);
erase_len = MIN(ROUNDUP(addr, erase_size) - addr, erase_len);
sector_count = erase_len / sector_size;
erase_len = sector_count * sector_size;
ssdk_printf(SSDK_DEBUG, "mmc disk erase addr:%llx erase_len:%lld\n",
addr, erase_len);
}
/* Address & size aligned portion of sector,Memory CACHE_LINE alignment
*/
else if ((remaining > erase_size) && MMC_DEFAULT_FLAG(flag)
&& !scr_data_erase) {
ssdk_printf(SSDK_DEBUG,
"mmc disk erase %s address erase_size aligned & size "
"erase_size aligned portion\n",
info->disk_name);
buf = NULL;
sector_addr = addr / sector_size;
erase_count = remaining / erase_size;
erase_len = erase_count * erase_size;
ssdk_printf(SSDK_DEBUG, "mmc disk erase addr:%llx erase_len:%lld\n",
addr, erase_len);
}
else if ((remaining > erase_size) && !MMC_DEFAULT_FLAG(flag)
&& scr_data_erase) {
ssdk_printf(SSDK_DEBUG,
"mmc disk erase %s address erase_size aligned & size "
"erase_size aligned portion\n",
info->disk_name);
buf = NULL;
sector_addr = addr / sector_size;
erase_count = remaining / erase_size;
erase_len = erase_count * erase_size;
ssdk_printf(SSDK_DEBUG, "mmc disk erase addr:%llx erase_len:%lld\n",
addr, erase_len);
}
/* Address & size aligned portion of sector,Memory CACHE_LINE is not
aligned */
else {
ssdk_printf(SSDK_DEBUG, "mmc disk erase %s remaining portion\n",
info->disk_name);
buf = mmc_info->buff_sector;
sector_addr = addr / sector_size;
erase_len = MIN(buff_size, remaining);
if (!IS_ALIGNED(erase_len, sector_size) &&
(erase_len > sector_size))
erase_len = ROUNDDOWN(erase_len, sector_size);
sector_count = ROUNDUP(erase_len, sector_size) / sector_size;
ssdk_printf(SSDK_DEBUG, "mmc disk erase addr:%llx erase_len:%lld\n",
addr, erase_len);
}
if (NULL == buf) {
ret = mmc_dev_erase(mmc_info, sector_addr, erase_len / sector_size);
if (ret)
return ret;
}
else {
ret = mmc_buff_erase(mmc_info, buf, sector_addr, sector_count, addr,
erase_len, c_flag);
if (ret)
return ret;
}
/* Address & size offset */
addr += erase_len;
remaining -= erase_len;
}
return 0;
}
static int disk_mmc_erase_group(struct disk_info *info, uint32_t erase_block,
uint32_t count, uint32_t erase_sz,
uint32_t flag)
{
struct disk_mmc_info *mmc_info = info->private_data;
uint32_t sector_size = mmc_info->config.sector_size;
uint32_t scr_data_erase = mmc_info->config.scr_data_erase;
if (erase_sz != info->erase_size) {
ssdk_printf(SSDK_DEBUG,
"mmc disk %s erase group erase_sz error,erase_sz:0x%x "
"erase_size:0x%x \n",
info->disk_name, erase_sz, info->erase_size);
return -DISK_ERROR_INVALID_PARAMETER;
}
if (!IS_ALIGNED(erase_sz, sector_size)) {
ssdk_printf(SSDK_DEBUG,
"mmc disk %s erase group erase_sz error,erase_sz:0x%x "
"sector_size:0x%x \n",
info->disk_name, erase_sz, sector_size);
return -DISK_ERROR_INVALID_PARAMETER;
}
uint32_t sector_addr = erase_block * erase_sz / sector_size;
switch (flag) {
case DISK_ERASE_DEFAULT:
case DISK_ERASE_ZERO:
if (!scr_data_erase) {
return mmc_dev_erase(mmc_info, sector_addr, count * erase_sz / sector_size);
}
else {
return disk_mmc_erase(info, (disk_addr_t)erase_block * erase_sz,
(disk_size_t)count * erase_sz, flag);
}
case DISK_ERASE_ONE:
if (scr_data_erase) {
return mmc_dev_erase(mmc_info, sector_addr, count * erase_sz / sector_size);
}
else {
return disk_mmc_erase(info, (disk_addr_t)erase_block * erase_sz,
(disk_size_t)count * erase_sz, flag);
}
default:
ssdk_printf(SSDK_ERR, "disk mmc erase flag error\n");
return -1;
}
}
static int disk_mmc_status(struct disk_info *info) { return DISK_STATUS_OK; }
static int disk_mmc_ioctl(struct disk_info *info, uint32_t cmd, void *buff)
{
struct disk_mmc_info *mmc_info = info->private_data;
switch (cmd) {
case MMC_IOCTL_PON:
uint32_t pon_type = 0;
memcpy(&pon_type, buff, sizeof(pon_type));
if (mmc_info->config.mmc_pon)
return mmc_info->config.mmc_pon(mmc_info->config.mmc_dev, pon_type);
else
return DISK_ERROR_INVALID_PARAMETER;
break;
default:
ssdk_printf(SSDK_ERR, "disk mmc ioctl cmd error:%d\n", cmd);
return DISK_ERROR_INVALID_PARAMETER;
}
return 0;
}
struct disk_operations disk_mmc_ops = {
.disk_read = disk_mmc_read,
.disk_write = disk_mmc_write,
.disk_read_block = disk_mmc_read_block,
.disk_write_block = disk_mmc_write_block,
.disk_erase = disk_mmc_erase,
.disk_erase_group = disk_mmc_erase_group,
.disk_status = disk_mmc_status,
.disk_ioctl = disk_mmc_ioctl,
};
int register_mmc_disk(struct disk_mmc_info *mmc_info)
{
int ret = 0;
struct disk_info *info = &mmc_info->info;
struct disk_mmc_config *mmc_config = &mmc_info->config;
/* init mmc_info */
mmc_info->buff_size = 4 * 1024;
if (!IS_ALIGNED(mmc_info->buff_size, mmc_config->sector_size)) {
ssdk_printf(
SSDK_ERR,
"mmc disk %s mmc_max_xfer_size not aligned to sector_size\n",
mmc_config->disk_name);
return -1;
}
mmc_info->buff_sector = (uint8_t *)pvPortMallocAligned(
mmc_info->buff_size, CONFIG_ARCH_CACHE_LINE);
if (NULL == mmc_info->buff_sector) {
ssdk_printf(SSDK_ERR, "mmc disk %s buff malloc error\n",
mmc_config->disk_name);
return -1;
}
mmc_info->disk_mmc_mutex = xSemaphoreCreateMutex();
mmc_info->mmc_max_xfer_size = MMC_MAX_XFER_SIZE;
if (!IS_ALIGNED(mmc_info->mmc_max_xfer_size, mmc_config->sector_size)) {
ssdk_printf(
SSDK_ERR,
"mmc disk %s mmc_max_xfer_size not aligned to sector_size\n",
mmc_config->disk_name);
return -1;
}
/* base on config init info */
info->disk_name = mmc_config->disk_name;
info->disk_size = mmc_config->disk_size;
info->disk_offset = 0;
info->erase_size = mmc_config->erase_size;
info->mem_align_size = CONFIG_ARCH_CACHE_LINE;
info->block_align_size = mmc_config->sector_size;
info->private_data = mmc_info;
info->disk_ops = &disk_mmc_ops;
#ifdef CONFIG_DISK_CACHE_NOOP
struct disk_cache_noop *cache_noop = &mmc_info->cache_noop;
/* base on config cache,cache in disk dev layer??? */
cache_noop->cache_align_size = 4u * 1024u;
cache_noop->cache.private_data = cache_noop;
ret = register_disk_cache_noop(cache_noop);
if (ret < 0) {
ssdk_printf(SSDK_ERR, "mmc disk register cache noop:%d\n", ret);
return ret;
}
#endif
ssdk_printf(SSDK_INFO,
"register mmc disk:%s disk_size:%lld erase_size:0x%x "
"sector_size:0x%x\n",
info->disk_name, info->disk_size, info->erase_size,
mmc_config->sector_size);
xSemaphoreTake(g_disk_mutex, portMAX_DELAY);
list_add_tail(&g_disk_list, &info->node);
info->use_count = 0;
xSemaphoreGive(g_disk_mutex);
return ret;
}
int unregister_mmc_disk(struct disk_mmc_info *mmc_info)
{
int ret = 0;
struct disk_info *info = &mmc_info->info;
ssdk_printf(SSDK_INFO, "unregister mmc disk:%s\n", info->disk_name);
#ifdef CONFIG_DISK_CACHE_NOOP
struct disk_cache_noop *cache_noop = &mmc_info->cache_noop;
/* unregister disk cache noop */
ret = unregister_disk_cache_noop(cache_noop);
if (ret < 0) {
ssdk_printf(SSDK_ERR, "mmc disk unregister cache noop:%d\n", ret);
return ret;
}
#endif
vPortFree(mmc_info->buff_sector);
vSemaphoreDelete(mmc_info->disk_mmc_mutex);
xSemaphoreTake(g_disk_mutex, portMAX_DELAY);
if (info->use_count) {
xSemaphoreGive(g_disk_mutex);
ret = -DISK_ERROR_BUSY;
ssdk_printf(SSDK_ERR, "mmc disk unregister:%d\n", ret);
return ret;
}
if (list_in_list(&info->node))
list_delete(&info->node);
xSemaphoreGive(g_disk_mutex);
return ret;
}