Files
luban-lite-t3e-pro/packages/third-party/dfs/filesystems/elmfat/spinand_disk/spinand_disk.c

348 lines
9.7 KiB
C
Raw Normal View History

2023-11-09 20:19:51 +08:00
/*
* Copyright (c) 2023, ArtInChip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Authors: xuan.wen <xuan.wen@artinchip.com>
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <dfs_bare.h>
#include <rtconfig.h>
#include "aic_osal.h"
#include "spinand_disk.h"
#include "mtd.h"
2024-01-27 08:47:24 +08:00
static DRESULT spinand_disk_nonftl_read(struct spinand_blk_device *blk_device,
uint8_t *buf, uint32_t sector,
uint8_t cnt)
2023-11-09 20:19:51 +08:00
{
rt_size_t sectors_per_page;
rt_size_t start_page, block, offset;
unsigned char *copybuf = NULL;
rt_size_t sectors_read = 0;
rt_size_t copysize = 0;
rt_size_t pages_per_block;
rt_size_t ret = 0;
struct mtd_dev *mtd = blk_device->mtd_device;
2024-01-27 08:47:24 +08:00
struct rt_device_blk_geometry *info;
2023-11-09 20:19:51 +08:00
2024-01-27 08:47:24 +08:00
if (!mtd)
2023-11-09 20:19:51 +08:00
return RES_NOTRDY;
2024-01-27 08:47:24 +08:00
info = &blk_device->info;
2023-11-09 20:19:51 +08:00
pages_per_block = mtd->erasesize / mtd->writesize;
2024-01-27 08:47:24 +08:00
sectors_per_page = mtd->writesize / info->bytes_per_sector;
2023-11-09 20:19:51 +08:00
2024-01-27 08:47:24 +08:00
block = sector * info->bytes_per_sector / mtd->erasesize;
2023-11-09 20:19:51 +08:00
offset = block * mtd->erasesize;
/* Search for the first good block after the given offset */
while (mtd->ops.block_isbad(mtd, offset)) {
pr_warn("Find a bad block, sector adjust to the next block\n");
sector += pages_per_block * sectors_per_page;
block++;
}
start_page = sector / sectors_per_page;
offset = start_page * mtd->writesize;
/*sector is not aligned with page, read unalign part first*/
if (sector % sectors_per_page) {
memset(blk_device->pagebuf, 0xFF, mtd->writesize);
ret = mtd->ops.read_oob(mtd, offset, blk_device->pagebuf,
mtd->writesize, NULL, 0);
if (ret != RT_EOK) {
pr_err("Mtd read page failed!\n");
return -RT_ERROR;
}
2024-01-27 08:47:24 +08:00
copybuf = blk_device->pagebuf;
copybuf += (sector % sectors_per_page) * info->bytes_per_sector;
2023-11-09 20:19:51 +08:00
if (cnt > (sectors_per_page - sector % sectors_per_page)) {
copysize = (sectors_per_page - sector % sectors_per_page) *
2024-01-27 08:47:24 +08:00
info->bytes_per_sector;
2023-11-09 20:19:51 +08:00
sectors_read += (sectors_per_page - sector % sectors_per_page);
} else {
2024-01-27 08:47:24 +08:00
copysize = cnt * info->bytes_per_sector;
2023-11-09 20:19:51 +08:00
sectors_read += cnt;
}
memcpy(buf, copybuf, copysize);
buf += copysize;
start_page++;
}
if (cnt - sectors_read == 0)
return RES_OK;
#ifdef AIC_SPINAND_CONT_READ
if ((cnt - sectors_read) > sectors_per_page) {
uint8_t *data_ptr = RT_NULL;
2024-01-27 08:47:24 +08:00
uint32_t copydata = (cnt - sectors_read) * info->bytes_per_sector;
2023-11-09 20:19:51 +08:00
data_ptr = (uint8_t *)aicos_malloc_align(0, copydata, CACHE_LINE_SIZE);
if (data_ptr == RT_NULL) {
pr_err("Malloc buf data_ptr failed\n");
goto exit_spinand_disk_read_malloc;
}
memset(data_ptr, 0, copydata);
offset = start_page * mtd->writesize;
ret = mtd->ops.cont_read(mtd, offset, data_ptr, copydata);
if (ret != RT_EOK) {
pr_err("continuous_read failed!\n");
goto exit_spinand_disk_read;
}
memcpy(buf, data_ptr, copydata);
if (data_ptr)
aicos_free_align(0, data_ptr);
return RES_OK;
exit_spinand_disk_read:
if (data_ptr)
aicos_free_align(0, data_ptr);
}
exit_spinand_disk_read_malloc:
#endif
/*sector is aligned with page*/
while (cnt > sectors_read) {
if (start_page / pages_per_block != block) {
block = start_page / pages_per_block;
offset = block * mtd->erasesize;
while (mtd->ops.block_isbad(mtd, offset)) {
pr_warn("Find a bad block, sector adjust to the next block\n");
block++;
start_page += pages_per_block;
}
}
memset(blk_device->pagebuf, 0xFF, mtd->writesize);
offset = start_page * mtd->writesize;
ret = mtd->ops.read_oob(mtd, offset, blk_device->pagebuf,
mtd->writesize, NULL, 0);
if (ret != RT_EOK) {
pr_err("Mtd read page failed!\n");
return -RT_ERROR;
}
if ((cnt - sectors_read) > sectors_per_page) {
2024-01-27 08:47:24 +08:00
copysize = sectors_per_page * info->bytes_per_sector;
2023-11-09 20:19:51 +08:00
sectors_read += sectors_per_page;
} else {
2024-01-27 08:47:24 +08:00
copysize = (cnt - sectors_read) * info->bytes_per_sector;
2023-11-09 20:19:51 +08:00
sectors_read += (cnt - sectors_read);
}
memcpy(buf, blk_device->pagebuf, copysize);
buf += copysize;
start_page++;
}
return RES_OK;
}
2024-01-27 08:47:24 +08:00
#ifdef AIC_NFTL_SUPPORT
DRESULT spinand_disk_nftl_read(struct spinand_blk_device *blk_device,
uint8_t *buf, uint32_t sector, uint8_t cnt)
{
if (!blk_device->nftl_handler)
return RES_NOTRDY;
nftl_api_read(blk_device->nftl_handler, sector, cnt, buf);
return RES_OK;
}
static DRESULT spinand_disk_nftl_write(struct spinand_blk_device *blk_device,
const uint8_t *buf, uint32_t sector,
uint8_t cnt)
2023-11-09 20:19:51 +08:00
{
2024-01-27 08:47:24 +08:00
if (!blk_device->nftl_handler)
return RES_NOTRDY;
nftl_api_write(blk_device->nftl_handler, sector, cnt, (uint8_t *)buf);
return RES_OK;
}
#endif
DRESULT spinand_disk_write(void *hdisk, const uint8_t *buf, uint32_t sector,
uint8_t cnt)
{
struct spinand_blk_device *dev;
dev = hdisk;
if (!dev)
return RES_NOTRDY;
#ifdef AIC_NFTL_SUPPORT
if (dev->attr == PART_ATTR_NFTL) {
return spinand_disk_nftl_write(dev, buf, sector, cnt);
}
#endif
return RES_OK;
}
DRESULT spinand_disk_read(void *hdisk, uint8_t *buf, uint32_t sector,
uint8_t cnt)
{
struct spinand_blk_device *dev;
dev = hdisk;
if (!dev)
return RES_NOTRDY;
#ifdef AIC_NFTL_SUPPORT
if (dev->attr == PART_ATTR_NFTL)
return spinand_disk_nftl_read(dev, buf, sector, cnt);
#endif
return spinand_disk_nonftl_read(dev, buf, sector, cnt);
}
DRESULT spinand_disk_ioctl(void *hdisk, uint8_t command, void *buf)
{
struct spinand_blk_device *dev;
2023-11-09 20:19:51 +08:00
DRESULT result = RES_OK;
2024-01-27 08:47:24 +08:00
dev = hdisk;
if (!dev || !dev->mtd_device)
return RES_NOTRDY;
2023-11-09 20:19:51 +08:00
switch (command) {
case GET_SECTOR_COUNT:
if (buf) {
2024-01-27 08:47:24 +08:00
*(uint32_t *)buf = dev->info.sector_count;
2023-11-09 20:19:51 +08:00
} else {
result = RES_PARERR;
}
break;
case GET_SECTOR_SIZE:
if (buf) {
2024-01-27 08:47:24 +08:00
*(uint32_t *)buf = dev->info.bytes_per_sector;
2023-11-09 20:19:51 +08:00
} else {
result = RES_PARERR;
}
break;
case GET_BLOCK_SIZE:
if (buf) {
2024-01-27 08:47:24 +08:00
*(uint32_t *)buf = dev->info.block_size;
2023-11-09 20:19:51 +08:00
} else {
result = RES_PARERR;
}
break;
case CTRL_SYNC:
2024-01-27 08:47:24 +08:00
#ifdef AIC_NFTL_SUPPORT
if (dev->attr == PART_ATTR_NFTL) {
nftl_api_write_cache(dev->nftl_handler, 0xffff);
}
#endif
2023-11-09 20:19:51 +08:00
result = RES_OK;
break;
default:
result = RES_PARERR;
break;
}
return result;
}
2024-01-27 08:47:24 +08:00
DSTATUS spinand_disk_status(void *hdisk)
2023-11-09 20:19:51 +08:00
{
return RES_OK;
}
2024-01-27 08:47:24 +08:00
void *spinand_disk_initialize(const char *device_name)
2023-11-09 20:19:51 +08:00
{
2024-01-27 08:47:24 +08:00
struct spinand_blk_device *blk_device;
struct mtd_dev *mtd;
blk_device = (void *)malloc(sizeof(*blk_device));
2023-11-09 20:19:51 +08:00
if (!blk_device) {
pr_err("Error: no memory for create SPI NAND block device");
2024-01-27 08:47:24 +08:00
return NULL;
2023-11-09 20:19:51 +08:00
}
2024-01-27 08:47:24 +08:00
memset(blk_device, 0, sizeof(*blk_device));
2023-11-09 20:19:51 +08:00
/*Obtain devices by part name*/
2024-01-27 08:47:24 +08:00
mtd = mtd_get_device(device_name);
if (!mtd) {
2023-11-09 20:19:51 +08:00
pr_err("Failed to get mtd %s\n", device_name);
2024-01-27 08:47:24 +08:00
goto err;
2023-11-09 20:19:51 +08:00
}
2024-01-27 08:47:24 +08:00
blk_device->mtd_device = mtd;
#ifdef AIC_NFTL_SUPPORT
if (blk_device->mtd_device->attr == PART_ATTR_NFTL) {
struct nftl_api_handler_t *nftl_hdl;
nftl_hdl = malloc(sizeof(struct nftl_api_handler_t));
if (!nftl_hdl) {
pr_err("Failed to allocate memory for nftl_handler");
goto err;
}
blk_device->nftl_handler = nftl_hdl;
memset(nftl_hdl, 0, sizeof(struct nftl_api_handler_t));
nftl_hdl->priv_mtd = (void *)mtd;
nftl_hdl->nandt = malloc(sizeof(struct nftl_api_nand_t));
nftl_hdl->nandt->page_size = mtd->writesize;
nftl_hdl->nandt->oob_size = mtd->oobsize;
nftl_hdl->nandt->pages_per_block = mtd->erasesize / mtd->writesize;
nftl_hdl->nandt->block_total = mtd->size / mtd->erasesize;
nftl_hdl->nandt->block_start = mtd->start / mtd->erasesize;
nftl_hdl->nandt->block_end = (mtd->start + mtd->size) / mtd->erasesize;
if (nftl_api_init(nftl_hdl, 1)) {
pr_err("[NE]nftl_initialize failed\n");
goto err;
}
}
#endif
blk_device->attr = mtd->attr;
blk_device->pagebuf =
aicos_malloc_align(0, mtd->writesize, CACHE_LINE_SIZE);
2023-11-09 20:19:51 +08:00
if (!blk_device->pagebuf) {
pr_err("Malloc buf failed\n");
2024-01-27 08:47:24 +08:00
goto err;
2023-11-09 20:19:51 +08:00
}
2024-01-27 08:47:24 +08:00
blk_device->info.bytes_per_sector = 512;
blk_device->info.block_size = blk_device->info.bytes_per_sector;
blk_device->info.sector_count =
mtd->size / blk_device->info.bytes_per_sector;
return blk_device;
err:
if (blk_device && blk_device->pagebuf)
aicos_free_align(0, blk_device->pagebuf);
#ifdef AIC_NFTL_SUPPORT
if (blk_device->nftl_handler && blk_device->nftl_handler->nandt)
free(blk_device->nftl_handler->nandt);
if (blk_device->nftl_handler)
free(blk_device->nftl_handler);
#endif
if (blk_device)
free(blk_device);
return NULL;
2023-11-09 20:19:51 +08:00
}