linux下read函式缺失位元組_Linux系統移植之——在 U-BOOT 對 Nand Flash 的支援,先收藏...
技術標籤:linux下read函式缺失位元組
上章節講了Nand flash驅動的編寫與移植,本章節主要講在 U-BOOT 對 Nand Flash 的支援,學linux的建議收藏,以後工作中會遇到。
說明:本人作為一名工作多年的程式設計師,給大家都是精心挑選的資料,希望對大家的學習有幫助。
本章主要內容如下:
3 在 UBOOT 對 Nand Flash 的支援
3.1 UBOOT 對從 Nand Flash 啟動的支援
3.1.1 從 Nand Flash 啟動 UBOOT 的基本原理
3.1.2 支援 Nand Flash 啟動程式碼說明
3.2 UBOOT 對 Nand Flash 命令的支援
3.2.1 主要資料結構介紹
3.2.2 支援的命令函式說明
4 在 Linux 對 Nand Flash 的支援
4.1 Linux 下 Nand Flash 呼叫關係
4.1.1 Nand Flash 裝置新增時資料結構包含關係
4.1.2 Nand Flash 設備註冊時資料結構包含關係
4.2 Linux 下 Nand Flash 驅動主要資料結構說明
4.2.1 s3c2410 專有資料結構
4.2.2 Linux 通用資料結構說明
4.3.1 註冊 driver_register
4.3.2 探測裝置 probe
4.3.3 初始化 Nand Flash 控制器
4.3.4 移除裝置
4.3.5 Nand Flash 晶片初始化
4.3.6 讀 Nand Flash
4.3.7 寫 Nand Flash
3.1 U-BOOT 對從 Nand Flash 啟動的支援
3.1.1 從 Nand Flash 啟動 U-BOOT 的基本原理
1. 前 4K 的問題
如果 S3C2410 被配置成從 Nand Flash 啟動(配置由硬體工程師在電路板設定), S3C2410 的 Nand Flash 控制器
有一個特殊的功能, 在 S3C2410 上電後, Nand Flash 控制器會自動的把 Nand Flash 上的前 4K 資料搬移到 4K 內部
RAM 中, 並把 0x00000000 設定內部 RAM 的起始地址, CPU 從內部 RAM 的 0x00000000 位置開始啟動。這個過
程不需要程式干涉。
程式設計師需要完成的工作,是把最核心的啟動程式放在 Nand Flash 的前 4K 中。
2. 啟動程式的安排
由於 Nand Flash 控制器從 Nand Flash 中搬移到內部 RAM 的程式碼是有限的,所以, 在啟動程式碼的前 4K 裡,我
們必須完成 S3C2410 的核心配置以及把啟動程式碼(UBOOT)剩餘部分搬到 RAM 中執行。以 UBOOT 為例, 前 4K
完成的主要工作, 見第四部分的 2.2 節。
3.1.2 支援 Nand Flash 啟動程式碼說明
首先在 include/configs/crane2410.h 中加入 CONFIG_S3C2410_NAND_BOOT, 如下:
#define CONFIG_S3C2410_NAND_BOOT 1
支援從 Nand Flash 中啟動.
1. 執行 Nand Flash 初始化
下面程式碼在 cpu/arm920t/start.S 中
#ifdef CONFIG_S3C2410_NAND_BOOT
copy_myself:
mov r10, lr
ldr sp, DW_STACK_START @安裝棧的起始地址
mov fp, #0 @初始化幀指標暫存器
bl nand_reset @跳到復位 C 函式去執行
...
DW_STACK_START:
.word STACK_BASE+STACK_SIZE4
2. nand_reset C 程式碼
下面程式碼被加在/board/crane2410/crane2410.c 中
void nand_reset(void)
{
int i;
/* 設定 Nand Flash 控制器 */
rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
/* 給 Nand Flash 晶片傳送復位命令 */
NF_nFCE_L();
NF_CMD(0xFF);
for(i=0; i<10; i++);
NF_WAITRB(); NF_nFCE_H();
}
3. 從 Nand Flash 中把 UBOOT 拷貝到 RAM
@read UBOOT from Nand Flash to RAM
ldr r0, =UBOOT_RAM_BASE @ 設定第 1 個引數: UBOOT 在 RAM 中的起始地址
mov r1, #0x0 @ 設定第 2 個引數:Nand Flash 的起始地址
mov r2, #0x20000 @ 設定第 3 個引數: UBOOT 的長度(128KB)
bl nand_read_whole @ 呼叫 nand_read_whole(), 該函式在 board/crane2410/crane2410.c 中
tst r0, #0x0 @ 如果函式的返回值為 0,表示執行成功.
beq ok_nand_read @ 執行記憶體比較
4. 從 Nand Flash 中把資料讀入到 RAM 中
int nand_read_whole(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
/* 如果起始地址和長度不是 512 位元組(1 頁)的倍數, 則返回錯誤程式碼 */
if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
return 1;
}
/* 啟用 Nand Flash */
NF_nFCE_L();
for(i=0; i<10; i++);
i = start_addr;
while(i < start_addr + size) {
/* 讀 A 區 */
rNFCMD = 0;
/* 寫入讀取地址 */
rNFADDR = i & 0xff;
rNFADDR = (i >> 9) & 0xff;
rNFADDR = (i >> 17) & 0xff;
rNFADDR = (i >> 25) & 0xff;
NF_WAITRB();
/* 讀出一頁(512 位元組) */
for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {
*buf = (rNFDATA & 0xff);
buf++;
}
}
/* 停止驅動 Nand Flash */
NF_nFCE_H();
return 0;
}5. 校查搬移後的資料
把 RAM 中的前 4K 與內部中前 4K 進行比較, 如果完全相同, 則表示搬移成功.
ok_nand_read:
mov r0, #0x00000000 @內部 RAM 的起始地址
ldr r1, =UBOOT_RAM_BASE @UBOOT 在 RAM 中的起始地址
mov r2, #0x400 @比較 1024 次, 每次 4 位元組, 4 bytes * 1024 = 4Kbytes
go_next: @ 比較 1024 次, 每次 4 個位元組
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq done_nand_read
bne go_next
notmatch:
1:b 1b
done_nand_read:
mov pc, r10
3.2 U-BOOT 對 Nand Flash 命令的支援
在 UBOOT 下對 Nand Flash 的支援主要是在命令列下實現對 nand flash 的操作。對 nand flash 實現的命令
為:nand info、nand device、nand read、nand write、nand erease、nand bad。
用到的主要資料結構有:struct nand_flash_dev、struct nand_chip。前者包括主要的晶片型號、儲存容量、
裝置 ID、I/O 匯流排寬度等資訊;後者是具體對 nand flash 進行操作時用到的資訊。
3.2.1 主要資料結構介紹
1. struct nand_flash_dev 資料結構
該資料結構在 include/linux/mtd/nand.h 中定義,在 include/linux/mtd/nand_ids.h 中賦初值。
struct nand_flash_dev {
char *name;
/* 晶片名稱 */
int manufacture_id; /* 廠商 ID */
int model_id; /* 模式 ID */
int chipshift; /* Nand Flash 地址位數 */
char page256; /* 表明是否時 256 位元組一頁。1:是;0:否。*/
char pageadrlen; /* 完成一次地址傳送需要往 NFADDR 中傳送幾次。*/
unsigned long erasesize; /* 一次塊擦除可以擦除多少位元組 */
int bus16; /* 地址線是否是 16 位,1:是;0:否 */
};
2. struct nand_chip 資料結構
該資料結構在 include/linux/mtd/nand.h 中定義. 該結構體定義出一個 Nand Flash 裝置陣列:
struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE];
該陣列在 nand_probe()中對其進行初始化.
struct nand_chip {
int
page_shift; /* Page 地址位數 */
u_char *data_buf; /* 本次讀出的一頁資料 */
u_char *data_cache; /* 讀出的一頁資料 */
int cache_page; /* 上次操作的頁號 */u_char ecc_code_buf[6]; /* ECC 校驗碼 */
u_char reserved[2];
char ChipID;
/* 晶片 ID 號 */
struct Nand *chips; /* Nand Flash 晶片列表, 表示支援幾個晶片為一個裝置*/
int chipshift;
char* chips_name; /* Nand Flash 晶片名稱 */
unsigned long erasesize; /* 塊擦寫的大小 */
unsigned long mfr; /* 廠商 ID */
unsigned long id; /* 模式 ID */
char* name; /* 裝置名稱 */
int numchips; /* 有幾塊 Nand Flash 晶片 */
char page256; /* 一頁是 256 位元組, 還是 512 位元組 */
char pageadrlen; /* 頁地址的長度 */
unsigned long IO_ADDR; /* 用於對 nand flash 進行定址的地址值存放處 */
unsigned long totlen; /* Nand Flash 總共大小 */
uint oobblock; /* 一頁的大小。本款 nand flash 為 512 */
uint oobsize;
/* spare array 大小。本款 nand flash 為 16 */
uint eccsize; /* ECC 大小 */
int bus16; /* 地址線是否是 16 位,1:是;0:否 */
};
3.2.2 支援的命令函式說明
1. nand info/nand device
功能:顯示當前 nand flash 晶片資訊。
函式呼叫關係如下(按先後順序):
static void nand_print(struct nand_chip *nand) ;
2. nand erase
功能:擦除指定塊上的資料。
函式呼叫關係如下(按先後順序):
int nand_erase(struct nand_chip* nand, size_t ofs, size_t len, int clean);
3. nand bad
功能:顯示壞塊。
函式呼叫關係如下(按先後順序):
static void nand_print_bad(struct nand_chip* nand);
int check_block (struct nand_chip *nand, unsigned long pos);
4. nand read
功能:讀取 nand flash 資訊到 SDRAM。
函式呼叫關係如下(按先後順序):
int nand_rw (struct nand_chip* nand, int cmd,size_t start, size_t len, size_t * retlen, u_char * buf);
static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len,
size_t * retlen, u_char *buf, u_char *ecc_code);
static void NanD_ReadBuf (struct nand_chip *nand, u_char * data_buf, int cntr);
READ_NAND(adr);
5. nand write
功能:從 SDRAM 寫資料到 nand flash 中。
函式呼叫關係如下(按先後順序):int nand_rw (struct nand_chip* nand, int cmd,size_t start, size_t len, size_t * retlen, u_char * buf);
static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len,
size_t * retlen, const u_char * buf, u_char * ecc_code);
static int nand_write_page (struct nand_chip *nand, int page, int col, int last, u_char * ecc_code);
WRITE_NAND(d , adr);
3.2.3 U-BOOT 支援 Nand Flash 命令移植說明
1. 設定配置選項
在 CONFIG_COMMANDS 中, 開啟 CFG_CMD_NAND 選項.
#define CONFIG_COMMANDS
(CONFIG_CMD_DFL
|
CFG_CMD_CACHE
|
CFG_CMD_NAND
|
/*CFG_CMD_EEPROM |*/
/*CFG_CMD_I2C
|*/
/*CFG_CMD_USB
|*/
CFG_CMD_PING |
CFG_CMD_REGINFO |
CFG_CMD_DATE
|
CFG_CMD_ELF)
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
#define CFG_NAND_BASE 0x4E000000 /* Nand Flash 控制器在 SFR 區中起始暫存器地址 */
#define CFG_MAX_NAND_DEVICE 1 /* 支援的最在 Nand Flash 資料 */
#define SECTORSIZE 512 /* 1 頁的大小 */
#define NAND_SECTOR_SIZE SECTORSIZE
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE – 1) /* 頁掩碼 */
#define ADDR_COLUMN 1 /* 一個位元組的 Column 地址 */
#define ADDR_PAGE 3 /* 3 位元組的頁塊地址, A9A25*/
#define ADDR_COLUMN_PAGE 4 /* 總共 4 位元組的頁塊地址 */
#define NAND_ChipID_UNKNOWN 0x00 /* 未知晶片的 ID 號 */
#define NAND_MAX_FLOORS 1
#define NAND_MAX_CHIPS 1
/* Nand Flash 命令層底層介面函式 */
#define WRITE_NAND_COMMAND(d, adr) do {rNFCMD = d;} while(0)
#define WRITE_NAND_ADDRESS(d, adr) do {rNFADDR = d;} while(0)
#define WRITE_NAND(d, adr) do {rNFDATA = d;} while(0)
#define READ_NAND(adr) (rNFDATA)
#define NAND_WAIT_READY(nand) {while(!(rNFSTAT&(1<<0)));}
#define NAND_DISABLE_CE(nand) {rNFCONF |= (1<<11);}
#define NAND_ENABLE_CE(nand) {rNFCONF &= ~(1<<11);}
/* 下面一組操作對 Nand Flash 無效 */
#define NAND_CTL_CLRALE(nandptr)
#define NAND_CTL_SETALE(nandptr)
#define NAND_CTL_CLRCLE(nandptr)
#define NAND_CTL_SETCLE(nandptr)/* 允許 Nand Flash 寫校驗 */
#define CONFIG_MTD_NAND_VERIFY_WRITE 1
#endif /* CONFIG_COMMANDS & CFG_CMD_NAND*/
2. 加入自己的 Nand Flash 晶片型號
在 include/linux/mtd/ nand_ids.h 中的對如下結構體賦值進行修改:
static struct nand_flash_dev nand_flash_ids[] = {
......
{"Samsung K9F1208U0B", NAND_MFR_SAMSUNG, 0x76, 26, 0, 4, 0x4000, 0},
.......
}
這樣對於該款 Nand Flash 晶片的操作才能正確執行。
3. 編寫自己的 Nand Flash 初始化函式
在 board/crane2410/crane2410.c 中加入 nand_init()函式.
void nand_init(void)
{
/* 初始化 Nand Flash 控制器, 以及 Nand Flash 晶片 */
nand_reset();
/* 呼叫 nand_probe()來檢測晶片型別 */
printf ("%4lu MB", nand_probe(CFG_NAND_BASE) >> 20);
}
該函式在啟動時被 start_armboot()呼叫.
4 在 Linux 對 Nand Flash 的支援
4.1 Linux 下 Nand Flash 呼叫關係
4.1.1 Nand Flash 裝置新增時資料結構包含關係
struct mtd_partition partition_info[]
--> struct s3c2410_nand_set nandset
--> struct s3c2410_platform_nand superlpplatfrom
--> struct platform_device s3c_device_nand
在該資料結構的 name欄位的初始化值"s3c2410-nand",必須與 Nand Flash裝置驅動註冊時
struct device_driver結構中的 name欄位相同,因為 platfrom bus 是依靠名字來匹配的.
--> struct platform_device *smdk2410_devices[]
4.1.2 Nand Flash 設備註冊時資料結構包含關係
struct device_driver s3c2410_nand_driver
-->struct device *dev
該資料構由系統分配.
-->struct platform_device *pdev
-->struct s3c2410_platform_nand *plat
-->struct s3c2410_nand_set nset
-->struct mtd_partition
4.1.3 當發生系統呼叫時資料結構呼叫關係
struct mtd_info它的*priv指向 chip
-->struct nand_chip
它的*priv指向 nmtd
-->struct s3c2410_nand_mtd
它是 s3c2410_nand_info 的一個欄位
-->s3c2410_nand_info
它被設為 Nand Flash裝置驅動的私有資料結構,在 Nand Flash裝置驅動註冊時分配空間.
-->struct device
4.2 Linux 下 Nand Flash 驅動主要資料結構說明
4.2.1 s3c2410 專有資料結構
1. s3c2410_nand_set
struct s3c2410_nand_set {
int nr_chips; /* 晶片的數目 */
int nr_partitions; /* 分割槽的數目 */
char *name; /* 集合名稱 */
int nr_map; /* 可選, 底層邏輯到物理的晶片數目 */
struct mtd_partition partitions; /* 分割槽列表 */
};
2. s3c2410_platform_and
struct s3c2410_platform_nand {
/* timing information for controller, all times in nanoseconds */
int tacls; /* 從 CLE/ALE有效到 nWE/nOE 的時間 */
int twrph0; /* nWE/nOE 的有效時間 */
int twrph1; /* 從釋放 CLE/ALE到 nWE/nOE 不活動的時間 */
int nr_sets; /* 集合數目 */
struct s3c2410_nand_set sets; /* 集合列表 */
/* 根據晶片編號選擇有效集合 */
void (*select_chip)(struct s3c2410_nand_set , int chip);
};
3. s3c2410_nand_mtd
在 drivers/mtd/nand/s3c2410.c 中,
struct s3c2410_nand_mtd {
struct mtd_info mtd; /* MTD 資訊 */
struct nand_chip chip; /* nand flash 晶片資訊 */
struct s3c2410_nand_set set; /* nand flash 集合 */
struct s3c2410_nand_info *info; /* nand flash 資訊 */
int scan_res;
};
4. s3c2410_nand_info
struct s3c2410_nand_info {
/* mtd info */
struct nand_hw_control controller; /* 硬體控制器 */
struct s3c2410_nand_mtd *mtds; /* MTD 裝置表 */
struct s3c2410_platform_nand platform; /* Nand 裝置的平臺 */ /* device info */
struct device *device; /* 裝置指標 */
struct resource *area; /* 資源指標 */
struct clk *clk; /* Nand Flash 時鐘 */
void __iomem *regs; /* 暫存器基地址(map後的邏輯地址) */
int mtd_count; /* MTD的數目 */
unsigned char is_s3c2440;
};
5. struct clk
在 arch/arm/machs3c2410/clock.h 中
struct clk {
struct list_head list; /* clock 列表結點 */
struct module *owner; /* 所屬模組 */
struct clk *parent; /* 父結點 */
const char *name; /* 名稱 */
int id; /* 編號 */
atomic_t used; /* 使用者計數 */
unsigned long rate; /* 時鐘速率 */
unsigned long ctrlbit; /* 控制位 */
int (*enable)(struct clk *, int enable); /* Clock 開啟方法 */
};
4.2.2 Linux 通用資料結構說明
1. device_driver
include/linux/device.h
struct device_driver {
const char * name; /* 驅動名稱 */
struct bus_type * bus; /* 匯流排型別 */
struct completion unloaded; /* 解除安裝事件通知機制 */
struct kobject kobj; /* sys中的物件 */
struct klist klist_devices; /* 裝置列表 */
struct klist_node knode_bus; /* 匯流排結點列表 */
struct module * owner;/* 所有者 */
/* 裝置驅動通用方法 */
int (*probe) (struct device * dev); /* 探測裝置 */
int (*remove) (struct device * dev); /* 移除裝置 */
void (*shutdown) (struct device * dev); /* 關閉裝置 */
/* 掛起裝置 */
int (*suspend) (struct device * dev, pm_message_t state, u32 level);
int (*resume) (struct device * dev, u32 level); /* 恢復 */
};
2. platform_device
include/linux/device.h
struct platform_device {
const char * name; /* 名稱 */
u32 id; /* 裝置編號, -1表示不支援同類多個裝置 */
struct device dev; /* 裝置 */
u32 num_resources; /* 資源數 */
struct resource * resource; /* 資源列表 */};
3. resource
struct resource {
const char name; /* 資源名稱 */
unsigned long start, end; /* 開始位置和結束位置 */
unsigned long flags; /* 資源型別 */
/* 資源在資源樹中的父親,兄弟和孩子 */
struct resource *parent, *sibling, *child;
};
4. device
include/linux/device.h
struct device {
struct klist klist_children; /* 在裝置列表中的孩子列表 */
struct klist_node knode_parent; /* 兄弟結點 */
struct klist_node knode_driver; /* 驅動結點 */
struct klist_node knode_bus; /* 匯流排結點 */
struct device parent; /* 父親 */
struct kobject kobj; /* sys結點 */
char bus_id[BUS_ID_SIZE];
struct semaphore sem; /* 同步驅動的訊號量 */
struct bus_type * bus; /* 匯流排型別 */
struct device_driver *driver; /* 裝置驅動 */
void *driver_data; /* 驅動的私有資料 */
void *platform_data; /* 平臺指定的資料,為 device核心驅動保留 */
void *firmware_data; /* 韌體指定的資料,為 device核心驅動保留 */
struct dev_pm_info power; /* 裝置電源管理資訊 */
u64 *dma_mask; /* DMA掩碼 */
u64 coherent_dma_mask;
struct list_head dma_pools; /* DMA緩衝池 */
struct dma_coherent_mem *dma_mem; /* 連續 DMA 記憶體的起始位置 */
void (*release)(struct device * dev); /* 釋放設定方法 */
};
5. nand_hw_control
include/linux/mtd/nand.h
struct nand_hw_control {
spinlock_t lock; /* 自旋鎖,用於硬體控制 */
struct nand_chip *active; /* 正在處理 MTD 裝置 */
wait_queue_head_t wq; /* 等待佇列 */
};
6. nand_chip
include/linux/mtd/nand.h
struct nand_chip {
void __iomem *IO_ADDR_R; /* 讀地址 */
void __iomem *IO_ADDR_W; /* 寫地址 */ /* 位元組操作 */
u_char (*read_byte)(struct mtd_info *mtd); /* 讀一個位元組 */
void (*write_byte)(struct mtd_info *mtd, u_char byte); /* 寫一個位元組 */
/* 雙位元組操作 */
u16 (*read_word)(struct mtd_info mtd); /* 讀一個字 */
void (*write_word)(struct mtd_info *mtd, u16 word); /* 寫一個字 */
/* buffer操作 */
void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len);
void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len);
int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len);
/* 選擇一個操作晶片 */
void (*select_chip)(struct mtd_info *mtd, int chip);
/* 壞塊檢查操作 */
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
/* 壞塊標記操作 */
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
/* 硬體控制操作 */
void (*hwcontrol)(struct mtd_info *mtd, int cmd);
/* 裝置準備操作 */
int (*dev_ready)(struct mtd_info *mtd);
/* 命令傳送操作 */
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int
page_addr);
/* 等待命令完成 */
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);
/* 計算 ECC 碼操作 */
int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char
*ecc_code);
/* 資料糾錯操作 */
int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc,
u_char *calc_ecc);
/* 開啟硬體 ECC */
void (*enable_hwecc)(struct mtd_info *mtd, int mode);
/* 擦除操作 */
void (*erase_cmd)(struct mtd_info *mtd, int page);
/* 檢查壞塊表 */
int (*scan_bbt)(struct mtd_info *mtd);
int eccmode; /* ECC 模式 */
int eccsize; /* ECC 計算時使用的位元組數 */
int eccbytes; /* ECC 碼的位元組數 */
int eccsteps; /* ECC 碼計算的步驟數 */
int chip_delay; /* 晶片的延遲時間 */
spinlock_t chip_lock; /* 晶片訪問的自旋鎖 */
wait_queue_head_t wq; /* 晶片訪問的等待佇列 */
nand_state_t state; /* Nand Flash狀態 */
int page_shift; /* 頁右移的位數,即 column地址位數 */
int phys_erase_shift; /* 塊右移的位數, 即 column和頁一共的地址位數 */
int bbt_erase_shift; /* 壞塊頁表的位數 */
int chip_shift; /* 該晶片總共的地址位數 */
u_char *data_buf; /* 資料緩衝區 */
u_char *oob_buf; /* oob 緩衝區 */
int oobdirty; /* oob 緩衝區是否需要重新初始化 */
u_char *data_poi; /* 資料緩衝區指標 */
unsigned int options; /* 晶片專有選項 */
int badblockpos;/* 壞塊標示位元組在 OOB 中的位置 */
int numchips; /* 晶片的個數 */ unsigned long chipsize; /* 在多個晶片組中, 一個晶片的大小 */
int pagemask; /* 每個晶片頁數的遮蔽字, 通過它取出每個晶片包含多少個頁 */
int pagebuf; /* 在頁緩衝區中的頁號 */
struct nand_oobinfo *autooob; /* oob 資訊 */
uint8_t *bbt; /* 壞塊頁表 */
struct nand_bbt_descr *bbt_td; /* 壞塊表描述 */
struct nand_bbt_descr *bbt_md; /* 壞塊表映象描述 */
struct nand_bbt_descr *badblock_pattern; /* 壞塊檢測模板 */
struct nand_hw_control *controller; /* 硬體控制 */
void *priv; /* 私有資料結構 */
/* 進行附加錯誤檢查 */
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int
status, int page);
};
7. mtd_info
include/linux/mtd/mtd.h
struct mtd_info {
u_char type; /* 裝置型別 */
u_int32_t flags; /* 裝置標誌位組 */
u_int32_t size; /* 總共裝置的大小 */
u_int32_t erasesize; /* 擦除塊的大小 */
u_int32_t oobblock; /* OOB塊的大小,如:512 個位元組有一個 OOB */
u_int32_t oobsize; /* OOB資料的大小,如:一個 OOB 塊有 16 個位元組 */
u_int32_t ecctype; /* ECC 校驗的型別 */
u_int32_t eccsize; /* ECC 碼的大小 */
char *name; /* 裝置名稱 */
int index; /* 裝置編號 */
/* oobinfo 資訊,它可以通過 MEMSETOOBINFO ioctl 命令來設定 */
struct nand_oobinfo oobinfo;
u_int32_t oobavail; /* OOB區的有效位元組數,為檔案系統提供 */
/* 資料擦除邊界資訊 */
int numeraseregions;
struct mtd_erase_region_info *eraseregions;
u_int32_t bank_size; /* 保留 */
/* 擦除操作 */
int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
/* 指向某個執行程式碼位置 */
int (*point) (struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char **mtdbuf);
/* 取消指向 */
void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len);
/* 讀/寫操作 */
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write) (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf);
/* 帶 ECC 碼的讀/寫操作 */
int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); /* 帶 OOB 碼的讀/寫操作 */
int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
u_char *buf);
int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
const u_char *buf);
/* 提供訪問保護暫存器區的方法 */
int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
/* 提供 readv和 writev 方法 */
int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count,
loff_t from, size_t *retlen);
int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count,
loff_t from, size_t *retlen, u_char *eccbuf,
struct nand_oobinfo *oobsel);
int (*writev) (struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen);
int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen,
u_char *eccbuf, struct nand_oobinfo *oobsel);
/* 同步操作 */
void (*sync) (struct mtd_info *mtd);
/* 晶片級支援的加/解鎖操作 */
int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
/* 電源管理操作 */
int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd);
/* 壞塊管理操作 */
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
/* 重啟前的通知事件 */
struct notifier_block reboot_notifier;
void *priv; /* 私有資料結構 */
struct module *owner; /* 模組所有者 */
int usecount; /* 使用次數 */
};
4.3 Linux 下 Nand Flash 驅動說明4.3.1 註冊 driver_register
通過 module_init(s3c2410_nand_init);註冊 Nand Flash 驅動. 在 s3c2410_nand_init ()中通過 driver_register()註冊
s3c2410_nand_driver驅動程式,如下所示:
static struct device_driver s3c2410_nand_driver = {
.name = "s3c2410-nand",
.bus = &platform_bus_type, /* 在 drivers/base/platform.c中定義 */
.probe = s3c2410_nand_probe,
.remove = s3c2410_nand_remove,
};
4.3.2 探測裝置 probe
在註冊的 Nand Flash驅動程式中, probe 方法為 s3c2410_nand_probe(). s3c2410_nand_probe()再呼叫
s3c24xx_nand_probe(). 在該函式中, 把*info 作為 Nand Flash 驅動的私有資料結構, 並通過 dev_set_drvdata(dev,
info)把*info 儲存在*device 的*driver_data 欄位中.然後通過 clk_get(dev, "nand")獲取 Nand Flash的時鐘資
源, clk_use(info->clk)增加時鐘資源的使用計數, clk_enable(info->clk)開啟資源.填寫*info 的其它欄位,
其中包括:
1. 通過 request_mem_region()為 Nand Flash 暫存器區申請 I/O 記憶體地址空間區,並通過 ioremap()把它對映到虛
擬地址空間.
2. 呼叫 s3c2410_nand_inithw()初始化 Nand Flash 控制器.
3. 為 mtd 裝置分配裝置資訊的儲存空間.
4. 對當前 mtd 裝置,呼叫 s3c2410_nand_init_chip()進行初始化.
5. 對當前 mtd 裝置, 呼叫 nand_scan()檢測 Nand Flash 晶片, nand_scan()函式在 drivers/mtd/nand/nand_base.c 中
定義.該函式的作用是初始化 struct nand_chip 中一些方法, 並從 Nand Flash 中讀取晶片 ID, 並初始化 struct
mtd_info 中的方法.
6. 對當前 mtd 裝置,加入其分割槽資訊.
7. 如果還有更多 mtd 裝置,到 4 執行.
4.3.3 初始化 Nand Flash 控制器
s3c2410_nand_inithw()函式會初始化 Nand Flash 控制器, 通過設定 Nand Flash 控制暫存器(S3C2410_NFCONF)來
完成, 這裡最重要的是根據 S3C2410 的 PCLK 計算出 tacls, twrph0 以及 twrph1 值.
4.3.4 移除裝置
s3c2410_nand_remove()當裝置被移除時,被 device 核心驅動呼叫.它完成的主要工作如下:
1. 把*device 的*driver_data 欄位置空.
2. 釋放 mtd 裝置資訊.
3. 釋放 clk 資源.
4. 通過 iounmap()取消映地址空間.
5. 釋放申請的 I/O 記憶體資源.
6. 釋放裝置私有資料*info 的空間.
4.3.5 Nand Flash 晶片初始化
s3c2410_nand_init_chip()初始化 struct nand_chip 中的一些主要欄位以及方法.其中主要包括的方法有:
1. s3c2410_nand_hwcontrol(); 硬體控制
2. s3c2410_nand_devready(); 裝置是否準備好
3. s3c2410_nand_write_buf(); 寫一個 buffer 到 nand flash
4. s3c2410_nand_read_buf(); 讀一個 buffer 到 nand flash
5. s3c2410_nand_select_chip(); 選擇操作晶片
如果支援 ECC 硬體校驗,還設定如下方法: 1. s3c2410_nand_correct_data(); 通過 ECC 碼校正資料
2. s3c2410_nand_enable_hwecc(); 開啟硬體 ECC 檢查
3. s3c2410_nand_calculate_ecc(); 計算 ECC 碼
4.3.6 讀 Nand Flash
當對 nand flash 的裝置檔案(nand flash 在/dev 下對應的檔案)執行系統呼叫 read(),或在某個檔案系統中對該
裝置進行讀操作時. 會呼叫 struct mtd_info 中的 read方法,他們預設呼叫函式為 nand_read(),在
drivers/mtd/nand/nand_base.c中定義.nand_read()呼叫 nand_do_read_ecc(),執行讀操作. 在
nand_do_read_ecc()函式中,主要完成如下幾項工作:
1. 會呼叫在 nand flash驅動中對 struct nand_chip 過載的 select_chip 方法,即
s3c2410_nand_select_chip()選擇要操作的 MTD 晶片.
2. 會呼叫在 struct nand_chip 中系統預設的方法 cmdfunc 傳送讀命令到 nand flash.
3. 會呼叫在 nand flash驅動中對 struct nand_chip 過載的 read_buf(),即 s3c2410_nand_read_buf()
從 Nand Flash的控制器的資料暫存器中讀出資料.
4. 如果有必要的話,會呼叫在 nand flash驅動中對 struct nand_chip 過載的
enable_hwecc,correct_data 以及 calculate_ecc方法,進行資料 ECC 校驗。
4.3.7 寫 Nand Flash
當對 nand flash 的裝置檔案(nand flash 在/dev 下對應的檔案)執行系統呼叫 write(),或在某個檔案系統中對該裝置
進行讀操作時, 會呼叫 struct mtd_info 中 write 方法,他們預設呼叫函式為 nand_write(),這兩個函式在
drivers/mtd/nand/nand_base.c中定義. nand_write()呼叫 nand_write_ecc(),執行寫操作.在
nand_do_write_ecc()函式中,主要完成如下幾項工作:
1. 會呼叫在 nand flash驅動中對 struct nand_chip 過載的 select_chip 方法,即
s3c2410_nand_select_chip()選擇要操作的 MTD 晶片.
2. 呼叫 nand_write_page()寫一個頁.
3. 在 nand_write_page()中,會呼叫在 struct nand_chip 中系統預設的方法 cmdfunc 傳送寫命令
到 nand flash.
4. 在 nand_write_page()中,會呼叫在 nand flash驅動中對 struct nand_chip 過載的
write_buf(),即 s3c2410_nand_write_buf()從 Nand Flash的控制器的資料暫存器中寫入資料.
5. 在 nand_write_page()中,會呼叫在 nand flash驅動中對 struct nand_chip 過載 waitfunc 方法,
該方法呼叫系統預設函式 nand_wait(),該方法獲取操作狀態,並等待 nand flash操作完成.等
待操作完成,是呼叫 nand flash驅動中對 struct nand_chip 中過載的 dev_ready方法,即
s3c2410_nand_devready()函式.