UBOOT_NAND驱动分析

一、 编写目的 2

二、 驱动分析 2

1. 驱动入口 2

2. NAND相关的结构体 2

3. NAND初始化过程 7

4. NAND操作函数分析 15

 


一、编写目的

        在《NAND_FLASH(K9F1208U0C)驱动分析》一文中,通过分析AT91SAM9260EK开发板Bootstrap中的NAND驱动时,发现该代码为了优化代码体积(4KB限制),没有实现nand的擦写驱动,因此现重新对U-BOOT的代码的NAND驱动进行分析。以加强对NAND的理解。

本文档用于记录NAND FLASH驱动分析的过程。所使用的代码为以前开发过的一个U-BOOT(64MB版本)。


二、驱动分析

1. 驱动入口

        U-BOOT的C语言入口位于./lib_arm/board.c的start_armboot()中。在该函数中有调用NAND的初始化函数nand_init()。

start_armboot()中的代码片段(./lib_arm/board.c)

#if defined(CONFIG_CMD_NAND)

puts ("NAND:  ");

nand_init(); /* go init the NAND */

#endif

 

#if defined(CONFIG_CMD_ONENAND)

onenand_init();

#endif

 

2. NAND相关的结构体

a) NAND设备参数描述表

./include/linux/mtd/nand_ids.h中的结构定义

static struct nand_flash_dev nand_flash_ids[] = {

{"Toshiba TC5816BDC",     NAND_MFR_TOSHIBA, 0x64, 21, 1, 2, 0x1000, 0},

{"Toshiba TC5832DC",      NAND_MFR_TOSHIBA, 0x6b, 22, 0, 2, 0x2000, 0},

{"Toshiba TH58V128DC",    NAND_MFR_TOSHIBA, 0x73, 24, 0, 2, 0x4000, 0},

{"Toshiba TC58256FT/DC",  NAND_MFR_TOSHIBA, 0x75, 25, 0, 2, 0x4000, 0},

{"Toshiba TH58512FT",     NAND_MFR_TOSHIBA, 0x76, 26, 0, 3, 0x4000, 0},

{"Toshiba TC58V32DC",     NAND_MFR_TOSHIBA, 0xe5, 22, 0, 2, 0x2000, 0},

{"Toshiba TC58V64AFT/DC", NAND_MFR_TOSHIBA, 0xe6, 23, 0, 2, 0x2000, 0},

{"Toshiba TC58V16BDC",    NAND_MFR_TOSHIBA, 0xea, 21, 1, 2, 0x1000, 0},

{"Toshiba TH58100FT",     NAND_MFR_TOSHIBA, 0x79, 27, 0, 3, 0x4000, 0},

{"Samsung KM29N16000",    NAND_MFR_SAMSUNG, 0x64, 21, 1, 2, 0x1000, 0},

{"Samsung unknown 4Mb",   NAND_MFR_SAMSUNG, 0x6b, 22, 0, 2, 0x2000, 0},

{"Samsung KM29U128T",     NAND_MFR_SAMSUNG, 0x73, 24, 0, 2, 0x4000, 0},

{"Samsung KM29U256T",     NAND_MFR_SAMSUNG, 0x75, 25, 0, 2, 0x4000, 0},

{"Samsung unknown 64Mb",  NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000, 0},  // 本核心板使用的NAND

{"Samsung KM29W32000",    NAND_MFR_SAMSUNG, 0xe3, 22, 0, 2, 0x2000, 0},   // NAND_MFR_SAMSUNG = 0xEC

{"Samsung unknown 4Mb",   NAND_MFR_SAMSUNG, 0xe5, 22, 0, 2, 0x2000, 0},

{"Samsung KM29U64000",    NAND_MFR_SAMSUNG, 0xe6, 23, 0, 2, 0x2000, 0},

{"Samsung KM29W16000",    NAND_MFR_SAMSUNG, 0xea, 21, 1, 2, 0x1000, 0},

{"Samsung K9F5616Q0C",    NAND_MFR_SAMSUNG, 0x45, 25, 0, 2, 0x4000, 1},

{"Samsung K9K1216Q0C",    NAND_MFR_SAMSUNG, 0x46, 26, 0, 3, 0x4000, 1},

{"Samsung K9F1G08U0M",    NAND_MFR_SAMSUNG, 0xf1, 27, 0, 2, 0, 0},

{NULL,}

};

 

        该表记录着UBOOT所支持的NAND型号列表,软件读取NAND的ID值后,与该表中的ID值(K9F1208U0C的ID等于0xEC76)进行比较,如ID相等,则说明UBOOT目前支持该NAND芯片,则从该表中获取NAND的信息(BLOCK SIZE、PAGE SIZE等)。

 

b) MTD设备信息结构体typedef struct mtd_info nand_info_t

./include/linux/mtd/mtd.h中的结构定义

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);

#if 0

/* kvec-based read/write methods. We need these especially for NAND flash,

   with its limited number of write cycles per erase.

   NB: The 'count' parameter is the number of _vectors_, each of

   which contains an (ofs, len) tuple.

*/

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);

#endif

/* Sync */

void (*sync) (struct mtd_info *mtd);

#if 0

/* Chip-supported device locking */

int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);

int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);

 

/* Power Management functions */

int (*suspend) (struct mtd_info *mtd);

void (*resume) (struct mtd_info *mtd);

#endif

/* 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;

};

 

        该结构体中包含着NAND的MTD设备信息和操作函数指针。

 

c) NAND芯片信息结构体struct 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);

 

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;

};

        该结构体中包含着NAND的芯片级设备信息和操作函数指针。其中MTD信息结构中应该包含芯片级操作。


3. NAND初始化过程

a) nand_init函数

        该函数调用nand_init_chip初始化进行芯片级初始化。nand_curr_device记录当前使用的NAND芯片引索号,本系统只使用到一片NAND,因此等于0。接着输出NAND的大小信息,这就是UBOOT启动时输出的“MiB”了,当时还有过疑问为什么不是输出“MB”为单位的呢,难道是手误?应该是故意的吧。

        最后调用board_nand_select_device选择相应NAND,但该系统中只有一块NAND,实际上CFG_NAND_SELECT_DEVICE是没有定义的,所以不会去执行。

./drivers/mtd/nand/nand.c中定义该函数

void nand_init(void)

{

int i;

unsigned int size = 0;

for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) {

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("%u 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

}

 

b) nand_init_chip函数

        此函数传进行两个结构体的地址,调用成功后填充这两个结构体的数据。

                Ø 连接MTD与CHIP结构信息

                Ø 设置NAND的基地址。该核心板中,NAND挂在CPU的EBI CS3区,寻址的起始地址为0x4000 000。NAND的每一次操作都是往该地址操作数据,哪怕是操作ALE、        CLE也是。

                Ø 调用板极NAND初始化board_nand_init

                Ø 调用nand_scan初始化设备信息

./drivers/mtd/nand/nand.c中定义该函数

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;

if (board_nand_init(nand) == 0) {

if (nand_scan(mtd, 1) == 0) {

if (!mtd->name)

mtd->name = (char *)default_nand_name;

} else

mtd->name = NULL;

} else {

mtd->name = NULL;

mtd->size = 0;

}

 

}

 

c) board_nand_init函数

        板级NAND初始化函数,主要设置ECC模式、硬件控制函数(ALE、CLE)、状态读取函数(READY/BUSY)、延时等待值。

Nand.c (board\atmel\at91sam9260ek)

int board_nand_init(struct nand_chip *nand)

{

nand->eccmode = NAND_ECC_SOFT;

#ifdef CFG_NAND_DBW_16

nand->options = NAND_BUSWIDTH_16;

#endif

nand->hwcontrol = at91sam9260ek_nand_hwcontrol;

nand->dev_ready = at91sam9260ek_nand_ready;

nand->chip_delay = 20;

 

return 0;

}

 

 

d) nand_scan函数

        该函数扫描NAND芯片,并设置相关结构体数据。

                Ø 先设置一系列的操作函数

                Ø 再通过读取ID命令读取ID,并与NAND设备参数描述表中的ID进行比较

                Ø 找到支持的NAND后开始设置MTD设备信息结构体

Nand_base.c (drivers\mtd\nand)

int nand_scan (struct mtd_info *mtd, int maxchips)

{

int i, j, nand_maf_id, nand_dev_id, busw;

struct nand_chip *this = mtd->priv;

 

/* Get buswidth to select the correct functions*/

busw = this->options & NAND_BUSWIDTH_16;

 

/* check for proper chip_delay setup, set 20us if not */

if (!this->chip_delay)

this->chip_delay = 20;

 

/* check, if a user supplied command function given */

if (this->cmdfunc == NULL)

this->cmdfunc = nand_command;

 

/* check, if a user supplied wait function given */

if (this->waitfunc == NULL)

this->waitfunc = nand_wait;

 

if (!this->select_chip)

this->select_chip = nand_select_chip;

if (!this->write_byte)

this->write_byte = busw ? nand_write_byte16 : nand_write_byte;

if (!this->read_byte)

this->read_byte = busw ? nand_read_byte16 : nand_read_byte;

if (!this->write_word)

this->write_word = nand_write_word;

if (!this->read_word)

this->read_word = nand_read_word;

if (!this->block_bad)

this->block_bad = nand_block_bad;

if (!this->block_markbad)

this->block_markbad = nand_default_block_markbad;

if (!this->write_buf)

this->write_buf = busw ? nand_write_buf16 : nand_write_buf;

if (!this->read_buf)

this->read_buf = busw ? nand_read_buf16 : nand_read_buf;

if (!this->verify_buf)

this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;

if (!this->scan_bbt)

this->scan_bbt = nand_default_bbt;

 

/* Select the device */

this->select_chip(mtd, 0);

 

/* Send the command for reading device ID */

this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);

 

/* Read manufacturer and device IDs */

nand_maf_id = this->read_byte(mtd);

nand_dev_id = this->read_byte(mtd);

 

/* Print and store flash device information */

for (i = 0; nand_flash_ids[i].name != NULL; i++) {

 

if (nand_dev_id != nand_flash_ids[i].id)

continue;

 

if (!mtd->name) mtd->name = nand_flash_ids[i].name;

this->chipsize = nand_flash_ids[i].chipsize << 20;

 

/* New devices have all the information in additional id bytes */

if (!nand_flash_ids[i].pagesize) {

int extid;

/* The 3rd id byte contains non relevant data ATM */

extid = this->read_byte(mtd);

/* The 4th id byte is the important one */

extid = this->read_byte(mtd);

/* Calc pagesize */

mtd->oobblock = 1024 << (extid & 0x3);

extid >>= 2;

/* Calc oobsize */

mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock / 512);

extid >>= 2;

/* Calc blocksize. Blocksize is multiples of 64KiB */

mtd->erasesize = (64 * 1024)  << (extid & 0x03);

extid >>= 2;

/* Get buswidth information */

busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;

 

} else {

/* Old devices have this data hardcoded in the

 * device id table */

mtd->erasesize = nand_flash_ids[i].erasesize;

mtd->oobblock = nand_flash_ids[i].pagesize;

mtd->oobsize = mtd->oobblock / 32;

busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;

}

 

/* Check, if buswidth is correct. Hardware drivers should set

 * this correct ! */

if (busw != (this->options & NAND_BUSWIDTH_16)) {

printk (KERN_INFO "NAND device: Manufacturer ID:"

" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,

nand_manuf_ids[i].name , mtd->name);

printk (KERN_WARNING

"NAND bus width %d instead %d bit\n",

(this->options & NAND_BUSWIDTH_16) ? 16 : 8,

busw ? 16 : 8);

this->select_chip(mtd, -1);

return 1;

}

 

/* Calculate the address shift from the page size */

this->page_shift = ffs(mtd->oobblock) - 1;

this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;

this->chip_shift = ffs(this->chipsize) - 1;

 

/* Set the bad block position */

this->badblockpos = mtd->oobblock > 512 ?

NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;

 

/* Get chip options, preserve non chip based options */

this->options &= ~NAND_CHIPOPTIONS_MSK;

this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;

/* Set this as a default. Board drivers can override it, if neccecary */

this->options |= NAND_NO_AUTOINCR;

/* Check if this is a not a samsung device. Do not clear the options

 * for chips which are not having an extended id.

 */

if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)

this->options &= ~NAND_SAMSUNG_LP_OPTIONS;

 

/* Check for AND chips with 4 page planes */

if (this->options & NAND_4PAGE_ARRAY)

this->erase_cmd = multi_erase_cmd;

else

this->erase_cmd = single_erase_cmd;

 

/* Do not replace user supplied command function ! */

if (mtd->oobblock > 512 && this->cmdfunc == nand_command)

this->cmdfunc = nand_command_lp;

 

/* Try to identify manufacturer */

for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {

if (nand_manuf_ids[j].id == nand_maf_id)

break;

}

break;

}

 

if (!nand_flash_ids[i].name) {

#ifndef CFG_NAND_QUIET_TEST

printk (KERN_WARNING "No NAND device found!!!\n");

#endif

this->select_chip(mtd, -1);

return 1;

}

 

for (i=1; i < maxchips; i++) {

this->select_chip(mtd, i);

 

/* Send the command for reading device ID */

this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);

 

/* Read manufacturer and device IDs */

if (nand_maf_id != this->read_byte(mtd) ||

    nand_dev_id != this->read_byte(mtd))

break;

}

if (i > 1)

printk(KERN_INFO "%d NAND chips detected\n", i);

 

/* Allocate buffers, if neccecary */

if (!this->oob_buf) {

size_t len;

len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);

this->oob_buf = kmalloc (len, GFP_KERNEL);

if (!this->oob_buf) {

printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");

return -ENOMEM;

}

this->options |= NAND_OOBBUF_ALLOC;

}

 

if (!this->data_buf) {

size_t len;

len = mtd->oobblock + mtd->oobsize;

this->data_buf = kmalloc (len, GFP_KERNEL);

if (!this->data_buf) {

if (this->options & NAND_OOBBUF_ALLOC)

kfree (this->oob_buf);

printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");

return -ENOMEM;

}

this->options |= NAND_DATABUF_ALLOC;

}

 

/* Store the number of chips and calc total size for mtd */

this->numchips = i;

mtd->size = i * this->chipsize;

/* Convert chipsize to number of pages per chip -1. */

this->pagemask = (this->chipsize >> this->page_shift) - 1;

/* Preset the internal oob buffer */

memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));

 

/* If no default placement scheme is given, select an

 * appropriate one */

if (!this->autooob) {

/* Select the appropriate default oob placement scheme for

 * placement agnostic filesystems */

switch (mtd->oobsize) {

case 8:

this->autooob = &nand_oob_8;

break;

case 16:

this->autooob = &nand_oob_16;

break;

case 64:

this->autooob = &nand_oob_64;

break;

case 128:

this->autooob = &nand_oob_128;

break;

default:

printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",

mtd->oobsize);

/* BUG(); */

}

}

 

/* The number of bytes available for the filesystem to place fs dependend

 * oob data */

mtd->oobavail = 0;

for (i=0; this->autooob->oobfree[i][1]; i++)

mtd->oobavail += this->autooob->oobfree[i][1];

 

/*

 * check ECC mode, default to software

 * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize

 * fallback to software ECC

*/

this->eccsize = 256; /* set default eccsize */

this->eccbytes = 3;

 

switch (this->eccmode) {

case NAND_ECC_HW12_2048:

if (mtd->oobblock < 2048) {

printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",

       mtd->oobblock);

this->eccmode = NAND_ECC_SOFT;

this->calculate_ecc = nand_calculate_ecc;

this->correct_data = nand_correct_data;

} else

this->eccsize = 2048;

break;

 

case NAND_ECC_HW3_512:

case NAND_ECC_HW6_512:

case NAND_ECC_HW8_512:

if (mtd->oobblock == 256) {

printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");

this->eccmode = NAND_ECC_SOFT;

this->calculate_ecc = nand_calculate_ecc;

this->correct_data = nand_correct_data;

} else

this->eccsize = 512; /* set eccsize to 512 */

break;

 

case NAND_ECC_HW3_256:

break;

 

case NAND_ECC_NONE:

printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");

this->eccmode = NAND_ECC_NONE;

break;

 

case NAND_ECC_SOFT:

this->calculate_ecc = nand_calculate_ecc;

this->correct_data = nand_correct_data;

break;

 

default:

printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);

/* BUG(); */

}

 

/* Check hardware ecc function availability and adjust number of ecc bytes per

 * calculation step

*/

switch (this->eccmode) {

case NAND_ECC_HW12_2048:

this->eccbytes += 4;

case NAND_ECC_HW8_512:

this->eccbytes += 2;

case NAND_ECC_HW6_512:

this->eccbytes += 3;

case NAND_ECC_HW3_512:

case NAND_ECC_HW3_256:

if (this->calculate_ecc && this->correct_data && this->enable_hwecc)

break;

printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");

/* BUG(); */

}

 

mtd->eccsize = this->eccsize;

 

/* Set the number of read / write steps for one page to ensure ECC generation */

switch (this->eccmode) {

case NAND_ECC_HW12_2048:

this->eccsteps = mtd->oobblock / 2048;

break;

case NAND_ECC_HW3_512:

case NAND_ECC_HW6_512:

case NAND_ECC_HW8_512:

this->eccsteps = mtd->oobblock / 512;

break;

case NAND_ECC_HW3_256:

case NAND_ECC_SOFT:

this->eccsteps = mtd->oobblock / 256;

break;

 

case NAND_ECC_NONE:

this->eccsteps = 1;

break;

}

 

/* XXX U-BOOT XXX */

#if 0

/* Initialize state, waitqueue and spinlock */

this->state = FL_READY;

init_waitqueue_head (&this->wq);

spin_lock_init (&this->chip_lock);

#endif

 

/* De-select the device */

this->select_chip(mtd, -1);

 

/* Invalidate the pagebuffer reference */

this->pagebuf = -1;

 

/* Fill in remaining MTD driver data */

mtd->type = MTD_NANDFLASH;

mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;

mtd->ecctype = MTD_ECC_SW;

mtd->erase = nand_erase;

mtd->point = NULL;

mtd->unpoint = NULL;

mtd->read = nand_read;

mtd->write = nand_write;

mtd->read_ecc = nand_read_ecc;

mtd->write_ecc = nand_write_ecc;

mtd->read_oob = nand_read_oob;

mtd->write_oob = nand_write_oob;

/* XXX U-BOOT XXX */

#if 0

mtd->readv = NULL;

mtd->writev = nand_writev;

mtd->writev_ecc = nand_writev_ecc;

#endif

mtd->sync = nand_sync;

/* XXX U-BOOT XXX */

#if 0

mtd->lock = NULL;

mtd->unlock = NULL;

mtd->suspend = NULL;

mtd->resume = NULL;

#endif

mtd->block_isbad = nand_block_isbad;

mtd->block_markbad = nand_block_markbad;

 

/* and make the autooob the default one */

memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));

/* XXX U-BOOT XXX */

#if 0

mtd->owner = THIS_MODULE;

#endif

/* Build bad block table */

return this->scan_bbt (mtd);

}

 

4. NAND操作函数分析

a) 芯片使能引脚控制this->select_chip = nand_select_chip

        设置CE引脚


b) 命令发送控制this->cmdfunc = nand_command

        i. 设置CLE

        ii. 如果为编程页命令,则需要处理OBB相关项

        iii. 发送命令

        iv. 如果有地址的话,则发送相关地址

        v. 等待就绪

Nand_base.c (drivers\mtd\nand)

static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)

{

register struct nand_chip *this = mtd->priv;

 

/* Begin command latch cycle */

this->hwcontrol(mtd, NAND_CTL_SETCLE);

/*

 * Write out the command to the device.

 */

if (command == NAND_CMD_SEQIN) {

int readcmd;

 

if (column >= mtd->oobblock) {

/* OOB area */

column -= mtd->oobblock;

readcmd = NAND_CMD_READOOB;

} else if (column < 256) {

/* First 256 bytes --> READ0 */

readcmd = NAND_CMD_READ0;

} else {

column -= 256;

readcmd = NAND_CMD_READ1;

}

this->write_byte(mtd, readcmd);

}

this->write_byte(mtd, command);

 

/* Set ALE and clear CLE to start address cycle */

this->hwcontrol(mtd, NAND_CTL_CLRCLE);

 

if (column != -1 || page_addr != -1) {

this->hwcontrol(mtd, NAND_CTL_SETALE);

 

/* Serially input address */

if (column != -1) {

/* Adjust columns for 16 bit buswidth */

if (this->options & NAND_BUSWIDTH_16)

column >>= 1;

this->write_byte(mtd, column);

}

if (page_addr != -1) {

this->write_byte(mtd, (unsigned char) (page_addr & 0xff));

this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));

/* One more address cycle for devices > 32MiB */

if (this->chipsize > (32 << 20))

this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));

}

/* Latch in address */

this->hwcontrol(mtd, NAND_CTL_CLRALE);

}

 

/*

 * program and erase have their own busy handlers

 * status and sequential in needs no delay

*/

switch (command) {

 

case NAND_CMD_PAGEPROG:

case NAND_CMD_ERASE1:

case NAND_CMD_ERASE2:

case NAND_CMD_SEQIN:

case NAND_CMD_STATUS:

return;

 

case NAND_CMD_RESET:

if (this->dev_ready)

break;

udelay(this->chip_delay);

this->hwcontrol(mtd, NAND_CTL_SETCLE);

this->write_byte(mtd, NAND_CMD_STATUS);

this->hwcontrol(mtd, NAND_CTL_CLRCLE);

while ( !(this->read_byte(mtd) & 0x40));

return;

 

/* This applies to read commands */

default:

/*

 * If we don't have access to the busy pin, we apply the given

 * command delay

*/

if (!this->dev_ready) {

udelay (this->chip_delay);

return;

}

}

 

/* Apply this short delay always to ensure that we do wait tWB in

 * any case on any machine. */

ndelay (100);

/* wait until command is processed */

while (!this->dev_ready(mtd));

}

 

 

你可能感兴趣的:(AT91SAM9260EK,ARM,LINUX,ARM)