5.3.5. 设计说明

5.3.5.1. Baremetal 源码说明

相关模块

源码路径

Application

bsp/examples_bare/mtd.c

mtd

bsp/artinchip/drv_bare/mtd/

spinand

bsp/peripheral/spinand
bsp/artinchip/drv_bare/spinand/spinand_port.c
bsp/artinchip/drv_bare/spinand/spinand_mtd.c

qspi

bsp/artinchip/hal/qspi/hal_qspi.c

注解

客户可以参考 bsp/examples_bare/mtd.c 进行测试开发

其它重要文件: bsp/examples_bare/test-spinand/spinand.c

5.3.5.2. Baremetal 层次关系

../../../_images/bar_spinand.png

注解

客户开发项目,在 MTD 层上开发, SPINAND 层上开发可能会破坏 spl、os 分区的数据。

5.3.5.3. Baremetal 关键流程

5.3.5.3.1. SPI NAND 初始化流程

mtd_probe //bsp/artinchip/drv_bare/mtd/mtd_probe.c
|-> spinand_probe(AIC_BOOTLOADER_SPINAND_QSPI_ID); //bsp/artinchip/drv_bare/spinand/spinand_mtd.c
    |-> get_qspi_by_index(spi_bus);   // 获取aic_qspi_bus对象
    |-> qspi_configure(qspi, NULL);  //bsp/artinchip/drv_bare/spinand/spinand_port.c
        |-> hal_qspi_master_init(&qspi->handle, &cfg);
        |-> hal_qspi_master_dma_config(&qspi->handle, &dmacfg);
        |-> hal_qspi_master_set_bus_freq(&qspi->handle, qspi->bus_hz);
    |-> spinand_flash_init(flash);  //bsp/peripheral/spinand/spinand.c
        |-> spinand_info_read(flash);
        |-> nand_bbt_init(flash);
    |-> part = mtd_parts_parse(NOR_MTD_PARTS); //解析分区信息
    |-> mtd->ops.erase = sfud_mtd_erase;
    |-> mtd->ops.erase = mtd_spinand_erase;
    |-> mtd->ops.block_isbad = mtd_spinand_block_isbad;
    |-> mtd->ops.block_markbad = mtd_spinand_block_markbad;
    |-> mtd->ops.read = mtd_spinand_read;
    |-> mtd->ops.write = mtd_spinand_write;
    |-> mtd->ops.read_oob = mtd_spinand_read_oob;
    |-> mtd->ops.write_oob = mtd_spinand_write_oob;
    |-> mtd->ops.cont_read = mtd_spinand_continuous_read;
    |-> mtd_add_device(mtd);  //添加mtd分区

5.3.5.3.2. SPI NAND 读数据流程

mtd_read //bsp/artinchip/drv_bare/mtd/mtdcore.c
|-> mtd_spinand_read(struct mtd_dev *mtd, u32 offset, u8 *data, u32 len); //bsp/artinchip/drv_bare/spinand/spinand_mtd.c
    |-> spinand_read(flash, data, start, dolen); //bsp/peripheral/spinand/spinand.c
        |-> spinand_block_isbad(flash, blk);
        |-> spinand_read_page(flash, page, p, flash->info->page_size, NULL, 0);
            |-> spinand_load_page_op(flash, page);
            |-> spinand_read_from_cache_op(flash, column, buf, nbytes);

5.3.5.4. SPINAND 库数据结构设计

5.3.5.4.1. struct aic_spinand

定义了SPINAND控制器管理信息:

struct aic_spinand {
    const struct aic_spinand_info *info;
    struct spinand_id id;
    void *user_data;
    void *lock;
    u8 use_continuous_read;
    u8 qspi_dl_width;
    u8 IsInited;
    u8 *databuf;
    u8 *oobbuf;
    struct nand_bbt bbt;
};

小技巧

info 表示SPINAND设备信息 use_continuous_read 表示SPINAND使能了连续读取模式(部分厂家芯片支持) qspi_dl_width 表示qspi总线数据宽度 bbt 表示坏块管理信息

5.3.5.4.2. struct aic_spinand_info

定义了 SPINAND 设备配置信息

/* SPI NAND flash information */
struct aic_spinand_info {
    u32 devid;
    u16 page_size;
    u16 oob_size;
    u32 block_per_lun;
    u32 pages_per_eraseblock;
    u8 is_die_select;
    const char *sz_description;
    struct spi_nand_cmd_cfg *cmd;
};

5.3.5.4.3. struct spi_nand_cmd_cfg

定义了 SPINAND 操作命令配置的格式

struct spi_nand_cmd_cfg {
    u8 opcode;
    u8 opcode_bits;
    u8 addr_bytes;
    u8 addr_bits;
    u8 dummy_bytes;
    u8 data_bits;
};

5.3.5.5. SPINAND 库相关接口介绍

5.3.5.5.1. spinand_flash_init

函数原型

int spinand_flash_init(struct aic_spinand *flash);

功能说明

初始化 spinand 芯片

参数定义

struct aic_spinand *flash
spinand 设备对象

返回值

0: 成功
其他: 失败

注意事项

5.3.5.5.2. spinand_read_id_op

函数原型

int spinand_read_id_op(struct aic_spinand *flash, u8 *id);

功能说明

读取 spinand 芯片的 id

参数定义

struct aic_spinand *flash
spinand 设备对象
u8 *id
读取 spinand id 缓存地址

返回值

0: 成功
其他: 失败

注意事项

5.3.5.5.3. spinand_read

函数原型

int spinand_read(struct aic_spinand *flash, u8 *addr, u32 offset, u32 size);

功能说明

从 spinand 读取数据

参数定义

struct aic_spinand *flash
spinand 设备对象
u8 *addr
读取 spinand 数据缓存地址
u32 offset
从 spinand 读取数据的偏移地址
u32 size
从 spinand 读取数据的长度

返回值

0: 成功
其他: 失败

注意事项

offset 需要与 page 大小对齐

5.3.5.5.4. spinand_write

函数原型

int spinand_write(struct aic_spinand *flash, u8 *addr, u32 offset, u32 size);

功能说明

写入数据到 spinand

参数定义

struct aic_spinand *flash
spinand 设备对象
u8 *addr
写入 spinand 数据缓存地址
u32 offset
写入 spinand 数据的偏移地址
u32 size
写入 spinand 数据的长度

返回值

0: 成功
其他: 失败

注意事项

offset 需要与 page 大小对齐

5.3.5.5.5. spinand_erase

函数原型

int spinand_erase(struct aic_spinand *flash, u32 offset, u32 size);

功能说明

擦除 spinand 数据

参数定义

struct aic_spinand *flash
spinand 设备对象
u32 offset
擦除 spinand 数据的偏移地址
u32 size
擦除 spinand 数据的长度

返回值

0: 成功
其他: 失败

注意事项

offset 和 size 需要与 block 大小对齐

5.3.5.5.6. spinand_block_erase

函数原型

int spinand_block_erase(struct aic_spinand *flash, u16 blk);

功能说明

擦除 spinand 指定块地址的块数据

参数定义

struct aic_spinand *flash
spinand 设备对象
u16 blk
spinand 块地址

返回值

0: 成功
其他: 失败

注意事项

5.3.5.5.7. spinand_block_isbad

函数原型

int spinand_block_isbad(struct aic_spinand *flash, u16 blk);

功能说明

判断 spinand 指定块地址的块是否是坏块

参数定义

struct aic_spinand *flash
spinand 设备对象
u16 blk
spinand 块地址

返回值

0: 成功
其他: 失败

注意事项

5.3.5.5.8. spinand_block_markbad

函数原型

int spinand_block_markbad(struct aic_spinand *flash, u16 blk);

功能说明

标记 spinand 指定块地址的块为坏块

参数定义

struct aic_spinand *flash
spinand 设备对象
u16 blk
spinand 块地址

返回值

0: 成功
其他: 失败

注意事项

5.3.5.5.9. spinand_read_page

函数原型

int spinand_read_page(struct aic_spinand *flash, u32 page, u8 *data,
u32 data_len, u8 *spare, u32 spare_len);

功能说明

读取 spinand 指定 page 上的数据

参数定义

struct aic_spinand *flash
spinand 设备对象
u32 page
spinand 页地址
u8 *data
读 page 主区域数据的缓存地址
u32 data_len
读 page 主区域数据的长度
u8 *spare
读 page spare 区域数据的缓存地址
u32 spare_len
读 page spare 区域数据的长度

返回值

0: 成功
其他: 失败

注意事项

5.3.5.5.10. spinand_write_page

函数原型

int spinand_write_page(struct aic_spinand *flash, u32 page, const u8 *data,
u32 data_len, const u8 *spare, u32 spare_len);

功能说明

写入数据到 spinand 指定 page 上

参数定义

struct aic_spinand *flash
spinand 设备对象
u32 page
spinand 页地址
u8 *data
写 page 主区域数据的缓存地址
u32 data_len
写 page 主区域数据的长度
u8 *spare
写 page spare 区域数据的缓存地址
u32 spare_len
写 page spare 区域数据的长度

返回值

0: 成功
其他: 失败

注意事项

5.3.5.5.11. spinand_continuous_read

函数原型

int spinand_continuous_read(struct aic_spinand *flash, u32 page, u8 *data, u32 size);

功能说明

使用连续读取模式读取 spinand 上指定长度的数据

参数定义

struct aic_spinand *flash
spinand 设备对象
u32 page
spinand 页地址
u8 *data
读 spinand 数据的缓存地址
u32 size
读 spinand 数据的长度

返回值

0: 成功
其他: 失败

注意事项

size 需要大于等于 2 个 page 大小

5.3.5.5.12. spinand_config_set

函数原型

int spinand_config_set(struct aic_spinand *flash, u8 mask, u8 val);

功能说明

设置 spinand 配置寄存器的值

参数定义

struct aic_spinand *flash
spinand 设备对象
u8 mask
配置寄存器的掩码
u8 val
配置寄存器的值

返回值

0: 成功
其他: 失败

注意事项

5.3.5.6. Baremetal 应用接口设计

5.3.5.6.1. mtd_probe

函数原型

int mtd_probe(void);

功能说明

初始化所有mtd设备

返回值

0: 成功
其他: 失败

注意事项

只有初始化mtd设备之后, 才能正常使用其它函数

5.3.5.6.2. mtd_add_device

函数原型

int mtd_add_device(struct mtd_dev *mtd);

功能说明

增加mtd设备

参数定义

struct mtd_dev *mtd
mtd 设备对象

返回值

0: 成功
其他: 失败

注意事项

5.3.5.6.3. mtd_get_device_count

函数原型

u32 mtd_get_device_count(void);

功能说明

获取注册的mtd设备个数

返回值

注册的mtd设备个数

注意事项

5.3.5.6.4. mtd_get_device_by_id

函数原型

struct mtd_dev *mtd_get_device_by_id(u32 id);

功能说明

通过索引号获取mtd设备句柄

返回值

mtd设备句柄
为空:失败

注意事项

5.3.5.6.5. mtd_get_device

函数原型

struct mtd_dev *mtd_get_device(const char *name);

功能说明

通过分区名获取mtd设备句柄

参数定义

char *name
mtd 分区名

返回值

mtd设备句柄
为空:失败

注意事项

5.3.5.6.6. mtd_read

函数原型

int mtd_read(struct mtd_dev *mtd, u32 offset, u8 *data, u32 len);

功能说明

读取 mtd 分区数据到缓冲区上

参数定义

struct mtd_dev *mtd
mtd 分区设备句柄
u32 offset
偏移值
u8 *data
接收数据缓冲区
u32 len
接收数据长度

返回值

0: 成功
其他: 失败

注意事项

offset 与 page 对齐,在读取数据的过程中,如果遇到了坏块,会自动寻找下一个好块,继续读取数据

5.3.5.6.7. mtd_erase

函数原型

int mtd_erase(struct mtd_dev *mtd, u32 offset, u32 len);

功能说明

擦除 mtd 分区某块区域的数据

参数定义

struct mtd_dev *mtd
mtd 分区设备句柄
u32 offset
偏移值
u32 len
擦除数据长度

返回值

0: 成功
其他: 失败

注意事项

offset 必须与 block 对齐

5.3.5.6.8. mtd_write

函数原型

int mtd_write(struct mtd_dev *mtd, u32 offset, u8 *data, u32 len);

功能说明

写入缓冲区上的数据到 mtd 分区上

参数定义

struct mtd_dev *mtd
mtd 分区设备句柄
u32 offset
偏移值
u8 *data
发送缓冲区地址
u32 len
擦除数据的长度

返回值

0: 成功
其他: 失败

注意事项

offset 与 page 对齐,在写入数据的过程中,如果遇到了坏块,会自动寻找下一个好块,继续写入数据

5.3.5.6.9. mtd_block_isbad

函数原型

int mtd_block_isbad(struct mtd_dev *mtd, u32 offset);

功能说明

判断指定的块是否是坏块

参数定义

struct mtd_dev *mtd
mtd 分区设备句柄
u32 offset
偏移值

返回值

1: 坏块
0: 正常块
其他: 失败

注意事项

offset 必须与 block 大小对齐

5.3.5.6.10. mtd_block_markbad

函数原型

int mtd_block_markbad(struct mtd_dev *mtd, u32 offset);

功能说明

标记指定的块为坏块

参数定义

struct mtd_dev *mtd
mtd 分区设备句柄
u32 offset
偏移值

返回值

0: 成功
其他: 失败

注意事项

offset 必须与 block 大小对齐

5.3.5.6.11. mtd_contread

函数原型

int mtd_contread(struct mtd_dev *mtd, u32 offset, uint8_t *data, u32 len);

功能说明

使用连续读取模式读取 mtd 分区数据到缓冲区上

参数定义

struct mtd_dev *mtd
mtd 分区设备句柄
u32 offset
偏移值
u8 *data
接收数据缓冲区
u32 len
接收数据长度

返回值

0: 成功
其他: 失败

注意事项

芯片必须支持连续读取模式, offset 大小与 page 对齐

5.3.5.7. RTOS 源码说明

相关模块

源码路径

mtd blk

kernel/rt-thread/components/drivers/mtd/mtd_nand.c //mtd 设备通用平台
bsp/artinchip/drv/spinand/spinand_port.c //mtd 设备通用平台对接
bsp/artinchip/drv/spinand/spinand_block.c //blk 设备接口实现

spinand

bsp/peripheral/spinand/

qspi

bsp/artinchip/drv/qspi/drv_qspi.c

5.3.5.8. RTOS 层次关系

../../../_images/rtt_spinand.png

图 5.33 SPI NAND 驱动框架

注解

mtd 设备用来挂载 uffs 文件系统,blk 设备用来挂载 FatFS 文件系统。

5.3.5.9. RTOS 关键流程

5.3.5.9.1. SPI NAND 初始化流程

rt_hw_spinand_register //bsp/artinchip/drv/spinand/spinand_port.c
|-> aic_qspi_bus_attach_device("qspi0", "spinand0", 0, 4, RT_NULL, RT_NULL); //创建 qspi 总线
|-> rt_hw_mtd_spinand_register("spinand0");
    |-> rt_hw_mtd_spinand_init(spinand);
        |-> spinand_flash_init(flash);    //bsp/peripheral/spinand/spinand.c
            |-> spinand_info_read(flash);
            |-> nand_bbt_init(flash);
        |-> mtd_parts_parse(IMAGE_CFG_JSON_PARTS_MTD);
        |-> rt_mtd_nand_register_device(p->name, &g_mtd_partitions[i]); //注册mtd设备
        |-> rt_blk_nand_register_device(p->name, &g_mtd_partitions[i]); //注册block设备

5.3.5.9.2. 读数据流程

mtd_read //kernel/rt-thread/components/drivers/mtd/mtd_nand.c
|-> spinand_mtd_read(device ...) //bsp/artinchip/drv/spinand/spinand_port.c
    |-> spinand_read(flash, data, start, dolen); //bsp/peripheral/spinand/spinand.c
        |-> spinand_block_isbad(flash, blk);
        |-> spinand_read_page(flash, page, p, flash->info->page_size, NULL, 0);
            |-> spinand_load_page_op(flash, page);
            |-> spinand_read_from_cache_op(flash, column, buf, nbytes);

5.3.5.9.3. blk 设备读数据流程

rt_spinand_read //bsp/artinchip/drv/spinand/spinand_block.c
|-> spinand_mtd_read(device ...) //bsp/artinchip/drv/spinand/spinand_port.c
    |-> spinand_read(flash, data, start, dolen); //bsp/peripheral/spinand/spinand.c
        |-> spinand_block_isbad(flash, blk);
        |-> spinand_read_page(flash, page, p, flash->info->page_size, NULL, 0);
            |-> spinand_load_page_op(flash, page);
            |-> spinand_read_from_cache_op(flash, column, buf, nbytes);