设计说明
Baremetal 源码说明
相关模块 | 源码路径 |
Application | bsp/examples_bare/mtd.c |
mtd | bsp/artinchip/drv_bare/mtd/ |
mtd | bsp/peripheral/spinand bsp/artinchip/drv_bare/spinand/spinand_port.c bsp/artinchip/drv_bare/spinand/spinand_mtd.c |
mtd | bsp/artinchip/drv_bare/mtd/ |
注解!
客户可以参考 bsp/examples_bare/mtd.c 进行测试开发
其它重要文件: bsp/examples_bare/test-spinand/spinand.c
Baremetal 层次关系
注解!
客户开发项目,在 MTD 层上开发, SPINAND 层上开发可能会破坏 spl、os 分区的数据。
Baremetal 关键流程
SPI NAND 初始化流程
c
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分区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SPI NAND 读数据流程
c
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);
1
2
3
4
5
6
7
2
3
4
5
6
7
SPINAND 库数据结构设计
struct aic_spinand
定义了SPINAND控制器管理信息:
c
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;
};
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
!小技巧
info
表示SPINAND设备信息 use_continuous_read
表示SPINAND使能了连续读取模式(部分厂家芯片支持) qspi_dl_width 表示qspi
总线数据宽度bbt
表示坏块管理信息
struct aic_spinand_info
定义了 SPINAND 设备配置信息
c
/* 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;
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
struct spi_nand_cmd_cfg
定义了 SPINAND 操作命令配置的格式
c
struct spi_nand_cmd_cfg {
u8 opcode;
u8 opcode_bits;
u8 addr_bytes;
u8 addr_bits;
u8 dummy_bytes;
u8 data_bits;
};
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
SPINAND 库相关接口介绍
spinand_flash_init
函数原型 | int spinand_flash_init(struct aic_spinand *flash); |
功能说明 | 初始化 spinand 芯片 |
参数定义 | struct aic_spinand *flash spinand 设备对象 |
返回值 | 0: 成功 其他: 失败 |
注意事项 |
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: 成功 其他: 失败 |
注意事项 |
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 大小对齐 |
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 大小对齐 |
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 大小对齐 |
spinand_block_erase
函数原型 | int spinand_block_erase(struct aic_spinand *flash, u16 blk); |
功能说明 | 擦除 spinand 指定块地址的块数据 |
参数定义 | struct aic_spinand *flash spinand 设备对象 u16 blk spinand 块地址 |
返回值 | 0: 成功 其他: 失败 |
注意事项 |
spinand_block_isbad
函数原型 | int spinand_block_isbad(struct aic_spinand *flash, u16 blk); |
功能说明 | 擦除 spinand 指定块地址的块数据 |
参数定义 | struct aic_spinand *flash spinand 设备对象 u16 blk spinand 块地址 |
返回值 | 0: 成功 其他: 失败 |
注意事项 |
spinand_block_markbad
函数原型 | int spinand_block_markbad(struct aic_spinand *flash, u16 blk); |
功能说明 | 标记 spinand 指定块地址的块为坏块 |
参数定义 | struct aic_spinand *flash spinand 设备对象 u16 blk spinand 块地址 |
返回值 | 0: 成功 其他: 失败 |
注意事项 |
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: 成功 其他: 失败 |
注意事项 |
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: 成功 其他: 失败 |
注意事项 |
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 大小 |
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: 成功 其他: 失败 |
注意事项 |
Baremetal 应用接口设计
mtd_probe
函数原型 | int mtd_probe(void); |
功能说明 | 初始化所有mtd设备 |
返回值 | 0: 成功 其他: 失败 |
注意事项 | 只有初始化mtd设备之后, 才能正常使用其它函数 |
mtd_add_device
函数原型 | int mtd_add_device(struct mtd_dev *mtd); |
功能说明 | 增加mtd设备 |
参数定义 | struct mtd_dev *mtd mtd 设备对象 |
返回值 | 0: 成功 其他: 失败 |
注意事项 |
mtd_get_device_count
函数原型 | u32 mtd_get_device_count(void); |
功能说明 | 获取注册的mtd设备个数 |
返回值 | 注册的mtd设备个数 |
注意事项 |
mtd_get_device_by_id
函数原型 | struct mtd_dev *mtd_get_device_by_id(u32 id); |
功能说明 | 通过索引号获取mtd设备句柄 |
返回值 | mtd设备句柄 为空:失败 |
注意事项 |
mtd_get_device
函数原型 | struct mtd_dev *mtd_get_device(const char *name); |
功能说明 | 通过分区名获取mtd设备句柄 |
参数定义 | char *name mtd 分区名 |
返回值 | mtd设备句柄 为空:失败 |
注意事项 |
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 对齐,在读取数据的过程中,如果遇到了坏块,会自动寻找下一个好块,继续读取数据 |
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 对齐 |
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 对齐,在写入数据的过程中,如果遇到了坏块,会自动寻找下一个好块,继续写入数据 |
mtd_block_isbad
函数原型 | int mtd_block_isbad(struct mtd_dev *mtd, u32 offset); |
功能说明 | 判断指定的块是否是坏块 |
参数定义 | struct mtd_dev *mtd mtd 分区设备句柄 u32 offset 偏移值 |
返回值 | 1: 坏块 0: 正常块 其他: 失败 |
注意事项 | offset 必须与 block 大小对齐 |
mtd_block_markbad
函数原型 | int mtd_block_markbad(struct mtd_dev *mtd, u32 offset); |
功能说明 | 标记指定的块为坏块 |
参数定义 | struct mtd_dev *mtd mtd 分区设备句柄 u32 offset 偏移值 |
返回值 | 0: 成功 其他: 失败 |
注意事项 | offset 必须与 block 大小对齐 |
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 对齐 |
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 |
RTOS 层次关系
注解!
mtd 设备用来挂载 uffs 文件系统,blk 设备用来挂载 FatFS 文件系统。
RTOS 关键流程
SPI NAND 初始化流程
c
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设备
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
读数据流程
c
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);
1
2
3
4
5
6
7
2
3
4
5
6
7
blk 设备读数据流程
c
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);
1
2
3
4
5
6
7
2
3
4
5
6
7