linux移植(8)

3 在U-BOOT对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\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/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()函数.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值