内容:
1, 裸机 nandflash
2,ubootnandflash
3, linux nandflash
QQ2440V3 S3C2440
========================================================================================
第一层, 裸机nandflash
nandflash 组织结构是 芯片,块 ,页,
一个页内有 512 +16 B 也就是 528×8bit
页内地址,首先通过三个命令分别访问三个区, A B C
前面512个字节的数据需要用9位来访问。我们使用 第九位来决定访问A 还是B但是第九位不是通过地址线来赋值的,而是不同的命令赋予不同的值。
命令00 赋予第八位地址为0, 命令01赋予第八位地址为1. 这样就可以访问一个页的上半页,下半页。0-7位来访问半页内的数据。
同时命令50来访问C区。 C区只有16B 所以地址部分只使用了0-3位。
页的上一个单位是块,一个块含有32个页,所以块内地址应该是5。
我们当前操作的芯片有4096块,我们姑且把这个叫做芯片的范畴吧,那么芯片内块地址就是12位。
好了,访问地址解释清楚了。
---------------------------------------------------------------------------------------------------------------------------------
说说s3c2440 的NAND flash控制器:
一共有这么几个寄存器可以用 (2440 )
NFCONF
这个寄存器,主要是用于配置时序,等等,在芯片初始化的时候,一次行配置好就不用惯了。
NFCONT
这个寄存器,主要是用于使能/ 禁能我们的flash芯片。 这里要有一个意识: 操作flash芯片的流程是这样的: 使能芯片,操作芯片,禁能芯片。这个要有一个意识。
还有就是,韦东山书上一直说这个是选择芯片,窃以为说使能比说选择来的容易理解,但是linux里面也是使用了select这个名词。是不是因为我们很多时候flash芯片不止一种,那么我们选择哪个芯片就把芯片对应的这一位使能,所以我们说这一位是选择芯片的位。到底什么原因,且不深究,回头再说。知道意思即可。
NFCMD
命令寄存器,往这个地址写命令就会发送到flash芯片上去。
主要命令有这么几个:
读命令:前面介绍了,有三种读命令。
reset命令:芯片初始化完成后,我们往往要reset下芯片,他会终止当前芯片正在进行的所有,读,写,擦出等命令。
NFADDR
地址寄存器。我们写一个地址的时候,地址是24位的,这个寄存器只有8位,所以我们要分次写。0-7 一次。 8位前面提到了通过命令来设置。 9-16 17-24 25-32(只有25有效)
NFDATA
数据寄存器,我们写数据的时候,就8位为单位,不断往里面写就行。 读数据的时候,一直读,读够数就OK。
NFSTAT
状态寄存器,使用他的第0位,我们可以判断前面的命令执行完成了没有,目前的方法是死循环检查。
死循环检查???效率不低???看看linux如何实现了,按道理该用中断哈。
---------------------------------------------------------------------------------------------------------------------------------
命令序列
重置flash芯片:
NFCONT &=~(1<<1) 使能/选择芯片
NFCMD = 0xff 发送重置命令
while (NFSTAT 第0位为1) 说明重置命令执行完毕了
NFCONT |= 0x2 禁能flash
读数据
NFCONT &=~(1<<1) 使能/选择芯片
NFCMD = 0 发送读命令
写入地址:
NFADDR = addr & 0x ff 把地址的0-7位写入地址寄存器
NFADDR = (addr>> 9 ) 把地址的8-16位写入地址寄存器
NFADDR = (addr>> 17 ) 把地址的17-24位写入地址寄存器
NFADDR = (addr>> 25 ) 把地址的25-31位写入地址寄存器
while (NFSTAT 第0位为1) 说明写地址命令执行完毕了
连续读取寄存器NFDATA512次,因为我们读取是按照页为单位的。
NFCONT |= (1<<1)
其他的命令序列,可以参考韦东山那本书上的章节。都是差不多的东西。
操作nandflash的方法就是这样的:使能芯片--》 发送命令 ---》 发送地址-----》读/写 数据------》禁能芯片
==================================================================================
第一次升华:把对寄存器的直接引用转换为结构体的方式使用
我们看uboot的代码可以分析的出,以前曾经提过 http://blog.chinaunix.net/uid-28708203-id-3570133.html
那里讲到了头文件的作用,我们这里再叙述一遍。
我们在实际的工程中,不会直接使用一个寄存器地址,而是会把寄存器的地址以 结构体加 函数的方式来使用,比如说这里我们的nandflash,他的结构体就这么来管理:
结构体在 include/s3c24x0.h中添加(结构提结构来自s3c2440 芯片手册的nandflash部分):
-
/* NAND FLASH (see S3C2440 manual) */
-
typedef struct {
-
S3C24X0_REG32 NFCONF;
-
S3C24X0_REG32 NFCONT;
-
S3C24X0_REG32 NFCMD;
-
S3C24X0_REG32 NFADDR;
-
S3C24X0_REG32 NFDATA;
-
S3C24X0_REG32 NFMECCD0;
-
S3C24X0_REG32 NFMECCD1;
-
S3C24X0_REG32 NFSECCD;
-
S3C24X0_REG32 NFSTAT;
-
S3C24X0_REG32 NFESTAT0;
-
S3C24X0_REG32 NFESTAT1;
-
S3C24X0_REG32 NFMECC0;
-
S3C24X0_REG32 NFMECC1;
-
S3C24X0_REG32 NFSECC;
-
S3C24X0_REG32 NFSBLK;
-
S3C24X0_REG32 NFEBLK
-
} /*__attribute__((__packed__))*/ S3C2440_NAND;
include/config/s3c2440.h 中添加函数
点击(此处)折叠或打开
-
#define S3C2410_NAND_BASE 0x4E000000 来自s3c2440 芯片手册的nandflash部分。
-
-
static inline S3C2440_NAND * const S3C2440_GetBase_NAND(void)
-
{
-
return (S3C2440_NAND * const)S3C2440_NAND_BASE;
-
}
------------------------------------------------------------------------------------------------------------------------------
第一次升华 函数的使用。
上面的改造是把寄存器的使用,从直接使用寄存器的地址转变为使用结构体的方式。
下面要改造命令序列,把他们改造成为函数。
选择/使能 芯片函数,chip为-1的时候禁能,其余的时候,使能芯片。
-
static void s3c2440_nand_select_chip( int chip)
-
{
-
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
-
-
if (chip == -1) {
-
s3c2440nand->NFCONT |= S3C2440_NFCONT_nFCE;
-
} else {
-
s3c2440nand->NFCONT &= ~S3C2440_NFCONT_nFCE;
-
}
-
}
查询NFSTAT寄存器第0位,以判断上一次命令是否执行完毕,的函数。
-
static int s3c2440_nand_devready()
-
{
-
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
-
-
return (s3c2440nand->NFSTAT & S3C2440_NFSTAT_READY);
-
}
其余读,写等等的操作,也都可以转换为函数。
==================================================================================
下面谈谈uboot中nandflash的工作系统。
从上面我们可以的出,给uboot添加nandflash支持需要做这么几个操作。
1,添加相应的结构体和那个函数,以使用nandflash的寄存器。
2,构造各个函数,完成操作nandflash的各个操作。同时我们不能直接使用这些个函数,还需要一个结构提,这个结构体的成员都是函数指针,只想刚刚提到的函数们,把结构体成员(函数指针)指向函数的过程可以理解为软件的初始化。
3,同时,一直没有提到的是,nandflash初始化的时候,需要配置时序。这个需要一个额外的函数,这个是真正的硬件初始。
---------------------------------------------------------------------------------------------------------------------------------
看看uboot的flash初始化流程:
lib_arm/board.c : void start_armboot (void) --->
drivers/nand/nand.c: void nand_init(void) --->
---------同上--------------: static void nand_init_chip( ----->
cpu/arm920t/s3c24x0/nand.c: board_nand_init(
board_nand_init 这个函数就是我们要自己实现的。前面三个函数都是uboot管理nandflash的架构函数。下面分析下下。
第一个 地方不用分析了,就是系统启动的时候挨个调用初始化函数。
-
void nand_init(void)
-
{
-
int i;
-
unsigned int size = 0;
-
for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) { //这个地方是遍历初始化所有的nandflash芯片
-
nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);
-
size += nand_info[i].size;
-
if (nand_curr_device == -1)
-
nand_curr_device = i;
-
}
-
printf("%lu MiB\n", size / (1024 * 1024));
-
-
#ifdef CFG_NAND_SELECT_DEVICE
-
/*
-
* Select the chip in the board/cpu specific driver
-
*/
-
board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
-
#endif
-
}
这里这个结构体nand_info的结构定义是这样的,uboot借鉴了linux管理nandflash对象的方式:
使用一个结构体struct mtd_info 来表示一个nandflash设备。就我目前的分析来看,真正起作用的是mtd->priv 这个成员变量。考虑到以前的看内核的经验,这个成员变量就是private的那部分。
从下面代码中我们可以看到,我们为这个成员变量赋值了这么个结构 struct nand_chip 。
-
static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,
-
ulong base_addr)
-
{
-
mtd->priv = nand;
-
-
nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
-
board_nand_init(nand);
-
-
if (nand_scan(mtd, 1) == 0) {
-
if (!mtd->name)
-
mtd->name = (char *)default_nand_name;
-
} else
-
mtd->name = NULL;
-
-
}
然后以 struct nand_chip为参数,调用我们自己的nand初始化函数。这个函数说白了,就是定义一个struct nand_chip,然后关联到mtd这个对象上去,然后调用我们自己的函数,来初始化函数。
看看这个定义在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);
-
-
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);
-
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);
-
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;
-
int eccsize;
-
int eccbytes;
-
int eccsteps;
-
int chip_delay;
-
#if 0
-
spinlock_t chip_lock;
-
wait_queue_head_t wq;
-
nand_state_t state;
-
#endif
-
int page_shift;
-
int phys_erase_shift;
-
int bbt_erase_shift;
-
int chip_shift;
-
u_char *data_buf;
-
u_char *oob_buf;
-
int oobdirty;
-
u_char *data_poi;
-
unsigned int options;
-
int badblockpos;
-
int numchips;
-
unsigned long chipsize;
-
int pagemask;
-
int pagebuf;
-
struct nand_oobinfo *autooob;
-
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;
-
}
丫的这么长,肯定 又是从linux那边抄过来的。
说白了,就是把上面我们提到的那些个函数,赋值到这个结构体中的成员变量中去。
我们这个初始化函数是这么写的:
-
void board_nand_init(struct nand_chip *chip)
-
{
-
S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
-
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
-
-
s3c24x0_nand_inithw();
-
-
if (gd->bd->bi_arch_number == MACH_TYPE_SMDK2410) {
-
chip->IO_ADDR_R = (void *)&s3c2410nand->NFDATA;
-
chip->IO_ADDR_W = (void *)&s3c2410nand->NFDATA;
-
chip->hwcontrol = s3c2410_nand_hwcontrol;
-
chip->dev_ready = s3c2410_nand_devready;
-
chip->select_chip = s3c2410_nand_select_chip;
-
chip->options = 0;
-
} else {
-
chip->IO_ADDR_R = (void *)&s3c2440nand->NFDATA;
-
chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;
-
chip->hwcontrol = s3c2440_nand_hwcontrol;
-
chip->dev_ready = s3c2440_nand_devready;
-
chip->select_chip = s3c2440_nand_select_chip;
-
chip->options = 0;
-
}
-
-
chip->eccmode = NAND_ECC_SOFT;
-
}
这个函数,首先执行的是前面提到的硬件初始化函数,调用 s3c24x0_nand_inithw(); 来进行时序等的配置。
然后把读,写,重置,等待等等的函数,赋值到 struct nand_chip 这个结构体的成员变量中去。至于这些个函数怎么实现,大多是参考了linux的代码。其实仔细看看也不难理解,
挨个分析一下下:
chip->IO_ADDR_R = (void *)&s3c2440nand->NFDATA;
这个成员变量是为了提供我们读外设存储的地址。我们读取外设的内容的时候,肯定是从NFDATA中读取。
chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;
我们往外设的存储空间写内容的时候,有这么几种情况,从功能流程上我们就可以看出,首先是写命令,写入命令寄存器。然后是写地址,写入地址寄存器,最后是写数据,写入数据寄存器。所以这个成员变量可以取得值有三个。分别是命令寄存器NFCMD ,地址寄存器 NFADDR, 数据寄存器 NFDATA
chip->hwcontrol = s3c2440_nand_hwcontrol;
这个函数稍微有点特殊,他是配合上面那个成员变量IO_ADDR_W来配合使用的。
上面提到我们写外设的时候有三种情况,可以分别实现三个函数,写命令函数,写地址函数,写数据函数。如果我们想想这三个函数的共同点,都是有一个“写”的操作,他们的区别是,写的地方不同,那么我们就可以把这个相同点抽象出来。
本来是三个函数:
cmd_write()
address_wirte()
data_write()
现在变为两个函数:
s3c2440_nand_hwcontrol
{
switch (写入功能)
case 写入命令 :chip->IO_ADDR_W = NFCMD
case 写入地址: chip->IO_ADDR_W = NFADDR
case 写入数据 :chip->IO_ADDR_W = NFDATA
}
----------------------------------------------------------------------------------
output_write()
{
*(chip->IO_ADDR_W) =数据内容(命令/地址/数据)
}
chip->dev_ready = s3c2440_nand_devready;
这个成员是查询NFSTAT这个结构体的0位,查看上一次的执行结果有没有结束。
chip->select_chip = s3c2440_nand_select_chip;
这个函数就是为了使能/禁能flash芯片。
chip->options = 0;
---------------------------------------------------------------------------------------------------------------------------------
现在解释完了,我们只需要把这些个函数的实现放到文件中,编译到uboot中(修改makefile吗)即可。
cpu/arm920t/s3c24x0/nand_flash.c
文件内容分析见:http://blog.chinaunix.net/uid-28708203-id-3641000.html
同时我们还需要修改头文件的几个内容:
include/configs/s3c2440.h
添加nandflash命令
-
#define CONFIG_COMMANDS \
-
((CONFIG_CMD_DFL | \
-
CFG_CMD_CACHE | \
-
CFG_CMD_PING | \
-
CFG_CMD_JFFS2 | \
-
CFG_CMD_NAND | \
-
…………
前面代码分析的时候看到了,我们遍历所有的flash芯片,一一初始化,这里我们定义下设备,芯片的个数。
-
#define CFG_NAND_BASE 0
-
#define CFG_MAX_NAND_DEVICE 1
-
#define NAND_MAX_CHIPS 1
1,3 还没找到哪里用了。
阅读(550) | 评论(0) | 转发(0) |