Linux系统移植之——在 U-BOOT 对 Nand Flash 的支持,先收藏

上章节讲了Nand flash驱动的编写与移植,本章节主要讲在 U-BOOT 对 Nand Flash 的支持,学linux的建议收藏,以后工作中会遇到。

<code>说明:
本人作为一名工作多年的程序员,给大家都是精心挑选的资料,希望对大家的学习有帮助。/<code>

本章主要内容如下:

3 在 U­BOOT 对 Nand Flash 的支持

3.1 U­BOOT 对从 Nand Flash 启动的支持

3.1.1 从 Nand Flash 启动 U­BOOT 的基本原理

3.1.2 支持 Nand Flash 启动代码说明

3.2 U­BOOT 对 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


Linux系统移植之——在 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_SIZE­4

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 U­BOOT 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 = 4K­bytes

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 命令的支持

在 U­BOOT 下对 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 字节的页块地址, A9­A25*/

#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/mach­s3c2410/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()函数.


分享到:


相關文章: