这几天在做和nandflash相关的东西,之前uboot中nandflash部分搞得模模糊糊。这次就将uboot中nand flash相关部分分析清楚
。本文uboot版本1.3.3
按照uboot的执行流程,在lib_arm/board.c文件中的start_armboot函数中会调用到nand初始化。
初始化的调用流程大致为:
start_armboot
nand_init
//driver/mtd/nand/nand.c
nand_init_chip //driver/mtd/nand/nand.c
board_nand_init
//cpu/sep4020/nand_flash.c
nand_scan //driver/mtd/nand/nand_base.c
start_armboot函数中,nand初始化部分
点击(此处)折叠或打开
- #if defined(CONFIG_CMD_NAND)
/*有nand的板都会定义CONFIG_CMD_NAND*/
- puts ("NAND: ");
- nand_init();
/* go init the NAND
*/
- #endif
nand_init()在driver/mtd/nand/nand.c文件中定义,此为nand初始化入口函数。
点击(此处)折叠或打开
- void nand_init(void)
- {
- int i;
- unsigned int size
= 0;
- for (i
= 0; i < CFG_MAX_NAND_DEVICE; i++)
{ //(1)
- nand_init_chip(&nand_info[i],
&nand_chip[i], base_address[i]);
//(2)
- size += nand_info[i].size;
//(3)
- 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
- }
(1)CFG_MAX_NAND_DEVICE,nand的数量,一般板上有一个nand,就定义为1
(2)此函数初始化一个nand flash。首先看函数参数的3个变量。
参数1 nand_info[i],其定义如
nand_info_t nand_info[CFG_MAX_NAND_DEVICE];
//driver/mtd/nand/nand.c
定义了一个nand_info_t类型的全局数组,当然这里其CFG_MAX_NAND_DEVICE等于1,只有一个成员,再看nand_info_t定义
typedef struct mtd_info nand_info_t; //include/Nand.h
mtd_info定义在include/linux/mtd/mtd.h中,它表示一个mtd设备的结构体,包含了mtd属性和其操作函数。
点击(此处)折叠或打开
- struct mtd_info {
- u_char type;
- u_int32_t flags;
- u_int32_t size;
/* Total size of the MTD
*/
- /*
"Major" erase size
for the device. Na飗e users may take this
- * to be the only
erase size available,
or may use the more detailed
- * information below
if they desire
- */
- u_int32_t erasesize;
- u_int32_t oobblock;
/* Size of OOB blocks
(e.g. 512)
*/
- u_int32_t oobsize;
/* Amount of OOB data per block
(e.g. 16)
*/
- u_int32_t oobavail;
/* Number of bytes
in OOB area available for fs
*/
- u_int32_t ecctype;
- u_int32_t eccsize;
- /* Kernel-only stuff starts here.
*/
- char *name;
- int index;
- /* oobinfo
is a nand_oobinfo structure, which can be
set by iotcl (MEMSETOOBINFO)
*/
- struct nand_oobinfo oobinfo;
- /* Data
for variable erase regions.
If numeraseregions
is zero,
- * it means that the whole device has erasesize as given above.
- */
- int numeraseregions;
- struct mtd_erase_region_info *eraseregions;
- /* This really shouldn't be here. It can go away
in 2.5
*/
- u_int32_t bank_size;
- int (*erase)
(struct mtd_info *mtd, struct erase_info
*instr);
- /* This stuff
for eXecute-In-Place
*/
- int (*point)
(struct mtd_info *mtd, loff_t from, size_t
len, size_t
*retlen, u_char
**mtdbuf);
- /* We probably shouldn't allow XIP
if the unpoint isn't a
NULL */
- 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);
- 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);
- 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);
- /*
- * Methods to access the protection register area, present
in some
- * flash devices. The user data
is one time programmable but the
- * factory data
is read only.
- */
- int (*read_user_prot_reg)
(struct mtd_info *mtd, loff_t from, size_t
len, size_t
*retlen, u_char
*buf);
- int (*read_fact_prot_reg)
(struct mtd_info *mtd, loff_t from, size_t
len, size_t
*retlen, u_char
*buf);
- /* This
function is
not yet implemented
*/
- int (*write_user_prot_reg)
(struct mtd_info *mtd, loff_t from, size_t
len, size_t
*retlen, u_char
*buf);
- /* Sync
*/
- void (*sync)
(struct mtd_info *mtd);
- /* Bad block management functions
*/
- int (*block_isbad)
(struct mtd_info *mtd, loff_t ofs);
- int (*block_markbad)
(struct mtd_info *mtd, loff_t ofs);
- void *priv;
- struct module *owner;
- int usecount;
- };
参数2 nand_chip[i] ,如下
static struct nand_chip nand_chip[CFG_MAX_NAND_DEVICE];
再看struct nand_chip定义,当前文件(driver/mtd/nand/nand.c)包含nand.h(include目录),nand.h又包含#include <linux/mtd/nand.h>,所以nand_chip的定义是linux/mtd/nand.h中的。不是nand_legacy.h。这个结构体表示一个nand flash其包含所有属性和操作函数。
点击(此处)折叠或打开
- /**
- * struct nand_chip
- NAND Private Flash Chip Data
- * @IO_ADDR_R:
[BOARDSPECIFIC] address
to read the 8 I/O lines of the flash device
- * @IO_ADDR_W:
[BOARDSPECIFIC] address
to write the 8 I/O lines of the flash device
- * @read_byte:
[REPLACEABLE] read one byte from the chip
- * @write_byte:
[REPLACEABLE] write one byte
to the chip
- * @read_word:
[REPLACEABLE] read one word from the chip
- * @write_word:
[REPLACEABLE] write one word
to the chip
- * @write_buf:
[REPLACEABLE] write data from the buffer
to the chip
- * @read_buf:
[REPLACEABLE] read data from the chip into the buffer
- * @verify_buf:
[REPLACEABLE] verify buffer contents against the chip data
- * @select_chip:
[REPLACEABLE]
select chip nr
- * @block_bad:
[REPLACEABLE] check,
if the block is bad
- * @block_markbad:
[REPLACEABLE] mark the block bad
- * @hwcontrol:
[BOARDSPECIFIC] hardwarespecific
function for accesing control-lines
- * @dev_ready:
[BOARDSPECIFIC] hardwarespecific
function for accesing device ready/busy line
- * If
set to NULL no access
to ready/busy
is available and the ready/busy information
- * is read from the chip status register
- * @cmdfunc:
[REPLACEABLE] hardwarespecific
function for writing commands
to the chip
- * @waitfunc:
[REPLACEABLE] hardwarespecific
function for wait
on ready
- * @calculate_ecc:
[REPLACEABLE]
function for ecc calculation
or readback from ecc hardware
- * @correct_data:
[REPLACEABLE]
function for ecc correction, matching
to ecc generator (sw/hw)
- * @enable_hwecc:
[BOARDSPECIFIC]
function to enable
(reset) hardware ecc generator. Must only
- * be provided
if a hardware ECC is available
- * @erase_cmd:
[INTERN]
erase command write function, selectable due
to AND support
- * @scan_bbt:
[REPLACEABLE]
function to scan bad block table
- * @eccmode:
[BOARDSPECIFIC] mode of ecc, see defines
- * @eccsize:
[INTERN] databytes used per ecc-calculation
- * @eccbytes:
[INTERN] number of ecc bytes per ecc-calculation
step
- * @eccsteps:
[INTERN] number of ecc calculation steps per page
- * @chip_delay:
[BOARDSPECIFIC] chip dependent delay
for transfering data from
array to read regs
(tR)
- * @chip_lock:
[INTERN] spinlock used
to protect access
to this structure and the chip
- * @wq:
[INTERN] wait queue
to sleep on
if a NAND operation is
in progress
- * @state:
[INTERN] the current state of the NAND device
- * @page_shift:
[INTERN] number of address bits
in a page (column address bits)
- * @phys_erase_shift:
[INTERN] number of address bits
in a physical eraseblock
- * @bbt_erase_shift:
[INTERN] number of address bits
in a bbt entry
- * @chip_shift:
[INTERN] number of address bits
in one chip
- * @data_buf:
[INTERN] internal buffer
for one page + oob
- * @oob_buf:
[INTERN] oob buffer
for one eraseblock
- * @oobdirty:
[INTERN] indicates that oob_buf must be reinitialized
- * @data_poi:
[INTERN] pointer
to a data buffer
- * @options:
[BOARDSPECIFIC] various chip options. They can partly be
set to inform nand_scan about
- * special functionality. See the defines
for further explanation
- * @badblockpos:
[INTERN] position of the bad block marker
in the oob area
- * @numchips:
[INTERN] number of physical chips
- * @chipsize:
[INTERN] the size of one chip
for multichip arrays
- * @pagemask:
[INTERN] page number mask
= number of (pages
/ chip)
- 1
- * @pagebuf:
[INTERN] holds the pagenumber which
is currently in data_buf
- * @autooob:
[REPLACEABLE] the default
(auto)placement scheme
- * @bbt:
[INTERN] bad block table pointer
- * @bbt_td:
[REPLACEABLE] bad block table descriptor
for flash lookup
- * @bbt_md:
[REPLACEABLE] bad block table mirror descriptor
- * @badblock_pattern:
[REPLACEABLE] bad block scan pattern used
for initial bad block scan
- * @controller:
[OPTIONAL] a pointer
to a hardware controller structure which
is shared among multiple independend devices
- * @priv:
[OPTIONAL] pointer
to private chip
date
- */
- 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;
- };
参数3 base_address[i],如下
static ulong base_address[CFG_MAX_NAND_DEVICE] = CFG_NAND_BASE_LIST;
CFG_NAND_BASE_LIST如下,
#ifndef CFG_NAND_BASE_LIST
#define CFG_NAND_BASE_LIST { CFG_NAND_BASE }
#endif
CFG_NAND_BASE是在include/configs/UB4020.h中配置为
#define CFG_NAND_BASE 0x11000200 (nand FIFO 数据寄存器)
(3)计算出总共nandflash多少容量,在紧随其后的printf语句中打印出来。
接着看同文件(driver/mtd/nand/nand.c)中nand_init_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;
//(1)
- if (board_nand_init(nand)
== 0)
{ //(2)
- if (nand_scan(mtd, 1)
== 0)
{ //(3)
- if (!mtd->name)
//这个名字在nand_scan中设置,如果在table中找到就有了。
- mtd->name
= (char
*)default_nand_name;
- } else
- mtd->name
= NULL;
- } else
{
- mtd->name
= NULL;
- mtd->size
= 0;
- }
- }
(1)从上面的nand_chip结构体中写道IO_ADDR_R,IO_ADDR_W是nand flash的读写地址, base_add这里设置为0x11000200,正好的nand FIFO的数据寄存器,读写flash的接口寄存器。
(2)此函数设置相关的nand 初始化,它和具体的体现结构有关系,不是共性的东西,在cpu/xxx/Nand_flash.c文件中。
(3) 此函数设置通用默认处理,获得flash id,并匹配等等。
这里我们分析的是sep4020 cpu的board_nand_init() 在cpu/sep4020/nand_flash.c
点击(此处)折叠或打开
- int board_nand_init( struct nand_chip
*chip )
- {
- memset((char
*) chip, 0, sizeof(struct nand_chip));
- INTC_IMR = 0XFFFFFFFF;
//(REGW(INTC_BASE+0X008))IRQ中断屏蔽寄存器
置1为屏蔽 0为通过
- INTC_IMR = 0X00000000;
- EMI_NAND_CONF1 = 0x06402857;
//(1)
- EMI_NAND_CONF2 = 0x00d14353;
//(2)
- vaddr = malloc(2112);
//(3)
- oob64 = malloc(2112);
- memset(vaddr,0,2112);
- memset(oob64,0,2112);
- int erasepage;
- /*设置nand_chip结构中的各个函数指针*/
- /*
Set address of NAND IO lines */
- chip->IO_ADDR_R
= (void
*) EMI_NAND_DATA_RAW;
//设置nand flash读写寄存器地址,其实在调用函数中已经设置过了
- chip->IO_ADDR_W
= (void
*) EMI_NAND_DATA_RAW;
- /*
Set address of hardware control function
*/
- chip->hwcontrol
= sep4020_hwcontrol;
- /* 15 us command delay
time */
- chip->dev_ready
= sep4020_nand_dev_ready;
- chip->chip_delay
= 15;
- chip->write_buf
= sep4020_nand_write_buf;
- chip->read_buf
= sep4020_nand_read_buf;
- chip->write_byte
= sep4020_nand_write_byte;
- chip->read_byte
= sep4020_nand_read_byte;
- chip->eccmode
= NAND_ECC_SOFT;
- chip->select_chip
= sep4020_nand_select_chip;
- chip->cmdfunc
= sep4020_nand_command;
- chip->erase_cmd
= sep4020_nand_cmd_erase;
- /* Return happy
*/
- return 0;
- }
(1)NAND FLASH的配置器存器1 110-0--100--000000--101000--0101--0111 可查看芯片手册,其中一项设置成5级地址
(2)NAND FLASH的配置器存器2 1--1--0--100--010100--0011--01--01--00--11 可查看芯片手册,页大小配置为2K
(3)分配一个整页空间,后续的读写操作中会用到它来暂存一页数据。