上章节讲了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/config 中加入 CONFIG_S3C2410_NAND_BOOT, 如下:
#define CONFIG_S3C2410_NAND_BOOT 1
支持从 Nand Flash 中启动.
1. 执行 Nand Flash 初始化
下面代码在 cpu/arm920 中
#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 中
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 中
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 中定义,在 include/linux/mtd 中赋初值。
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 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 中加入 nand_init()函数.
void nand_init(void)
{
/* 初始化 Nand Flash 控制器, 以及 Nand Flash 芯片 */
nand_reset();
/* 调用 nand_probe()来检测芯片类型 */
printf ("%4lu MB\n", 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 中,
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 中
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
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
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
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
struct nand_hw_control {
spinlock_t lock; /* 自旋锁,用于硬件控制 */
struct nand_chip *active; /* 正在处理 MTD 设备 */
wait_queue_head_t wq; /* 等待队列 */
};
6. nand_chip
include/linux/mtd
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
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中定义 */
.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 中
定义.该函数的作用是初始化 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_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_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()函数.