NAND update
PATCH 4/9 NAND update
The following modifications have been made in drivers/nand/nand_bbt.c
Note that the patch-series is broken unless it is seen as one single patch. It is broken up to multiple emails to fit the size limit.
We have set up a git repository were you can pull the complete patch:
http://git.tandberg.com/tandberg/u-boot.git
Best regards
William
-------------------------------------------------
William Juul, Senior Development Engineer
Data Respons Norge AS
Sandviksveien 26
P.O. Box 489
NO-1323 H?vik, Norway
www.datarespons.no
-------------------------------------------------
drivers/nand/nand_bbt.c +370 -182
@@ -6,7 +6,7 @@* * Copyright (C) 2004 Thomas Gleixner (tglx at linutronix.de) * -* $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $ +* $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as
@@ -48,7 +48,7 @@* * Following assumptions are made: * - bbts start at a page boundary, if autolocated on a block boundary -* - the space neccecary for a bbt in FLASH does not exceed a block boundary +* - the space necessary for a bbt in FLASH does not exceed a block boundary * */
@@ -63,6 +63,19 @@#include <asm/errno.h> +/* XXX U-BOOT XXX */ +#if 0 +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/compatmac.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#endif +/** * check_pattern - [GENERIC] check if a pattern is in the buffer * @buf: the buffer to search
@@ -76,9 +89,9 @@* pattern area contain 0xff * */ -static int check_pattern (uint8_t *buf,int len,int paglen,struct nand_bbt_descr *td) +static int check_pattern (uint8_t *buf,int len,int paglen,struct nand_bbt_descr *td) { -int i, end; +int i, end =0 ;uint8_t *p = buf; end = paglen + td->offs;
@@ -96,9 +109,9 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_desreturn -1 ; } - p += td->len; - end += td->len;if (td->options &NAND_BBT_SCANEMPTY ) { + p += td->len; + end += td->len;for (i = end; i < len; i++) {if (*p++ !=0xff )return -1 ;
@@ -108,6 +121,29 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des }/** +* check_short_pattern - [GENERIC] check if a pattern is in the buffer +* @buf: the buffer to search +* @td: search pattern descriptor +* +* Check for a pattern@the given place. Used to search bad block +* tables and good / bad block identifiers. Same as check_pattern, but +* no optional empty check +* +*/ +static int check_short_pattern (uint8_t *buf,struct nand_bbt_descr *td) +{ +int i; +uint8_t *p = buf; + +/* Compare the pattern */ +for (i =0 ; i < td->len; i++) { +if (p[td->offs + i] != td->pattern[i]) +return -1 ; + } +return 0 ; +} + +/** * read_bbt - [GENERIC] Read the bad block table starting from page * @mtd: MTD device structure * @buf: temporary buffer
@@ -120,8 +156,8 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des* Read the bad block table starting from page. * */ -static int read_bbt (struct mtd_info *mtd,uint8_t *buf,int page,int num, -int bits,int offs,int reserved_block_code) +static int read_bbt (struct mtd_info *mtd,uint8_t *buf,int page,int num, +int bits,int offs,int reserved_block_code) {int res, i, j, act =0 ;struct nand_chip *this = mtd->priv;
@@ -130,17 +166,17 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,uint8_t msk = (uint8_t ) ((1 << bits) -1 ); totlen = (num * bits) >>3 ; - from = ((loff_t )page) << this->page_shift; + from = ((loff_t ) page) << this->page_shift;while (totlen) { - len =min (totlen, (size_t ) (1 << this->bbt_erase_shift)); - res = mtd->read_ecc (mtd, from, len, &retlen, buf,NULL , this->autooob); + len =min (totlen, (size_t ) (1 << this->bbt_erase_shift)); + res = mtd->read (mtd, from, len, &retlen, buf);if (res <0 ) {if (retlen != len) { -printk (KERN_INFO "nand_bbt: Error reading bad block table\n" ); +printk (KERN_INFO "nand_bbt: Error reading bad block table\n" );return res; } -printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n" ); +printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n" ); }/* Analyse data */
@@ -150,22 +186,23 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,uint8_t tmp = (dat >> j) & msk;if (tmp == msk)continue ; -if (reserved_block_code && - (tmp == reserved_block_code)) { -printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n" , - ((offs <<2 ) + (act >>1 )) << this->bbt_erase_shift); +if (reserved_block_code && (tmp == reserved_block_code)) { +printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n" , + ((offs <<2 ) + (act >>1 )) << this->bbt_erase_shift); this->bbt[offs + (act >>3 )] |=0x2 << (act &0x06 ); + mtd->ecc_stats.bbtblocks++;continue ; }/* Leave it for now, if its matured we can move this * message to MTD_DEBUG_LEVEL0 */ -printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n" , - ((offs <<2 ) + (act >>1 )) << this->bbt_erase_shift); +printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n" , + ((offs <<2 ) + (act >>1 )) << this->bbt_erase_shift);/* Factory marked bad or worn out ? */ if (tmp ==0 ) this->bbt[offs + (act >>3 )] |=0x3 << (act &0x06 );else this->bbt[offs + (act >>3 )] |=0x1 << (act &0x06 ); + mtd->ecc_stats.badblocks++; } } totlen -= len;
@@ -185,7 +222,7 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,* Read the bad block table for all chips starting at a given page * We assume that the bbt bits are in consecutive order. */ -static int read_abs_bbt (struct mtd_info *mtd,uint8_t *buf,struct nand_bbt_descr *td,int chip) +static int read_abs_bbt (struct mtd_info *mtd,uint8_t *buf,struct nand_bbt_descr *td,int chip) {struct nand_chip *this = mtd->priv;int res =0 , i;
@@ -209,6 +246,42 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desreturn 0 ; } +/* +* Scan read raw data from flash +*/ +static int scan_read_raw (struct mtd_info *mtd,uint8_t *buf,loff_t offs, +size_t len) +{ +struct mtd_oob_ops ops; + + ops.mode =MTD_OOB_RAW ; + ops.ooboffs =0 ; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.datbuf = buf; + ops.len = len; + +return mtd->read_oob (mtd, offs, &ops); +} + +/* +* Scan write data with oob to flash +*/ +static int scan_write_bbt (struct mtd_info *mtd,loff_t offs,size_t len, +uint8_t *buf,uint8_t *oob) +{ +struct mtd_oob_ops ops; + + ops.mode =MTD_OOB_PLACE ; + ops.ooboffs =0 ; + ops.ooblen = mtd->oobsize; + ops.datbuf = buf; + ops.oobbuf = oob; + ops.len = len; + +return mtd->write_oob (mtd, offs, &ops); +} +/** * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page * @mtd: MTD device structure
@@ -220,28 +293,84 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des* We assume that the bbt bits are in consecutive order. * */ -static int read_abs_bbts (struct mtd_info *mtd,uint8_t *buf,struct nand_bbt_descr *td, -struct nand_bbt_descr *md) +static int read_abs_bbts (struct mtd_info *mtd,uint8_t *buf, +struct nand_bbt_descr *td,struct nand_bbt_descr *md) {struct nand_chip *this = mtd->priv;/* Read the primary version, if available */ if (td->options &NAND_BBT_VERSION ) { -nand_read_raw (mtd, buf, td->pages[0 ] << this->page_shift, mtd->oobblock, mtd->oobsize); - td->version[0 ] = buf[mtd->oobblock + td->veroffs]; -printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n" , td->pages[0 ], td->version[0 ]); +scan_read_raw (mtd, buf, td->pages[0 ] << this->page_shift, + mtd->writesize); + td->version[0 ] = buf[mtd->writesize + td->veroffs]; +printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n" , + td->pages[0 ], td->version[0 ]); }/* Read the mirror version, if available */ if (md && (md->options &NAND_BBT_VERSION )) { -nand_read_raw (mtd, buf, md->pages[0 ] << this->page_shift, mtd->oobblock, mtd->oobsize); - md->version[0 ] = buf[mtd->oobblock + md->veroffs]; -printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n" , md->pages[0 ], md->version[0 ]); +scan_read_raw (mtd, buf, md->pages[0 ] << this->page_shift, + mtd->writesize); + md->version[0 ] = buf[mtd->writesize + md->veroffs]; +printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n" , + md->pages[0 ], md->version[0 ]); } -return 1 ; } +/* +* Scan a given block full +*/ +static int scan_block_full (struct mtd_info *mtd,struct nand_bbt_descr *bd, +loff_t offs,uint8_t *buf,size_t readlen, +int scanlen,int len) +{ +int ret, j; + + ret =scan_read_raw (mtd, buf, offs, readlen); +if (ret) +return ret; + +for (j =0 ; j < len; j++, buf += scanlen) { +if (check_pattern (buf, scanlen, mtd->writesize, bd)) +return 1 ; + } +return 0 ; +} + +/* +* Scan a given block partially +*/ +static int scan_block_fast (struct mtd_info *mtd,struct nand_bbt_descr *bd, +loff_t offs,uint8_t *buf,int len) +{ +struct mtd_oob_ops ops; +int j, ret; + + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.ooboffs =0 ; + ops.datbuf =NULL ; + ops.mode =MTD_OOB_PLACE ; + +for (j =0 ; j < len; j++) { +/* +* Read the full oob until read_oob is fixed to +* handle single byte reads for 16 bit +* buswidth +*/ + ret = mtd->read_oob (mtd, offs, &ops); +if (ret) +return ret; + +if (check_short_pattern (buf, bd)) +return 1 ; + + offs += mtd->writesize; + } +return 0 ; +} +/** * create_bbt - [GENERIC] Create a bad block table by scanning the device * @mtd: MTD device structure
@@ -253,13 +382,16 @@ static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_de* Create a bad block table by scanning the device * for the given good/bad block identify pattern */ -static void create_bbt (struct mtd_info *mtd,uint8_t *buf,struct nand_bbt_descr *bd,int chip) +static int create_bbt (struct mtd_info *mtd,uint8_t *buf, +struct nand_bbt_descr *bd,int chip) {struct nand_chip *this = mtd->priv; -int i, j, numblocks, len, scanlen; +int i, numblocks, len, scanlen;int startblock;loff_t from; -size_t readlen, ooblen; +size_t readlen; + +printk (KERN_INFO "Scanning device for bad blocks\n" );if (bd->options &NAND_BBT_SCANALLPAGES ) len =1 << (this->bbt_erase_shift - this->page_shift);
@@ -269,21 +401,28 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descelse len =1 ; } - scanlen = mtd->oobblock + mtd->oobsize; - readlen = len * mtd->oobblock; - ooblen = len * mtd->oobsize; + +if (!(bd->options &NAND_BBT_SCANEMPTY )) { +/* We need only read few bytes from the OOB area */ + scanlen =0 ; + readlen = bd->len; + }else { +/* Full page content should be read */ + scanlen = mtd->writesize + mtd->oobsize; + readlen = len * mtd->writesize; + }if (chip ==-1 ) { -/* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it -* makes shifting and masking less painful */ +/* Note that numblocks is 2 * (real numblocks) here, see i+=2 +* below as it makes shifting and masking less painful */ numblocks = mtd->size >> (this->bbt_erase_shift -1 ); startblock =0 ; from =0 ; }else {if (chip >= this->numchips) { -printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n" , - chip +1 , this->numchips); -return ; +printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n" , + chip +1 , this->numchips); +return -EINVAL ; } numblocks = this->chipsize >> (this->bbt_erase_shift -1 ); startblock = chip * numblocks;
@@ -292,16 +431,28 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc }for (i = startblock; i < numblocks;) { -nand_read_raw (mtd, buf, from, readlen, ooblen); -for (j =0 ; j < len; j++) { -if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { - this->bbt[i >>3 ] |=0x03 << (i &0x6 ); -break ; - } +int ret; + +if (bd->options &NAND_BBT_SCANALLPAGES ) + ret =scan_block_full (mtd, bd, from, buf, readlen, + scanlen, len); +else + ret =scan_block_fast (mtd, bd, from, buf, len); + +if (ret <0 ) +return ret; + +if (ret) { + this->bbt[i >>3 ] |=0x03 << (i &0x6 ); +printk (KERN_WARNING "Bad eraseblock %d@0x%08x\n" , + i >>1 , (unsigned int )from); + mtd->ecc_stats.badblocks++; } + i +=2 ; from += (1 << this->bbt_erase_shift); } +return 0 ; }/**
@@ -316,22 +467,23 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc* block. * If the option NAND_BBT_PERCHIP is given, each chip is searched * for a bbt, which contains the bad block information of this chip. -* This is neccecary to provide support for certain DOC devices. +* This is necessary to provide support for certain DOC devices. * * The bbt ident pattern resides in the oob area of the first page * in a block. */ -static int search_bbt (struct mtd_info *mtd,uint8_t *buf,struct nand_bbt_descr *td) +static int search_bbt (struct mtd_info *mtd,uint8_t *buf,struct nand_bbt_descr *td) {struct nand_chip *this = mtd->priv;int i, chips;int bits, startblock, block, dir; -int scanlen = mtd->oobblock + mtd->oobsize; +int scanlen = mtd->writesize + mtd->oobsize;int bbtblocks; +int blocktopage = this->bbt_erase_shift - this->page_shift;/* Search direction top -> down ? */ if (td->options &NAND_BBT_LASTBLOCK ) { - startblock = (mtd->size >> this->bbt_erase_shift) -1 ; + startblock = (mtd->size >> this->bbt_erase_shift) -1 ; dir =-1 ; }else { startblock =0 ;
@@ -357,13 +509,16 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr td->pages[i] =-1 ;/* Scan the maximum number of blocks */ for (block =0 ; block < td->maxblocks; block++) { +int actblock = startblock + dir * block; +loff_t offs = actblock << this->bbt_erase_shift; +/* Read first page */ -nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); -if (!check_pattern (buf, scanlen, mtd->oobblock, td)) { - td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); +scan_read_raw (mtd, buf, offs, mtd->writesize); +if (!check_pattern (buf, scanlen, mtd->writesize, td)) { + td->pages[i] = actblock << blocktopage;if (td->options &NAND_BBT_VERSION ) { - td->version[i] = buf[mtd->oobblock + td->veroffs]; + td->version[i] = buf[mtd->writesize + td->veroffs]; }break ; }
@@ -373,9 +528,10 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr/* Check, if we found a bbt for each requested chip */ for (i =0 ; i < chips; i++) {if (td->pages[i] ==-1 ) -printk (KERN_WARNING "Bad block table not found for chip %d\n" , i); +printk (KERN_WARNING "Bad block table not found for chip %d\n" , i);else -printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n" , td->pages[i], td->version[i]); +printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n" , td->pages[i], + td->version[i]); }return 0 ; }
@@ -389,21 +545,19 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr* * Search and read the bad block table(s) */ -static int search_read_bbts (struct mtd_info *mtd,uint8_t *buf, -struct nand_bbt_descr *td,struct nand_bbt_descr *md) +static int search_read_bbts (struct mtd_info *mtd,uint8_t * buf,struct nand_bbt_descr *td,struct nand_bbt_descr *md) {/* Search the primary table */ -search_bbt (mtd, buf, td); +search_bbt (mtd, buf, td);/* Search the mirror table */ if (md) -search_bbt (mtd, buf, md); +search_bbt (mtd, buf, md);/* Force result check */ return 1 ; } -/** * write_bbt - [GENERIC] (Re)write the bad block table *
@@ -416,25 +570,31 @@ static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf,* (Re)write the bad block table * */ -static int write_bbt (struct mtd_info *mtd,uint8_t *buf, -struct nand_bbt_descr *td,struct nand_bbt_descr *md,int chipsel) +static int write_bbt (struct mtd_info *mtd,uint8_t *buf, +struct nand_bbt_descr *td,struct nand_bbt_descr *md, +int chipsel) {struct nand_chip *this = mtd->priv; -struct nand_oobinfo oobinfo;struct erase_info einfo;int i, j, res, chip =0 ;int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; -int nrchips, bbtoffs, pageoffs; +int nrchips, bbtoffs, pageoffs, ooboffs;uint8_t msk[4 ];uint8_t rcode = td->reserved_block_code;size_t retlen, len =0 ;loff_t to; +struct mtd_oob_ops ops; + + ops.ooblen = mtd->oobsize; + ops.ooboffs =0 ; + ops.datbuf =NULL ; + ops.mode =MTD_OOB_PLACE ;if (!rcode) rcode =0xff ;/* Write bad block table per chip rather than per device ? */ if (td->options &NAND_BBT_PERCHIP ) { - numblocks = (int ) (this->chipsize >> this->bbt_erase_shift); + numblocks = (int )(this->chipsize >> this->bbt_erase_shift);/* Full device write or specific chip ? */ if (chipsel ==-1 ) { nrchips = this->numchips;
@@ -443,7 +603,7 @@ static int write_bbt (struct mtd_info *mtd, uint8_t *buf, chip = chipsel; } }else { - numblocks = (int ) (mtd->size >> this->bbt_erase_shift); + numblocks = (int )(mtd->size >> this->bbt_erase_shift); nrchips =1 ; }
@@ -472,27 +632,38 @@ static int write_bbt (struct mtd_info *mtd, uint8_t *buf,for (i =0 ; i < td->maxblocks; i++) {int block = startblock + dir * i;/* Check, if the block is bad */ -switch ((this->bbt[block >>2 ] >> (2 * (block &0x03 ))) &0x03 ) { +switch ((this->bbt[block >>2 ] >> + (2 * (block &0x03 ))) &0x03 ) {case 0x01 :case 0x03 :continue ; } - page = block << (this->bbt_erase_shift - this->page_shift); + page = block << + (this->bbt_erase_shift - this->page_shift);/* Check, if the block is used by the mirror table */ if (!md || md->pages[chip] != page) goto write; } -printk (KERN_ERR "No space left to write bad block table\n" ); +printk (KERN_ERR "No space left to write bad block table\n" );return -ENOSPC ; -write: + write:/* Set up shift count and masks for the flash table */ bits = td->options &NAND_BBT_NRBITS_MSK ; + msk[2 ] = ~rcode;switch (bits) { -case 1 : sft =3 ; sftmsk =0x07 ; msk[0 ] =0x00 ; msk[1 ] =0x01 ; msk[2 ] = ~rcode; msk[3 ] =0x01 ;break ; -case 2 : sft =2 ; sftmsk =0x06 ; msk[0 ] =0x00 ; msk[1 ] =0x01 ; msk[2 ] = ~rcode; msk[3 ] =0x03 ;break ; -case 4 : sft =1 ; sftmsk =0x04 ; msk[0 ] =0x00 ; msk[1 ] =0x0C ; msk[2 ] = ~rcode; msk[3 ] =0x0f ;break ; -case 8 : sft =0 ; sftmsk =0x00 ; msk[0 ] =0x00 ; msk[1 ] =0x0F ; msk[2 ] = ~rcode; msk[3 ] =0xff ;break ; +case 1 : sft =3 ; sftmsk =0x07 ; msk[0 ] =0x00 ; msk[1 ] =0x01 ; + msk[3 ] =0x01 ; +break ; +case 2 : sft =2 ; sftmsk =0x06 ; msk[0 ] =0x00 ; msk[1 ] =0x01 ; + msk[3 ] =0x03 ; +break ; +case 4 : sft =1 ; sftmsk =0x04 ; msk[0 ] =0x00 ; msk[1 ] =0x0C ; + msk[3 ] =0x0f ; +break ; +case 8 : sft =0 ; sftmsk =0x00 ; msk[0 ] =0x00 ; msk[1 ] =0x0F ; + msk[3 ] =0xff ; +break ;default :return -EINVAL ; }
@@ -500,82 +671,92 @@ write: to = ((loff_t ) page) << this->page_shift; -memcpy (&oobinfo, this->autooob,sizeof (oobinfo)); - oobinfo.useecc =MTD_NANDECC_PLACEONLY ; -/* Must we save the block contents ? */ if (td->options &NAND_BBT_SAVECONTENT ) {/* Make it block aligned */ to &= ~((loff_t) ((1 << this->bbt_erase_shift) -1 )); len =1 << this->bbt_erase_shift; - res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); + res = mtd->read (mtd, to, len, &retlen, buf);if (res <0 ) {if (retlen != len) { -printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n" ); +printk (KERN_INFO "nand_bbt: Error " +"reading block for writing " +"the bad block table\n" );return res; } -printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n" ); +printk (KERN_WARNING "nand_bbt: ECC error " +"while reading block for writing " +"bad block table\n" ); } +/* Read oob data */ + ops.ooblen = (len >> this->page_shift) * mtd->oobsize; + ops.oobbuf = &buf[len]; + res = mtd->read_oob (mtd, to + mtd->writesize, &ops); +if (res <0 || ops.oobretlen != ops.ooblen) + goto outerr; +/* Calc the byte offset in the buffer */ pageoffs = page - (int )(to >> this->page_shift); offs = pageoffs << this->page_shift;/* Preset the bbt area with 0xff */ -memset (&buf[offs],0xff , (size_t )(numblocks >> sft)); -/* Preset the bbt's oob area with 0xff */ -memset (&buf[len + pageoffs * mtd->oobsize],0xff , - ((len >> this->page_shift) - pageoffs) * mtd->oobsize); -if (td->options &NAND_BBT_VERSION ) { - buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip]; - } +memset (&buf[offs],0xff , (size_t ) (numblocks >> sft)); + ooboffs = len + (pageoffs * mtd->oobsize); + }else {/* Calc length */ len = (size_t ) (numblocks >> sft);/* Make it page aligned ! */ - len = (len + (mtd->oobblock-1 )) & ~(mtd->oobblock-1 ); + len = (len + (mtd->writesize -1 )) & + ~(mtd->writesize -1 );/* Preset the buffer with 0xff */ -memset (buf,0xff , len + (len >> this->page_shift) * mtd->oobsize); +memset (buf,0xff , len + + (len >> this->page_shift)* mtd->oobsize); offs =0 ; + ooboffs = len;/* Pattern is located in oob area of first page */ -memcpy (&buf[len + td->offs], td->pattern, td->len); -if (td->options &NAND_BBT_VERSION ) { - buf[len + td->veroffs] = td->version[chip]; - } +memcpy (&buf[ooboffs + td->offs], td->pattern, td->len); } +if (td->options &NAND_BBT_VERSION ) + buf[ooboffs + td->veroffs] = td->version[chip]; +/* walk through the memory table */ -for (i =0 ; i < numblocks; ) { +for (i =0 ; i < numblocks;) {uint8_t dat; dat = this->bbt[bbtoffs + (i >>2 )]; -for (j =0 ; j <4 ; j++ , i++) { +for (j =0 ; j <4 ; j++, i++) {int sftcnt = (i << (3 - sft)) & sftmsk;/* Do not store the reserved bbt blocks ! */ - buf[offs + (i >> sft)] &= ~(msk[dat &0x03 ] << sftcnt); + buf[offs + (i >> sft)] &= + ~(msk[dat &0x03 ] << sftcnt); dat >>=2 ; } } -memset (&einfo,0 ,sizeof (einfo)); +memset (&einfo,0 ,sizeof (einfo)); einfo.mtd = mtd; - einfo.addr = (unsigned long ) to; + einfo.addr = (unsigned long )to; einfo.len =1 << this->bbt_erase_shift; - res =nand_erase_nand (mtd, &einfo,1 ); -if (res <0 ) { -printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n" , res); -return res; - } + res =nand_erase_nand (mtd, &einfo,1 ); +if (res <0 ) + goto outerr; - res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); -if (res <0 ) { -printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n" , res); -return res; - } -printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n" , - (unsigned int ) to, td->version[chip]); + res =scan_write_bbt (mtd, to, len, buf, &buf[len]); +if (res <0 ) + goto outerr; + +printk (KERN_DEBUG "Bad block table written to 0x%08x, version " +"0x%02X\n" , (unsigned int )to, td->version[chip]);/* Mark it as used */ td->pages[chip] = page; }return 0 ; + + outerr: +printk (KERN_WARNING +"nand_bbt: Error while writing bad block table %d\n" , res); +return res; }/**
@@ -586,29 +767,27 @@ write:* The function creates a memory based bbt by scanning the device * for manufacturer / software marked good / bad blocks */ -static int nand_memory_bbt (struct mtd_info *mtd,struct nand_bbt_descr *bd) +static inline int nand_memory_bbt (struct mtd_info *mtd,struct nand_bbt_descr *bd) {struct nand_chip *this = mtd->priv; -/* Ensure that we only scan for the pattern and nothing else */ - bd->options =0 ; -create_bbt (mtd, this->data_buf, bd,-1 ); -return 0 ; + bd->options &= ~NAND_BBT_SCANEMPTY ; +return create_bbt (mtd, this->buffers->databuf, bd,-1 ); }/** -* check_create - [GENERIC] create and write bbt(s) if neccecary +* check_create - [GENERIC] create and write bbt(s) if necessary * @mtd: MTD device structure * @buf: temporary buffer * @bd: descriptor for the good/bad block search pattern * * The function checks the results of the previous call to read_bbt -* and creates / updates the bbt(s) if neccecary -* Creation is neccecary if no bbt was found for the chip/device -* Update is neccecary if one of the tables is missing or the +* and creates / updates the bbt(s) if necessary +* Creation is necessary if no bbt was found for the chip/device +* Update is necessary if one of the tables is missing or the * version nr. of one table is less than the other */ -static int check_create (struct mtd_info *mtd,uint8_t *buf,struct nand_bbt_descr *bd) +static int check_create (struct mtd_info *mtd,uint8_t *buf,struct nand_bbt_descr *bd) {int i, chips, writeops, chipsel, res;struct nand_chip *this = mtd->priv;
@@ -676,35 +855,35 @@ static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des rd = td; goto writecheck; } -create: + create:/* Create the bad block table by scanning the device ? */ if (!(td->options &NAND_BBT_CREATE ))continue ;/* Create the table in memory by scanning the chip(s) */ -create_bbt (mtd, buf, bd, chipsel); +create_bbt (mtd, buf, bd, chipsel); td->version[i] =1 ;if (md) md->version[i] =1 ; -writecheck: + writecheck:/* read back first ? */ if (rd) -read_abs_bbt (mtd, buf, rd, chipsel); +read_abs_bbt (mtd, buf, rd, chipsel);/* If they weren't versioned, read both. */ if (rd2) -read_abs_bbt (mtd, buf, rd2, chipsel); +read_abs_bbt (mtd, buf, rd2, chipsel);/* Write the bad block table to the device ? */ if ((writeops &0x01 ) && (td->options &NAND_BBT_WRITE )) { - res =write_bbt (mtd, buf, td, md, chipsel); + res =write_bbt (mtd, buf, td, md, chipsel);if (res <0 )return res; }/* Write the mirror bad block table to the device ? */ if ((writeops &0x02 ) && md && (md->options &NAND_BBT_WRITE )) { - res =write_bbt (mtd, buf, md, td, chipsel); + res =write_bbt (mtd, buf, md, td, chipsel);if (res <0 )return res; }
@@ -721,7 +900,7 @@ writecheck:* accidental erasures / writes. The regions are identified by * the mark 0x02. */ -static void mark_bbt_region (struct mtd_info *mtd,struct nand_bbt_descr *td) +static void mark_bbt_region (struct mtd_info *mtd,struct nand_bbt_descr *td) {struct nand_chip *this = mtd->priv;int i, j, chips, block, nrblocks, update;
@@ -739,7 +918,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)for (i =0 ; i < chips; i++) {if ((td->options &NAND_BBT_ABSPAGE ) || !(td->options &NAND_BBT_WRITE )) { -if (td->pages[i] ==-1 )continue ; +if (td->pages[i] ==-1 ) +continue ; block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); block <<=1 ; oldval = this->bbt[(block >>3 )];
@@ -759,7 +939,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td) oldval = this->bbt[(block >>3 )]; newval = oldval | (0x2 << (block &0x06 )); this->bbt[(block >>3 )] = newval; -if (oldval != newval) update =1 ; +if (oldval != newval) + update =1 ; block +=2 ; }/* If we want reserved blocks to be recorded to flash, and some
@@ -784,7 +965,7 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)* by calling the nand_free_bbt function. * */ -int nand_scan_bbt (struct mtd_info *mtd,struct nand_bbt_descr *bd) +int nand_scan_bbt (struct mtd_info *mtd,struct nand_bbt_descr *bd) {struct nand_chip *this = mtd->priv;int len, res =0 ;
@@ -793,53 +974,56 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)struct nand_bbt_descr *md = this->bbt_md; len = mtd->size >> (this->bbt_erase_shift +2 ); -/* Allocate memory (2bit per block) */ - this->bbt =kmalloc (len,GFP_KERNEL ); +/* Allocate memory (2bit per block) and clear the memory bad block table */ + this->bbt =kzalloc (len,GFP_KERNEL );if (!this->bbt) { -printk (KERN_ERR "nand_scan_bbt: Out of memory\n" ); +printk (KERN_ERR "nand_scan_bbt: Out of memory\n" );return -ENOMEM ; } -/* Clear the memory bad block table */ -memset (this->bbt,0x00 , len);/* If no primary table decriptor is given, scan the device * to build a memory based bad block table */ -if (!td) -return nand_memory_bbt (mtd, bd); +if (!td) { +if ((res =nand_memory_bbt (mtd, bd))) { +printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n" ); +kfree (this->bbt); + this->bbt =NULL ; + } +return res; + }/* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); len += (len >> this->page_shift) * mtd->oobsize; - buf =kmalloc (len,GFP_KERNEL ); + buf =vmalloc (len);if (!buf) { -printk (KERN_ERR "nand_bbt: Out of memory\n" ); -kfree (this->bbt); +printk (KERN_ERR "nand_bbt: Out of memory\n" ); +kfree (this->bbt); this->bbt =NULL ;return -ENOMEM ; }/* Is the bbt at a given page ? */ if (td->options &NAND_BBT_ABSPAGE ) { - res =read_abs_bbts (mtd, buf, td, md); + res =read_abs_bbts (mtd, buf, td, md); }else {/* Search the bad block table using a pattern in oob */ - res =search_read_bbts (mtd, buf, td, md); + res =search_read_bbts (mtd, buf, td, md); }if (res) - res =check_create (mtd, buf, bd); + res =check_create (mtd, buf, bd);/* Prevent the bbt regions from erasing / writing */ -mark_bbt_region (mtd, td); +mark_bbt_region (mtd, td);if (md) -mark_bbt_region (mtd, md); +mark_bbt_region (mtd, md); -kfree (buf); +vfree (buf);return res; } -/** * nand_update_bbt - [NAND Interface] update bad block table(s) * @mtd: MTD device structure
@@ -847,7 +1031,7 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)* * The function updates the bad block table(s) */ -int nand_update_bbt (struct mtd_info *mtd,loff_t offs) +int nand_update_bbt (struct mtd_info *mtd,loff_t offs) {struct nand_chip *this = mtd->priv;int len, res =0 , writeops =0 ;
@@ -863,9 +1047,9 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)/* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); len += (len >> this->page_shift) * mtd->oobsize; - buf =kmalloc (len,GFP_KERNEL ); + buf =kmalloc (len,GFP_KERNEL );if (!buf) { -printk (KERN_ERR "nand_update_bbt: Out of memory\n" ); +printk (KERN_ERR "nand_update_bbt: Out of memory\n" );return -ENOMEM ; }
@@ -873,7 +1057,7 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)/* Do we have a bbt per chip ? */ if (td->options &NAND_BBT_PERCHIP ) { - chip = (int ) (offs >> this->chip_shift); + chip = (int )(offs >> this->chip_shift); chipsel = chip; }else { chip =0 ;
@@ -886,29 +1070,26 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)/* Write the bad block table to the device ? */ if ((writeops &0x01 ) && (td->options &NAND_BBT_WRITE )) { - res =write_bbt (mtd, buf, td, md, chipsel); + res =write_bbt (mtd, buf, td, md, chipsel);if (res <0 ) goto out; }/* Write the mirror bad block table to the device ? */ if ((writeops &0x02 ) && md && (md->options &NAND_BBT_WRITE )) { - res =write_bbt (mtd, buf, md, td, chipsel); + res =write_bbt (mtd, buf, md, td, chipsel); } -out: -kfree (buf); + out: +kfree (buf);return res; }/* Define some generic bad / good block scan pattern which are used -* while scanning a device for factory marked good / bad blocks -* -* The memory based patterns just -*/ +* while scanning a device for factory marked good / bad blocks. */ static uint8_t scan_ff_pattern[] = {0xff ,0xff };static struct nand_bbt_descr smallpage_memorybased = { - .options =0 , + .options =NAND_BBT_SCAN2NDPAGE , .offs =5 , .len =1 , .pattern = scan_ff_pattern
@@ -922,14 +1103,14 @@ static struct nand_bbt_descr largepage_memorybased = { };static struct nand_bbt_descr smallpage_flashbased = { - .options =NAND_BBT_SCANEMPTY |NAND_BBT_SCANALLPAGES , + .options =NAND_BBT_SCAN2NDPAGE , .offs =5 , .len =1 , .pattern = scan_ff_pattern };static struct nand_bbt_descr largepage_flashbased = { - .options =NAND_BBT_SCANEMPTY |NAND_BBT_SCANALLPAGES , + .options =NAND_BBT_SCAN2NDPAGE , .offs =0 , .len =2 , .pattern = scan_ff_pattern
@@ -977,7 +1158,7 @@ static struct nand_bbt_descr bbt_mirror_descr = {* support for the device and calls the nand_scan_bbt function * */ -int nand_default_bbt (struct mtd_info *mtd) +int nand_default_bbt (struct mtd_info *mtd) {struct nand_chip *this = mtd->priv;
@@ -987,7 +1168,7 @@ int nand_default_bbt (struct mtd_info *mtd)* of the good / bad information, so we _must_ store * this information in a good / bad table during * startup -*/ +*/ if (this->options &NAND_IS_AND ) {/* Use the default pattern descriptors */ if (!this->bbt_td) {
@@ -995,10 +1176,9 @@ int nand_default_bbt (struct mtd_info *mtd) this->bbt_md = &bbt_mirror_descr; } this->options |=NAND_USE_FLASH_BBT ; -return nand_scan_bbt (mtd, &agand_flashbased); +return nand_scan_bbt (mtd, &agand_flashbased); } -/* Is a flash based bad block table requested ? */ if (this->options &NAND_USE_FLASH_BBT ) {/* Use the default pattern descriptors */
@@ -1007,18 +1187,17 @@ int nand_default_bbt (struct mtd_info *mtd) this->bbt_md = &bbt_mirror_descr; }if (!this->badblock_pattern) { - this->badblock_pattern = (mtd->oobblock >512 ) ? - &largepage_flashbased : &smallpage_flashbased; + this->badblock_pattern = (mtd->writesize >512 ) ? &largepage_flashbased : &smallpage_flashbased; } }else { this->bbt_td =NULL ; this->bbt_md =NULL ;if (!this->badblock_pattern) { - this->badblock_pattern = (mtd->oobblock >512 ) ? - &largepage_memorybased : &smallpage_memorybased; + this->badblock_pattern = (mtd->writesize >512 ) ? + &largepage_memorybased : &smallpage_memorybased; } } -return nand_scan_bbt (mtd, this->badblock_pattern); +return nand_scan_bbt (mtd, this->badblock_pattern); }/**
@@ -1027,26 +1206,35 @@ int nand_default_bbt (struct mtd_info *mtd)* @offs: offset in the device * @allowbbt: allow access to bad block table region * -*/ -int nand_isbad_bbt (struct mtd_info *mtd,loff_t offs,int allowbbt) +*/ +int nand_isbad_bbt (struct mtd_info *mtd,loff_t offs,int allowbbt) {struct nand_chip *this = mtd->priv;int block; -uint8_t res; +uint8_t res;/* Get block number * 2 */ - block = (int ) (offs >> (this->bbt_erase_shift -1 )); + block = (int )(offs >> (this->bbt_erase_shift -1 )); res = (this->bbt[block >>3 ] >> (block &0x06 )) &0x03 ; -DEBUG (MTD_DEBUG_LEVEL2 ,"nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n" , - (unsigned int )offs, res, block >>1 ); +DEBUG (MTD_DEBUG_LEVEL2 ,"nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n" , + (unsigned int )offs, block >>1 , res);switch ((int )res) { -case 0x00 :return 0 ; -case 0x01 :return 1 ; -case 0x02 :return allowbbt ?0 :1 ; +case 0x00 : +return 0 ; +case 0x01 : +return 1 ; +case 0x02 : +return allowbbt ?0 :1 ; }return 1 ; } +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL (nand_scan_bbt); +EXPORT_SYMBOL (nand_default_bbt); +#endif +#endif