Linux SPI-NAND 驅(qū)動開發(fā)指南1 概述1.1 編寫目的1.2 適用范圍1.3 相關(guān)人員3 流程設(shè)計3.1 體系結(jié)構(gòu)3.2 源碼結(jié)構(gòu)3.3 關(guān)鍵數(shù)據(jù)定義3.3.1 flash 設(shè)備信息數(shù)據(jù)結(jié)構(gòu)3.3.2 flash chip 數(shù)據(jù)結(jié)構(gòu)3.3.3 aw_spinand_chip_request3.3.4 ubi_ec_hdr3.3.5 ubi_vid_hdr3.4 關(guān)鍵接口說明3.4.1 MTD 層接口3.4.1.1 aw_rawnand_mtd_erase3.4.1.2 aw_rawnand_mtd_read3.4.1.3 aw_rawnand_mtd_read_oob3.4.1.4 aw_rawnand_mtd_write3.4.1.5 aw_rawnand_mtd_write_oob3.4.1.6 aw_rawnand_mtd_block_isbad3.4.1.7 aw_rawnand_mtd_block_markbad3.4.2 物理層接口3.4.2.1 aw_spinand_chip_read_single_page3.4.2.3 aw_spinand_chip_erase_single_block3.4.2.4 aw_spinand_chip_isbad_single_block3.4.2.5 aw_spinand_chip_markbad_single_block4 模塊配置4.1 uboot 模塊配置4.2 kernel 模塊配置4.3 env.cfg
Linux SPI-NAND 驅(qū)動開發(fā)指南
1 概述
1.1 編寫目的
介紹 Sunxi SPINand mtd/ubi 驅(qū)動設(shè)計, 方便相關(guān)驅(qū)動和應(yīng)用開發(fā)人員
1.2 適用范圍
本設(shè)計適用于所有 sunxi 平臺
1.3 相關(guān)人員
Nand 模塊開發(fā)人員,及應(yīng)用開發(fā)人員等
2 術(shù)語、縮略語及概念
MTD:(Memory Technology device)是用于訪問存儲設(shè)備的 linux 子系統(tǒng)。本模塊是MTD 子系統(tǒng)的 flash 驅(qū)動部分
UBI:UBI 子系統(tǒng)是基于 MTD 子系統(tǒng)的,在 MTD 上實現(xiàn) nand 特性的管理邏輯,向上屏蔽nand 的特性
壞塊 (Bad Block):制作工藝和 nand 本身的物理性質(zhì)導(dǎo)致在出廠和正常使用過程中都會產(chǎn)生壞塊
3 流程設(shè)計
3.1 體系結(jié)構(gòu)
NAND MTD/UBI 驅(qū)動主要包括 5 大組件,如下圖:
圖 3-1: UBI 架構(gòu)
說明:
MTD standard interface: 對接 MTD 層通用讀寫接口
FLASH bad block manager: 驅(qū)動層對 flash 壞塊的管理
FLASH SPL: 主要是實現(xiàn)讀寫 boot0、boot1,可用于 ioctl 對boot0、boot1 的升級
SECURESTORAGE:主要是給上層提供私有數(shù)據(jù)的管理 SPI:HOST端控制器層的實現(xiàn)。
3.2 源碼結(jié)構(gòu)
kernel 源碼目錄:linux-5.4/drivers/mtd/awnand/spinand
.
├── Kconfig
├── Makefile
├── physic
│ ├── bbt.c
│ ├── cache.c
│ ├── core.c
│ ├── ecc.c
│ ├── id.c
│ ├── Makefile
│ ├── ops.c
│ └── physic.h
├── secure-storage.c
├── sunxi-common.c
├── sunxi-core.c
├── sunxi-debug.c
├── sunxi-nftl-core.c
└── sunxi-spinand.h
內(nèi)核目錄下
`-- include
`-- linux
`-- mtd
|-- aw-spinand.h
3.3 關(guān)鍵數(shù)據(jù)定義
3.3.1 flash 設(shè)備信息數(shù)據(jù)結(jié)構(gòu)
struct aw_spinand_phy_info {
const char *Model;
unsigned char NandID[MAX_ID_LEN];
unsigned int DieCntPerChip;
unsigned int BlkCntPerDie;
unsigned int PageCntPerBlk;
unsigned int SectCntPerPage;
unsigned int OobSizePerPage;
#define BAD_BLK_FLAG_MARK 0x03
#define BAD_BLK_FLAG_FRIST_1_PAGE 0x00
#define BAD_BLK_FLAG_FIRST_2_PAGE 0x01
#define BAD_BLK_FLAG_LAST_1_PAGE 0x02
#define BAD_BLK_FLAG_LAST_2_PAGE 0x03
int BadBlockFlag;
#define SPINAND_DUAL_READ BIT(0)
#define SPINAND_QUAD_READ BIT(1)
#define SPINAND_QUAD_PROGRAM BIT(2)
#define SPINAND_QUAD_NO_NEED_ENABLE BIT(3)
#define SPINAND_ONEDUMMY_AFTER_RANDOMREAD BIT(8)
int OperationOpt;
int MaxEraseTimes;
#define HAS_EXT_ECC_SE01 BIT(0)
#define HAS_EXT_ECC_STATUS BIT(1)
enum ecc_status_shift ecc_status_shift;
int EccFlag;
enum ecc_limit_err EccType;
enum ecc_oob_protected EccProtectedType;
};
說明:
? Model:flash 的 model 名字
? NandID:flash 的 id 碼
? DieCntPerChip:每 chip 的 die 個數(shù)
? BlkCntPerDie:每 die 有多少個 block
? PageCntPerBlk:每 block 有多少個 page
? SectCntPerPage:每 page 有多少個扇區(qū)
? OobSizePerPage:每 page 的 obb 大小
? BadBlockFlag:壞塊標(biāo)志存放在每個 block 的那個 page 中
BAD_BLK_FLAG_FRIST_1_PAGE
BAD_BLK_FLAG_FIRST_2_PAGE
BAD_BLK_FLAG_LAST_1_PAGE
BAD_BLK_FLAG_LAST_2_PAGE
? OperationOpt:支持的操作
SPINAND_DUAL_READ
SPINAND_QUAD_READ
SPINAND_QUAD_PROGRAM
SPINAND_QUAD_NO_NEED_ENABLE
SPINAND_ONEDUMMY_AFTER_RANDOMREAD
? MaxEraseTimes:最大擦除數(shù)據(jù)
? EccFlag:特性物料讀 ecc status 說需目錄不同
? GD5F1GQ4UCYIG 通過 0Fh + C0h 獲取 ecc status,則無需配置 EccFlag
? MX35LF1GE4AB 通過 7Ch + one dummy byte 獲取 ecc status,則配置 EccFlag = HAS_EXT_ECC_STATUS
? EccType:設(shè)置 ecc 值對應(yīng)的狀態(tài)關(guān)系
? EccProtectedType:在 spare 去選擇收 ecc 保護(hù)的 16byte 作為 oob 區(qū)
例(MX35LF2GE4AD):
{
.Model = "MX35LF2GE4AD",
.NandID = {0xc2, 0x26, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff},
.DieCntPerChip = 1,
.SectCntPerPage = 4,
.PageCntPerBlk = 64,
.BlkCntPerDie = 2048,
.OobSizePerPage = 64,
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM | SPINAND_DUAL_READ,
.MaxEraseTimes = 65000,
.EccFlag = HAS_EXT_ECC_STATUS,
.EccType = BIT4_LIMIT5_TO_8_ERR9_TO_15,
.EccProtectedType = SIZE16_OFF4_LEN4_OFF8,
.BadBlockFlag = BAD_BLK_FLAG_FIRST_2_PAGE,
},
?
?
3.3.2 flash chip 數(shù)據(jù)結(jié)構(gòu)
struct aw_spinand_chip {
struct aw_spinand_chip_ops *ops;
struct aw_spinand_ecc *ecc;
struct aw_spinand_cache *cache;
struct aw_spinand_info *info;
struct aw_spinand_bbt *bbt;
struct spi_device *spi;
unsigned int rx_bit;
unsigned int tx_bit;
unsigned int freq;
void *priv;
};
此結(jié)構(gòu)定義了 flash chip 層的物理模型數(shù)據(jù)結(jié)構(gòu)以及 chip 層對 flash 的操作接口。
? aw_spinand_chip_ops:flash 讀、寫、擦等操作接口
? aw_spinand_ecc:flash ecc 讀、寫和校驗操作接口
? aw_spinand_cache:對緩存 page 的管理,提高讀寫效率
? aw_spinand_info:flash ID、page size 等信息及獲取信息的操作接口
? aw_spinand_bbt:flash 壞塊表及管理等操作接口
? spi_device:spi 父設(shè)備的操作結(jié)構(gòu)體
? rx_bit:讀狀態(tài)操作標(biāo)志
? tx_bit:寫狀態(tài)操作標(biāo)志
3.3.3 aw_spinand_chip_request
struct aw_spinand_chip_request {
unsigned int block;
unsigned int page;
unsigned int pageoff;
unsigned int ooblen;
unsigned int datalen;
void *databuf;
void *oobbuf;
unsigned int oobleft;
unsigned int dataleft;
};
操作目標(biāo)結(jié)構(gòu)體,改結(jié)構(gòu)體填充我們待操作的 block 的那個 page 的多少偏移的數(shù)據(jù)
databuf/oobbuf
? block:待操作塊
? page:待操作頁
? pageoff:操作偏移
? ooblen:操作 oob 長度
? datalen:操作數(shù)據(jù)長度
? databuf:操作目標(biāo)數(shù)據(jù)
? oobbuf:操作目標(biāo) oob
3.3.4 ubi_ec_hdr
struct ubi_ec_hdr {
__be32 magic;
__u8 version;
__u8 padding1[3];
__be64 ec; /* Warning: the current limit is 31-bit anyway! */
__be32 vid_hdr_offset;
__be32 data_offset;
__be32 image_seq;
__u8 padding2[32];
__be32 hdr_crc;
} __packed;
@magic: erase counter header magic number (%UBI_EC_HDR_MAGIC)
@version: version of UBI implementation which is supposed to accept this UBI image
@padding1: reserved for future, zeroes
@ec: the erase counter
@vid_hdr_offset: where the VID header starts
@data_offset: where the user data start
@image_seq: image sequence number
@padding2: reserved for future, zeroes
@hdr_crc: erase counter header CRC checksum
EC: Erase Count,記錄塊的擦除次數(shù),在 ubiattach 的時候指定一個 mtd,如果 PEB 上沒有
EC,則用平均的 EC 值,寫入 EC 值只有在擦除的時候才會增加 1
3.3.5 ubi_vid_hdr
struct ubi_vid_hdr {
__be32 magic;
__u8 version;
__u8 vol_type;
__u8 copy_flag;
__u8 compat;
__be32 vol_id;
__be32 lnum;
__u8 padding1[4];
__be32 data_size;
__be32 used_ebs;
__be32 data_pad;
__be32 data_crc;
__u8 padding2[4];
__be64 sqnum;
__u8 padding3[12];
__be32 hdr_crc;
} __packed;
@magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC)
@version: UBI implementation version which is supposed to accept this UBI image(%UBI_VERSION)
@vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC)
@copy_flag: if this logical eraseblock was copied from another physical eraseblock(for wear-leveling reasons)
@compat: compatibility of this volume(%0, %UBI_COMPAT_DELETE, %UBI_COMPAT_IGNORE,%UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
@vol_id: ID of this volume
@lnum: logical eraseblock number
@padding1: reserved for future, zeroes
@data_size: how many bytes of data this logical eraseblock contains
@used_ebs: total number of used logical eraseblocks in this volume
@data_pad: how many bytes at the end of this physical eraseblock are not used
@data_crc: CRC checksum of the data stored in this logical eraseblock
@padding2: reserved for future, zeroes
@sqnum: sequence number
@padding3: reserved for future, zeroes
@hdr_crc: volume identifier header CRC checksum
參數(shù)說明
@sqnum 是創(chuàng)建此 VID 頭時的全局序列計數(shù)器的值。每次 UBI 寫一個新的 VID 頭到 flash 時,全局序列計數(shù)器都會增加,比如當(dāng)它將一個邏輯的 eraseblock 映射到一個新的物理的 erase-block 時。全局序列計數(shù)器是一個無符號 64 位整數(shù),我們假設(shè)它永遠(yuǎn)不會溢出。@sqnum(序列號) 用于區(qū)分新舊版本的邏輯擦除塊。
有兩種情況,可能有多個物理 eraseblock 對應(yīng)同一個邏輯 eraseblock,即在卷標(biāo)識頭中有相同的 @vol_id 和 @lnum 值。假設(shè)我們有一個邏輯的擦除塊 L,它被映射到物理的擦除塊 P。
因為 UBI 可以異步擦除物理上的擦除塊,所以可能出現(xiàn)以下情況:L 被異步擦除,所以 P 被安排擦除,然后 L 被寫入,即。映射到另一個物理的擦除塊 P1,所以 P1 被寫入,然后不干凈的重啟發(fā)生。結(jié)果-有兩個物理的 eraseblock P 和 P1 對應(yīng)同一個邏輯的 eraseblock L。但是 P1 的序列號更大,所以 UBI 在連接 flash 時選擇 P1。
UBI 不時地將邏輯擦除塊移動到其他物理擦除塊,以達(dá)到損耗均衡的目的。例如,如果 UBI將 L 從 P 移動到 P1,在 P 被物理擦除之前會發(fā)生不干凈的重啟,有兩個物理擦除塊 P 和 P1 對應(yīng)于 L, UBI 必須在 flash 連接時選擇其中一個。@sqnum 字段表示哪個 PEB 是原始的 (顯然 P 的 @sqnum 更低) 和副本。但是選擇具有更高序列號的物理擦除塊是不夠的,因為不干凈的重新引導(dǎo)可能發(fā)生在復(fù)制過程的中間,因此 P 中的數(shù)據(jù)被損壞(P->P1 沒復(fù)制完)。僅僅選擇序號較低的物理擦除塊是不夠的,因為那里的數(shù)據(jù)可能很舊 (考慮在復(fù)制之后向 P1 添加更多數(shù)據(jù)的情況)。此外,不干凈的重啟可能發(fā)生在擦除 P 剛剛開始的時候,所以它會導(dǎo)致不穩(wěn)定的 P,“大部分” 是 OK 的,但仍然有不穩(wěn)定的情況。
UBI 使用 @copy_flag 字段表示這個邏輯擦除塊是一個副本。UBI 還計算數(shù)據(jù)的 CRC,當(dāng)數(shù)據(jù)被移動時,并將其存儲在副本 (P1) 的 @data_crc 字段。因此,當(dāng) UBI 需要從兩個 (P 或 P1)中選擇一個物理擦除塊時,會檢查新塊 (P1) 的 @copy_flag。如果它被清除,情況就簡單了,新的就會被選中。如果設(shè)置了該值,則檢查副本 (P1) 的數(shù)據(jù) CRC。如果 CRC 校驗和是正確的,這個物理擦除塊被選中 (P1)。否則,將選擇較老的 P。如果是靜態(tài)卷,@data_crc 字段包含邏輯擦除塊內(nèi)容的 CRC 校驗和。對于動態(tài)卷,它不包含 CRC 校驗和規(guī)則。唯一的例外情況是,當(dāng)物理擦除塊的數(shù)據(jù)被磨損均衡子系統(tǒng)移動時,磨損均衡子系統(tǒng)計算數(shù)據(jù) CRC,并將其存儲在 @data_crc 字段中。
@used_ebs 字段僅用于靜態(tài)卷,它表示該卷的數(shù)據(jù)需要多少個擦除塊。對于動態(tài)卷,這個字段不被使用并且總是包含 0。
@data_pad 在創(chuàng)建卷時使用對齊參數(shù)計算。因此,@data_pad 字段有效地減少了該卷的邏輯擦除塊的大小。當(dāng)一個人在 UBI 卷上使用面向塊的軟件 (比如,cramfs) 時,這是非常方便的。
LEB 與 PEB
圖 3-2: PEB-LEB
3.4 關(guān)鍵接口說明
3.4.1 MTD 層接口
3.4.1.1 aw_rawnand_mtd_erase
static int aw_rawnand_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
description:mtd erase interface
@mtd:MTD device structure
@instr:erase operation descrition structure
return:success return 0,fail return fail code
3.4.1.2 aw_rawnand_mtd_read
static int aw_rawnand_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, u_char *buf)
description:mtd read interface
@mtd:MTD device structure
@from:offset to read from MTD device
@len: data len
@retlen:had read data len
@buf:data buffer
return:success return max_bitflips,fail return fail code
3.4.1.3 aw_rawnand_mtd_read_oob
static int aw_rawnand_mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
description:mtd read data with oob
@mtd:MTD device structure
@ops:oob eperation descrition structure
return:success return max_bitflips,fail return fail code
3.4.1.4 aw_rawnand_mtd_write
static int aw_rawnand_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
description:mtd write data interface
@to:offset to MTD device
@len:want write data len
@retlen:return the writen len
@buf:data buffer
return:success return 0, fail return code fail
3.4.1.5 aw_rawnand_mtd_write_oob
static int aw_rawnand_mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops * ops)
description:write data with oob
@mtd:MTD device structure
@to:offset to MTD device
@ops:oob operation descrition structure
return:success return 0, fail return code fail
3.4.1.6 aw_rawnand_mtd_block_isbad
static int aw_rawnand_mtd_block_isbad(struct mtd_info *mtd, loff_t ofs)
description:check block is badblock or not
@mtd:MTD device structure
@ofs: offset the mtd device start (align to simu block size)
return:true if the block is bad, or false if the block is good
3.4.1.7 aw_rawnand_mtd_block_markbad
static int aw_rawnand_mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
description:mark block at the given offset as bad block
@mtd:MTD device structure
@ofs:offset the mtd device start
return:success to mark return 0, or fail return fail code.
3.4.2 物理層接口
3.4.2.1 aw_spinand_chip_read_single_page
static int aw_spinand_chip_read_single_page(struct aw_spinand_chip *chip, struct aw_spinand_chip_request *req)
description:Read physics on a page
@chip:See 3.3.2
@req:See 3.3.3
return:zero on success, else a negative error code.
3.4.2.2 aw_spinand_chip_write_single_page
static int aw_spinand_chip_write_single_page(struct aw_spinand_chip *chip, struct aw_spinand_chip_request *req)
description:Write physics on a page
@chip:See 3.3.2
@req:See 3.3.3
return:zero on success, else a negative error code.
3.4.2.3 aw_spinand_chip_erase_single_block
static int aw_spinand_chip_erase_single_block(struct aw_spinand_chip *chip, struct aw_spinand_chip_request *req)
description:Erase physics on a block
@chip:See 3.3.2
@req: See 3.3.3
return:zero on success, else a negative error code.
3.4.2.4 aw_spinand_chip_isbad_single_block
static int aw_spinand_chip_isbad_single_block(struct aw_spinand_chip *chip, struct aw_spinand_chip_request *req)
description:Set to bad block
@chip:See 3.3.2
@req:See 3.3.3
return:zero on success, else a negative error code.
3.4.2.5 aw_spinand_chip_markbad_single_block
static int aw_spinand_chip_markbad_single_block(struct aw_spinand_chip *chip, struct aw_spinand_chip_request *req)
description:Set to bad block
@chip:See 3.3.2
@req:See 3.3.3
return:zero on success, else a negative error code.
4 模塊配置
4.1 uboot 模塊配置
Device Drivers-->Sunxi flash support--> [*]Support sunxi nand devices [*]Support sunxi nand ubifs devices [*]Support COMM NAND V1 interface
如下圖:
圖 4-1: u-boot-spinand-menuconfig
4.2 kernel 模塊配置
Device Drivers->Memory Technology Device(MTD) support-->sunxi-nand
圖 4-2: UBI
圖 4-3: ker_nand-cfg
圖 4-4: ker_spinand
Device Drivers->SPI support
圖 4-5: spi-1
圖 4-6: spi-2
Device Drivers->DMA Engine support
圖 4-7: DMA-1
圖 4-8: DMA-2
Device Drivers->SOC(System On Chip)
圖 4-9: SID
File systems-->Miscellaneous filesystems-->
圖 4-10: menuconfig_spinand_ubifs
4.3 env.cfg
在 env.cfg 中添加修改下值,setargs_nand_ubi 先 copy 一份 setargs_nand 再添加對應(yīng)變量
圖 4-11: build-mkcmd
審核編輯黃宇
-
NAND
+關(guān)注
關(guān)注
16文章
1692瀏覽量
136471 -
驅(qū)動
+關(guān)注
關(guān)注
12文章
1851瀏覽量
85651 -
Linux
+關(guān)注
關(guān)注
87文章
11345瀏覽量
210417 -
SPI
+關(guān)注
關(guān)注
17文章
1722瀏覽量
92139
發(fā)布評論請先 登錄
相關(guān)推薦
評論