一、 编写目的 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版本)。
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 |
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信息结构中应该包含芯片级操作。
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); } |
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)); } |