Files
luban-lite-t3e-pro/bsp/artinchip/drv/spinand/spinand_port.c
刘可亮 803cac77d5 V1.0.6
2024-09-03 11:16:08 +08:00

925 lines
25 KiB
C

/*
* Copyright (c) 2022-2024, ArtInChip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Authors: xuan.wen <xuan.wen@artinchip.com>
*/
#include <string.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <finsh.h>
#include <drv_qspi.h>
#include <drv_spienc.h>
#include <partition_table.h>
#include <boot_param.h>
#include <private_param.h>
#include "spinand.h"
#include "spinand_parts.h"
#include "spinand_block.h"
static struct rt_mtd_nand_device *g_mtd_partitions;
static int g_mtd_partitions_cnt;
struct aic_spinand *aic_nand;
void qspi_messages_init(struct rt_qspi_message *qspi_messages,
struct spi_nand_cmd_cfg *cfg, u32 addr, u8 *sendbuff,
u8 *recvbuff, u32 datacount)
{
/* 1-bit mode */
qspi_messages->instruction.content = cfg->opcode;
qspi_messages->instruction.qspi_lines = cfg->opcode_bits;
qspi_messages->address.content = addr;
qspi_messages->address.size = cfg->addr_bytes;
qspi_messages->address.qspi_lines = cfg->addr_bits;
if (cfg->addr_bits)
qspi_messages->dummy_cycles =
(cfg->dummy_bytes * 8) / qspi_messages->address.qspi_lines;
else
qspi_messages->dummy_cycles =
(cfg->dummy_bytes * 8) / qspi_messages->instruction.qspi_lines;
/* 4-bit mode */
qspi_messages->qspi_data_lines = cfg->data_bits;
qspi_messages->parent.cs_take = 1;
qspi_messages->parent.cs_release = 1;
qspi_messages->parent.send_buf = sendbuff;
qspi_messages->parent.recv_buf = recvbuff;
qspi_messages->parent.length = datacount;
qspi_messages->parent.next = NULL;
}
int aic_spinand_transfer_message(struct aic_spinand *flash,
struct spi_nand_cmd_cfg *cfg, u32 addr,
u8 *sendbuff, u8 *recvbuff, u32 datacount)
{
int result;
struct rt_qspi_message qspi_messages = { 0 };
struct rt_qspi_device *device = (struct rt_qspi_device *)flash->user_data;
RT_ASSERT(flash != RT_NULL);
RT_ASSERT(device != RT_NULL);
qspi_messages_init(&qspi_messages, cfg, addr, sendbuff, recvbuff,
datacount);
result = rt_mutex_take(&(device->parent.bus->lock), RT_WAITING_FOREVER);
if (result != SPINAND_SUCCESS) {
rt_set_errno(-RT_EBUSY);
return result;
}
/* reset errno */
rt_set_errno(SPINAND_SUCCESS);
/* configure SPI bus */
if (device->parent.bus->owner != &device->parent) {
/* not the same owner as current, re-configure SPI bus */
result = device->parent.bus->ops->configure(&device->parent,
&device->config.parent);
if (result == SPINAND_SUCCESS) {
/* set SPI bus owner */
device->parent.bus->owner = &device->parent;
} else {
pr_err("Configure SPI bus failed\n");
rt_set_errno(-SPINAND_ERR);
goto __exit;
}
}
/* transmit each SPI message */
if (device->parent.bus->ops->xfer(&device->parent, &qspi_messages.parent) <
0) {
pr_err("Xfer SPI bus failed\n");
result = -SPINAND_ERR;
rt_set_errno(-SPINAND_ERR);
goto __exit;
}
result = SPINAND_SUCCESS;
__exit:
/* release bus lock */
rt_mutex_release(&(device->parent.bus->lock));
return result;
}
static void spinand_dump_buffer(int page, rt_uint8_t *buf, int len,
const char *title)
{
#if defined(AIC_SPINAND_DRV_DEBUG)
int i;
RT_ASSERT(buf != RT_NULL);
RT_ASSERT(len != 0);
rt_kprintf("[%s-Page-%d]\n", title, page);
for (i = 0; i < len; i++) {
rt_kprintf("%02X ", buf[i]);
if (i % 32 == 31)
rt_kprintf("\n");
}
rt_kprintf("\n");
#endif
}
static rt_err_t spinand_read_id(struct rt_mtd_nand_device *device)
{
rt_err_t result = RT_EOK;
u32 id = 0;
struct aic_spinand *flash = (struct aic_spinand *)device->priv;
RT_ASSERT(device != RT_NULL);
result = rt_mutex_take(flash->lock, RT_WAITING_FOREVER);
RT_ASSERT(result == RT_EOK);
spinand_read_id_op(flash, (u8 *)&id);
result = rt_mutex_release(flash->lock);
RT_ASSERT(result == RT_EOK);
pr_info("Id: 0x%08x\n", id);
result = (id != 0x0) ? RT_EOK : -RT_ERROR;
return result;
}
static rt_err_t spinand_mtd_read(struct rt_mtd_nand_device *device,
rt_off_t page, rt_uint8_t *data,
rt_uint32_t data_len, rt_uint8_t *spare,
rt_uint32_t spare_len)
{
struct aic_spinand *flash;
rt_err_t result;
RT_ASSERT(device != RT_NULL);
if (page / device->pages_per_block > device->block_end) {
pr_err("[Error] read page:%d\n", page);
return -RT_MTD_EIO;
}
flash = (struct aic_spinand *)device->priv;
result = rt_mutex_take(flash->lock, RT_WAITING_FOREVER);
RT_ASSERT(result == RT_EOK);
result = spinand_read_page(flash, page, data, data_len, spare, spare_len);
rt_mutex_release(flash->lock);
return result;
}
#ifdef AIC_SPINAND_CONT_READ
static rt_err_t spinand_mtd_continuous_read(struct rt_mtd_nand_device *device,
rt_off_t page, rt_uint8_t *data,
rt_uint32_t size)
{
rt_err_t result;
struct aic_spinand *flash = (struct aic_spinand *)device->priv;
result = rt_mutex_take(flash->lock, RT_WAITING_FOREVER);
RT_ASSERT(result == RT_EOK);
result = spinand_continuous_read(flash, page, data, size);
rt_mutex_release(flash->lock);
return result;
}
#else
static rt_err_t spinand_mtd_continuous_read(struct rt_mtd_nand_device *device,
rt_off_t page, rt_uint8_t *data,
rt_uint32_t size)
{
pr_err("Please enable config AIC_SPINAND_CONT_READ!.\n");
return -1;
}
#endif
static rt_err_t spinand_mtd_write(struct rt_mtd_nand_device *device,
rt_off_t page, const rt_uint8_t *data,
rt_uint32_t data_len, const rt_uint8_t *spare,
rt_uint32_t spare_len)
{
struct aic_spinand *flash = (struct aic_spinand *)device->priv;
RT_ASSERT(device != RT_NULL);
rt_err_t result = RT_EOK;
if (page / device->pages_per_block > device->block_end) {
pr_err("[Error] write page:%d\n", page);
return -RT_MTD_EIO;
}
spinand_dump_buffer(page, (uint8_t *)data, data_len, "WRITE DATA");
spinand_dump_buffer(page, (uint8_t *)spare, spare_len, "WRITE SPARE");
result = rt_mutex_take(flash->lock, RT_WAITING_FOREVER);
RT_ASSERT(result == RT_EOK);
result = spinand_write_page(flash, page, data, data_len, spare, spare_len);
rt_mutex_release(flash->lock);
return result;
}
static rt_err_t spinand_mtd_erase(struct rt_mtd_nand_device *device,
rt_uint32_t block)
{
rt_err_t result = RT_EOK;
struct aic_spinand *flash = (struct aic_spinand *)device->priv;
RT_ASSERT(device != RT_NULL);
if (block > device->block_end) {
pr_err("[Error] block:%d block_end:%d\n", block, device->block_end);
return -RT_MTD_EIO;
}
pr_debug("erase block: %d\n", block);
result = rt_mutex_take(flash->lock, RT_WAITING_FOREVER);
RT_ASSERT(result == RT_EOK);
result = spinand_block_erase(flash, block);
if (result != RT_EOK)
goto exit_spinand_mtd_erase;
result = RT_EOK;
exit_spinand_mtd_erase:
rt_mutex_release(flash->lock);
return result;
}
static rt_err_t spinand_mtd_block_isbad(struct rt_mtd_nand_device *device,
rt_uint32_t block)
{
struct aic_spinand *flash = (struct aic_spinand *)device->priv;
rt_err_t result = RT_EOK;
RT_ASSERT(device != RT_NULL);
if (block > device->block_end) {
pr_err("[Error] block:%d\n", block);
return -RT_MTD_EIO;
}
pr_debug("check block status: %d\n", block);
result = rt_mutex_take(flash->lock, RT_WAITING_FOREVER);
RT_ASSERT(result == RT_EOK);
result = spinand_block_isbad(flash, block);
rt_mutex_release(flash->lock);
return result;
}
static rt_err_t spinand_mtd_block_markbad(struct rt_mtd_nand_device *device,
rt_uint32_t block)
{
rt_err_t result = RT_EOK;
struct aic_spinand *flash = (struct aic_spinand *)device->priv;
RT_ASSERT(device != RT_NULL);
if (block > device->block_end) {
pr_err("[Error] block:%d\n", block);
return -RT_MTD_EIO;
}
pr_info("mark bad block: %d\n", block);
result = rt_mutex_take(flash->lock, RT_WAITING_FOREVER);
RT_ASSERT(result == RT_EOK);
/* Erase block after checking it is bad or not. */
if (spinand_block_isbad(flash, block) != 0) {
pr_warn("Block %d is bad.\n", block);
result = RT_EOK;
} else {
result = spinand_block_markbad(flash, block);
}
rt_mutex_release(flash->lock);
return result;
}
static rt_uint32_t spinand_get_block_status(struct rt_mtd_nand_device *device,
rt_uint32_t block)
{
rt_uint32_t result = RT_EOK;
struct aic_spinand *flash = (struct aic_spinand *)device->priv;
RT_ASSERT(device != RT_NULL);
if (block > device->block_end) {
pr_err("[Error] block:%d\n", block);
return -RT_MTD_EIO;
}
result = rt_mutex_take(flash->lock, RT_WAITING_FOREVER);
RT_ASSERT(result == RT_EOK);
result = spinand_get_status(flash, block);
rt_mutex_release(flash->lock);
return result;
}
static rt_err_t spinand_set_block_status(struct rt_mtd_nand_device *device,
rt_uint32_t block, rt_uint32_t block_pos,
rt_uint32_t status)
{
rt_err_t result = RT_EOK;
struct aic_spinand *flash = (struct aic_spinand *)device->priv;
RT_ASSERT(device != RT_NULL);
if (block > device->block_end) {
pr_err("[Error] block:%d\n", block);
return -RT_MTD_EIO;
}
result = rt_mutex_take(flash->lock, RT_WAITING_FOREVER);
RT_ASSERT(result == RT_EOK);
result = spinand_set_status(flash, block, block_pos, status);
rt_mutex_release(flash->lock);
return result;
}
static int nand_read_data(void *dev, unsigned long offset, void *buf,
unsigned long len)
{
struct aic_spinand *flash = dev;
rt_err_t result;
rt_off_t page;
result = rt_mutex_take(flash->lock, RT_WAITING_FOREVER);
RT_ASSERT(result == RT_EOK);
page = offset / flash->info->page_size;
result = spinand_read_page(flash, page, buf, len, NULL, 0);
rt_mutex_release(flash->lock);
return result;
}
static char *aic_spinand_get_partition_string(struct aic_spinand *flash)
{
char *parts = NULL;
void *res_addr = NULL;
#ifdef AIC_SECONED_FLASH_NAND
parts = rt_strdup(IMAGE_CFG_JSON_PARTS_MTD);
return parts;
#endif
res_addr = aic_get_boot_resource_from_nand(flash, flash->info->page_size,
nand_read_data);
parts = private_get_partition_string(res_addr);
if (parts == NULL)
parts = IMAGE_CFG_JSON_PARTS_MTD;
if (parts) {
parts = rt_strdup(parts);
}
if (res_addr)
free(res_addr);
return parts;
}
static struct rt_mtd_nand_driver_ops spinand_ops = {
spinand_read_id, spinand_mtd_read,
spinand_mtd_write, NULL,
spinand_mtd_erase, spinand_mtd_block_isbad,
spinand_mtd_block_markbad, spinand_mtd_continuous_read,
spinand_set_block_status, spinand_get_block_status
};
static uint32_t get_spinand_bus_id(void)
{
#if defined(AIC_SECONED_FLASH_NAND)
#if defined(AIC_QSPI1_DEVICE_SPINAND)
return 1;
#elif defined(AIC_QSPI2_DEVICE_SPINAND)
return 2;
#endif
#endif
return 0;
}
rt_err_t rt_hw_mtd_spinand_init(struct aic_spinand *flash)
{
struct nftl_mtd *nftl_parts = NULL;
struct mtd_partition *parts, *p;
rt_uint32_t blocksize;
char *partstr = NULL;
uint32_t spi_bus = 0;
rt_err_t result;
int i = 0, cnt;
if (flash->IsInited)
return RT_EOK;
flash->lock = rt_mutex_create("spinand", RT_IPC_FLAG_PRIO);
if (flash->lock == RT_NULL) {
pr_err("Create mutex in rt_hw_mtd_spinand_init failed\n");
return -1;
}
result = spinand_flash_init(flash);
if (result != RT_EOK)
return -RT_ERROR;
partstr = aic_spinand_get_partition_string(flash);
#ifdef IMAGE_CFG_JSON_PARTS_NFTL
nftl_parts = build_nftl_list(IMAGE_CFG_JSON_PARTS_NFTL);
#endif
spi_bus = get_spinand_bus_id();
parts = mtd_parts_parse(partstr, spi_bus);
if (partstr)
free(partstr);
p = parts;
cnt = 0;
while (p) {
cnt++;
if (partition_nftl_is_exist(p->name, nftl_parts)) {
p->attr = PART_ATTR_NFTL;
} else {
p->attr = PART_ATTR_MTD;
}
p = p->next;
}
#ifdef IMAGE_CFG_JSON_PARTS_NFTL
free_nftl_list(nftl_parts);
#endif
g_mtd_partitions_cnt = cnt;
g_mtd_partitions = rt_malloc(sizeof(struct rt_mtd_nand_device) * cnt);
if (!g_mtd_partitions) {
pr_err("malloc buf failed\n");
return -1;
}
blocksize = flash->info->page_size * flash->info->pages_per_eraseblock;
p = parts;
for (i = 0; i < cnt; i++) {
g_mtd_partitions[i].page_size = flash->info->page_size;
g_mtd_partitions[i].pages_per_block = flash->info->pages_per_eraseblock;
g_mtd_partitions[i].oob_size = flash->info->oob_size;
g_mtd_partitions[i].oob_free = 32;
g_mtd_partitions[i].ops = &spinand_ops;
g_mtd_partitions[i].block_start = p->start / blocksize;
g_mtd_partitions[i].block_end = (p->start + p->size - 1) / blocksize;
g_mtd_partitions[i].block_total = p->size / blocksize;
g_mtd_partitions[i].priv = flash;
g_mtd_partitions[i].attr = p->attr;
result = rt_mtd_nand_register_device(p->name, &g_mtd_partitions[i]);
RT_ASSERT(result == RT_EOK);
p = p->next;
}
p = parts;
for (i = 0; i < cnt; i++) {
result = rt_blk_nand_register_device(p->name, &g_mtd_partitions[i]);
RT_ASSERT(result == RT_EOK);
p = p->next;
}
flash->databuf = aicos_malloc_align(
0, flash->info->page_size + flash->info->oob_size, CACHE_LINE_SIZE);
if (!flash->databuf) {
pr_err("malloc buf failed\n");
return -1;
}
flash->oobbuf = flash->databuf + flash->info->page_size;
flash->IsInited = true;
return result;
}
rt_err_t rt_hw_mtd_spinand_register(const char *device_name, u32 bus_hz)
{
rt_device_t pDev;
rt_err_t result;
struct rt_qspi_device *device;
struct aic_spinand *spinand;
if ((pDev = rt_device_find(device_name)) == RT_NULL)
return -RT_ERROR;
spinand = rt_calloc(1, sizeof(struct aic_spinand));
if (!spinand) {
pr_err("malloc buf failed\n");
return -RT_ERROR;
}
device = (struct rt_qspi_device *)pDev;
spinand->user_data = device;
device->config.parent.mode = RT_SPI_MODE_0;
device->config.parent.data_width = 8;
device->config.parent.max_hz = bus_hz;
device->config.ddr_mode = 0;
spinand->qspi_dl_width = device->config.qspi_dl_width;
aic_nand = spinand;
result = rt_hw_mtd_spinand_init(spinand);
return result;
}
#if defined(AIC_SPINAND_DRV_DEBUG)
static int nread(int argc, char **argv)
{
int ret = -1;
rt_uint8_t *spare = RT_NULL;
rt_uint8_t *data_ptr = RT_NULL;
struct rt_mtd_nand_device *device;
rt_uint32_t partition, page;
struct aic_spinand *flash = RT_NULL;
if (argc != 3) {
pr_err("Usage %s: %s <partition_no> <page>.\n", __func__, __func__);
goto exit_nread;
}
page = atoi(argv[2]);
partition = atoi(argv[1]);
if (partition >= g_mtd_partitions_cnt)
goto exit_nread;
device = &g_mtd_partitions[partition];
flash = (struct aic_spinand *)device->priv;
data_ptr = (rt_uint8_t *)rt_malloc(flash->info->page_size);
if (data_ptr == RT_NULL) {
pr_err("data_ptr: no memory\n");
goto exit_nread;
}
spare = (rt_uint8_t *)rt_malloc(flash->info->oob_size);
if (spare == RT_NULL) {
pr_err("spare: no memory\n");
goto exit_nread;
}
rt_memset(spare, 0, flash->info->oob_size);
rt_memset(data_ptr, 0, flash->info->page_size);
page = page + device->block_start * device->pages_per_block;
if (spinand_mtd_read(device, page, &data_ptr[0], flash->info->page_size,
&spare[0], flash->info->oob_size) != RT_EOK)
goto exit_nread;
pr_info("Partition:%d page-%d\n", partition, page);
spinand_dump_buffer(page, data_ptr, flash->info->page_size, "Read Data");
spinand_dump_buffer(page, spare, flash->info->oob_size, "Read Spare");
ret = 0;
exit_nread:
/* release memory */
if (data_ptr)
rt_free(data_ptr);
if (spare)
rt_free(spare);
return ret;
}
#ifdef AIC_SPINAND_CONT_READ
static int ncontread(int argc, char **argv)
{
int ret = -1;
rt_uint8_t *data_ptr = RT_NULL;
struct rt_mtd_nand_device *device;
rt_uint32_t partition, page, size;
rt_uint64_t start_us;
if (argc != 4) {
pr_err("Usage %s: %s <partition_no> <page> <size>.\n", __func__,
__func__, __func__);
goto exit_ncontread;
}
partition = atoi(argv[1]);
page = atoi(argv[2]);
size = atoi(argv[3]);
if (partition >= g_mtd_partitions_cnt)
goto exit_ncontread;
device = &g_mtd_partitions[partition];
data_ptr = (rt_uint8_t *)rt_malloc_align(size, CACHE_LINE_SIZE);
if (data_ptr == RT_NULL) {
pr_err("data_ptr: no memory\n");
goto exit_ncontread;
}
rt_memset(data_ptr, 0, size);
page = page + device->block_start * device->pages_per_block;
start_us = aic_get_time_us();
if (spinand_mtd_continuous_read(device, page, data_ptr, size) != RT_EOK) {
pr_err("spinand continuous read failed\n");
goto exit_ncontread;
}
start_us = aic_get_time_us() - start_us;
pr_info("start_us = %d size = %ud\n", start_us, size);
pr_info("Partition:%d page-%d\n", partition, page);
spinand_dump_buffer(page, (uint8_t *)data_ptr, size, "CONT READ");
ret = 0;
exit_ncontread:
/* release memory */
if (data_ptr)
rt_free_align(data_ptr);
return ret;
}
#endif
static int nwrite(int argc, char **argv)
{
int i, ret = -1;
rt_uint8_t *data_ptr = RT_NULL;
struct rt_mtd_nand_device *device;
rt_uint32_t partition, page;
struct aic_spinand *flash = RT_NULL;
if (argc != 3) {
pr_err("Usage %s: %s <partition_no> <page>.\n", __func__, __func__);
goto exit_nwrite;
}
partition = atoi(argv[1]);
page = atoi(argv[2]);
if (partition >= g_mtd_partitions_cnt)
goto exit_nwrite;
device = &g_mtd_partitions[partition];
flash = (struct aic_spinand *)device->priv;
data_ptr = (rt_uint8_t *)rt_malloc(flash->info->page_size);
if (data_ptr == RT_NULL) {
pr_err("data_ptr: no memory\n");
goto exit_nwrite;
}
/* Need random data to test ECC */
for (i = 0; i < flash->info->page_size; i++)
data_ptr[i] = i / 5 - i;
page = page + device->block_start * device->pages_per_block;
spinand_mtd_write(device, page, &data_ptr[0], flash->info->page_size, NULL,
0);
pr_debug("Write data into %d in partition-index %d.\n", page, partition);
ret = 0;
exit_nwrite:
/* release memory */
if (data_ptr)
rt_free(data_ptr);
return ret;
}
#endif
static int nerase(int argc, char **argv)
{
struct rt_mtd_nand_device *device;
int partition, block;
if (argc != 3) {
pr_err("Usage %s: %s <partition_no> <block_no>.\n", __func__, __func__);
goto exit_nerase;
}
partition = atoi(argv[1]);
block = atoi(argv[2]);
if (partition >= g_mtd_partitions_cnt)
goto exit_nerase;
device = &g_mtd_partitions[partition];
if (spinand_mtd_erase(device, block + device->block_start) != RT_EOK)
goto exit_nerase;
pr_info("Erased block %d in partition-index %d.\n",
block + device->block_start, partition);
return 0;
exit_nerase:
return -1;
}
static rt_err_t nmarkbad(int argc, char **argv)
{
struct rt_mtd_nand_device *device;
int partition, block;
if (argc != 3) {
pr_err("Usage %s: %s <partition_no> <block_no>.\n", __func__, __func__);
goto exit_nmarkbad;
}
partition = atoi(argv[1]);
block = atoi(argv[2]);
if (partition >= g_mtd_partitions_cnt)
goto exit_nmarkbad;
device = &g_mtd_partitions[partition];
if (spinand_mtd_block_markbad(device, block + device->block_start) !=
RT_EOK)
goto exit_nmarkbad;
pr_info("Marked block %d in partition-index %d.\n",
block + device->block_start, partition);
return 0;
exit_nmarkbad:
return -1;
}
static int nerase_all(int argc, char **argv)
{
rt_uint32_t index;
rt_uint32_t partition;
struct rt_mtd_nand_device *device;
if (argc != 2) {
pr_err("Usage %s: %s <partition_no>.\n", __func__, __func__);
goto exit_nerase_all;
}
partition = atoi(argv[1]);
if (partition >= g_mtd_partitions_cnt)
goto exit_nerase_all;
device = &g_mtd_partitions[partition];
for (index = 0; index < device->block_total; index++) {
spinand_mtd_erase(device, index + device->block_start);
}
pr_info("Erased all block in partition-index %d.\n", partition);
return 0;
exit_nerase_all:
return -1;
}
static int ncheck_all(int argc, char **argv)
{
rt_uint32_t index;
rt_uint32_t partition;
struct rt_mtd_nand_device *device;
if (argc != 2) {
pr_err("Usage %s: %s <partition_no>.\n", __func__, __func__);
return -1;
}
partition = atoi(argv[1]);
if (partition >= g_mtd_partitions_cnt)
return -1;
device = &g_mtd_partitions[partition];
for (index = 0; index < device->block_total; index++) {
pr_info("Partition:%d Block-%d is %s\n", partition, index,
spinand_mtd_block_isbad(device, index + device->block_start) ?
"bad" :
"good");
}
return 0;
}
static int nid(int argc, char **argv)
{
rt_uint32_t id;
if (NULL == aic_nand)
return -1;
spinand_read_id_op(aic_nand, (u8 *)&id);
rt_kprintf("nid: 0x%x\n", id);
return 0;
}
static int nlist(int argc, char **argv)
{
rt_uint32_t index;
struct rt_mtd_nand_device *device;
rt_kprintf("\n");
for (index = 0; index < g_mtd_partitions_cnt; index++) {
device = &g_mtd_partitions[index];
rt_kprintf("[Partition #%d]\n", index);
rt_kprintf("Name: %s\n", device->parent.parent.name);
rt_kprintf("Start block: %d\n", device->block_start);
rt_kprintf("End block: %d\n", device->block_end);
rt_kprintf("Block number: %d\n", device->block_total);
rt_kprintf("Plane number: %d\n", device->plane_num);
rt_kprintf("Pages per Block: %d\n", device->pages_per_block);
rt_kprintf("Page size: %d bytes\n", device->page_size);
rt_kprintf("Spare size: %d bytes\n", device->oob_size);
rt_kprintf("Total size: %d bytes (%d KB)\n",
device->block_total * device->pages_per_block *
device->page_size,
device->block_total * device->pages_per_block *
device->page_size / 1024);
rt_kprintf("\n");
}
return 0;
}
#ifdef FINSH_USING_MSH
MSH_CMD_EXPORT(nid, nand id);
MSH_CMD_EXPORT(nlist, list all partition information on nand);
MSH_CMD_EXPORT(nerase, nand erase a block of a partiton);
MSH_CMD_EXPORT(nerase_all, erase all blocks of a partition);
MSH_CMD_EXPORT(ncheck_all, check all blocks of a partition);
MSH_CMD_EXPORT(nmarkbad, nand mark a bad block of a partition);
#if defined(AIC_SPINAND_DRV_DEBUG)
MSH_CMD_EXPORT(nwrite, test nand write page);
MSH_CMD_EXPORT(nread, test nand read page);
#ifdef AIC_SPINAND_CONT_READ
MSH_CMD_EXPORT(ncontread, test nand cont read);
#endif
#endif
#endif
static int rt_hw_spinand_register(void)
{
#if defined(AIC_QSPI0_DEVICE_SPINAND)
aic_qspi_bus_attach_device("qspi0", "spinand0", 0, AIC_QSPI0_BUS_WIDTH,
RT_NULL, RT_NULL);
rt_hw_mtd_spinand_register("spinand0", AIC_QSPI0_DEVICE_SPINAND_FREQ);
#endif
#if defined(AIC_SECONED_FLASH_NAND)
#if defined(AIC_QSPI2_DEVICE_SPINAND)
aic_qspi_bus_attach_device("qspi2", "spinand1", 0, AIC_QSPI2_BUS_WIDTH,
RT_NULL, RT_NULL);
rt_hw_mtd_spinand_register("spinand1", AIC_QSPI2_DEVICE_SPINAND_FREQ);
#elif defined(AIC_QSPI1_DEVICE_SPINAND)
aic_qspi_bus_attach_device("qspi1", "spinand1", 0, AIC_QSPI1_BUS_WIDTH,
RT_NULL, RT_NULL);
rt_hw_mtd_spinand_register("spinand1", AIC_QSPI1_DEVICE_SPINAND_FREQ);
#endif
#endif // AIC_SECONED_FLASH_NAND
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_spinand_register);