162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Overview: 462306a36Sopenharmony_ci * Bad block table support for the NAND driver 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de) 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Description: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * When nand_scan_bbt is called, then it tries to find the bad block table 1162306a36Sopenharmony_ci * depending on the options in the BBT descriptor(s). If no flash based BBT 1262306a36Sopenharmony_ci * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory 1362306a36Sopenharmony_ci * marked good / bad blocks. This information is used to create a memory BBT. 1462306a36Sopenharmony_ci * Once a new bad block is discovered then the "factory" information is updated 1562306a36Sopenharmony_ci * on the device. 1662306a36Sopenharmony_ci * If a flash based BBT is specified then the function first tries to find the 1762306a36Sopenharmony_ci * BBT on flash. If a BBT is found then the contents are read and the memory 1862306a36Sopenharmony_ci * based BBT is created. If a mirrored BBT is selected then the mirror is 1962306a36Sopenharmony_ci * searched too and the versions are compared. If the mirror has a greater 2062306a36Sopenharmony_ci * version number, then the mirror BBT is used to build the memory based BBT. 2162306a36Sopenharmony_ci * If the tables are not versioned, then we "or" the bad block information. 2262306a36Sopenharmony_ci * If one of the BBTs is out of date or does not exist it is (re)created. 2362306a36Sopenharmony_ci * If no BBT exists at all then the device is scanned for factory marked 2462306a36Sopenharmony_ci * good / bad blocks and the bad block tables are created. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * For manufacturer created BBTs like the one found on M-SYS DOC devices 2762306a36Sopenharmony_ci * the BBT is searched and read but never created 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * The auto generated bad block table is located in the last good blocks 3062306a36Sopenharmony_ci * of the device. The table is mirrored, so it can be updated eventually. 3162306a36Sopenharmony_ci * The table is marked in the OOB area with an ident pattern and a version 3262306a36Sopenharmony_ci * number which indicates which of both tables is more up to date. If the NAND 3362306a36Sopenharmony_ci * controller needs the complete OOB area for the ECC information then the 3462306a36Sopenharmony_ci * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of 3562306a36Sopenharmony_ci * course): it moves the ident pattern and the version byte into the data area 3662306a36Sopenharmony_ci * and the OOB area will remain untouched. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * The table uses 2 bits per block 3962306a36Sopenharmony_ci * 11b: block is good 4062306a36Sopenharmony_ci * 00b: block is factory marked bad 4162306a36Sopenharmony_ci * 01b, 10b: block is marked bad due to wear 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * The memory bad block table uses the following scheme: 4462306a36Sopenharmony_ci * 00b: block is good 4562306a36Sopenharmony_ci * 01b: block is marked bad due to wear 4662306a36Sopenharmony_ci * 10b: block is reserved (to protect the bbt area) 4762306a36Sopenharmony_ci * 11b: block is factory marked bad 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * Multichip devices like DOC store the bad block info per floor. 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * Following assumptions are made: 5262306a36Sopenharmony_ci * - bbts start at a page boundary, if autolocated on a block boundary 5362306a36Sopenharmony_ci * - the space necessary for a bbt in FLASH does not exceed a block boundary 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#include <linux/slab.h> 5762306a36Sopenharmony_ci#include <linux/types.h> 5862306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 5962306a36Sopenharmony_ci#include <linux/mtd/bbm.h> 6062306a36Sopenharmony_ci#include <linux/bitops.h> 6162306a36Sopenharmony_ci#include <linux/delay.h> 6262306a36Sopenharmony_ci#include <linux/vmalloc.h> 6362306a36Sopenharmony_ci#include <linux/export.h> 6462306a36Sopenharmony_ci#include <linux/string.h> 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#include "internals.h" 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define BBT_BLOCK_GOOD 0x00 6962306a36Sopenharmony_ci#define BBT_BLOCK_WORN 0x01 7062306a36Sopenharmony_ci#define BBT_BLOCK_RESERVED 0x02 7162306a36Sopenharmony_ci#define BBT_BLOCK_FACTORY_BAD 0x03 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define BBT_ENTRY_MASK 0x03 7462306a36Sopenharmony_ci#define BBT_ENTRY_SHIFT 2 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; 7962306a36Sopenharmony_ci entry >>= (block & BBT_ENTRY_MASK) * 2; 8062306a36Sopenharmony_ci return entry & BBT_ENTRY_MASK; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic inline void bbt_mark_entry(struct nand_chip *chip, int block, 8462306a36Sopenharmony_ci uint8_t mark) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); 8762306a36Sopenharmony_ci chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci if (memcmp(buf, td->pattern, td->len)) 9362306a36Sopenharmony_ci return -1; 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/** 9862306a36Sopenharmony_ci * check_pattern - [GENERIC] check if a pattern is in the buffer 9962306a36Sopenharmony_ci * @buf: the buffer to search 10062306a36Sopenharmony_ci * @len: the length of buffer to search 10162306a36Sopenharmony_ci * @paglen: the pagelength 10262306a36Sopenharmony_ci * @td: search pattern descriptor 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * Check for a pattern at the given place. Used to search bad block tables and 10562306a36Sopenharmony_ci * good / bad block identifiers. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci if (td->options & NAND_BBT_NO_OOB) 11062306a36Sopenharmony_ci return check_pattern_no_oob(buf, td); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* Compare the pattern */ 11362306a36Sopenharmony_ci if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) 11462306a36Sopenharmony_ci return -1; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/** 12062306a36Sopenharmony_ci * check_short_pattern - [GENERIC] check if a pattern is in the buffer 12162306a36Sopenharmony_ci * @buf: the buffer to search 12262306a36Sopenharmony_ci * @td: search pattern descriptor 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * Check for a pattern at the given place. Used to search bad block tables and 12562306a36Sopenharmony_ci * good / bad block identifiers. Same as check_pattern, but no optional empty 12662306a36Sopenharmony_ci * check. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_cistatic int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci /* Compare the pattern */ 13162306a36Sopenharmony_ci if (memcmp(buf + td->offs, td->pattern, td->len)) 13262306a36Sopenharmony_ci return -1; 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/** 13762306a36Sopenharmony_ci * add_marker_len - compute the length of the marker in data area 13862306a36Sopenharmony_ci * @td: BBT descriptor used for computation 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * The length will be 0 if the marker is located in OOB area. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_cistatic u32 add_marker_len(struct nand_bbt_descr *td) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci u32 len; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!(td->options & NAND_BBT_NO_OOB)) 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci len = td->len; 15062306a36Sopenharmony_ci if (td->options & NAND_BBT_VERSION) 15162306a36Sopenharmony_ci len++; 15262306a36Sopenharmony_ci return len; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/** 15662306a36Sopenharmony_ci * read_bbt - [GENERIC] Read the bad block table starting from page 15762306a36Sopenharmony_ci * @this: NAND chip object 15862306a36Sopenharmony_ci * @buf: temporary buffer 15962306a36Sopenharmony_ci * @page: the starting page 16062306a36Sopenharmony_ci * @num: the number of bbt descriptors to read 16162306a36Sopenharmony_ci * @td: the bbt describtion table 16262306a36Sopenharmony_ci * @offs: block number offset in the table 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci * Read the bad block table starting from page. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cistatic int read_bbt(struct nand_chip *this, uint8_t *buf, int page, int num, 16762306a36Sopenharmony_ci struct nand_bbt_descr *td, int offs) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 17062306a36Sopenharmony_ci int res, ret = 0, i, j, act = 0; 17162306a36Sopenharmony_ci size_t retlen, len, totlen; 17262306a36Sopenharmony_ci loff_t from; 17362306a36Sopenharmony_ci int bits = td->options & NAND_BBT_NRBITS_MSK; 17462306a36Sopenharmony_ci uint8_t msk = (uint8_t)((1 << bits) - 1); 17562306a36Sopenharmony_ci u32 marker_len; 17662306a36Sopenharmony_ci int reserved_block_code = td->reserved_block_code; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci totlen = (num * bits) >> 3; 17962306a36Sopenharmony_ci marker_len = add_marker_len(td); 18062306a36Sopenharmony_ci from = ((loff_t)page) << this->page_shift; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci while (totlen) { 18362306a36Sopenharmony_ci len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); 18462306a36Sopenharmony_ci if (marker_len) { 18562306a36Sopenharmony_ci /* 18662306a36Sopenharmony_ci * In case the BBT marker is not in the OOB area it 18762306a36Sopenharmony_ci * will be just in the first page. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ci len -= marker_len; 19062306a36Sopenharmony_ci from += marker_len; 19162306a36Sopenharmony_ci marker_len = 0; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci res = mtd_read(mtd, from, len, &retlen, buf); 19462306a36Sopenharmony_ci if (res < 0) { 19562306a36Sopenharmony_ci if (mtd_is_eccerr(res)) { 19662306a36Sopenharmony_ci pr_info("nand_bbt: ECC error in BBT at 0x%012llx\n", 19762306a36Sopenharmony_ci from & ~mtd->writesize); 19862306a36Sopenharmony_ci return res; 19962306a36Sopenharmony_ci } else if (mtd_is_bitflip(res)) { 20062306a36Sopenharmony_ci pr_info("nand_bbt: corrected error in BBT at 0x%012llx\n", 20162306a36Sopenharmony_ci from & ~mtd->writesize); 20262306a36Sopenharmony_ci ret = res; 20362306a36Sopenharmony_ci } else { 20462306a36Sopenharmony_ci pr_info("nand_bbt: error reading BBT\n"); 20562306a36Sopenharmony_ci return res; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Analyse data */ 21062306a36Sopenharmony_ci for (i = 0; i < len; i++) { 21162306a36Sopenharmony_ci uint8_t dat = buf[i]; 21262306a36Sopenharmony_ci for (j = 0; j < 8; j += bits, act++) { 21362306a36Sopenharmony_ci uint8_t tmp = (dat >> j) & msk; 21462306a36Sopenharmony_ci if (tmp == msk) 21562306a36Sopenharmony_ci continue; 21662306a36Sopenharmony_ci if (reserved_block_code && (tmp == reserved_block_code)) { 21762306a36Sopenharmony_ci pr_info("nand_read_bbt: reserved block at 0x%012llx\n", 21862306a36Sopenharmony_ci (loff_t)(offs + act) << 21962306a36Sopenharmony_ci this->bbt_erase_shift); 22062306a36Sopenharmony_ci bbt_mark_entry(this, offs + act, 22162306a36Sopenharmony_ci BBT_BLOCK_RESERVED); 22262306a36Sopenharmony_ci mtd->ecc_stats.bbtblocks++; 22362306a36Sopenharmony_ci continue; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * Leave it for now, if it's matured we can 22762306a36Sopenharmony_ci * move this message to pr_debug. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci pr_info("nand_read_bbt: bad block at 0x%012llx\n", 23062306a36Sopenharmony_ci (loff_t)(offs + act) << 23162306a36Sopenharmony_ci this->bbt_erase_shift); 23262306a36Sopenharmony_ci /* Factory marked bad or worn out? */ 23362306a36Sopenharmony_ci if (tmp == 0) 23462306a36Sopenharmony_ci bbt_mark_entry(this, offs + act, 23562306a36Sopenharmony_ci BBT_BLOCK_FACTORY_BAD); 23662306a36Sopenharmony_ci else 23762306a36Sopenharmony_ci bbt_mark_entry(this, offs + act, 23862306a36Sopenharmony_ci BBT_BLOCK_WORN); 23962306a36Sopenharmony_ci mtd->ecc_stats.badblocks++; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci totlen -= len; 24362306a36Sopenharmony_ci from += len; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci return ret; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/** 24962306a36Sopenharmony_ci * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page 25062306a36Sopenharmony_ci * @this: NAND chip object 25162306a36Sopenharmony_ci * @buf: temporary buffer 25262306a36Sopenharmony_ci * @td: descriptor for the bad block table 25362306a36Sopenharmony_ci * @chip: read the table for a specific chip, -1 read all chips; applies only if 25462306a36Sopenharmony_ci * NAND_BBT_PERCHIP option is set 25562306a36Sopenharmony_ci * 25662306a36Sopenharmony_ci * Read the bad block table for all chips starting at a given page. We assume 25762306a36Sopenharmony_ci * that the bbt bits are in consecutive order. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_cistatic int read_abs_bbt(struct nand_chip *this, uint8_t *buf, 26062306a36Sopenharmony_ci struct nand_bbt_descr *td, int chip) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 26362306a36Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 26462306a36Sopenharmony_ci int res = 0, i; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) { 26762306a36Sopenharmony_ci int offs = 0; 26862306a36Sopenharmony_ci for (i = 0; i < nanddev_ntargets(&this->base); i++) { 26962306a36Sopenharmony_ci if (chip == -1 || chip == i) 27062306a36Sopenharmony_ci res = read_bbt(this, buf, td->pages[i], 27162306a36Sopenharmony_ci targetsize >> this->bbt_erase_shift, 27262306a36Sopenharmony_ci td, offs); 27362306a36Sopenharmony_ci if (res) 27462306a36Sopenharmony_ci return res; 27562306a36Sopenharmony_ci offs += targetsize >> this->bbt_erase_shift; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci } else { 27862306a36Sopenharmony_ci res = read_bbt(this, buf, td->pages[0], 27962306a36Sopenharmony_ci mtd->size >> this->bbt_erase_shift, td, 0); 28062306a36Sopenharmony_ci if (res) 28162306a36Sopenharmony_ci return res; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* BBT marker is in the first page, no OOB */ 28762306a36Sopenharmony_cistatic int scan_read_data(struct nand_chip *this, uint8_t *buf, loff_t offs, 28862306a36Sopenharmony_ci struct nand_bbt_descr *td) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 29162306a36Sopenharmony_ci size_t retlen; 29262306a36Sopenharmony_ci size_t len; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci len = td->len; 29562306a36Sopenharmony_ci if (td->options & NAND_BBT_VERSION) 29662306a36Sopenharmony_ci len++; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return mtd_read(mtd, offs, len, &retlen, buf); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/** 30262306a36Sopenharmony_ci * scan_read_oob - [GENERIC] Scan data+OOB region to buffer 30362306a36Sopenharmony_ci * @this: NAND chip object 30462306a36Sopenharmony_ci * @buf: temporary buffer 30562306a36Sopenharmony_ci * @offs: offset at which to scan 30662306a36Sopenharmony_ci * @len: length of data region to read 30762306a36Sopenharmony_ci * 30862306a36Sopenharmony_ci * Scan read data from data+OOB. May traverse multiple pages, interleaving 30962306a36Sopenharmony_ci * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" 31062306a36Sopenharmony_ci * ECC condition (error or bitflip). May quit on the first (non-ECC) error. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_cistatic int scan_read_oob(struct nand_chip *this, uint8_t *buf, loff_t offs, 31362306a36Sopenharmony_ci size_t len) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 31662306a36Sopenharmony_ci struct mtd_oob_ops ops = { }; 31762306a36Sopenharmony_ci int res, ret = 0; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 32062306a36Sopenharmony_ci ops.ooboffs = 0; 32162306a36Sopenharmony_ci ops.ooblen = mtd->oobsize; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci while (len > 0) { 32462306a36Sopenharmony_ci ops.datbuf = buf; 32562306a36Sopenharmony_ci ops.len = min(len, (size_t)mtd->writesize); 32662306a36Sopenharmony_ci ops.oobbuf = buf + ops.len; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci res = mtd_read_oob(mtd, offs, &ops); 32962306a36Sopenharmony_ci if (res) { 33062306a36Sopenharmony_ci if (!mtd_is_bitflip_or_eccerr(res)) 33162306a36Sopenharmony_ci return res; 33262306a36Sopenharmony_ci else if (mtd_is_eccerr(res) || !ret) 33362306a36Sopenharmony_ci ret = res; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci buf += mtd->oobsize + mtd->writesize; 33762306a36Sopenharmony_ci len -= mtd->writesize; 33862306a36Sopenharmony_ci offs += mtd->writesize; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci return ret; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic int scan_read(struct nand_chip *this, uint8_t *buf, loff_t offs, 34462306a36Sopenharmony_ci size_t len, struct nand_bbt_descr *td) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci if (td->options & NAND_BBT_NO_OOB) 34762306a36Sopenharmony_ci return scan_read_data(this, buf, offs, td); 34862306a36Sopenharmony_ci else 34962306a36Sopenharmony_ci return scan_read_oob(this, buf, offs, len); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* Scan write data with oob to flash */ 35362306a36Sopenharmony_cistatic int scan_write_bbt(struct nand_chip *this, loff_t offs, size_t len, 35462306a36Sopenharmony_ci uint8_t *buf, uint8_t *oob) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 35762306a36Sopenharmony_ci struct mtd_oob_ops ops = { }; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 36062306a36Sopenharmony_ci ops.ooboffs = 0; 36162306a36Sopenharmony_ci ops.ooblen = mtd->oobsize; 36262306a36Sopenharmony_ci ops.datbuf = buf; 36362306a36Sopenharmony_ci ops.oobbuf = oob; 36462306a36Sopenharmony_ci ops.len = len; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return mtd_write_oob(mtd, offs, &ops); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic u32 bbt_get_ver_offs(struct nand_chip *this, struct nand_bbt_descr *td) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 37262306a36Sopenharmony_ci u32 ver_offs = td->veroffs; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!(td->options & NAND_BBT_NO_OOB)) 37562306a36Sopenharmony_ci ver_offs += mtd->writesize; 37662306a36Sopenharmony_ci return ver_offs; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/** 38062306a36Sopenharmony_ci * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page 38162306a36Sopenharmony_ci * @this: NAND chip object 38262306a36Sopenharmony_ci * @buf: temporary buffer 38362306a36Sopenharmony_ci * @td: descriptor for the bad block table 38462306a36Sopenharmony_ci * @md: descriptor for the bad block table mirror 38562306a36Sopenharmony_ci * 38662306a36Sopenharmony_ci * Read the bad block table(s) for all chips starting at a given page. We 38762306a36Sopenharmony_ci * assume that the bbt bits are in consecutive order. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cistatic void read_abs_bbts(struct nand_chip *this, uint8_t *buf, 39062306a36Sopenharmony_ci struct nand_bbt_descr *td, struct nand_bbt_descr *md) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* Read the primary version, if available */ 39562306a36Sopenharmony_ci if (td->options & NAND_BBT_VERSION) { 39662306a36Sopenharmony_ci scan_read(this, buf, (loff_t)td->pages[0] << this->page_shift, 39762306a36Sopenharmony_ci mtd->writesize, td); 39862306a36Sopenharmony_ci td->version[0] = buf[bbt_get_ver_offs(this, td)]; 39962306a36Sopenharmony_ci pr_info("Bad block table at page %d, version 0x%02X\n", 40062306a36Sopenharmony_ci td->pages[0], td->version[0]); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* Read the mirror version, if available */ 40462306a36Sopenharmony_ci if (md && (md->options & NAND_BBT_VERSION)) { 40562306a36Sopenharmony_ci scan_read(this, buf, (loff_t)md->pages[0] << this->page_shift, 40662306a36Sopenharmony_ci mtd->writesize, md); 40762306a36Sopenharmony_ci md->version[0] = buf[bbt_get_ver_offs(this, md)]; 40862306a36Sopenharmony_ci pr_info("Bad block table at page %d, version 0x%02X\n", 40962306a36Sopenharmony_ci md->pages[0], md->version[0]); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* Scan a given block partially */ 41462306a36Sopenharmony_cistatic int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd, 41562306a36Sopenharmony_ci loff_t offs, uint8_t *buf) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci struct mtd_oob_ops ops = { }; 42062306a36Sopenharmony_ci int ret, page_offset; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ops.ooblen = mtd->oobsize; 42362306a36Sopenharmony_ci ops.oobbuf = buf; 42462306a36Sopenharmony_ci ops.ooboffs = 0; 42562306a36Sopenharmony_ci ops.datbuf = NULL; 42662306a36Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci page_offset = nand_bbm_get_next_page(this, 0); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci while (page_offset >= 0) { 43162306a36Sopenharmony_ci /* 43262306a36Sopenharmony_ci * Read the full oob until read_oob is fixed to handle single 43362306a36Sopenharmony_ci * byte reads for 16 bit buswidth. 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ci ret = mtd_read_oob(mtd, offs + (page_offset * mtd->writesize), 43662306a36Sopenharmony_ci &ops); 43762306a36Sopenharmony_ci /* Ignore ECC errors when checking for BBM */ 43862306a36Sopenharmony_ci if (ret && !mtd_is_bitflip_or_eccerr(ret)) 43962306a36Sopenharmony_ci return ret; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (check_short_pattern(buf, bd)) 44262306a36Sopenharmony_ci return 1; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci page_offset = nand_bbm_get_next_page(this, page_offset + 1); 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/* Check if a potential BBT block is marked as bad */ 45162306a36Sopenharmony_cistatic int bbt_block_checkbad(struct nand_chip *this, struct nand_bbt_descr *td, 45262306a36Sopenharmony_ci loff_t offs, uint8_t *buf) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct nand_bbt_descr *bd = this->badblock_pattern; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * No need to check for a bad BBT block if the BBM area overlaps with 45862306a36Sopenharmony_ci * the bad block table marker area in OOB since writing a BBM here 45962306a36Sopenharmony_ci * invalidates the bad block table marker anyway. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci if (!(td->options & NAND_BBT_NO_OOB) && 46262306a36Sopenharmony_ci td->offs >= bd->offs && td->offs < bd->offs + bd->len) 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* 46662306a36Sopenharmony_ci * There is no point in checking for a bad block marker if writing 46762306a36Sopenharmony_ci * such marker is not supported 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci if (this->bbt_options & NAND_BBT_NO_OOB_BBM || 47062306a36Sopenharmony_ci this->options & NAND_NO_BBM_QUIRK) 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (scan_block_fast(this, bd, offs, buf) > 0) 47462306a36Sopenharmony_ci return 1; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/** 48062306a36Sopenharmony_ci * create_bbt - [GENERIC] Create a bad block table by scanning the device 48162306a36Sopenharmony_ci * @this: NAND chip object 48262306a36Sopenharmony_ci * @buf: temporary buffer 48362306a36Sopenharmony_ci * @bd: descriptor for the good/bad block search pattern 48462306a36Sopenharmony_ci * @chip: create the table for a specific chip, -1 read all chips; applies only 48562306a36Sopenharmony_ci * if NAND_BBT_PERCHIP option is set 48662306a36Sopenharmony_ci * 48762306a36Sopenharmony_ci * Create a bad block table by scanning the device for the given good/bad block 48862306a36Sopenharmony_ci * identify pattern. 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_cistatic int create_bbt(struct nand_chip *this, uint8_t *buf, 49162306a36Sopenharmony_ci struct nand_bbt_descr *bd, int chip) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 49462306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 49562306a36Sopenharmony_ci int i, numblocks, startblock; 49662306a36Sopenharmony_ci loff_t from; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci pr_info("Scanning device for bad blocks\n"); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (chip == -1) { 50162306a36Sopenharmony_ci numblocks = mtd->size >> this->bbt_erase_shift; 50262306a36Sopenharmony_ci startblock = 0; 50362306a36Sopenharmony_ci from = 0; 50462306a36Sopenharmony_ci } else { 50562306a36Sopenharmony_ci if (chip >= nanddev_ntargets(&this->base)) { 50662306a36Sopenharmony_ci pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n", 50762306a36Sopenharmony_ci chip + 1, nanddev_ntargets(&this->base)); 50862306a36Sopenharmony_ci return -EINVAL; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci numblocks = targetsize >> this->bbt_erase_shift; 51162306a36Sopenharmony_ci startblock = chip * numblocks; 51262306a36Sopenharmony_ci numblocks += startblock; 51362306a36Sopenharmony_ci from = (loff_t)startblock << this->bbt_erase_shift; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci for (i = startblock; i < numblocks; i++) { 51762306a36Sopenharmony_ci int ret; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci BUG_ON(bd->options & NAND_BBT_NO_OOB); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci ret = scan_block_fast(this, bd, from, buf); 52262306a36Sopenharmony_ci if (ret < 0) 52362306a36Sopenharmony_ci return ret; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (ret) { 52662306a36Sopenharmony_ci bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); 52762306a36Sopenharmony_ci pr_warn("Bad eraseblock %d at 0x%012llx\n", 52862306a36Sopenharmony_ci i, (unsigned long long)from); 52962306a36Sopenharmony_ci mtd->ecc_stats.badblocks++; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci from += (1 << this->bbt_erase_shift); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci/** 53862306a36Sopenharmony_ci * search_bbt - [GENERIC] scan the device for a specific bad block table 53962306a36Sopenharmony_ci * @this: NAND chip object 54062306a36Sopenharmony_ci * @buf: temporary buffer 54162306a36Sopenharmony_ci * @td: descriptor for the bad block table 54262306a36Sopenharmony_ci * 54362306a36Sopenharmony_ci * Read the bad block table by searching for a given ident pattern. Search is 54462306a36Sopenharmony_ci * preformed either from the beginning up or from the end of the device 54562306a36Sopenharmony_ci * downwards. The search starts always at the start of a block. If the option 54662306a36Sopenharmony_ci * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains 54762306a36Sopenharmony_ci * the bad block information of this chip. This is necessary to provide support 54862306a36Sopenharmony_ci * for certain DOC devices. 54962306a36Sopenharmony_ci * 55062306a36Sopenharmony_ci * The bbt ident pattern resides in the oob area of the first page in a block. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_cistatic int search_bbt(struct nand_chip *this, uint8_t *buf, 55362306a36Sopenharmony_ci struct nand_bbt_descr *td) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 55662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 55762306a36Sopenharmony_ci int i, chips; 55862306a36Sopenharmony_ci int startblock, block, dir; 55962306a36Sopenharmony_ci int scanlen = mtd->writesize + mtd->oobsize; 56062306a36Sopenharmony_ci int bbtblocks; 56162306a36Sopenharmony_ci int blocktopage = this->bbt_erase_shift - this->page_shift; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* Search direction top -> down? */ 56462306a36Sopenharmony_ci if (td->options & NAND_BBT_LASTBLOCK) { 56562306a36Sopenharmony_ci startblock = (mtd->size >> this->bbt_erase_shift) - 1; 56662306a36Sopenharmony_ci dir = -1; 56762306a36Sopenharmony_ci } else { 56862306a36Sopenharmony_ci startblock = 0; 56962306a36Sopenharmony_ci dir = 1; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Do we have a bbt per chip? */ 57362306a36Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) { 57462306a36Sopenharmony_ci chips = nanddev_ntargets(&this->base); 57562306a36Sopenharmony_ci bbtblocks = targetsize >> this->bbt_erase_shift; 57662306a36Sopenharmony_ci startblock &= bbtblocks - 1; 57762306a36Sopenharmony_ci } else { 57862306a36Sopenharmony_ci chips = 1; 57962306a36Sopenharmony_ci bbtblocks = mtd->size >> this->bbt_erase_shift; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci for (i = 0; i < chips; i++) { 58362306a36Sopenharmony_ci /* Reset version information */ 58462306a36Sopenharmony_ci td->version[i] = 0; 58562306a36Sopenharmony_ci td->pages[i] = -1; 58662306a36Sopenharmony_ci /* Scan the maximum number of blocks */ 58762306a36Sopenharmony_ci for (block = 0; block < td->maxblocks; block++) { 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci int actblock = startblock + dir * block; 59062306a36Sopenharmony_ci loff_t offs = (loff_t)actblock << this->bbt_erase_shift; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Check if block is marked bad */ 59362306a36Sopenharmony_ci if (bbt_block_checkbad(this, td, offs, buf)) 59462306a36Sopenharmony_ci continue; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* Read first page */ 59762306a36Sopenharmony_ci scan_read(this, buf, offs, mtd->writesize, td); 59862306a36Sopenharmony_ci if (!check_pattern(buf, scanlen, mtd->writesize, td)) { 59962306a36Sopenharmony_ci td->pages[i] = actblock << blocktopage; 60062306a36Sopenharmony_ci if (td->options & NAND_BBT_VERSION) { 60162306a36Sopenharmony_ci offs = bbt_get_ver_offs(this, td); 60262306a36Sopenharmony_ci td->version[i] = buf[offs]; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci startblock += targetsize >> this->bbt_erase_shift; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci /* Check, if we found a bbt for each requested chip */ 61062306a36Sopenharmony_ci for (i = 0; i < chips; i++) { 61162306a36Sopenharmony_ci if (td->pages[i] == -1) 61262306a36Sopenharmony_ci pr_warn("Bad block table not found for chip %d\n", i); 61362306a36Sopenharmony_ci else 61462306a36Sopenharmony_ci pr_info("Bad block table found at page %d, version 0x%02X\n", 61562306a36Sopenharmony_ci td->pages[i], td->version[i]); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci/** 62162306a36Sopenharmony_ci * search_read_bbts - [GENERIC] scan the device for bad block table(s) 62262306a36Sopenharmony_ci * @this: NAND chip object 62362306a36Sopenharmony_ci * @buf: temporary buffer 62462306a36Sopenharmony_ci * @td: descriptor for the bad block table 62562306a36Sopenharmony_ci * @md: descriptor for the bad block table mirror 62662306a36Sopenharmony_ci * 62762306a36Sopenharmony_ci * Search and read the bad block table(s). 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_cistatic void search_read_bbts(struct nand_chip *this, uint8_t *buf, 63062306a36Sopenharmony_ci struct nand_bbt_descr *td, 63162306a36Sopenharmony_ci struct nand_bbt_descr *md) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci /* Search the primary table */ 63462306a36Sopenharmony_ci search_bbt(this, buf, td); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Search the mirror table */ 63762306a36Sopenharmony_ci if (md) 63862306a36Sopenharmony_ci search_bbt(this, buf, md); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci/** 64262306a36Sopenharmony_ci * get_bbt_block - Get the first valid eraseblock suitable to store a BBT 64362306a36Sopenharmony_ci * @this: the NAND device 64462306a36Sopenharmony_ci * @td: the BBT description 64562306a36Sopenharmony_ci * @md: the mirror BBT descriptor 64662306a36Sopenharmony_ci * @chip: the CHIP selector 64762306a36Sopenharmony_ci * 64862306a36Sopenharmony_ci * This functions returns a positive block number pointing a valid eraseblock 64962306a36Sopenharmony_ci * suitable to store a BBT (i.e. in the range reserved for BBT), or -ENOSPC if 65062306a36Sopenharmony_ci * all blocks are already used of marked bad. If td->pages[chip] was already 65162306a36Sopenharmony_ci * pointing to a valid block we re-use it, otherwise we search for the next 65262306a36Sopenharmony_ci * valid one. 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_cistatic int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td, 65562306a36Sopenharmony_ci struct nand_bbt_descr *md, int chip) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 65862306a36Sopenharmony_ci int startblock, dir, page, numblocks, i; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* 66162306a36Sopenharmony_ci * There was already a version of the table, reuse the page. This 66262306a36Sopenharmony_ci * applies for absolute placement too, as we have the page number in 66362306a36Sopenharmony_ci * td->pages. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci if (td->pages[chip] != -1) 66662306a36Sopenharmony_ci return td->pages[chip] >> 66762306a36Sopenharmony_ci (this->bbt_erase_shift - this->page_shift); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci numblocks = (int)(targetsize >> this->bbt_erase_shift); 67062306a36Sopenharmony_ci if (!(td->options & NAND_BBT_PERCHIP)) 67162306a36Sopenharmony_ci numblocks *= nanddev_ntargets(&this->base); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* 67462306a36Sopenharmony_ci * Automatic placement of the bad block table. Search direction 67562306a36Sopenharmony_ci * top -> down? 67662306a36Sopenharmony_ci */ 67762306a36Sopenharmony_ci if (td->options & NAND_BBT_LASTBLOCK) { 67862306a36Sopenharmony_ci startblock = numblocks * (chip + 1) - 1; 67962306a36Sopenharmony_ci dir = -1; 68062306a36Sopenharmony_ci } else { 68162306a36Sopenharmony_ci startblock = chip * numblocks; 68262306a36Sopenharmony_ci dir = 1; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci for (i = 0; i < td->maxblocks; i++) { 68662306a36Sopenharmony_ci int block = startblock + dir * i; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* Check, if the block is bad */ 68962306a36Sopenharmony_ci switch (bbt_get_entry(this, block)) { 69062306a36Sopenharmony_ci case BBT_BLOCK_WORN: 69162306a36Sopenharmony_ci case BBT_BLOCK_FACTORY_BAD: 69262306a36Sopenharmony_ci continue; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci page = block << (this->bbt_erase_shift - this->page_shift); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* Check, if the block is used by the mirror table */ 69862306a36Sopenharmony_ci if (!md || md->pages[chip] != page) 69962306a36Sopenharmony_ci return block; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci return -ENOSPC; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci/** 70662306a36Sopenharmony_ci * mark_bbt_block_bad - Mark one of the block reserved for BBT bad 70762306a36Sopenharmony_ci * @this: the NAND device 70862306a36Sopenharmony_ci * @td: the BBT description 70962306a36Sopenharmony_ci * @chip: the CHIP selector 71062306a36Sopenharmony_ci * @block: the BBT block to mark 71162306a36Sopenharmony_ci * 71262306a36Sopenharmony_ci * Blocks reserved for BBT can become bad. This functions is an helper to mark 71362306a36Sopenharmony_ci * such blocks as bad. It takes care of updating the in-memory BBT, marking the 71462306a36Sopenharmony_ci * block as bad using a bad block marker and invalidating the associated 71562306a36Sopenharmony_ci * td->pages[] entry. 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_cistatic void mark_bbt_block_bad(struct nand_chip *this, 71862306a36Sopenharmony_ci struct nand_bbt_descr *td, 71962306a36Sopenharmony_ci int chip, int block) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci loff_t to; 72262306a36Sopenharmony_ci int res; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci bbt_mark_entry(this, block, BBT_BLOCK_WORN); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci to = (loff_t)block << this->bbt_erase_shift; 72762306a36Sopenharmony_ci res = nand_markbad_bbm(this, to); 72862306a36Sopenharmony_ci if (res) 72962306a36Sopenharmony_ci pr_warn("nand_bbt: error %d while marking block %d bad\n", 73062306a36Sopenharmony_ci res, block); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci td->pages[chip] = -1; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci/** 73662306a36Sopenharmony_ci * write_bbt - [GENERIC] (Re)write the bad block table 73762306a36Sopenharmony_ci * @this: NAND chip object 73862306a36Sopenharmony_ci * @buf: temporary buffer 73962306a36Sopenharmony_ci * @td: descriptor for the bad block table 74062306a36Sopenharmony_ci * @md: descriptor for the bad block table mirror 74162306a36Sopenharmony_ci * @chipsel: selector for a specific chip, -1 for all 74262306a36Sopenharmony_ci * 74362306a36Sopenharmony_ci * (Re)write the bad block table. 74462306a36Sopenharmony_ci */ 74562306a36Sopenharmony_cistatic int write_bbt(struct nand_chip *this, uint8_t *buf, 74662306a36Sopenharmony_ci struct nand_bbt_descr *td, struct nand_bbt_descr *md, 74762306a36Sopenharmony_ci int chipsel) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 75062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 75162306a36Sopenharmony_ci struct erase_info einfo; 75262306a36Sopenharmony_ci int i, res, chip = 0; 75362306a36Sopenharmony_ci int bits, page, offs, numblocks, sft, sftmsk; 75462306a36Sopenharmony_ci int nrchips, pageoffs, ooboffs; 75562306a36Sopenharmony_ci uint8_t msk[4]; 75662306a36Sopenharmony_ci uint8_t rcode = td->reserved_block_code; 75762306a36Sopenharmony_ci size_t retlen, len = 0; 75862306a36Sopenharmony_ci loff_t to; 75962306a36Sopenharmony_ci struct mtd_oob_ops ops = { }; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci ops.ooblen = mtd->oobsize; 76262306a36Sopenharmony_ci ops.ooboffs = 0; 76362306a36Sopenharmony_ci ops.datbuf = NULL; 76462306a36Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (!rcode) 76762306a36Sopenharmony_ci rcode = 0xff; 76862306a36Sopenharmony_ci /* Write bad block table per chip rather than per device? */ 76962306a36Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) { 77062306a36Sopenharmony_ci numblocks = (int)(targetsize >> this->bbt_erase_shift); 77162306a36Sopenharmony_ci /* Full device write or specific chip? */ 77262306a36Sopenharmony_ci if (chipsel == -1) { 77362306a36Sopenharmony_ci nrchips = nanddev_ntargets(&this->base); 77462306a36Sopenharmony_ci } else { 77562306a36Sopenharmony_ci nrchips = chipsel + 1; 77662306a36Sopenharmony_ci chip = chipsel; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci } else { 77962306a36Sopenharmony_ci numblocks = (int)(mtd->size >> this->bbt_erase_shift); 78062306a36Sopenharmony_ci nrchips = 1; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Loop through the chips */ 78462306a36Sopenharmony_ci while (chip < nrchips) { 78562306a36Sopenharmony_ci int block; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci block = get_bbt_block(this, td, md, chip); 78862306a36Sopenharmony_ci if (block < 0) { 78962306a36Sopenharmony_ci pr_err("No space left to write bad block table\n"); 79062306a36Sopenharmony_ci res = block; 79162306a36Sopenharmony_ci goto outerr; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* 79562306a36Sopenharmony_ci * get_bbt_block() returns a block number, shift the value to 79662306a36Sopenharmony_ci * get a page number. 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci page = block << (this->bbt_erase_shift - this->page_shift); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* Set up shift count and masks for the flash table */ 80162306a36Sopenharmony_ci bits = td->options & NAND_BBT_NRBITS_MSK; 80262306a36Sopenharmony_ci msk[2] = ~rcode; 80362306a36Sopenharmony_ci switch (bits) { 80462306a36Sopenharmony_ci case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; 80562306a36Sopenharmony_ci msk[3] = 0x01; 80662306a36Sopenharmony_ci break; 80762306a36Sopenharmony_ci case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; 80862306a36Sopenharmony_ci msk[3] = 0x03; 80962306a36Sopenharmony_ci break; 81062306a36Sopenharmony_ci case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; 81162306a36Sopenharmony_ci msk[3] = 0x0f; 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; 81462306a36Sopenharmony_ci msk[3] = 0xff; 81562306a36Sopenharmony_ci break; 81662306a36Sopenharmony_ci default: return -EINVAL; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci to = ((loff_t)page) << this->page_shift; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* Must we save the block contents? */ 82262306a36Sopenharmony_ci if (td->options & NAND_BBT_SAVECONTENT) { 82362306a36Sopenharmony_ci /* Make it block aligned */ 82462306a36Sopenharmony_ci to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1); 82562306a36Sopenharmony_ci len = 1 << this->bbt_erase_shift; 82662306a36Sopenharmony_ci res = mtd_read(mtd, to, len, &retlen, buf); 82762306a36Sopenharmony_ci if (res < 0) { 82862306a36Sopenharmony_ci if (retlen != len) { 82962306a36Sopenharmony_ci pr_info("nand_bbt: error reading block for writing the bad block table\n"); 83062306a36Sopenharmony_ci return res; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci pr_warn("nand_bbt: ECC error while reading block for writing bad block table\n"); 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci /* Read oob data */ 83562306a36Sopenharmony_ci ops.ooblen = (len >> this->page_shift) * mtd->oobsize; 83662306a36Sopenharmony_ci ops.oobbuf = &buf[len]; 83762306a36Sopenharmony_ci res = mtd_read_oob(mtd, to + mtd->writesize, &ops); 83862306a36Sopenharmony_ci if (res < 0 || ops.oobretlen != ops.ooblen) 83962306a36Sopenharmony_ci goto outerr; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* Calc the byte offset in the buffer */ 84262306a36Sopenharmony_ci pageoffs = page - (int)(to >> this->page_shift); 84362306a36Sopenharmony_ci offs = pageoffs << this->page_shift; 84462306a36Sopenharmony_ci /* Preset the bbt area with 0xff */ 84562306a36Sopenharmony_ci memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); 84662306a36Sopenharmony_ci ooboffs = len + (pageoffs * mtd->oobsize); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci } else if (td->options & NAND_BBT_NO_OOB) { 84962306a36Sopenharmony_ci ooboffs = 0; 85062306a36Sopenharmony_ci offs = td->len; 85162306a36Sopenharmony_ci /* The version byte */ 85262306a36Sopenharmony_ci if (td->options & NAND_BBT_VERSION) 85362306a36Sopenharmony_ci offs++; 85462306a36Sopenharmony_ci /* Calc length */ 85562306a36Sopenharmony_ci len = (size_t)(numblocks >> sft); 85662306a36Sopenharmony_ci len += offs; 85762306a36Sopenharmony_ci /* Make it page aligned! */ 85862306a36Sopenharmony_ci len = ALIGN(len, mtd->writesize); 85962306a36Sopenharmony_ci /* Preset the buffer with 0xff */ 86062306a36Sopenharmony_ci memset(buf, 0xff, len); 86162306a36Sopenharmony_ci /* Pattern is located at the begin of first page */ 86262306a36Sopenharmony_ci memcpy(buf, td->pattern, td->len); 86362306a36Sopenharmony_ci } else { 86462306a36Sopenharmony_ci /* Calc length */ 86562306a36Sopenharmony_ci len = (size_t)(numblocks >> sft); 86662306a36Sopenharmony_ci /* Make it page aligned! */ 86762306a36Sopenharmony_ci len = ALIGN(len, mtd->writesize); 86862306a36Sopenharmony_ci /* Preset the buffer with 0xff */ 86962306a36Sopenharmony_ci memset(buf, 0xff, len + 87062306a36Sopenharmony_ci (len >> this->page_shift)* mtd->oobsize); 87162306a36Sopenharmony_ci offs = 0; 87262306a36Sopenharmony_ci ooboffs = len; 87362306a36Sopenharmony_ci /* Pattern is located in oob area of first page */ 87462306a36Sopenharmony_ci memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (td->options & NAND_BBT_VERSION) 87862306a36Sopenharmony_ci buf[ooboffs + td->veroffs] = td->version[chip]; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* Walk through the memory table */ 88162306a36Sopenharmony_ci for (i = 0; i < numblocks; i++) { 88262306a36Sopenharmony_ci uint8_t dat; 88362306a36Sopenharmony_ci int sftcnt = (i << (3 - sft)) & sftmsk; 88462306a36Sopenharmony_ci dat = bbt_get_entry(this, chip * numblocks + i); 88562306a36Sopenharmony_ci /* Do not store the reserved bbt blocks! */ 88662306a36Sopenharmony_ci buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci memset(&einfo, 0, sizeof(einfo)); 89062306a36Sopenharmony_ci einfo.addr = to; 89162306a36Sopenharmony_ci einfo.len = 1 << this->bbt_erase_shift; 89262306a36Sopenharmony_ci res = nand_erase_nand(this, &einfo, 1); 89362306a36Sopenharmony_ci if (res < 0) { 89462306a36Sopenharmony_ci pr_warn("nand_bbt: error while erasing BBT block %d\n", 89562306a36Sopenharmony_ci res); 89662306a36Sopenharmony_ci mark_bbt_block_bad(this, td, chip, block); 89762306a36Sopenharmony_ci continue; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci res = scan_write_bbt(this, to, len, buf, 90162306a36Sopenharmony_ci td->options & NAND_BBT_NO_OOB ? 90262306a36Sopenharmony_ci NULL : &buf[len]); 90362306a36Sopenharmony_ci if (res < 0) { 90462306a36Sopenharmony_ci pr_warn("nand_bbt: error while writing BBT block %d\n", 90562306a36Sopenharmony_ci res); 90662306a36Sopenharmony_ci mark_bbt_block_bad(this, td, chip, block); 90762306a36Sopenharmony_ci continue; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", 91162306a36Sopenharmony_ci (unsigned long long)to, td->version[chip]); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* Mark it as used */ 91462306a36Sopenharmony_ci td->pages[chip++] = page; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci return 0; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci outerr: 91962306a36Sopenharmony_ci pr_warn("nand_bbt: error while writing bad block table %d\n", res); 92062306a36Sopenharmony_ci return res; 92162306a36Sopenharmony_ci} 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci/** 92462306a36Sopenharmony_ci * nand_memory_bbt - [GENERIC] create a memory based bad block table 92562306a36Sopenharmony_ci * @this: NAND chip object 92662306a36Sopenharmony_ci * @bd: descriptor for the good/bad block search pattern 92762306a36Sopenharmony_ci * 92862306a36Sopenharmony_ci * The function creates a memory based bbt by scanning the device for 92962306a36Sopenharmony_ci * manufacturer / software marked good / bad blocks. 93062306a36Sopenharmony_ci */ 93162306a36Sopenharmony_cistatic inline int nand_memory_bbt(struct nand_chip *this, 93262306a36Sopenharmony_ci struct nand_bbt_descr *bd) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci u8 *pagebuf = nand_get_data_buf(this); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci return create_bbt(this, pagebuf, bd, -1); 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci/** 94062306a36Sopenharmony_ci * check_create - [GENERIC] create and write bbt(s) if necessary 94162306a36Sopenharmony_ci * @this: the NAND device 94262306a36Sopenharmony_ci * @buf: temporary buffer 94362306a36Sopenharmony_ci * @bd: descriptor for the good/bad block search pattern 94462306a36Sopenharmony_ci * 94562306a36Sopenharmony_ci * The function checks the results of the previous call to read_bbt and creates 94662306a36Sopenharmony_ci * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found 94762306a36Sopenharmony_ci * for the chip/device. Update is necessary if one of the tables is missing or 94862306a36Sopenharmony_ci * the version nr. of one table is less than the other. 94962306a36Sopenharmony_ci */ 95062306a36Sopenharmony_cistatic int check_create(struct nand_chip *this, uint8_t *buf, 95162306a36Sopenharmony_ci struct nand_bbt_descr *bd) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci int i, chips, writeops, create, chipsel, res, res2; 95462306a36Sopenharmony_ci struct nand_bbt_descr *td = this->bbt_td; 95562306a36Sopenharmony_ci struct nand_bbt_descr *md = this->bbt_md; 95662306a36Sopenharmony_ci struct nand_bbt_descr *rd, *rd2; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* Do we have a bbt per chip? */ 95962306a36Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) 96062306a36Sopenharmony_ci chips = nanddev_ntargets(&this->base); 96162306a36Sopenharmony_ci else 96262306a36Sopenharmony_ci chips = 1; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci for (i = 0; i < chips; i++) { 96562306a36Sopenharmony_ci writeops = 0; 96662306a36Sopenharmony_ci create = 0; 96762306a36Sopenharmony_ci rd = NULL; 96862306a36Sopenharmony_ci rd2 = NULL; 96962306a36Sopenharmony_ci res = res2 = 0; 97062306a36Sopenharmony_ci /* Per chip or per device? */ 97162306a36Sopenharmony_ci chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; 97262306a36Sopenharmony_ci /* Mirrored table available? */ 97362306a36Sopenharmony_ci if (md) { 97462306a36Sopenharmony_ci if (td->pages[i] == -1 && md->pages[i] == -1) { 97562306a36Sopenharmony_ci create = 1; 97662306a36Sopenharmony_ci writeops = 0x03; 97762306a36Sopenharmony_ci } else if (td->pages[i] == -1) { 97862306a36Sopenharmony_ci rd = md; 97962306a36Sopenharmony_ci writeops = 0x01; 98062306a36Sopenharmony_ci } else if (md->pages[i] == -1) { 98162306a36Sopenharmony_ci rd = td; 98262306a36Sopenharmony_ci writeops = 0x02; 98362306a36Sopenharmony_ci } else if (td->version[i] == md->version[i]) { 98462306a36Sopenharmony_ci rd = td; 98562306a36Sopenharmony_ci if (!(td->options & NAND_BBT_VERSION)) 98662306a36Sopenharmony_ci rd2 = md; 98762306a36Sopenharmony_ci } else if (((int8_t)(td->version[i] - md->version[i])) > 0) { 98862306a36Sopenharmony_ci rd = td; 98962306a36Sopenharmony_ci writeops = 0x02; 99062306a36Sopenharmony_ci } else { 99162306a36Sopenharmony_ci rd = md; 99262306a36Sopenharmony_ci writeops = 0x01; 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci } else { 99562306a36Sopenharmony_ci if (td->pages[i] == -1) { 99662306a36Sopenharmony_ci create = 1; 99762306a36Sopenharmony_ci writeops = 0x01; 99862306a36Sopenharmony_ci } else { 99962306a36Sopenharmony_ci rd = td; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci if (create) { 100462306a36Sopenharmony_ci /* Create the bad block table by scanning the device? */ 100562306a36Sopenharmony_ci if (!(td->options & NAND_BBT_CREATE)) 100662306a36Sopenharmony_ci continue; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci /* Create the table in memory by scanning the chip(s) */ 100962306a36Sopenharmony_ci if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) 101062306a36Sopenharmony_ci create_bbt(this, buf, bd, chipsel); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci td->version[i] = 1; 101362306a36Sopenharmony_ci if (md) 101462306a36Sopenharmony_ci md->version[i] = 1; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci /* Read back first? */ 101862306a36Sopenharmony_ci if (rd) { 101962306a36Sopenharmony_ci res = read_abs_bbt(this, buf, rd, chipsel); 102062306a36Sopenharmony_ci if (mtd_is_eccerr(res)) { 102162306a36Sopenharmony_ci /* Mark table as invalid */ 102262306a36Sopenharmony_ci rd->pages[i] = -1; 102362306a36Sopenharmony_ci rd->version[i] = 0; 102462306a36Sopenharmony_ci i--; 102562306a36Sopenharmony_ci continue; 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci /* If they weren't versioned, read both */ 102962306a36Sopenharmony_ci if (rd2) { 103062306a36Sopenharmony_ci res2 = read_abs_bbt(this, buf, rd2, chipsel); 103162306a36Sopenharmony_ci if (mtd_is_eccerr(res2)) { 103262306a36Sopenharmony_ci /* Mark table as invalid */ 103362306a36Sopenharmony_ci rd2->pages[i] = -1; 103462306a36Sopenharmony_ci rd2->version[i] = 0; 103562306a36Sopenharmony_ci i--; 103662306a36Sopenharmony_ci continue; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci /* Scrub the flash table(s)? */ 104162306a36Sopenharmony_ci if (mtd_is_bitflip(res) || mtd_is_bitflip(res2)) 104262306a36Sopenharmony_ci writeops = 0x03; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* Update version numbers before writing */ 104562306a36Sopenharmony_ci if (md) { 104662306a36Sopenharmony_ci td->version[i] = max(td->version[i], md->version[i]); 104762306a36Sopenharmony_ci md->version[i] = td->version[i]; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci /* Write the bad block table to the device? */ 105162306a36Sopenharmony_ci if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { 105262306a36Sopenharmony_ci res = write_bbt(this, buf, td, md, chipsel); 105362306a36Sopenharmony_ci if (res < 0) 105462306a36Sopenharmony_ci return res; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci /* Write the mirror bad block table to the device? */ 105862306a36Sopenharmony_ci if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { 105962306a36Sopenharmony_ci res = write_bbt(this, buf, md, td, chipsel); 106062306a36Sopenharmony_ci if (res < 0) 106162306a36Sopenharmony_ci return res; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci return 0; 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci/** 106862306a36Sopenharmony_ci * nand_update_bbt - update bad block table(s) 106962306a36Sopenharmony_ci * @this: the NAND device 107062306a36Sopenharmony_ci * @offs: the offset of the newly marked block 107162306a36Sopenharmony_ci * 107262306a36Sopenharmony_ci * The function updates the bad block table(s). 107362306a36Sopenharmony_ci */ 107462306a36Sopenharmony_cistatic int nand_update_bbt(struct nand_chip *this, loff_t offs) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 107762306a36Sopenharmony_ci int len, res = 0; 107862306a36Sopenharmony_ci int chip, chipsel; 107962306a36Sopenharmony_ci uint8_t *buf; 108062306a36Sopenharmony_ci struct nand_bbt_descr *td = this->bbt_td; 108162306a36Sopenharmony_ci struct nand_bbt_descr *md = this->bbt_md; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (!this->bbt || !td) 108462306a36Sopenharmony_ci return -EINVAL; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* Allocate a temporary buffer for one eraseblock incl. oob */ 108762306a36Sopenharmony_ci len = (1 << this->bbt_erase_shift); 108862306a36Sopenharmony_ci len += (len >> this->page_shift) * mtd->oobsize; 108962306a36Sopenharmony_ci buf = kmalloc(len, GFP_KERNEL); 109062306a36Sopenharmony_ci if (!buf) 109162306a36Sopenharmony_ci return -ENOMEM; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* Do we have a bbt per chip? */ 109462306a36Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) { 109562306a36Sopenharmony_ci chip = (int)(offs >> this->chip_shift); 109662306a36Sopenharmony_ci chipsel = chip; 109762306a36Sopenharmony_ci } else { 109862306a36Sopenharmony_ci chip = 0; 109962306a36Sopenharmony_ci chipsel = -1; 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci td->version[chip]++; 110362306a36Sopenharmony_ci if (md) 110462306a36Sopenharmony_ci md->version[chip]++; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* Write the bad block table to the device? */ 110762306a36Sopenharmony_ci if (td->options & NAND_BBT_WRITE) { 110862306a36Sopenharmony_ci res = write_bbt(this, buf, td, md, chipsel); 110962306a36Sopenharmony_ci if (res < 0) 111062306a36Sopenharmony_ci goto out; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci /* Write the mirror bad block table to the device? */ 111362306a36Sopenharmony_ci if (md && (md->options & NAND_BBT_WRITE)) { 111462306a36Sopenharmony_ci res = write_bbt(this, buf, md, td, chipsel); 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci out: 111862306a36Sopenharmony_ci kfree(buf); 111962306a36Sopenharmony_ci return res; 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci/** 112362306a36Sopenharmony_ci * mark_bbt_region - [GENERIC] mark the bad block table regions 112462306a36Sopenharmony_ci * @this: the NAND device 112562306a36Sopenharmony_ci * @td: bad block table descriptor 112662306a36Sopenharmony_ci * 112762306a36Sopenharmony_ci * The bad block table regions are marked as "bad" to prevent accidental 112862306a36Sopenharmony_ci * erasures / writes. The regions are identified by the mark 0x02. 112962306a36Sopenharmony_ci */ 113062306a36Sopenharmony_cistatic void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 113362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 113462306a36Sopenharmony_ci int i, j, chips, block, nrblocks, update; 113562306a36Sopenharmony_ci uint8_t oldval; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci /* Do we have a bbt per chip? */ 113862306a36Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) { 113962306a36Sopenharmony_ci chips = nanddev_ntargets(&this->base); 114062306a36Sopenharmony_ci nrblocks = (int)(targetsize >> this->bbt_erase_shift); 114162306a36Sopenharmony_ci } else { 114262306a36Sopenharmony_ci chips = 1; 114362306a36Sopenharmony_ci nrblocks = (int)(mtd->size >> this->bbt_erase_shift); 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci for (i = 0; i < chips; i++) { 114762306a36Sopenharmony_ci if ((td->options & NAND_BBT_ABSPAGE) || 114862306a36Sopenharmony_ci !(td->options & NAND_BBT_WRITE)) { 114962306a36Sopenharmony_ci if (td->pages[i] == -1) 115062306a36Sopenharmony_ci continue; 115162306a36Sopenharmony_ci block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); 115262306a36Sopenharmony_ci oldval = bbt_get_entry(this, block); 115362306a36Sopenharmony_ci bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); 115462306a36Sopenharmony_ci if ((oldval != BBT_BLOCK_RESERVED) && 115562306a36Sopenharmony_ci td->reserved_block_code) 115662306a36Sopenharmony_ci nand_update_bbt(this, (loff_t)block << 115762306a36Sopenharmony_ci this->bbt_erase_shift); 115862306a36Sopenharmony_ci continue; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci update = 0; 116162306a36Sopenharmony_ci if (td->options & NAND_BBT_LASTBLOCK) 116262306a36Sopenharmony_ci block = ((i + 1) * nrblocks) - td->maxblocks; 116362306a36Sopenharmony_ci else 116462306a36Sopenharmony_ci block = i * nrblocks; 116562306a36Sopenharmony_ci for (j = 0; j < td->maxblocks; j++) { 116662306a36Sopenharmony_ci oldval = bbt_get_entry(this, block); 116762306a36Sopenharmony_ci bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); 116862306a36Sopenharmony_ci if (oldval != BBT_BLOCK_RESERVED) 116962306a36Sopenharmony_ci update = 1; 117062306a36Sopenharmony_ci block++; 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci /* 117362306a36Sopenharmony_ci * If we want reserved blocks to be recorded to flash, and some 117462306a36Sopenharmony_ci * new ones have been marked, then we need to update the stored 117562306a36Sopenharmony_ci * bbts. This should only happen once. 117662306a36Sopenharmony_ci */ 117762306a36Sopenharmony_ci if (update && td->reserved_block_code) 117862306a36Sopenharmony_ci nand_update_bbt(this, (loff_t)(block - 1) << 117962306a36Sopenharmony_ci this->bbt_erase_shift); 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci/** 118462306a36Sopenharmony_ci * verify_bbt_descr - verify the bad block description 118562306a36Sopenharmony_ci * @this: the NAND device 118662306a36Sopenharmony_ci * @bd: the table to verify 118762306a36Sopenharmony_ci * 118862306a36Sopenharmony_ci * This functions performs a few sanity checks on the bad block description 118962306a36Sopenharmony_ci * table. 119062306a36Sopenharmony_ci */ 119162306a36Sopenharmony_cistatic void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd) 119262306a36Sopenharmony_ci{ 119362306a36Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 119462306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 119562306a36Sopenharmony_ci u32 pattern_len; 119662306a36Sopenharmony_ci u32 bits; 119762306a36Sopenharmony_ci u32 table_size; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (!bd) 120062306a36Sopenharmony_ci return; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci pattern_len = bd->len; 120362306a36Sopenharmony_ci bits = bd->options & NAND_BBT_NRBITS_MSK; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) && 120662306a36Sopenharmony_ci !(this->bbt_options & NAND_BBT_USE_FLASH)); 120762306a36Sopenharmony_ci BUG_ON(!bits); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (bd->options & NAND_BBT_VERSION) 121062306a36Sopenharmony_ci pattern_len++; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci if (bd->options & NAND_BBT_NO_OOB) { 121362306a36Sopenharmony_ci BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH)); 121462306a36Sopenharmony_ci BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB)); 121562306a36Sopenharmony_ci BUG_ON(bd->offs); 121662306a36Sopenharmony_ci if (bd->options & NAND_BBT_VERSION) 121762306a36Sopenharmony_ci BUG_ON(bd->veroffs != bd->len); 121862306a36Sopenharmony_ci BUG_ON(bd->options & NAND_BBT_SAVECONTENT); 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci if (bd->options & NAND_BBT_PERCHIP) 122262306a36Sopenharmony_ci table_size = targetsize >> this->bbt_erase_shift; 122362306a36Sopenharmony_ci else 122462306a36Sopenharmony_ci table_size = mtd->size >> this->bbt_erase_shift; 122562306a36Sopenharmony_ci table_size >>= 3; 122662306a36Sopenharmony_ci table_size *= bits; 122762306a36Sopenharmony_ci if (bd->options & NAND_BBT_NO_OOB) 122862306a36Sopenharmony_ci table_size += pattern_len; 122962306a36Sopenharmony_ci BUG_ON(table_size > (1 << this->bbt_erase_shift)); 123062306a36Sopenharmony_ci} 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci/** 123362306a36Sopenharmony_ci * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) 123462306a36Sopenharmony_ci * @this: the NAND device 123562306a36Sopenharmony_ci * @bd: descriptor for the good/bad block search pattern 123662306a36Sopenharmony_ci * 123762306a36Sopenharmony_ci * The function checks, if a bad block table(s) is/are already available. If 123862306a36Sopenharmony_ci * not it scans the device for manufacturer marked good / bad blocks and writes 123962306a36Sopenharmony_ci * the bad block table(s) to the selected place. 124062306a36Sopenharmony_ci * 124162306a36Sopenharmony_ci * The bad block table memory is allocated here. It must be freed by calling 124262306a36Sopenharmony_ci * the nand_free_bbt function. 124362306a36Sopenharmony_ci */ 124462306a36Sopenharmony_cistatic int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 124762306a36Sopenharmony_ci int len, res; 124862306a36Sopenharmony_ci uint8_t *buf; 124962306a36Sopenharmony_ci struct nand_bbt_descr *td = this->bbt_td; 125062306a36Sopenharmony_ci struct nand_bbt_descr *md = this->bbt_md; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1; 125362306a36Sopenharmony_ci /* 125462306a36Sopenharmony_ci * Allocate memory (2bit per block) and clear the memory bad block 125562306a36Sopenharmony_ci * table. 125662306a36Sopenharmony_ci */ 125762306a36Sopenharmony_ci this->bbt = kzalloc(len, GFP_KERNEL); 125862306a36Sopenharmony_ci if (!this->bbt) 125962306a36Sopenharmony_ci return -ENOMEM; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* 126262306a36Sopenharmony_ci * If no primary table descriptor is given, scan the device to build a 126362306a36Sopenharmony_ci * memory based bad block table. 126462306a36Sopenharmony_ci */ 126562306a36Sopenharmony_ci if (!td) { 126662306a36Sopenharmony_ci if ((res = nand_memory_bbt(this, bd))) { 126762306a36Sopenharmony_ci pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); 126862306a36Sopenharmony_ci goto err_free_bbt; 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci return 0; 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci verify_bbt_descr(this, td); 127362306a36Sopenharmony_ci verify_bbt_descr(this, md); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci /* Allocate a temporary buffer for one eraseblock incl. oob */ 127662306a36Sopenharmony_ci len = (1 << this->bbt_erase_shift); 127762306a36Sopenharmony_ci len += (len >> this->page_shift) * mtd->oobsize; 127862306a36Sopenharmony_ci buf = vmalloc(len); 127962306a36Sopenharmony_ci if (!buf) { 128062306a36Sopenharmony_ci res = -ENOMEM; 128162306a36Sopenharmony_ci goto err_free_bbt; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci /* Is the bbt at a given page? */ 128562306a36Sopenharmony_ci if (td->options & NAND_BBT_ABSPAGE) { 128662306a36Sopenharmony_ci read_abs_bbts(this, buf, td, md); 128762306a36Sopenharmony_ci } else { 128862306a36Sopenharmony_ci /* Search the bad block table using a pattern in oob */ 128962306a36Sopenharmony_ci search_read_bbts(this, buf, td, md); 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci res = check_create(this, buf, bd); 129362306a36Sopenharmony_ci if (res) 129462306a36Sopenharmony_ci goto err_free_buf; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci /* Prevent the bbt regions from erasing / writing */ 129762306a36Sopenharmony_ci mark_bbt_region(this, td); 129862306a36Sopenharmony_ci if (md) 129962306a36Sopenharmony_ci mark_bbt_region(this, md); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci vfree(buf); 130262306a36Sopenharmony_ci return 0; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_cierr_free_buf: 130562306a36Sopenharmony_ci vfree(buf); 130662306a36Sopenharmony_cierr_free_bbt: 130762306a36Sopenharmony_ci kfree(this->bbt); 130862306a36Sopenharmony_ci this->bbt = NULL; 130962306a36Sopenharmony_ci return res; 131062306a36Sopenharmony_ci} 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci/* 131362306a36Sopenharmony_ci * Define some generic bad / good block scan pattern which are used 131462306a36Sopenharmony_ci * while scanning a device for factory marked good / bad blocks. 131562306a36Sopenharmony_ci */ 131662306a36Sopenharmony_cistatic uint8_t scan_ff_pattern[] = { 0xff, 0xff }; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci/* Generic flash bbt descriptors */ 131962306a36Sopenharmony_cistatic uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; 132062306a36Sopenharmony_cistatic uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_main_descr = { 132362306a36Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 132462306a36Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, 132562306a36Sopenharmony_ci .offs = 8, 132662306a36Sopenharmony_ci .len = 4, 132762306a36Sopenharmony_ci .veroffs = 12, 132862306a36Sopenharmony_ci .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 132962306a36Sopenharmony_ci .pattern = bbt_pattern 133062306a36Sopenharmony_ci}; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_mirror_descr = { 133362306a36Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 133462306a36Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, 133562306a36Sopenharmony_ci .offs = 8, 133662306a36Sopenharmony_ci .len = 4, 133762306a36Sopenharmony_ci .veroffs = 12, 133862306a36Sopenharmony_ci .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 133962306a36Sopenharmony_ci .pattern = mirror_pattern 134062306a36Sopenharmony_ci}; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_main_no_oob_descr = { 134362306a36Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 134462306a36Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP 134562306a36Sopenharmony_ci | NAND_BBT_NO_OOB, 134662306a36Sopenharmony_ci .len = 4, 134762306a36Sopenharmony_ci .veroffs = 4, 134862306a36Sopenharmony_ci .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 134962306a36Sopenharmony_ci .pattern = bbt_pattern 135062306a36Sopenharmony_ci}; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_mirror_no_oob_descr = { 135362306a36Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 135462306a36Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP 135562306a36Sopenharmony_ci | NAND_BBT_NO_OOB, 135662306a36Sopenharmony_ci .len = 4, 135762306a36Sopenharmony_ci .veroffs = 4, 135862306a36Sopenharmony_ci .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 135962306a36Sopenharmony_ci .pattern = mirror_pattern 136062306a36Sopenharmony_ci}; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) 136362306a36Sopenharmony_ci/** 136462306a36Sopenharmony_ci * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure 136562306a36Sopenharmony_ci * @this: NAND chip to create descriptor for 136662306a36Sopenharmony_ci * 136762306a36Sopenharmony_ci * This function allocates and initializes a nand_bbt_descr for BBM detection 136862306a36Sopenharmony_ci * based on the properties of @this. The new descriptor is stored in 136962306a36Sopenharmony_ci * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when 137062306a36Sopenharmony_ci * passed to this function. 137162306a36Sopenharmony_ci */ 137262306a36Sopenharmony_cistatic int nand_create_badblock_pattern(struct nand_chip *this) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci struct nand_bbt_descr *bd; 137562306a36Sopenharmony_ci if (this->badblock_pattern) { 137662306a36Sopenharmony_ci pr_warn("Bad block pattern already allocated; not replacing\n"); 137762306a36Sopenharmony_ci return -EINVAL; 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci bd = kzalloc(sizeof(*bd), GFP_KERNEL); 138062306a36Sopenharmony_ci if (!bd) 138162306a36Sopenharmony_ci return -ENOMEM; 138262306a36Sopenharmony_ci bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; 138362306a36Sopenharmony_ci bd->offs = this->badblockpos; 138462306a36Sopenharmony_ci bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; 138562306a36Sopenharmony_ci bd->pattern = scan_ff_pattern; 138662306a36Sopenharmony_ci bd->options |= NAND_BBT_DYNAMICSTRUCT; 138762306a36Sopenharmony_ci this->badblock_pattern = bd; 138862306a36Sopenharmony_ci return 0; 138962306a36Sopenharmony_ci} 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci/** 139262306a36Sopenharmony_ci * nand_create_bbt - [NAND Interface] Select a default bad block table for the device 139362306a36Sopenharmony_ci * @this: NAND chip object 139462306a36Sopenharmony_ci * 139562306a36Sopenharmony_ci * This function selects the default bad block table support for the device and 139662306a36Sopenharmony_ci * calls the nand_scan_bbt function. 139762306a36Sopenharmony_ci */ 139862306a36Sopenharmony_ciint nand_create_bbt(struct nand_chip *this) 139962306a36Sopenharmony_ci{ 140062306a36Sopenharmony_ci int ret; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci /* Is a flash based bad block table requested? */ 140362306a36Sopenharmony_ci if (this->bbt_options & NAND_BBT_USE_FLASH) { 140462306a36Sopenharmony_ci /* Use the default pattern descriptors */ 140562306a36Sopenharmony_ci if (!this->bbt_td) { 140662306a36Sopenharmony_ci if (this->bbt_options & NAND_BBT_NO_OOB) { 140762306a36Sopenharmony_ci this->bbt_td = &bbt_main_no_oob_descr; 140862306a36Sopenharmony_ci this->bbt_md = &bbt_mirror_no_oob_descr; 140962306a36Sopenharmony_ci } else { 141062306a36Sopenharmony_ci this->bbt_td = &bbt_main_descr; 141162306a36Sopenharmony_ci this->bbt_md = &bbt_mirror_descr; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci } else { 141562306a36Sopenharmony_ci this->bbt_td = NULL; 141662306a36Sopenharmony_ci this->bbt_md = NULL; 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci if (!this->badblock_pattern) { 142062306a36Sopenharmony_ci ret = nand_create_badblock_pattern(this); 142162306a36Sopenharmony_ci if (ret) 142262306a36Sopenharmony_ci return ret; 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci return nand_scan_bbt(this, this->badblock_pattern); 142662306a36Sopenharmony_ci} 142762306a36Sopenharmony_ciEXPORT_SYMBOL(nand_create_bbt); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci/** 143062306a36Sopenharmony_ci * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved 143162306a36Sopenharmony_ci * @this: NAND chip object 143262306a36Sopenharmony_ci * @offs: offset in the device 143362306a36Sopenharmony_ci */ 143462306a36Sopenharmony_ciint nand_isreserved_bbt(struct nand_chip *this, loff_t offs) 143562306a36Sopenharmony_ci{ 143662306a36Sopenharmony_ci int block; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci block = (int)(offs >> this->bbt_erase_shift); 143962306a36Sopenharmony_ci return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED; 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci/** 144362306a36Sopenharmony_ci * nand_isbad_bbt - [NAND Interface] Check if a block is bad 144462306a36Sopenharmony_ci * @this: NAND chip object 144562306a36Sopenharmony_ci * @offs: offset in the device 144662306a36Sopenharmony_ci * @allowbbt: allow access to bad block table region 144762306a36Sopenharmony_ci */ 144862306a36Sopenharmony_ciint nand_isbad_bbt(struct nand_chip *this, loff_t offs, int allowbbt) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci int block, res; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci block = (int)(offs >> this->bbt_erase_shift); 145362306a36Sopenharmony_ci res = bbt_get_entry(this, block); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", 145662306a36Sopenharmony_ci (unsigned int)offs, block, res); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci if (mtd_check_expert_analysis_mode()) 145962306a36Sopenharmony_ci return 0; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci switch (res) { 146262306a36Sopenharmony_ci case BBT_BLOCK_GOOD: 146362306a36Sopenharmony_ci return 0; 146462306a36Sopenharmony_ci case BBT_BLOCK_WORN: 146562306a36Sopenharmony_ci return 1; 146662306a36Sopenharmony_ci case BBT_BLOCK_RESERVED: 146762306a36Sopenharmony_ci return allowbbt ? 0 : 1; 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci return 1; 147062306a36Sopenharmony_ci} 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci/** 147362306a36Sopenharmony_ci * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT 147462306a36Sopenharmony_ci * @this: NAND chip object 147562306a36Sopenharmony_ci * @offs: offset of the bad block 147662306a36Sopenharmony_ci */ 147762306a36Sopenharmony_ciint nand_markbad_bbt(struct nand_chip *this, loff_t offs) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci int block, ret = 0; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci block = (int)(offs >> this->bbt_erase_shift); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci /* Mark bad block in memory */ 148462306a36Sopenharmony_ci bbt_mark_entry(this, block, BBT_BLOCK_WORN); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci /* Update flash-based bad block table */ 148762306a36Sopenharmony_ci if (this->bbt_options & NAND_BBT_USE_FLASH) 148862306a36Sopenharmony_ci ret = nand_update_bbt(this, offs); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci return ret; 149162306a36Sopenharmony_ci} 1492