451 lines
14 KiB
C
451 lines
14 KiB
C
/**
|
|
* @file disk_norflash.c
|
|
*
|
|
* Copyright (c) 2021 Semidrive Semiconductor.
|
|
* All rights reserved.
|
|
*
|
|
* Description:
|
|
*
|
|
* Revision History:
|
|
* -----------------
|
|
*/
|
|
|
|
#include <debug.h>
|
|
#include <disk.h>
|
|
#include <disk_norflash.h>
|
|
#include <param.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <types.h>
|
|
|
|
static inline int norflash_dev_read(struct disk_norflash_info *norflash_info,
|
|
disk_addr_t addr, uint8_t *buf,
|
|
disk_size_t length)
|
|
{
|
|
if (norflash_info->config.norflash_read)
|
|
return norflash_info->config.norflash_read(
|
|
norflash_info->config.norflash_dev, addr, buf, length);
|
|
else
|
|
return -DISK_ERROR_READ_ACCESS;
|
|
}
|
|
|
|
static inline int norflash_dev_write(struct disk_norflash_info *norflash_info,
|
|
disk_addr_t addr, const uint8_t *buf,
|
|
disk_size_t length)
|
|
{
|
|
if (norflash_info->config.norflash_write)
|
|
return norflash_info->config.norflash_write(
|
|
norflash_info->config.norflash_dev, addr, buf, length);
|
|
else
|
|
return -DISK_ERROR_WRITE_ACCESS;
|
|
}
|
|
|
|
static inline int norflash_dev_erase(struct disk_norflash_info *norflash_info,
|
|
disk_addr_t addr, disk_size_t length)
|
|
{
|
|
if (norflash_info->config.norflash_erase)
|
|
return norflash_info->config.norflash_erase(
|
|
norflash_info->config.norflash_dev, addr, length);
|
|
else
|
|
return -DISK_ERROR_ERASE_ACCESS;
|
|
}
|
|
|
|
static int disk_norflash_read(struct disk_info *info, uint8_t *dst,
|
|
disk_addr_t addr, disk_size_t size)
|
|
{
|
|
int ret = 0;
|
|
struct disk_norflash_info *norflash_info = info->private_data;
|
|
|
|
ssdk_printf(SSDK_DEBUG,
|
|
"norflash disk read:%s mem:%p addr:%lld size:%lld\n",
|
|
info->disk_name, dst, addr, size);
|
|
|
|
disk_size_t tmp_len = 0;
|
|
|
|
while (size) {
|
|
tmp_len = size;
|
|
if (!IS_ALIGNED(dst, CONFIG_ARCH_CACHE_LINE)) {
|
|
tmp_len =
|
|
MIN(ROUNDUP((addr_t)dst, CONFIG_ARCH_CACHE_LINE) - (addr_t)dst,
|
|
size);
|
|
}
|
|
|
|
tmp_len = tmp_len > CONFIG_ARCH_CACHE_LINE
|
|
? (tmp_len - tmp_len % CONFIG_ARCH_CACHE_LINE)
|
|
: tmp_len;
|
|
|
|
if (tmp_len < CONFIG_ARCH_CACHE_LINE) {
|
|
xSemaphoreTake(norflash_info->disk_norflash_mutex, portMAX_DELAY);
|
|
if (norflash_dev_read(norflash_info, addr,
|
|
norflash_info->buff_sector,
|
|
ROUNDUP(tmp_len, 4))) {
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
memcpy(dst, norflash_info->buff_sector, tmp_len);
|
|
xSemaphoreGive(norflash_info->disk_norflash_mutex);
|
|
} else {
|
|
if (norflash_dev_read(norflash_info, addr, dst, tmp_len)) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
addr += tmp_len;
|
|
size -= tmp_len;
|
|
dst += tmp_len;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
xSemaphoreGive(norflash_info->disk_norflash_mutex);
|
|
return ret;
|
|
}
|
|
|
|
#if CONFIG_DISK_NOR_FLASH_BYTES_API_SUPPORT
|
|
static int disk_norflash_write(struct disk_info *info, const uint8_t *src,
|
|
disk_addr_t addr, disk_size_t size)
|
|
{
|
|
int ret = 0;
|
|
struct disk_norflash_info *norflash_info = info->private_data;
|
|
ssdk_printf(SSDK_DEBUG,
|
|
"norflash disk write:%s mem:%p addr:%lld size:%lld\n",
|
|
info->disk_name, src, addr, size);
|
|
|
|
uint32_t sector_size = norflash_info->config.sector_size;
|
|
uint32_t tmp_len;
|
|
uint8_t *buf;
|
|
|
|
while (size) {
|
|
buf = norflash_info->buff_sector;
|
|
tmp_len = size;
|
|
if (!IS_ALIGNED(addr, sector_size)) {
|
|
tmp_len = MIN(ROUNDUP(addr, sector_size) - addr, size);
|
|
} else if (IS_ALIGNED(src, CONFIG_ARCH_CACHE_LINE) &&
|
|
size >= sector_size) {
|
|
buf = (uint8_t *)src;
|
|
}
|
|
|
|
tmp_len =
|
|
tmp_len > sector_size ? (tmp_len - tmp_len % sector_size) : tmp_len;
|
|
if (src == buf) {
|
|
if (norflash_dev_erase(norflash_info, addr, tmp_len)) {
|
|
return -1;
|
|
}
|
|
|
|
if (norflash_dev_write(norflash_info, addr, buf, tmp_len)) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (tmp_len < sector_size) {
|
|
xSemaphoreTake(norflash_info->disk_norflash_mutex,
|
|
portMAX_DELAY);
|
|
if (norflash_dev_read(norflash_info,
|
|
ROUNDDOWN(addr, sector_size), buf,
|
|
sector_size)) {
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
|
|
if (norflash_dev_erase(norflash_info,
|
|
ROUNDDOWN(addr, sector_size),
|
|
sector_size)) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (norflash_dev_erase(norflash_info, addr, tmp_len)) {
|
|
return -1;
|
|
}
|
|
xSemaphoreTake(norflash_info->disk_norflash_mutex,
|
|
portMAX_DELAY);
|
|
}
|
|
|
|
for (disk_size_t size_tmp = 0;
|
|
size_tmp < ROUNDUP(tmp_len, sector_size);
|
|
size_tmp += sector_size) {
|
|
memcpy(buf + ((addr + size_tmp) % sector_size), src,
|
|
MIN(sector_size, tmp_len));
|
|
if (norflash_dev_write(norflash_info,
|
|
ROUNDDOWN(addr + size_tmp, sector_size),
|
|
buf, sector_size)) {
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
}
|
|
xSemaphoreGive(norflash_info->disk_norflash_mutex);
|
|
}
|
|
|
|
addr += tmp_len;
|
|
src += tmp_len;
|
|
size -= tmp_len;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
xSemaphoreGive(norflash_info->disk_norflash_mutex);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int disk_norflash_read_block(struct disk_info *info, uint8_t *dst,
|
|
block_t block, block_count_t read_count,
|
|
block_size_t blk_sz)
|
|
{
|
|
ssdk_printf(SSDK_DEBUG,
|
|
"disk norflash read block:%s mem:%p block:%lld"
|
|
"read_count:%d blk_sz:%d addr:%lld size:%d\n",
|
|
info->disk_name, dst, block, read_count, blk_sz, block * blk_sz,
|
|
read_count * blk_sz);
|
|
|
|
return disk_norflash_read(info, dst, (disk_addr_t)block * blk_sz,
|
|
(disk_size_t)read_count * blk_sz);
|
|
}
|
|
|
|
static int disk_norflash_write_block(struct disk_info *info, const uint8_t *src,
|
|
block_t block, block_count_t write_count,
|
|
block_size_t blk_sz)
|
|
{
|
|
disk_size_t write_len;
|
|
disk_size_t remain_len = write_count * blk_sz;
|
|
disk_addr_t addr = block * blk_sz;
|
|
struct disk_norflash_info *norflash_info = info->private_data;
|
|
|
|
ssdk_printf(SSDK_DEBUG,
|
|
"disk norflash write block:%s mem:%p block:%lld"
|
|
"write_count:%d blk_sz:%d addr:%lld size:%d\n",
|
|
info->disk_name, src, block, write_count, blk_sz, block * blk_sz,
|
|
write_count * blk_sz);
|
|
|
|
while (remain_len) {
|
|
write_len = remain_len;
|
|
if (!IS_ALIGNED(src, CONFIG_ARCH_CACHE_LINE)) {
|
|
write_len =
|
|
MIN(ROUNDUP((addr_t)src, CONFIG_ARCH_CACHE_LINE) - (addr_t)src,
|
|
write_len);
|
|
}
|
|
|
|
write_len = write_len > CONFIG_ARCH_CACHE_LINE
|
|
? ROUNDDOWN(write_len, CONFIG_ARCH_CACHE_LINE)
|
|
: write_len;
|
|
|
|
if (write_len < CONFIG_ARCH_CACHE_LINE) {
|
|
xSemaphoreTake(norflash_info->disk_norflash_mutex, portMAX_DELAY);
|
|
memcpy(norflash_info->buff_sector, src, write_len);
|
|
if (norflash_dev_write(norflash_info, addr,
|
|
norflash_info->buff_sector,
|
|
ROUNDUP(write_len, 4))) {
|
|
xSemaphoreGive(norflash_info->disk_norflash_mutex);
|
|
return -1;
|
|
}
|
|
xSemaphoreGive(norflash_info->disk_norflash_mutex);
|
|
} else {
|
|
if (norflash_dev_write(norflash_info, addr, src, write_len)) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
addr += write_len;
|
|
src += write_len;
|
|
remain_len -= write_len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if CONFIG_DISK_NOR_FLASH_BYTES_API_SUPPORT
|
|
static int disk_norflash_erase(struct disk_info *info, disk_addr_t addr,
|
|
disk_size_t size, uint32_t flag)
|
|
{
|
|
int ret = 0;
|
|
struct disk_norflash_info *norflash_info = info->private_data;
|
|
ssdk_printf(SSDK_DEBUG,
|
|
"norflash disk %s erase,addr:%lld size:%lld:"
|
|
"flag:%d\n",
|
|
info->disk_name, addr, size, flag);
|
|
|
|
uint32_t sector_size = norflash_info->config.sector_size;
|
|
uint32_t tmp_len;
|
|
uint8_t *buf;
|
|
|
|
while (size) {
|
|
buf = norflash_info->buff_sector;
|
|
tmp_len = size;
|
|
if (!IS_ALIGNED(addr, sector_size)) {
|
|
tmp_len = MIN(ROUNDUP(addr, sector_size) - addr, size);
|
|
}
|
|
|
|
tmp_len =
|
|
tmp_len > sector_size ? (tmp_len - tmp_len % sector_size) : tmp_len;
|
|
|
|
if (tmp_len < sector_size) {
|
|
xSemaphoreTake(norflash_info->disk_norflash_mutex, portMAX_DELAY);
|
|
if (norflash_dev_read(norflash_info, ROUNDDOWN(addr, sector_size),
|
|
buf, sector_size)) {
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
|
|
if (norflash_dev_erase(norflash_info, ROUNDDOWN(addr, sector_size),
|
|
sector_size) < 0) {
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
|
|
memset(buf + addr % sector_size, 0xff, tmp_len);
|
|
|
|
if (norflash_dev_write(norflash_info, ROUNDDOWN(addr, sector_size),
|
|
buf, sector_size)) {
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
xSemaphoreGive(norflash_info->disk_norflash_mutex);
|
|
} else {
|
|
if (norflash_dev_erase(norflash_info, addr, tmp_len)) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
addr += tmp_len;
|
|
size -= tmp_len;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
xSemaphoreGive(norflash_info->disk_norflash_mutex);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int disk_norflash_erase_group(struct disk_info *info,
|
|
uint32_t erase_block, uint32_t erase_count,
|
|
uint32_t erase_sz, uint32_t flag)
|
|
{
|
|
return norflash_dev_erase(info->private_data,
|
|
(disk_addr_t)erase_block * erase_sz,
|
|
(disk_size_t)erase_count * erase_sz);
|
|
}
|
|
|
|
static int disk_norflash_status(struct disk_info *info)
|
|
{
|
|
return DISK_STATUS_OK;
|
|
}
|
|
|
|
static int disk_norflash_ioctl(struct disk_info *info, uint32_t cmd, void *buff)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static struct disk_operations disk_norflash_ops = {
|
|
#if CONFIG_DISK_NOR_FLASH_BYTES_API_SUPPORT
|
|
.disk_read = disk_norflash_read,
|
|
.disk_write = disk_norflash_write,
|
|
.disk_erase = disk_norflash_erase,
|
|
#else
|
|
.disk_read = NULL,
|
|
.disk_write = NULL,
|
|
.disk_erase = NULL,
|
|
#endif
|
|
.disk_read_block = disk_norflash_read_block,
|
|
.disk_write_block = disk_norflash_write_block,
|
|
|
|
.disk_erase_group = disk_norflash_erase_group,
|
|
.disk_status = disk_norflash_status,
|
|
.disk_ioctl = disk_norflash_ioctl,
|
|
};
|
|
|
|
int register_norflash_disk(struct disk_norflash_info *norflash_info)
|
|
{
|
|
int ret = 0;
|
|
|
|
struct disk_info *info = &norflash_info->info;
|
|
struct disk_norflash_config *norflash_config = &norflash_info->config;
|
|
#ifdef CONFIG_DISK_NOR_FLASH_BYTES_API_SUPPORT
|
|
norflash_info->buff_size = norflash_config->sector_size;
|
|
#else
|
|
norflash_info->buff_size = CONFIG_ARCH_CACHE_LINE;
|
|
#endif
|
|
norflash_info->buff_sector = (uint8_t *)pvPortMallocAligned(
|
|
norflash_info->buff_size, CONFIG_ARCH_CACHE_LINE);
|
|
|
|
if (NULL == norflash_info->buff_sector) {
|
|
ssdk_printf(SSDK_ERR, "norflash disk %s buff malloc error\n",
|
|
norflash_config->disk_name);
|
|
return -1;
|
|
}
|
|
|
|
norflash_info->disk_norflash_mutex = xSemaphoreCreateMutex();
|
|
|
|
/* base on config init info */
|
|
info->disk_name = norflash_config->disk_name;
|
|
info->disk_size = norflash_config->disk_size;
|
|
info->disk_offset = 0;
|
|
info->erase_size = norflash_config->sector_size;
|
|
info->mem_align_size = CONFIG_ARCH_CACHE_LINE;
|
|
info->block_align_size = 4;
|
|
info->private_data = norflash_info;
|
|
info->disk_ops = &disk_norflash_ops;
|
|
|
|
#ifdef CONFIG_DISK_CACHE_NOOP
|
|
struct disk_cache_noop *cache_noop = &norflash_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, "norflash disk register cache noop:%d\n", ret);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
ssdk_printf(SSDK_INFO,
|
|
"register norflash disk:%s disk_size:%lld"
|
|
"erase_size:%d sector_size:%d\n",
|
|
info->disk_name, info->disk_size, info->erase_size,
|
|
norflash_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_norflash_disk(struct disk_norflash_info *norflash_info)
|
|
{
|
|
int ret = 0;
|
|
struct disk_info *info = &norflash_info->info;
|
|
|
|
ssdk_printf(SSDK_INFO, "unregister norflash disk:%s\n", info->disk_name);
|
|
|
|
#ifdef CONFIG_DISK_CACHE_NOOP
|
|
struct disk_cache_noop *cache_noop = &norflash_info->cache_noop;
|
|
/* unregister disk cache noop */
|
|
ret = unregister_disk_cache_noop(cache_noop);
|
|
if (ret < 0) {
|
|
ssdk_printf(SSDK_ERR, "norflash disk unregister cache noop:%d\n", ret);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
vPortFree(norflash_info->buff_sector);
|
|
vSemaphoreDelete(norflash_info->disk_norflash_mutex);
|
|
|
|
xSemaphoreTake(g_disk_mutex, portMAX_DELAY);
|
|
|
|
if (info->use_count) {
|
|
xSemaphoreGive(g_disk_mutex);
|
|
ret = -DISK_ERROR_BUSY;
|
|
ssdk_printf(SSDK_ERR, "norflash disk unregister:%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (list_in_list(&info->node))
|
|
list_delete(&info->node);
|
|
|
|
xSemaphoreGive(g_disk_mutex);
|
|
|
|
return ret;
|
|
}
|