1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Overview: 41da177e4SLinus Torvalds * Bad block table support for the NAND driver 51da177e4SLinus Torvalds * 6d159c4e5SFabio Estevam * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de) 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Description: 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * When nand_scan_bbt is called, then it tries to find the bad block table 117cba7b14SSebastian Andrzej Siewior * depending on the options in the BBT descriptor(s). If no flash based BBT 12bb9ebd4eSBrian Norris * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory 137cba7b14SSebastian Andrzej Siewior * marked good / bad blocks. This information is used to create a memory BBT. 147cba7b14SSebastian Andrzej Siewior * Once a new bad block is discovered then the "factory" information is updated 157cba7b14SSebastian Andrzej Siewior * on the device. 167cba7b14SSebastian Andrzej Siewior * If a flash based BBT is specified then the function first tries to find the 177cba7b14SSebastian Andrzej Siewior * BBT on flash. If a BBT is found then the contents are read and the memory 187cba7b14SSebastian Andrzej Siewior * based BBT is created. If a mirrored BBT is selected then the mirror is 197cba7b14SSebastian Andrzej Siewior * searched too and the versions are compared. If the mirror has a greater 2044ed0ffdSHuang Shijie * version number, then the mirror BBT is used to build the memory based BBT. 211da177e4SLinus Torvalds * If the tables are not versioned, then we "or" the bad block information. 227cba7b14SSebastian Andrzej Siewior * If one of the BBTs is out of date or does not exist it is (re)created. 237cba7b14SSebastian Andrzej Siewior * If no BBT exists at all then the device is scanned for factory marked 241da177e4SLinus Torvalds * good / bad blocks and the bad block tables are created. 251da177e4SLinus Torvalds * 267cba7b14SSebastian Andrzej Siewior * For manufacturer created BBTs like the one found on M-SYS DOC devices 277cba7b14SSebastian Andrzej Siewior * the BBT is searched and read but never created 281da177e4SLinus Torvalds * 291da177e4SLinus Torvalds * The auto generated bad block table is located in the last good blocks 301da177e4SLinus Torvalds * of the device. The table is mirrored, so it can be updated eventually. 317cba7b14SSebastian Andrzej Siewior * The table is marked in the OOB area with an ident pattern and a version 327cba7b14SSebastian Andrzej Siewior * number which indicates which of both tables is more up to date. If the NAND 337cba7b14SSebastian Andrzej Siewior * controller needs the complete OOB area for the ECC information then the 34bb9ebd4eSBrian Norris * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of 35a40f7341SBrian Norris * course): it moves the ident pattern and the version byte into the data area 36a40f7341SBrian Norris * and the OOB area will remain untouched. 371da177e4SLinus Torvalds * 381da177e4SLinus Torvalds * The table uses 2 bits per block 391da177e4SLinus Torvalds * 11b: block is good 401da177e4SLinus Torvalds * 00b: block is factory marked bad 411da177e4SLinus Torvalds * 01b, 10b: block is marked bad due to wear 421da177e4SLinus Torvalds * 431da177e4SLinus Torvalds * The memory bad block table uses the following scheme: 441da177e4SLinus Torvalds * 00b: block is good 451da177e4SLinus Torvalds * 01b: block is marked bad due to wear 461da177e4SLinus Torvalds * 10b: block is reserved (to protect the bbt area) 471da177e4SLinus Torvalds * 11b: block is factory marked bad 481da177e4SLinus Torvalds * 491da177e4SLinus Torvalds * Multichip devices like DOC store the bad block info per floor. 501da177e4SLinus Torvalds * 511da177e4SLinus Torvalds * Following assumptions are made: 521da177e4SLinus Torvalds * - bbts start at a page boundary, if autolocated on a block boundary 53e0c7d767SDavid Woodhouse * - the space necessary for a bbt in FLASH does not exceed a block boundary 541da177e4SLinus Torvalds */ 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds #include <linux/slab.h> 571da177e4SLinus Torvalds #include <linux/types.h> 581da177e4SLinus Torvalds #include <linux/mtd/mtd.h> 5961de9da6SRichard Genoud #include <linux/mtd/bbm.h> 601da177e4SLinus Torvalds #include <linux/bitops.h> 611da177e4SLinus Torvalds #include <linux/delay.h> 62c3f8abf4SDavid Woodhouse #include <linux/vmalloc.h> 63f3bcc017SPaul Gortmaker #include <linux/export.h> 64491ed06fSBrian Norris #include <linux/string.h> 651da177e4SLinus Torvalds 66348d56a8SBoris Brezillon #include "internals.h" 67348d56a8SBoris Brezillon 68771c568bSBrian Norris #define BBT_BLOCK_GOOD 0x00 69771c568bSBrian Norris #define BBT_BLOCK_WORN 0x01 70771c568bSBrian Norris #define BBT_BLOCK_RESERVED 0x02 71771c568bSBrian Norris #define BBT_BLOCK_FACTORY_BAD 0x03 72771c568bSBrian Norris 73771c568bSBrian Norris #define BBT_ENTRY_MASK 0x03 74771c568bSBrian Norris #define BBT_ENTRY_SHIFT 2 75771c568bSBrian Norris 76771c568bSBrian Norris static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) 77771c568bSBrian Norris { 78771c568bSBrian Norris uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; 79771c568bSBrian Norris entry >>= (block & BBT_ENTRY_MASK) * 2; 80771c568bSBrian Norris return entry & BBT_ENTRY_MASK; 81771c568bSBrian Norris } 82771c568bSBrian Norris 83771c568bSBrian Norris static inline void bbt_mark_entry(struct nand_chip *chip, int block, 84771c568bSBrian Norris uint8_t mark) 85771c568bSBrian Norris { 86771c568bSBrian Norris uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); 87771c568bSBrian Norris chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; 88771c568bSBrian Norris } 89771c568bSBrian Norris 907cba7b14SSebastian Andrzej Siewior static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) 917cba7b14SSebastian Andrzej Siewior { 92718894adSBrian Norris if (memcmp(buf, td->pattern, td->len)) 937cba7b14SSebastian Andrzej Siewior return -1; 94718894adSBrian Norris return 0; 957cba7b14SSebastian Andrzej Siewior } 967cba7b14SSebastian Andrzej Siewior 971da177e4SLinus Torvalds /** 981da177e4SLinus Torvalds * check_pattern - [GENERIC] check if a pattern is in the buffer 991da177e4SLinus Torvalds * @buf: the buffer to search 1001da177e4SLinus Torvalds * @len: the length of buffer to search 1011da177e4SLinus Torvalds * @paglen: the pagelength 1021da177e4SLinus Torvalds * @td: search pattern descriptor 1031da177e4SLinus Torvalds * 1048b6e50c9SBrian Norris * Check for a pattern at the given place. Used to search bad block tables and 105dad22562SBrian Norris * good / bad block identifiers. 1061da177e4SLinus Torvalds */ 1071da177e4SLinus Torvalds static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) 1081da177e4SLinus Torvalds { 1097cba7b14SSebastian Andrzej Siewior if (td->options & NAND_BBT_NO_OOB) 1107cba7b14SSebastian Andrzej Siewior return check_pattern_no_oob(buf, td); 1117cba7b14SSebastian Andrzej Siewior 1121da177e4SLinus Torvalds /* Compare the pattern */ 113dad22562SBrian Norris if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) 1141da177e4SLinus Torvalds return -1; 11558373ff0SBrian Norris 1161da177e4SLinus Torvalds return 0; 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds /** 120c9e05365SThomas Gleixner * check_short_pattern - [GENERIC] check if a pattern is in the buffer 121c9e05365SThomas Gleixner * @buf: the buffer to search 122c9e05365SThomas Gleixner * @td: search pattern descriptor 123c9e05365SThomas Gleixner * 1248b6e50c9SBrian Norris * Check for a pattern at the given place. Used to search bad block tables and 1258b6e50c9SBrian Norris * good / bad block identifiers. Same as check_pattern, but no optional empty 1268b6e50c9SBrian Norris * check. 127c9e05365SThomas Gleixner */ 12819870da7SThomas Gleixner static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) 129c9e05365SThomas Gleixner { 130c9e05365SThomas Gleixner /* Compare the pattern */ 131491ed06fSBrian Norris if (memcmp(buf + td->offs, td->pattern, td->len)) 132c9e05365SThomas Gleixner return -1; 133c9e05365SThomas Gleixner return 0; 134c9e05365SThomas Gleixner } 135c9e05365SThomas Gleixner 136c9e05365SThomas Gleixner /** 1377cba7b14SSebastian Andrzej Siewior * add_marker_len - compute the length of the marker in data area 1387cba7b14SSebastian Andrzej Siewior * @td: BBT descriptor used for computation 1397cba7b14SSebastian Andrzej Siewior * 1407cba7b14SSebastian Andrzej Siewior * The length will be 0 if the marker is located in OOB area. 1417cba7b14SSebastian Andrzej Siewior */ 1427cba7b14SSebastian Andrzej Siewior static u32 add_marker_len(struct nand_bbt_descr *td) 1437cba7b14SSebastian Andrzej Siewior { 1447cba7b14SSebastian Andrzej Siewior u32 len; 1457cba7b14SSebastian Andrzej Siewior 1467cba7b14SSebastian Andrzej Siewior if (!(td->options & NAND_BBT_NO_OOB)) 1477cba7b14SSebastian Andrzej Siewior return 0; 1487cba7b14SSebastian Andrzej Siewior 1497cba7b14SSebastian Andrzej Siewior len = td->len; 1507cba7b14SSebastian Andrzej Siewior if (td->options & NAND_BBT_VERSION) 1517cba7b14SSebastian Andrzej Siewior len++; 1527cba7b14SSebastian Andrzej Siewior return len; 1537cba7b14SSebastian Andrzej Siewior } 1547cba7b14SSebastian Andrzej Siewior 1557cba7b14SSebastian Andrzej Siewior /** 1561da177e4SLinus Torvalds * read_bbt - [GENERIC] Read the bad block table starting from page 157455e7b38SRandy Dunlap * @this: NAND chip object 1581da177e4SLinus Torvalds * @buf: temporary buffer 1591da177e4SLinus Torvalds * @page: the starting page 1601da177e4SLinus Torvalds * @num: the number of bbt descriptors to read 161df5b4e34SSebastian Andrzej Siewior * @td: the bbt describtion table 162b4d20d60SBrian Norris * @offs: block number offset in the table 1631da177e4SLinus Torvalds * 1641da177e4SLinus Torvalds * Read the bad block table starting from page. 1651da177e4SLinus Torvalds */ 1660813621bSBoris Brezillon static int read_bbt(struct nand_chip *this, uint8_t *buf, int page, int num, 167df5b4e34SSebastian Andrzej Siewior struct nand_bbt_descr *td, int offs) 1681da177e4SLinus Torvalds { 1690813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 170167a8d52SBrian Norris int res, ret = 0, i, j, act = 0; 1711da177e4SLinus Torvalds size_t retlen, len, totlen; 1721da177e4SLinus Torvalds loff_t from; 173df5b4e34SSebastian Andrzej Siewior int bits = td->options & NAND_BBT_NRBITS_MSK; 1741da177e4SLinus Torvalds uint8_t msk = (uint8_t)((1 << bits) - 1); 1757cba7b14SSebastian Andrzej Siewior u32 marker_len; 176df5b4e34SSebastian Andrzej Siewior int reserved_block_code = td->reserved_block_code; 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds totlen = (num * bits) >> 3; 1797cba7b14SSebastian Andrzej Siewior marker_len = add_marker_len(td); 1801da177e4SLinus Torvalds from = ((loff_t)page) << this->page_shift; 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds while (totlen) { 1831da177e4SLinus Torvalds len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); 1847cba7b14SSebastian Andrzej Siewior if (marker_len) { 1857cba7b14SSebastian Andrzej Siewior /* 1867cba7b14SSebastian Andrzej Siewior * In case the BBT marker is not in the OOB area it 1877cba7b14SSebastian Andrzej Siewior * will be just in the first page. 1887cba7b14SSebastian Andrzej Siewior */ 1897cba7b14SSebastian Andrzej Siewior len -= marker_len; 1907cba7b14SSebastian Andrzej Siewior from += marker_len; 1917cba7b14SSebastian Andrzej Siewior marker_len = 0; 1927cba7b14SSebastian Andrzej Siewior } 193329ad399SArtem Bityutskiy res = mtd_read(mtd, from, len, &retlen, buf); 1941da177e4SLinus Torvalds if (res < 0) { 195167a8d52SBrian Norris if (mtd_is_eccerr(res)) { 1962ac63d90SRafał Miłecki pr_info("nand_bbt: ECC error in BBT at 0x%012llx\n", 1972ac63d90SRafał Miłecki from & ~mtd->writesize); 198167a8d52SBrian Norris return res; 199167a8d52SBrian Norris } else if (mtd_is_bitflip(res)) { 2002ac63d90SRafał Miłecki pr_info("nand_bbt: corrected error in BBT at 0x%012llx\n", 2012ac63d90SRafał Miłecki from & ~mtd->writesize); 202167a8d52SBrian Norris ret = res; 203167a8d52SBrian Norris } else { 204167a8d52SBrian Norris pr_info("nand_bbt: error reading BBT\n"); 2051da177e4SLinus Torvalds return res; 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds } 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds /* Analyse data */ 2101da177e4SLinus Torvalds for (i = 0; i < len; i++) { 2111da177e4SLinus Torvalds uint8_t dat = buf[i]; 212b4d20d60SBrian Norris for (j = 0; j < 8; j += bits, act++) { 2131da177e4SLinus Torvalds uint8_t tmp = (dat >> j) & msk; 2141da177e4SLinus Torvalds if (tmp == msk) 2151da177e4SLinus Torvalds continue; 216e0c7d767SDavid Woodhouse if (reserved_block_code && (tmp == reserved_block_code)) { 217d0370219SBrian Norris pr_info("nand_read_bbt: reserved block at 0x%012llx\n", 218b4d20d60SBrian Norris (loff_t)(offs + act) << 219b4d20d60SBrian Norris this->bbt_erase_shift); 220b4d20d60SBrian Norris bbt_mark_entry(this, offs + act, 221771c568bSBrian Norris BBT_BLOCK_RESERVED); 222f1a28c02SThomas Gleixner mtd->ecc_stats.bbtblocks++; 2231da177e4SLinus Torvalds continue; 2241da177e4SLinus Torvalds } 2258b6e50c9SBrian Norris /* 2268b6e50c9SBrian Norris * Leave it for now, if it's matured we can 227a0f5080eSBrian Norris * move this message to pr_debug. 2288b6e50c9SBrian Norris */ 229d0370219SBrian Norris pr_info("nand_read_bbt: bad block at 0x%012llx\n", 230b4d20d60SBrian Norris (loff_t)(offs + act) << 231b4d20d60SBrian Norris this->bbt_erase_shift); 2321da177e4SLinus Torvalds /* Factory marked bad or worn out? */ 2331da177e4SLinus Torvalds if (tmp == 0) 234b4d20d60SBrian Norris bbt_mark_entry(this, offs + act, 235771c568bSBrian Norris BBT_BLOCK_FACTORY_BAD); 2361da177e4SLinus Torvalds else 237b4d20d60SBrian Norris bbt_mark_entry(this, offs + act, 238771c568bSBrian Norris BBT_BLOCK_WORN); 239f1a28c02SThomas Gleixner mtd->ecc_stats.badblocks++; 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds } 2421da177e4SLinus Torvalds totlen -= len; 2431da177e4SLinus Torvalds from += len; 2441da177e4SLinus Torvalds } 245167a8d52SBrian Norris return ret; 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds /** 2491da177e4SLinus Torvalds * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page 2500813621bSBoris Brezillon * @this: NAND chip object 2511da177e4SLinus Torvalds * @buf: temporary buffer 2521da177e4SLinus Torvalds * @td: descriptor for the bad block table 253596d7452SBrian Norris * @chip: read the table for a specific chip, -1 read all chips; applies only if 2548b6e50c9SBrian Norris * NAND_BBT_PERCHIP option is set 2551da177e4SLinus Torvalds * 2568b6e50c9SBrian Norris * Read the bad block table for all chips starting at a given page. We assume 2578b6e50c9SBrian Norris * that the bbt bits are in consecutive order. 2581da177e4SLinus Torvalds */ 2590813621bSBoris Brezillon static int read_abs_bbt(struct nand_chip *this, uint8_t *buf, 2600813621bSBoris Brezillon struct nand_bbt_descr *td, int chip) 2611da177e4SLinus Torvalds { 2620813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 2636c836d51SBoris Brezillon u64 targetsize = nanddev_target_size(&this->base); 2641da177e4SLinus Torvalds int res = 0, i; 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds if (td->options & NAND_BBT_PERCHIP) { 2671da177e4SLinus Torvalds int offs = 0; 26832813e28SBoris Brezillon for (i = 0; i < nanddev_ntargets(&this->base); i++) { 2691da177e4SLinus Torvalds if (chip == -1 || chip == i) 2700813621bSBoris Brezillon res = read_bbt(this, buf, td->pages[i], 2716c836d51SBoris Brezillon targetsize >> this->bbt_erase_shift, 272df5b4e34SSebastian Andrzej Siewior td, offs); 2731da177e4SLinus Torvalds if (res) 2741da177e4SLinus Torvalds return res; 2756c836d51SBoris Brezillon offs += targetsize >> this->bbt_erase_shift; 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds } else { 2780813621bSBoris Brezillon res = read_bbt(this, buf, td->pages[0], 279df5b4e34SSebastian Andrzej Siewior mtd->size >> this->bbt_erase_shift, td, 0); 2801da177e4SLinus Torvalds if (res) 2811da177e4SLinus Torvalds return res; 2821da177e4SLinus Torvalds } 2831da177e4SLinus Torvalds return 0; 2841da177e4SLinus Torvalds } 2851da177e4SLinus Torvalds 2868b6e50c9SBrian Norris /* BBT marker is in the first page, no OOB */ 2870813621bSBoris Brezillon static int scan_read_data(struct nand_chip *this, uint8_t *buf, loff_t offs, 2887cba7b14SSebastian Andrzej Siewior struct nand_bbt_descr *td) 2897cba7b14SSebastian Andrzej Siewior { 2900813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 2917cba7b14SSebastian Andrzej Siewior size_t retlen; 2927cba7b14SSebastian Andrzej Siewior size_t len; 2937cba7b14SSebastian Andrzej Siewior 2947cba7b14SSebastian Andrzej Siewior len = td->len; 2957cba7b14SSebastian Andrzej Siewior if (td->options & NAND_BBT_VERSION) 2967cba7b14SSebastian Andrzej Siewior len++; 2977cba7b14SSebastian Andrzej Siewior 298329ad399SArtem Bityutskiy return mtd_read(mtd, offs, len, &retlen, buf); 2997cba7b14SSebastian Andrzej Siewior } 3007cba7b14SSebastian Andrzej Siewior 301a7e68834SBrian Norris /** 302af69dcd3SBrian Norris * scan_read_oob - [GENERIC] Scan data+OOB region to buffer 3030813621bSBoris Brezillon * @this: NAND chip object 304a7e68834SBrian Norris * @buf: temporary buffer 305a7e68834SBrian Norris * @offs: offset at which to scan 306a7e68834SBrian Norris * @len: length of data region to read 307a7e68834SBrian Norris * 308a7e68834SBrian Norris * Scan read data from data+OOB. May traverse multiple pages, interleaving 309a7e68834SBrian Norris * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" 310a7e68834SBrian Norris * ECC condition (error or bitflip). May quit on the first (non-ECC) error. 311a7e68834SBrian Norris */ 3120813621bSBoris Brezillon static int scan_read_oob(struct nand_chip *this, uint8_t *buf, loff_t offs, 3138593fbc6SThomas Gleixner size_t len) 3148593fbc6SThomas Gleixner { 3150813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 3168593fbc6SThomas Gleixner struct mtd_oob_ops ops; 317a7e68834SBrian Norris int res, ret = 0; 3188593fbc6SThomas Gleixner 319a7e68834SBrian Norris ops.mode = MTD_OPS_PLACE_OOB; 3208593fbc6SThomas Gleixner ops.ooboffs = 0; 3218593fbc6SThomas Gleixner ops.ooblen = mtd->oobsize; 322b64d39d8SMaxim Levitsky 323b64d39d8SMaxim Levitsky while (len > 0) { 3248593fbc6SThomas Gleixner ops.datbuf = buf; 325105513ccSBrian Norris ops.len = min(len, (size_t)mtd->writesize); 326105513ccSBrian Norris ops.oobbuf = buf + ops.len; 327903cd06cSBrian Norris 328fd2819bbSArtem Bityutskiy res = mtd_read_oob(mtd, offs, &ops); 329a7e68834SBrian Norris if (res) { 330a7e68834SBrian Norris if (!mtd_is_bitflip_or_eccerr(res)) 331b64d39d8SMaxim Levitsky return res; 332a7e68834SBrian Norris else if (mtd_is_eccerr(res) || !ret) 333a7e68834SBrian Norris ret = res; 334a7e68834SBrian Norris } 335b64d39d8SMaxim Levitsky 336b64d39d8SMaxim Levitsky buf += mtd->oobsize + mtd->writesize; 337b64d39d8SMaxim Levitsky len -= mtd->writesize; 33834a5704dSDmitry Maluka offs += mtd->writesize; 339b64d39d8SMaxim Levitsky } 340a7e68834SBrian Norris return ret; 3418593fbc6SThomas Gleixner } 3428593fbc6SThomas Gleixner 3430813621bSBoris Brezillon static int scan_read(struct nand_chip *this, uint8_t *buf, loff_t offs, 3447cba7b14SSebastian Andrzej Siewior size_t len, struct nand_bbt_descr *td) 3457cba7b14SSebastian Andrzej Siewior { 3467cba7b14SSebastian Andrzej Siewior if (td->options & NAND_BBT_NO_OOB) 3470813621bSBoris Brezillon return scan_read_data(this, buf, offs, td); 3487cba7b14SSebastian Andrzej Siewior else 3490813621bSBoris Brezillon return scan_read_oob(this, buf, offs, len); 3507cba7b14SSebastian Andrzej Siewior } 3517cba7b14SSebastian Andrzej Siewior 3528b6e50c9SBrian Norris /* Scan write data with oob to flash */ 3530813621bSBoris Brezillon static int scan_write_bbt(struct nand_chip *this, loff_t offs, size_t len, 3548593fbc6SThomas Gleixner uint8_t *buf, uint8_t *oob) 3558593fbc6SThomas Gleixner { 3560813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 3578593fbc6SThomas Gleixner struct mtd_oob_ops ops; 3588593fbc6SThomas Gleixner 3590612b9ddSBrian Norris ops.mode = MTD_OPS_PLACE_OOB; 3608593fbc6SThomas Gleixner ops.ooboffs = 0; 3618593fbc6SThomas Gleixner ops.ooblen = mtd->oobsize; 3628593fbc6SThomas Gleixner ops.datbuf = buf; 3638593fbc6SThomas Gleixner ops.oobbuf = oob; 3648593fbc6SThomas Gleixner ops.len = len; 3658593fbc6SThomas Gleixner 366a2cc5ba0SArtem Bityutskiy return mtd_write_oob(mtd, offs, &ops); 3678593fbc6SThomas Gleixner } 3688593fbc6SThomas Gleixner 3690813621bSBoris Brezillon static u32 bbt_get_ver_offs(struct nand_chip *this, struct nand_bbt_descr *td) 3707cba7b14SSebastian Andrzej Siewior { 3710813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 3727cba7b14SSebastian Andrzej Siewior u32 ver_offs = td->veroffs; 3737cba7b14SSebastian Andrzej Siewior 3747cba7b14SSebastian Andrzej Siewior if (!(td->options & NAND_BBT_NO_OOB)) 3757cba7b14SSebastian Andrzej Siewior ver_offs += mtd->writesize; 3767cba7b14SSebastian Andrzej Siewior return ver_offs; 3777cba7b14SSebastian Andrzej Siewior } 3787cba7b14SSebastian Andrzej Siewior 3791da177e4SLinus Torvalds /** 3801da177e4SLinus Torvalds * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page 3810813621bSBoris Brezillon * @this: NAND chip object 3821da177e4SLinus Torvalds * @buf: temporary buffer 3831da177e4SLinus Torvalds * @td: descriptor for the bad block table 3841da177e4SLinus Torvalds * @md: descriptor for the bad block table mirror 3851da177e4SLinus Torvalds * 3868b6e50c9SBrian Norris * Read the bad block table(s) for all chips starting at a given page. We 3878b6e50c9SBrian Norris * assume that the bbt bits are in consecutive order. 3881da177e4SLinus Torvalds */ 3890813621bSBoris Brezillon static void read_abs_bbts(struct nand_chip *this, uint8_t *buf, 3908593fbc6SThomas Gleixner struct nand_bbt_descr *td, struct nand_bbt_descr *md) 3911da177e4SLinus Torvalds { 3920813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 3931da177e4SLinus Torvalds 3941da177e4SLinus Torvalds /* Read the primary version, if available */ 3951da177e4SLinus Torvalds if (td->options & NAND_BBT_VERSION) { 3960813621bSBoris Brezillon scan_read(this, buf, (loff_t)td->pages[0] << this->page_shift, 3977cba7b14SSebastian Andrzej Siewior mtd->writesize, td); 3980813621bSBoris Brezillon td->version[0] = buf[bbt_get_ver_offs(this, td)]; 3999a4d4d69SBrian Norris pr_info("Bad block table at page %d, version 0x%02X\n", 4008593fbc6SThomas Gleixner td->pages[0], td->version[0]); 4011da177e4SLinus Torvalds } 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds /* Read the mirror version, if available */ 4041da177e4SLinus Torvalds if (md && (md->options & NAND_BBT_VERSION)) { 4050813621bSBoris Brezillon scan_read(this, buf, (loff_t)md->pages[0] << this->page_shift, 4067bb9c754SShmulik Ladkani mtd->writesize, md); 4070813621bSBoris Brezillon md->version[0] = buf[bbt_get_ver_offs(this, md)]; 4089a4d4d69SBrian Norris pr_info("Bad block table at page %d, version 0x%02X\n", 4098593fbc6SThomas Gleixner md->pages[0], md->version[0]); 4108593fbc6SThomas Gleixner } 4111da177e4SLinus Torvalds } 4121da177e4SLinus Torvalds 4138b6e50c9SBrian Norris /* Scan a given block partially */ 4140813621bSBoris Brezillon static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd, 415f90da781SFrieder Schrempf loff_t offs, uint8_t *buf) 4168593fbc6SThomas Gleixner { 4170813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 418f90da781SFrieder Schrempf 4198593fbc6SThomas Gleixner struct mtd_oob_ops ops; 420f90da781SFrieder Schrempf int ret, page_offset; 4218593fbc6SThomas Gleixner 4228593fbc6SThomas Gleixner ops.ooblen = mtd->oobsize; 4238593fbc6SThomas Gleixner ops.oobbuf = buf; 4248593fbc6SThomas Gleixner ops.ooboffs = 0; 4258593fbc6SThomas Gleixner ops.datbuf = NULL; 4260612b9ddSBrian Norris ops.mode = MTD_OPS_PLACE_OOB; 4278593fbc6SThomas Gleixner 428f90da781SFrieder Schrempf page_offset = nand_bbm_get_next_page(this, 0); 429f90da781SFrieder Schrempf 430f90da781SFrieder Schrempf while (page_offset >= 0) { 4318593fbc6SThomas Gleixner /* 4328b6e50c9SBrian Norris * Read the full oob until read_oob is fixed to handle single 4338b6e50c9SBrian Norris * byte reads for 16 bit buswidth. 4348593fbc6SThomas Gleixner */ 435f90da781SFrieder Schrempf ret = mtd_read_oob(mtd, offs + (page_offset * mtd->writesize), 436f90da781SFrieder Schrempf &ops); 437903cd06cSBrian Norris /* Ignore ECC errors when checking for BBM */ 438d57f4054SBrian Norris if (ret && !mtd_is_bitflip_or_eccerr(ret)) 4398593fbc6SThomas Gleixner return ret; 4408593fbc6SThomas Gleixner 4418593fbc6SThomas Gleixner if (check_short_pattern(buf, bd)) 4428593fbc6SThomas Gleixner return 1; 4438593fbc6SThomas Gleixner 444f90da781SFrieder Schrempf page_offset = nand_bbm_get_next_page(this, page_offset + 1); 4458593fbc6SThomas Gleixner } 446f90da781SFrieder Schrempf 4478593fbc6SThomas Gleixner return 0; 4488593fbc6SThomas Gleixner } 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds /** 4511da177e4SLinus Torvalds * create_bbt - [GENERIC] Create a bad block table by scanning the device 4520813621bSBoris Brezillon * @this: NAND chip object 4531da177e4SLinus Torvalds * @buf: temporary buffer 4541da177e4SLinus Torvalds * @bd: descriptor for the good/bad block search pattern 4558b6e50c9SBrian Norris * @chip: create the table for a specific chip, -1 read all chips; applies only 4568b6e50c9SBrian Norris * if NAND_BBT_PERCHIP option is set 4571da177e4SLinus Torvalds * 4588b6e50c9SBrian Norris * Create a bad block table by scanning the device for the given good/bad block 4598b6e50c9SBrian Norris * identify pattern. 4601da177e4SLinus Torvalds */ 4610813621bSBoris Brezillon static int create_bbt(struct nand_chip *this, uint8_t *buf, 4628593fbc6SThomas Gleixner struct nand_bbt_descr *bd, int chip) 4631da177e4SLinus Torvalds { 4646c836d51SBoris Brezillon u64 targetsize = nanddev_target_size(&this->base); 4650813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 466f90da781SFrieder Schrempf int i, numblocks, startblock; 4671da177e4SLinus Torvalds loff_t from; 4681da177e4SLinus Torvalds 4699a4d4d69SBrian Norris pr_info("Scanning device for bad blocks\n"); 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds if (chip == -1) { 472b4d20d60SBrian Norris numblocks = mtd->size >> this->bbt_erase_shift; 4731da177e4SLinus Torvalds startblock = 0; 4741da177e4SLinus Torvalds from = 0; 4751da177e4SLinus Torvalds } else { 47632813e28SBoris Brezillon if (chip >= nanddev_ntargets(&this->base)) { 4779a4d4d69SBrian Norris pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n", 47832813e28SBoris Brezillon chip + 1, nanddev_ntargets(&this->base)); 479eeada24dSArtem B. Bityuckiy return -EINVAL; 4801da177e4SLinus Torvalds } 4816c836d51SBoris Brezillon numblocks = targetsize >> this->bbt_erase_shift; 4821da177e4SLinus Torvalds startblock = chip * numblocks; 4831da177e4SLinus Torvalds numblocks += startblock; 484b4d20d60SBrian Norris from = (loff_t)startblock << this->bbt_erase_shift; 4851da177e4SLinus Torvalds } 4861da177e4SLinus Torvalds 487b4d20d60SBrian Norris for (i = startblock; i < numblocks; i++) { 488eeada24dSArtem B. Bityuckiy int ret; 489eeada24dSArtem B. Bityuckiy 4907cba7b14SSebastian Andrzej Siewior BUG_ON(bd->options & NAND_BBT_NO_OOB); 4917cba7b14SSebastian Andrzej Siewior 492f90da781SFrieder Schrempf ret = scan_block_fast(this, bd, from, buf); 4938593fbc6SThomas Gleixner if (ret < 0) 494eeada24dSArtem B. Bityuckiy return ret; 495171650afSArtem B. Bityuckiy 4968593fbc6SThomas Gleixner if (ret) { 497b4d20d60SBrian Norris bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); 4989a4d4d69SBrian Norris pr_warn("Bad eraseblock %d at 0x%012llx\n", 499b4d20d60SBrian Norris i, (unsigned long long)from); 500f1a28c02SThomas Gleixner mtd->ecc_stats.badblocks++; 501171650afSArtem B. Bityuckiy } 5028593fbc6SThomas Gleixner 5031da177e4SLinus Torvalds from += (1 << this->bbt_erase_shift); 5041da177e4SLinus Torvalds } 505eeada24dSArtem B. Bityuckiy return 0; 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds 5081da177e4SLinus Torvalds /** 5091da177e4SLinus Torvalds * search_bbt - [GENERIC] scan the device for a specific bad block table 5100813621bSBoris Brezillon * @this: NAND chip object 5111da177e4SLinus Torvalds * @buf: temporary buffer 5121da177e4SLinus Torvalds * @td: descriptor for the bad block table 5131da177e4SLinus Torvalds * 5148b6e50c9SBrian Norris * Read the bad block table by searching for a given ident pattern. Search is 5158b6e50c9SBrian Norris * preformed either from the beginning up or from the end of the device 5168b6e50c9SBrian Norris * downwards. The search starts always at the start of a block. If the option 5178b6e50c9SBrian Norris * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains 5188b6e50c9SBrian Norris * the bad block information of this chip. This is necessary to provide support 5198b6e50c9SBrian Norris * for certain DOC devices. 5201da177e4SLinus Torvalds * 5218b6e50c9SBrian Norris * The bbt ident pattern resides in the oob area of the first page in a block. 5221da177e4SLinus Torvalds */ 5230813621bSBoris Brezillon static int search_bbt(struct nand_chip *this, uint8_t *buf, 5240813621bSBoris Brezillon struct nand_bbt_descr *td) 5251da177e4SLinus Torvalds { 5266c836d51SBoris Brezillon u64 targetsize = nanddev_target_size(&this->base); 5270813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 5281da177e4SLinus Torvalds int i, chips; 529930de537SBrian Norris int startblock, block, dir; 53028318776SJoern Engel int scanlen = mtd->writesize + mtd->oobsize; 5311da177e4SLinus Torvalds int bbtblocks; 5328593fbc6SThomas Gleixner int blocktopage = this->bbt_erase_shift - this->page_shift; 5331da177e4SLinus Torvalds 5341da177e4SLinus Torvalds /* Search direction top -> down? */ 5351da177e4SLinus Torvalds if (td->options & NAND_BBT_LASTBLOCK) { 5361da177e4SLinus Torvalds startblock = (mtd->size >> this->bbt_erase_shift) - 1; 5371da177e4SLinus Torvalds dir = -1; 5381da177e4SLinus Torvalds } else { 5391da177e4SLinus Torvalds startblock = 0; 5401da177e4SLinus Torvalds dir = 1; 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds 5431da177e4SLinus Torvalds /* Do we have a bbt per chip? */ 5441da177e4SLinus Torvalds if (td->options & NAND_BBT_PERCHIP) { 54532813e28SBoris Brezillon chips = nanddev_ntargets(&this->base); 5466c836d51SBoris Brezillon bbtblocks = targetsize >> this->bbt_erase_shift; 5471da177e4SLinus Torvalds startblock &= bbtblocks - 1; 5481da177e4SLinus Torvalds } else { 5491da177e4SLinus Torvalds chips = 1; 5501da177e4SLinus Torvalds bbtblocks = mtd->size >> this->bbt_erase_shift; 5511da177e4SLinus Torvalds } 5521da177e4SLinus Torvalds 5531da177e4SLinus Torvalds for (i = 0; i < chips; i++) { 5541da177e4SLinus Torvalds /* Reset version information */ 5551da177e4SLinus Torvalds td->version[i] = 0; 5561da177e4SLinus Torvalds td->pages[i] = -1; 5571da177e4SLinus Torvalds /* Scan the maximum number of blocks */ 5581da177e4SLinus Torvalds for (block = 0; block < td->maxblocks; block++) { 5598593fbc6SThomas Gleixner 5601da177e4SLinus Torvalds int actblock = startblock + dir * block; 56169423d99SAdrian Hunter loff_t offs = (loff_t)actblock << this->bbt_erase_shift; 5628593fbc6SThomas Gleixner 5631da177e4SLinus Torvalds /* Read first page */ 5640813621bSBoris Brezillon scan_read(this, buf, offs, mtd->writesize, td); 56528318776SJoern Engel if (!check_pattern(buf, scanlen, mtd->writesize, td)) { 5668593fbc6SThomas Gleixner td->pages[i] = actblock << blocktopage; 5671da177e4SLinus Torvalds if (td->options & NAND_BBT_VERSION) { 5680813621bSBoris Brezillon offs = bbt_get_ver_offs(this, td); 5697cba7b14SSebastian Andrzej Siewior td->version[i] = buf[offs]; 5701da177e4SLinus Torvalds } 5711da177e4SLinus Torvalds break; 5721da177e4SLinus Torvalds } 5731da177e4SLinus Torvalds } 5746c836d51SBoris Brezillon startblock += targetsize >> this->bbt_erase_shift; 5751da177e4SLinus Torvalds } 5761da177e4SLinus Torvalds /* Check, if we found a bbt for each requested chip */ 5771da177e4SLinus Torvalds for (i = 0; i < chips; i++) { 5781da177e4SLinus Torvalds if (td->pages[i] == -1) 5799a4d4d69SBrian Norris pr_warn("Bad block table not found for chip %d\n", i); 5801da177e4SLinus Torvalds else 5812ac63d90SRafał Miłecki pr_info("Bad block table found at page %d, version 0x%02X\n", 5822ac63d90SRafał Miłecki td->pages[i], td->version[i]); 5831da177e4SLinus Torvalds } 5841da177e4SLinus Torvalds return 0; 5851da177e4SLinus Torvalds } 5861da177e4SLinus Torvalds 5871da177e4SLinus Torvalds /** 5881da177e4SLinus Torvalds * search_read_bbts - [GENERIC] scan the device for bad block table(s) 5890813621bSBoris Brezillon * @this: NAND chip object 5901da177e4SLinus Torvalds * @buf: temporary buffer 5911da177e4SLinus Torvalds * @td: descriptor for the bad block table 5921da177e4SLinus Torvalds * @md: descriptor for the bad block table mirror 5931da177e4SLinus Torvalds * 5948b6e50c9SBrian Norris * Search and read the bad block table(s). 5951da177e4SLinus Torvalds */ 5960813621bSBoris Brezillon static void search_read_bbts(struct nand_chip *this, uint8_t *buf, 5977b5a2d40SBrian Norris struct nand_bbt_descr *td, 5987b5a2d40SBrian Norris struct nand_bbt_descr *md) 5991da177e4SLinus Torvalds { 6001da177e4SLinus Torvalds /* Search the primary table */ 6010813621bSBoris Brezillon search_bbt(this, buf, td); 6021da177e4SLinus Torvalds 6031da177e4SLinus Torvalds /* Search the mirror table */ 6041da177e4SLinus Torvalds if (md) 6050813621bSBoris Brezillon search_bbt(this, buf, md); 6061da177e4SLinus Torvalds } 6071da177e4SLinus Torvalds 6081da177e4SLinus Torvalds /** 609c3baf278SBoris Brezillon * get_bbt_block - Get the first valid eraseblock suitable to store a BBT 610c3baf278SBoris Brezillon * @this: the NAND device 611c3baf278SBoris Brezillon * @td: the BBT description 612c3baf278SBoris Brezillon * @md: the mirror BBT descriptor 613c3baf278SBoris Brezillon * @chip: the CHIP selector 614c3baf278SBoris Brezillon * 615c3baf278SBoris Brezillon * This functions returns a positive block number pointing a valid eraseblock 616c3baf278SBoris Brezillon * suitable to store a BBT (i.e. in the range reserved for BBT), or -ENOSPC if 617c3baf278SBoris Brezillon * all blocks are already used of marked bad. If td->pages[chip] was already 618c3baf278SBoris Brezillon * pointing to a valid block we re-use it, otherwise we search for the next 619c3baf278SBoris Brezillon * valid one. 620c3baf278SBoris Brezillon */ 621c3baf278SBoris Brezillon static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td, 622c3baf278SBoris Brezillon struct nand_bbt_descr *md, int chip) 623c3baf278SBoris Brezillon { 6246c836d51SBoris Brezillon u64 targetsize = nanddev_target_size(&this->base); 625c3baf278SBoris Brezillon int startblock, dir, page, numblocks, i; 626c3baf278SBoris Brezillon 627c3baf278SBoris Brezillon /* 628c3baf278SBoris Brezillon * There was already a version of the table, reuse the page. This 629c3baf278SBoris Brezillon * applies for absolute placement too, as we have the page number in 630c3baf278SBoris Brezillon * td->pages. 631c3baf278SBoris Brezillon */ 632c3baf278SBoris Brezillon if (td->pages[chip] != -1) 633c3baf278SBoris Brezillon return td->pages[chip] >> 634c3baf278SBoris Brezillon (this->bbt_erase_shift - this->page_shift); 635c3baf278SBoris Brezillon 6366c836d51SBoris Brezillon numblocks = (int)(targetsize >> this->bbt_erase_shift); 637c3baf278SBoris Brezillon if (!(td->options & NAND_BBT_PERCHIP)) 63832813e28SBoris Brezillon numblocks *= nanddev_ntargets(&this->base); 639c3baf278SBoris Brezillon 640c3baf278SBoris Brezillon /* 641c3baf278SBoris Brezillon * Automatic placement of the bad block table. Search direction 642c3baf278SBoris Brezillon * top -> down? 643c3baf278SBoris Brezillon */ 644c3baf278SBoris Brezillon if (td->options & NAND_BBT_LASTBLOCK) { 645c3baf278SBoris Brezillon startblock = numblocks * (chip + 1) - 1; 646c3baf278SBoris Brezillon dir = -1; 647c3baf278SBoris Brezillon } else { 648c3baf278SBoris Brezillon startblock = chip * numblocks; 649c3baf278SBoris Brezillon dir = 1; 650c3baf278SBoris Brezillon } 651c3baf278SBoris Brezillon 652c3baf278SBoris Brezillon for (i = 0; i < td->maxblocks; i++) { 653c3baf278SBoris Brezillon int block = startblock + dir * i; 654c3baf278SBoris Brezillon 655c3baf278SBoris Brezillon /* Check, if the block is bad */ 656c3baf278SBoris Brezillon switch (bbt_get_entry(this, block)) { 657c3baf278SBoris Brezillon case BBT_BLOCK_WORN: 658c3baf278SBoris Brezillon case BBT_BLOCK_FACTORY_BAD: 659c3baf278SBoris Brezillon continue; 660c3baf278SBoris Brezillon } 661c3baf278SBoris Brezillon 662c3baf278SBoris Brezillon page = block << (this->bbt_erase_shift - this->page_shift); 663c3baf278SBoris Brezillon 664c3baf278SBoris Brezillon /* Check, if the block is used by the mirror table */ 665c3baf278SBoris Brezillon if (!md || md->pages[chip] != page) 666c3baf278SBoris Brezillon return block; 667c3baf278SBoris Brezillon } 668c3baf278SBoris Brezillon 669c3baf278SBoris Brezillon return -ENOSPC; 670c3baf278SBoris Brezillon } 671c3baf278SBoris Brezillon 672c3baf278SBoris Brezillon /** 67310ffd570SKyle Roeschley * mark_bbt_block_bad - Mark one of the block reserved for BBT bad 67410ffd570SKyle Roeschley * @this: the NAND device 67510ffd570SKyle Roeschley * @td: the BBT description 67610ffd570SKyle Roeschley * @chip: the CHIP selector 67710ffd570SKyle Roeschley * @block: the BBT block to mark 67810ffd570SKyle Roeschley * 67910ffd570SKyle Roeschley * Blocks reserved for BBT can become bad. This functions is an helper to mark 68010ffd570SKyle Roeschley * such blocks as bad. It takes care of updating the in-memory BBT, marking the 68110ffd570SKyle Roeschley * block as bad using a bad block marker and invalidating the associated 68210ffd570SKyle Roeschley * td->pages[] entry. 68310ffd570SKyle Roeschley */ 68410ffd570SKyle Roeschley static void mark_bbt_block_bad(struct nand_chip *this, 68510ffd570SKyle Roeschley struct nand_bbt_descr *td, 68610ffd570SKyle Roeschley int chip, int block) 68710ffd570SKyle Roeschley { 68810ffd570SKyle Roeschley loff_t to; 68910ffd570SKyle Roeschley int res; 69010ffd570SKyle Roeschley 69110ffd570SKyle Roeschley bbt_mark_entry(this, block, BBT_BLOCK_WORN); 69210ffd570SKyle Roeschley 69310ffd570SKyle Roeschley to = (loff_t)block << this->bbt_erase_shift; 694cdc784c7SBoris Brezillon res = nand_markbad_bbm(this, to); 69510ffd570SKyle Roeschley if (res) 69610ffd570SKyle Roeschley pr_warn("nand_bbt: error %d while marking block %d bad\n", 69710ffd570SKyle Roeschley res, block); 69810ffd570SKyle Roeschley 69910ffd570SKyle Roeschley td->pages[chip] = -1; 70010ffd570SKyle Roeschley } 70110ffd570SKyle Roeschley 70210ffd570SKyle Roeschley /** 7031da177e4SLinus Torvalds * write_bbt - [GENERIC] (Re)write the bad block table 7040813621bSBoris Brezillon * @this: NAND chip object 7051da177e4SLinus Torvalds * @buf: temporary buffer 7061da177e4SLinus Torvalds * @td: descriptor for the bad block table 7071da177e4SLinus Torvalds * @md: descriptor for the bad block table mirror 7081da177e4SLinus Torvalds * @chipsel: selector for a specific chip, -1 for all 7091da177e4SLinus Torvalds * 7108b6e50c9SBrian Norris * (Re)write the bad block table. 7111da177e4SLinus Torvalds */ 7120813621bSBoris Brezillon static int write_bbt(struct nand_chip *this, uint8_t *buf, 7139223a456SThomas Gleixner struct nand_bbt_descr *td, struct nand_bbt_descr *md, 7149223a456SThomas Gleixner int chipsel) 7151da177e4SLinus Torvalds { 7166c836d51SBoris Brezillon u64 targetsize = nanddev_target_size(&this->base); 7170813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 7181da177e4SLinus Torvalds struct erase_info einfo; 719b4d20d60SBrian Norris int i, res, chip = 0; 720c3baf278SBoris Brezillon int bits, page, offs, numblocks, sft, sftmsk; 721b4d20d60SBrian Norris int nrchips, pageoffs, ooboffs; 7221da177e4SLinus Torvalds uint8_t msk[4]; 7231da177e4SLinus Torvalds uint8_t rcode = td->reserved_block_code; 7248593fbc6SThomas Gleixner size_t retlen, len = 0; 7251da177e4SLinus Torvalds loff_t to; 7268593fbc6SThomas Gleixner struct mtd_oob_ops ops; 7278593fbc6SThomas Gleixner 7288593fbc6SThomas Gleixner ops.ooblen = mtd->oobsize; 7298593fbc6SThomas Gleixner ops.ooboffs = 0; 7308593fbc6SThomas Gleixner ops.datbuf = NULL; 7310612b9ddSBrian Norris ops.mode = MTD_OPS_PLACE_OOB; 7321da177e4SLinus Torvalds 7331da177e4SLinus Torvalds if (!rcode) 7341da177e4SLinus Torvalds rcode = 0xff; 7351da177e4SLinus Torvalds /* Write bad block table per chip rather than per device? */ 7361da177e4SLinus Torvalds if (td->options & NAND_BBT_PERCHIP) { 7376c836d51SBoris Brezillon numblocks = (int)(targetsize >> this->bbt_erase_shift); 7381da177e4SLinus Torvalds /* Full device write or specific chip? */ 7391da177e4SLinus Torvalds if (chipsel == -1) { 74032813e28SBoris Brezillon nrchips = nanddev_ntargets(&this->base); 7411da177e4SLinus Torvalds } else { 7421da177e4SLinus Torvalds nrchips = chipsel + 1; 7431da177e4SLinus Torvalds chip = chipsel; 7441da177e4SLinus Torvalds } 7451da177e4SLinus Torvalds } else { 7461da177e4SLinus Torvalds numblocks = (int)(mtd->size >> this->bbt_erase_shift); 7471da177e4SLinus Torvalds nrchips = 1; 7481da177e4SLinus Torvalds } 7491da177e4SLinus Torvalds 7501da177e4SLinus Torvalds /* Loop through the chips */ 75110ffd570SKyle Roeschley while (chip < nrchips) { 752c3baf278SBoris Brezillon int block; 7531da177e4SLinus Torvalds 754c3baf278SBoris Brezillon block = get_bbt_block(this, td, md, chip); 755c3baf278SBoris Brezillon if (block < 0) { 7569a4d4d69SBrian Norris pr_err("No space left to write bad block table\n"); 757c3baf278SBoris Brezillon res = block; 758c3baf278SBoris Brezillon goto outerr; 759c3baf278SBoris Brezillon } 760c3baf278SBoris Brezillon 761c3baf278SBoris Brezillon /* 762c3baf278SBoris Brezillon * get_bbt_block() returns a block number, shift the value to 763c3baf278SBoris Brezillon * get a page number. 764c3baf278SBoris Brezillon */ 765c3baf278SBoris Brezillon page = block << (this->bbt_erase_shift - this->page_shift); 7661da177e4SLinus Torvalds 7671da177e4SLinus Torvalds /* Set up shift count and masks for the flash table */ 7681da177e4SLinus Torvalds bits = td->options & NAND_BBT_NRBITS_MSK; 7699223a456SThomas Gleixner msk[2] = ~rcode; 7701da177e4SLinus Torvalds switch (bits) { 7719223a456SThomas Gleixner case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; 7729223a456SThomas Gleixner msk[3] = 0x01; 7739223a456SThomas Gleixner break; 7749223a456SThomas Gleixner case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; 7759223a456SThomas Gleixner msk[3] = 0x03; 7769223a456SThomas Gleixner break; 7779223a456SThomas Gleixner case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; 7789223a456SThomas Gleixner msk[3] = 0x0f; 7799223a456SThomas Gleixner break; 7809223a456SThomas Gleixner case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; 7819223a456SThomas Gleixner msk[3] = 0xff; 7829223a456SThomas Gleixner break; 7831da177e4SLinus Torvalds default: return -EINVAL; 7841da177e4SLinus Torvalds } 7851da177e4SLinus Torvalds 7861da177e4SLinus Torvalds to = ((loff_t)page) << this->page_shift; 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds /* Must we save the block contents? */ 7891da177e4SLinus Torvalds if (td->options & NAND_BBT_SAVECONTENT) { 7901da177e4SLinus Torvalds /* Make it block aligned */ 791f5cd2ae1SBrian Norris to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1); 7921da177e4SLinus Torvalds len = 1 << this->bbt_erase_shift; 793329ad399SArtem Bityutskiy res = mtd_read(mtd, to, len, &retlen, buf); 7941da177e4SLinus Torvalds if (res < 0) { 7951da177e4SLinus Torvalds if (retlen != len) { 7962ac63d90SRafał Miłecki pr_info("nand_bbt: error reading block for writing the bad block table\n"); 7971da177e4SLinus Torvalds return res; 7981da177e4SLinus Torvalds } 7992ac63d90SRafał Miłecki pr_warn("nand_bbt: ECC error while reading block for writing bad block table\n"); 8001da177e4SLinus Torvalds } 8019223a456SThomas Gleixner /* Read oob data */ 8027014568bSVitaly Wool ops.ooblen = (len >> this->page_shift) * mtd->oobsize; 8038593fbc6SThomas Gleixner ops.oobbuf = &buf[len]; 804fd2819bbSArtem Bityutskiy res = mtd_read_oob(mtd, to + mtd->writesize, &ops); 8057014568bSVitaly Wool if (res < 0 || ops.oobretlen != ops.ooblen) 8069223a456SThomas Gleixner goto outerr; 8079223a456SThomas Gleixner 8081da177e4SLinus Torvalds /* Calc the byte offset in the buffer */ 8091da177e4SLinus Torvalds pageoffs = page - (int)(to >> this->page_shift); 8101da177e4SLinus Torvalds offs = pageoffs << this->page_shift; 8111da177e4SLinus Torvalds /* Preset the bbt area with 0xff */ 8121da177e4SLinus Torvalds memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); 8139223a456SThomas Gleixner ooboffs = len + (pageoffs * mtd->oobsize); 8149223a456SThomas Gleixner 8157cba7b14SSebastian Andrzej Siewior } else if (td->options & NAND_BBT_NO_OOB) { 8167cba7b14SSebastian Andrzej Siewior ooboffs = 0; 8177cba7b14SSebastian Andrzej Siewior offs = td->len; 8188b6e50c9SBrian Norris /* The version byte */ 8197cba7b14SSebastian Andrzej Siewior if (td->options & NAND_BBT_VERSION) 8207cba7b14SSebastian Andrzej Siewior offs++; 8217cba7b14SSebastian Andrzej Siewior /* Calc length */ 8227cba7b14SSebastian Andrzej Siewior len = (size_t)(numblocks >> sft); 8237cba7b14SSebastian Andrzej Siewior len += offs; 8247cba7b14SSebastian Andrzej Siewior /* Make it page aligned! */ 8257cba7b14SSebastian Andrzej Siewior len = ALIGN(len, mtd->writesize); 8267cba7b14SSebastian Andrzej Siewior /* Preset the buffer with 0xff */ 8277cba7b14SSebastian Andrzej Siewior memset(buf, 0xff, len); 8287cba7b14SSebastian Andrzej Siewior /* Pattern is located at the begin of first page */ 8297cba7b14SSebastian Andrzej Siewior memcpy(buf, td->pattern, td->len); 8301da177e4SLinus Torvalds } else { 8311da177e4SLinus Torvalds /* Calc length */ 8321da177e4SLinus Torvalds len = (size_t)(numblocks >> sft); 8331da177e4SLinus Torvalds /* Make it page aligned! */ 834cda32091SSebastian Andrzej Siewior len = ALIGN(len, mtd->writesize); 8351da177e4SLinus Torvalds /* Preset the buffer with 0xff */ 8369223a456SThomas Gleixner memset(buf, 0xff, len + 8379223a456SThomas Gleixner (len >> this->page_shift)* mtd->oobsize); 8381da177e4SLinus Torvalds offs = 0; 8399223a456SThomas Gleixner ooboffs = len; 8401da177e4SLinus Torvalds /* Pattern is located in oob area of first page */ 8419223a456SThomas Gleixner memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); 8421da177e4SLinus Torvalds } 8439223a456SThomas Gleixner 8449223a456SThomas Gleixner if (td->options & NAND_BBT_VERSION) 8459223a456SThomas Gleixner buf[ooboffs + td->veroffs] = td->version[chip]; 8461da177e4SLinus Torvalds 8478b6e50c9SBrian Norris /* Walk through the memory table */ 848b4d20d60SBrian Norris for (i = 0; i < numblocks; i++) { 8491da177e4SLinus Torvalds uint8_t dat; 8501da177e4SLinus Torvalds int sftcnt = (i << (3 - sft)) & sftmsk; 851b4d20d60SBrian Norris dat = bbt_get_entry(this, chip * numblocks + i); 8521da177e4SLinus Torvalds /* Do not store the reserved bbt blocks! */ 853b4d20d60SBrian Norris buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); 8541da177e4SLinus Torvalds } 8551da177e4SLinus Torvalds 8561da177e4SLinus Torvalds memset(&einfo, 0, sizeof(einfo)); 85769423d99SAdrian Hunter einfo.addr = to; 8581da177e4SLinus Torvalds einfo.len = 1 << this->bbt_erase_shift; 859e4cdf9cbSBoris Brezillon res = nand_erase_nand(this, &einfo, 1); 86010ffd570SKyle Roeschley if (res < 0) { 86110ffd570SKyle Roeschley pr_warn("nand_bbt: error while erasing BBT block %d\n", 86210ffd570SKyle Roeschley res); 86310ffd570SKyle Roeschley mark_bbt_block_bad(this, td, chip, block); 86410ffd570SKyle Roeschley continue; 86510ffd570SKyle Roeschley } 8661da177e4SLinus Torvalds 8670813621bSBoris Brezillon res = scan_write_bbt(this, to, len, buf, 8680813621bSBoris Brezillon td->options & NAND_BBT_NO_OOB ? 8690813621bSBoris Brezillon NULL : &buf[len]); 87010ffd570SKyle Roeschley if (res < 0) { 87110ffd570SKyle Roeschley pr_warn("nand_bbt: error while writing BBT block %d\n", 87210ffd570SKyle Roeschley res); 87310ffd570SKyle Roeschley mark_bbt_block_bad(this, td, chip, block); 87410ffd570SKyle Roeschley continue; 87510ffd570SKyle Roeschley } 8769223a456SThomas Gleixner 877d0370219SBrian Norris pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", 878d0370219SBrian Norris (unsigned long long)to, td->version[chip]); 8791da177e4SLinus Torvalds 8801da177e4SLinus Torvalds /* Mark it as used */ 88110ffd570SKyle Roeschley td->pages[chip++] = page; 8821da177e4SLinus Torvalds } 8831da177e4SLinus Torvalds return 0; 8849223a456SThomas Gleixner 8859223a456SThomas Gleixner outerr: 886d0370219SBrian Norris pr_warn("nand_bbt: error while writing bad block table %d\n", res); 8879223a456SThomas Gleixner return res; 8881da177e4SLinus Torvalds } 8891da177e4SLinus Torvalds 8901da177e4SLinus Torvalds /** 8911da177e4SLinus Torvalds * nand_memory_bbt - [GENERIC] create a memory based bad block table 8920813621bSBoris Brezillon * @this: NAND chip object 8931da177e4SLinus Torvalds * @bd: descriptor for the good/bad block search pattern 8941da177e4SLinus Torvalds * 8958b6e50c9SBrian Norris * The function creates a memory based bbt by scanning the device for 8968b6e50c9SBrian Norris * manufacturer / software marked good / bad blocks. 8971da177e4SLinus Torvalds */ 8980813621bSBoris Brezillon static inline int nand_memory_bbt(struct nand_chip *this, 8990813621bSBoris Brezillon struct nand_bbt_descr *bd) 9001da177e4SLinus Torvalds { 901eeab7174SBoris Brezillon u8 *pagebuf = nand_get_data_buf(this); 902eeab7174SBoris Brezillon 903eeab7174SBoris Brezillon return create_bbt(this, pagebuf, bd, -1); 9041da177e4SLinus Torvalds } 9051da177e4SLinus Torvalds 9061da177e4SLinus Torvalds /** 907e0c7d767SDavid Woodhouse * check_create - [GENERIC] create and write bbt(s) if necessary 9080813621bSBoris Brezillon * @this: the NAND device 9091da177e4SLinus Torvalds * @buf: temporary buffer 9101da177e4SLinus Torvalds * @bd: descriptor for the good/bad block search pattern 9111da177e4SLinus Torvalds * 9128b6e50c9SBrian Norris * The function checks the results of the previous call to read_bbt and creates 9138b6e50c9SBrian Norris * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found 9148b6e50c9SBrian Norris * for the chip/device. Update is necessary if one of the tables is missing or 9158b6e50c9SBrian Norris * the version nr. of one table is less than the other. 9161da177e4SLinus Torvalds */ 9170813621bSBoris Brezillon static int check_create(struct nand_chip *this, uint8_t *buf, 9180813621bSBoris Brezillon struct nand_bbt_descr *bd) 9191da177e4SLinus Torvalds { 920623978deSBrian Norris int i, chips, writeops, create, chipsel, res, res2; 9211da177e4SLinus Torvalds struct nand_bbt_descr *td = this->bbt_td; 9221da177e4SLinus Torvalds struct nand_bbt_descr *md = this->bbt_md; 9231da177e4SLinus Torvalds struct nand_bbt_descr *rd, *rd2; 9241da177e4SLinus Torvalds 9251da177e4SLinus Torvalds /* Do we have a bbt per chip? */ 9261da177e4SLinus Torvalds if (td->options & NAND_BBT_PERCHIP) 92732813e28SBoris Brezillon chips = nanddev_ntargets(&this->base); 9281da177e4SLinus Torvalds else 9291da177e4SLinus Torvalds chips = 1; 9301da177e4SLinus Torvalds 9311da177e4SLinus Torvalds for (i = 0; i < chips; i++) { 9321da177e4SLinus Torvalds writeops = 0; 933b61bf5bbSBrian Norris create = 0; 9341da177e4SLinus Torvalds rd = NULL; 9351da177e4SLinus Torvalds rd2 = NULL; 936623978deSBrian Norris res = res2 = 0; 9371da177e4SLinus Torvalds /* Per chip or per device? */ 9381da177e4SLinus Torvalds chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; 93925985edcSLucas De Marchi /* Mirrored table available? */ 9401da177e4SLinus Torvalds if (md) { 9411da177e4SLinus Torvalds if (td->pages[i] == -1 && md->pages[i] == -1) { 942b61bf5bbSBrian Norris create = 1; 9431da177e4SLinus Torvalds writeops = 0x03; 944c5e8ef9cSBrian Norris } else if (td->pages[i] == -1) { 9451da177e4SLinus Torvalds rd = md; 946596d7452SBrian Norris writeops = 0x01; 947c5e8ef9cSBrian Norris } else if (md->pages[i] == -1) { 9481da177e4SLinus Torvalds rd = td; 949596d7452SBrian Norris writeops = 0x02; 950c5e8ef9cSBrian Norris } else if (td->version[i] == md->version[i]) { 9511da177e4SLinus Torvalds rd = td; 9521da177e4SLinus Torvalds if (!(td->options & NAND_BBT_VERSION)) 9531da177e4SLinus Torvalds rd2 = md; 954c5e8ef9cSBrian Norris } else if (((int8_t)(td->version[i] - md->version[i])) > 0) { 9551da177e4SLinus Torvalds rd = td; 956596d7452SBrian Norris writeops = 0x02; 9571da177e4SLinus Torvalds } else { 9581da177e4SLinus Torvalds rd = md; 959596d7452SBrian Norris writeops = 0x01; 9601da177e4SLinus Torvalds } 9611da177e4SLinus Torvalds } else { 9621da177e4SLinus Torvalds if (td->pages[i] == -1) { 963b61bf5bbSBrian Norris create = 1; 9641da177e4SLinus Torvalds writeops = 0x01; 965b61bf5bbSBrian Norris } else { 9661da177e4SLinus Torvalds rd = td; 9671da177e4SLinus Torvalds } 968b61bf5bbSBrian Norris } 969b61bf5bbSBrian Norris 970b61bf5bbSBrian Norris if (create) { 9711da177e4SLinus Torvalds /* Create the bad block table by scanning the device? */ 9721da177e4SLinus Torvalds if (!(td->options & NAND_BBT_CREATE)) 9731da177e4SLinus Torvalds continue; 9741da177e4SLinus Torvalds 9751da177e4SLinus Torvalds /* Create the table in memory by scanning the chip(s) */ 97653d5d888SBrian Norris if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) 9770813621bSBoris Brezillon create_bbt(this, buf, bd, chipsel); 9781da177e4SLinus Torvalds 9791da177e4SLinus Torvalds td->version[i] = 1; 9801da177e4SLinus Torvalds if (md) 9811da177e4SLinus Torvalds md->version[i] = 1; 982b61bf5bbSBrian Norris } 983b61bf5bbSBrian Norris 9848b6e50c9SBrian Norris /* Read back first? */ 985623978deSBrian Norris if (rd) { 9860813621bSBoris Brezillon res = read_abs_bbt(this, buf, rd, chipsel); 987623978deSBrian Norris if (mtd_is_eccerr(res)) { 988623978deSBrian Norris /* Mark table as invalid */ 989623978deSBrian Norris rd->pages[i] = -1; 990dadc17a3SBrian Norris rd->version[i] = 0; 991623978deSBrian Norris i--; 992623978deSBrian Norris continue; 993623978deSBrian Norris } 994623978deSBrian Norris } 9958b6e50c9SBrian Norris /* If they weren't versioned, read both */ 996623978deSBrian Norris if (rd2) { 9970813621bSBoris Brezillon res2 = read_abs_bbt(this, buf, rd2, chipsel); 998623978deSBrian Norris if (mtd_is_eccerr(res2)) { 999623978deSBrian Norris /* Mark table as invalid */ 1000623978deSBrian Norris rd2->pages[i] = -1; 1001dadc17a3SBrian Norris rd2->version[i] = 0; 1002623978deSBrian Norris i--; 1003623978deSBrian Norris continue; 1004623978deSBrian Norris } 1005623978deSBrian Norris } 1006623978deSBrian Norris 1007623978deSBrian Norris /* Scrub the flash table(s)? */ 1008623978deSBrian Norris if (mtd_is_bitflip(res) || mtd_is_bitflip(res2)) 1009623978deSBrian Norris writeops = 0x03; 10101da177e4SLinus Torvalds 1011dadc17a3SBrian Norris /* Update version numbers before writing */ 1012dadc17a3SBrian Norris if (md) { 1013dadc17a3SBrian Norris td->version[i] = max(td->version[i], md->version[i]); 1014dadc17a3SBrian Norris md->version[i] = td->version[i]; 1015dadc17a3SBrian Norris } 10161da177e4SLinus Torvalds 10171da177e4SLinus Torvalds /* Write the bad block table to the device? */ 10181da177e4SLinus Torvalds if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { 10190813621bSBoris Brezillon res = write_bbt(this, buf, td, md, chipsel); 10201da177e4SLinus Torvalds if (res < 0) 10211da177e4SLinus Torvalds return res; 10221da177e4SLinus Torvalds } 10231da177e4SLinus Torvalds 10241da177e4SLinus Torvalds /* Write the mirror bad block table to the device? */ 10251da177e4SLinus Torvalds if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { 10260813621bSBoris Brezillon res = write_bbt(this, buf, md, td, chipsel); 10271da177e4SLinus Torvalds if (res < 0) 10281da177e4SLinus Torvalds return res; 10291da177e4SLinus Torvalds } 10301da177e4SLinus Torvalds } 10311da177e4SLinus Torvalds return 0; 10321da177e4SLinus Torvalds } 10331da177e4SLinus Torvalds 10341da177e4SLinus Torvalds /** 103599f3351aSBoris Brezillon * nand_update_bbt - update bad block table(s) 103699f3351aSBoris Brezillon * @this: the NAND device 103799f3351aSBoris Brezillon * @offs: the offset of the newly marked block 103899f3351aSBoris Brezillon * 103999f3351aSBoris Brezillon * The function updates the bad block table(s). 104099f3351aSBoris Brezillon */ 104199f3351aSBoris Brezillon static int nand_update_bbt(struct nand_chip *this, loff_t offs) 104299f3351aSBoris Brezillon { 104399f3351aSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 104499f3351aSBoris Brezillon int len, res = 0; 104599f3351aSBoris Brezillon int chip, chipsel; 104699f3351aSBoris Brezillon uint8_t *buf; 104799f3351aSBoris Brezillon struct nand_bbt_descr *td = this->bbt_td; 104899f3351aSBoris Brezillon struct nand_bbt_descr *md = this->bbt_md; 104999f3351aSBoris Brezillon 105099f3351aSBoris Brezillon if (!this->bbt || !td) 105199f3351aSBoris Brezillon return -EINVAL; 105299f3351aSBoris Brezillon 105399f3351aSBoris Brezillon /* Allocate a temporary buffer for one eraseblock incl. oob */ 105499f3351aSBoris Brezillon len = (1 << this->bbt_erase_shift); 105599f3351aSBoris Brezillon len += (len >> this->page_shift) * mtd->oobsize; 105699f3351aSBoris Brezillon buf = kmalloc(len, GFP_KERNEL); 105799f3351aSBoris Brezillon if (!buf) 105899f3351aSBoris Brezillon return -ENOMEM; 105999f3351aSBoris Brezillon 106099f3351aSBoris Brezillon /* Do we have a bbt per chip? */ 106199f3351aSBoris Brezillon if (td->options & NAND_BBT_PERCHIP) { 106299f3351aSBoris Brezillon chip = (int)(offs >> this->chip_shift); 106399f3351aSBoris Brezillon chipsel = chip; 106499f3351aSBoris Brezillon } else { 106599f3351aSBoris Brezillon chip = 0; 106699f3351aSBoris Brezillon chipsel = -1; 106799f3351aSBoris Brezillon } 106899f3351aSBoris Brezillon 106999f3351aSBoris Brezillon td->version[chip]++; 107099f3351aSBoris Brezillon if (md) 107199f3351aSBoris Brezillon md->version[chip]++; 107299f3351aSBoris Brezillon 107399f3351aSBoris Brezillon /* Write the bad block table to the device? */ 107499f3351aSBoris Brezillon if (td->options & NAND_BBT_WRITE) { 107599f3351aSBoris Brezillon res = write_bbt(this, buf, td, md, chipsel); 107699f3351aSBoris Brezillon if (res < 0) 107799f3351aSBoris Brezillon goto out; 107899f3351aSBoris Brezillon } 107999f3351aSBoris Brezillon /* Write the mirror bad block table to the device? */ 108099f3351aSBoris Brezillon if (md && (md->options & NAND_BBT_WRITE)) { 108199f3351aSBoris Brezillon res = write_bbt(this, buf, md, td, chipsel); 108299f3351aSBoris Brezillon } 108399f3351aSBoris Brezillon 108499f3351aSBoris Brezillon out: 108599f3351aSBoris Brezillon kfree(buf); 108699f3351aSBoris Brezillon return res; 108799f3351aSBoris Brezillon } 108899f3351aSBoris Brezillon 108999f3351aSBoris Brezillon /** 1090*7998d898SMauro Carvalho Chehab * mark_bbt_region - [GENERIC] mark the bad block table regions 10910813621bSBoris Brezillon * @this: the NAND device 10921da177e4SLinus Torvalds * @td: bad block table descriptor 10931da177e4SLinus Torvalds * 10948b6e50c9SBrian Norris * The bad block table regions are marked as "bad" to prevent accidental 10958b6e50c9SBrian Norris * erasures / writes. The regions are identified by the mark 0x02. 10961da177e4SLinus Torvalds */ 10970813621bSBoris Brezillon static void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td) 10981da177e4SLinus Torvalds { 10996c836d51SBoris Brezillon u64 targetsize = nanddev_target_size(&this->base); 11000813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 11011da177e4SLinus Torvalds int i, j, chips, block, nrblocks, update; 1102771c568bSBrian Norris uint8_t oldval; 11031da177e4SLinus Torvalds 11041da177e4SLinus Torvalds /* Do we have a bbt per chip? */ 11051da177e4SLinus Torvalds if (td->options & NAND_BBT_PERCHIP) { 110632813e28SBoris Brezillon chips = nanddev_ntargets(&this->base); 11076c836d51SBoris Brezillon nrblocks = (int)(targetsize >> this->bbt_erase_shift); 11081da177e4SLinus Torvalds } else { 11091da177e4SLinus Torvalds chips = 1; 11101da177e4SLinus Torvalds nrblocks = (int)(mtd->size >> this->bbt_erase_shift); 11111da177e4SLinus Torvalds } 11121da177e4SLinus Torvalds 11131da177e4SLinus Torvalds for (i = 0; i < chips; i++) { 11141da177e4SLinus Torvalds if ((td->options & NAND_BBT_ABSPAGE) || 11151da177e4SLinus Torvalds !(td->options & NAND_BBT_WRITE)) { 1116e0c7d767SDavid Woodhouse if (td->pages[i] == -1) 1117e0c7d767SDavid Woodhouse continue; 11181da177e4SLinus Torvalds block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); 1119b4d20d60SBrian Norris oldval = bbt_get_entry(this, block); 1120b4d20d60SBrian Norris bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); 1121771c568bSBrian Norris if ((oldval != BBT_BLOCK_RESERVED) && 1122771c568bSBrian Norris td->reserved_block_code) 11230813621bSBoris Brezillon nand_update_bbt(this, (loff_t)block << 1124b4d20d60SBrian Norris this->bbt_erase_shift); 11251da177e4SLinus Torvalds continue; 11261da177e4SLinus Torvalds } 11271da177e4SLinus Torvalds update = 0; 11281da177e4SLinus Torvalds if (td->options & NAND_BBT_LASTBLOCK) 11291da177e4SLinus Torvalds block = ((i + 1) * nrblocks) - td->maxblocks; 11301da177e4SLinus Torvalds else 11311da177e4SLinus Torvalds block = i * nrblocks; 11321da177e4SLinus Torvalds for (j = 0; j < td->maxblocks; j++) { 1133b4d20d60SBrian Norris oldval = bbt_get_entry(this, block); 1134b4d20d60SBrian Norris bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); 1135771c568bSBrian Norris if (oldval != BBT_BLOCK_RESERVED) 1136e0c7d767SDavid Woodhouse update = 1; 1137b4d20d60SBrian Norris block++; 11381da177e4SLinus Torvalds } 11398b6e50c9SBrian Norris /* 11408b6e50c9SBrian Norris * If we want reserved blocks to be recorded to flash, and some 11418b6e50c9SBrian Norris * new ones have been marked, then we need to update the stored 11428b6e50c9SBrian Norris * bbts. This should only happen once. 11438b6e50c9SBrian Norris */ 11441da177e4SLinus Torvalds if (update && td->reserved_block_code) 11450813621bSBoris Brezillon nand_update_bbt(this, (loff_t)(block - 1) << 1146b4d20d60SBrian Norris this->bbt_erase_shift); 11471da177e4SLinus Torvalds } 11481da177e4SLinus Torvalds } 11491da177e4SLinus Torvalds 11501da177e4SLinus Torvalds /** 11517cba7b14SSebastian Andrzej Siewior * verify_bbt_descr - verify the bad block description 11520813621bSBoris Brezillon * @this: the NAND device 11537cba7b14SSebastian Andrzej Siewior * @bd: the table to verify 11547cba7b14SSebastian Andrzej Siewior * 11557cba7b14SSebastian Andrzej Siewior * This functions performs a few sanity checks on the bad block description 11567cba7b14SSebastian Andrzej Siewior * table. 11577cba7b14SSebastian Andrzej Siewior */ 11580813621bSBoris Brezillon static void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd) 11597cba7b14SSebastian Andrzej Siewior { 11606c836d51SBoris Brezillon u64 targetsize = nanddev_target_size(&this->base); 11610813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 11627912a5e7SStanislav Fomichev u32 pattern_len; 11637912a5e7SStanislav Fomichev u32 bits; 11647cba7b14SSebastian Andrzej Siewior u32 table_size; 11657cba7b14SSebastian Andrzej Siewior 11667cba7b14SSebastian Andrzej Siewior if (!bd) 11677cba7b14SSebastian Andrzej Siewior return; 11687912a5e7SStanislav Fomichev 11697912a5e7SStanislav Fomichev pattern_len = bd->len; 11707912a5e7SStanislav Fomichev bits = bd->options & NAND_BBT_NRBITS_MSK; 11717912a5e7SStanislav Fomichev 1172a40f7341SBrian Norris BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) && 1173bb9ebd4eSBrian Norris !(this->bbt_options & NAND_BBT_USE_FLASH)); 11747cba7b14SSebastian Andrzej Siewior BUG_ON(!bits); 11757cba7b14SSebastian Andrzej Siewior 11767cba7b14SSebastian Andrzej Siewior if (bd->options & NAND_BBT_VERSION) 11777cba7b14SSebastian Andrzej Siewior pattern_len++; 11787cba7b14SSebastian Andrzej Siewior 11797cba7b14SSebastian Andrzej Siewior if (bd->options & NAND_BBT_NO_OOB) { 1180bb9ebd4eSBrian Norris BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH)); 1181a40f7341SBrian Norris BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB)); 11827cba7b14SSebastian Andrzej Siewior BUG_ON(bd->offs); 11837cba7b14SSebastian Andrzej Siewior if (bd->options & NAND_BBT_VERSION) 11847cba7b14SSebastian Andrzej Siewior BUG_ON(bd->veroffs != bd->len); 11857cba7b14SSebastian Andrzej Siewior BUG_ON(bd->options & NAND_BBT_SAVECONTENT); 11867cba7b14SSebastian Andrzej Siewior } 11877cba7b14SSebastian Andrzej Siewior 11887cba7b14SSebastian Andrzej Siewior if (bd->options & NAND_BBT_PERCHIP) 11896c836d51SBoris Brezillon table_size = targetsize >> this->bbt_erase_shift; 11907cba7b14SSebastian Andrzej Siewior else 11917cba7b14SSebastian Andrzej Siewior table_size = mtd->size >> this->bbt_erase_shift; 11927cba7b14SSebastian Andrzej Siewior table_size >>= 3; 11937cba7b14SSebastian Andrzej Siewior table_size *= bits; 11947cba7b14SSebastian Andrzej Siewior if (bd->options & NAND_BBT_NO_OOB) 11957cba7b14SSebastian Andrzej Siewior table_size += pattern_len; 11967cba7b14SSebastian Andrzej Siewior BUG_ON(table_size > (1 << this->bbt_erase_shift)); 11977cba7b14SSebastian Andrzej Siewior } 11987cba7b14SSebastian Andrzej Siewior 11997cba7b14SSebastian Andrzej Siewior /** 12001da177e4SLinus Torvalds * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) 12010813621bSBoris Brezillon * @this: the NAND device 12021da177e4SLinus Torvalds * @bd: descriptor for the good/bad block search pattern 12031da177e4SLinus Torvalds * 12048b6e50c9SBrian Norris * The function checks, if a bad block table(s) is/are already available. If 12058b6e50c9SBrian Norris * not it scans the device for manufacturer marked good / bad blocks and writes 12068b6e50c9SBrian Norris * the bad block table(s) to the selected place. 12071da177e4SLinus Torvalds * 12088b6e50c9SBrian Norris * The bad block table memory is allocated here. It must be freed by calling 12098b6e50c9SBrian Norris * the nand_free_bbt function. 12101da177e4SLinus Torvalds */ 12110813621bSBoris Brezillon static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) 12121da177e4SLinus Torvalds { 12130813621bSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(this); 121483c59542SBrian Norris int len, res; 12151da177e4SLinus Torvalds uint8_t *buf; 12161da177e4SLinus Torvalds struct nand_bbt_descr *td = this->bbt_td; 12171da177e4SLinus Torvalds struct nand_bbt_descr *md = this->bbt_md; 12181da177e4SLinus Torvalds 1219192db1caSSheng Yong len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1; 12208b6e50c9SBrian Norris /* 12218b6e50c9SBrian Norris * Allocate memory (2bit per block) and clear the memory bad block 12228b6e50c9SBrian Norris * table. 12238b6e50c9SBrian Norris */ 122495b93a0cSBurman Yan this->bbt = kzalloc(len, GFP_KERNEL); 12250870066dSBrian Norris if (!this->bbt) 12261da177e4SLinus Torvalds return -ENOMEM; 12271da177e4SLinus Torvalds 12288b6e50c9SBrian Norris /* 1229735bf220SKieran Bingham * If no primary table descriptor is given, scan the device to build a 12308b6e50c9SBrian Norris * memory based bad block table. 12311da177e4SLinus Torvalds */ 1232eeada24dSArtem B. Bityuckiy if (!td) { 12330813621bSBoris Brezillon if ((res = nand_memory_bbt(this, bd))) { 1234d0370219SBrian Norris pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); 123586aa04f4SWenwen Wang goto err_free_bbt; 1236eeada24dSArtem B. Bityuckiy } 123783c59542SBrian Norris return 0; 1238eeada24dSArtem B. Bityuckiy } 12390813621bSBoris Brezillon verify_bbt_descr(this, td); 12400813621bSBoris Brezillon verify_bbt_descr(this, md); 12411da177e4SLinus Torvalds 12421da177e4SLinus Torvalds /* Allocate a temporary buffer for one eraseblock incl. oob */ 12431da177e4SLinus Torvalds len = (1 << this->bbt_erase_shift); 12441da177e4SLinus Torvalds len += (len >> this->page_shift) * mtd->oobsize; 1245c3f8abf4SDavid Woodhouse buf = vmalloc(len); 12461da177e4SLinus Torvalds if (!buf) { 124783c59542SBrian Norris res = -ENOMEM; 124886aa04f4SWenwen Wang goto err_free_bbt; 12491da177e4SLinus Torvalds } 12501da177e4SLinus Torvalds 12511da177e4SLinus Torvalds /* Is the bbt at a given page? */ 12521da177e4SLinus Torvalds if (td->options & NAND_BBT_ABSPAGE) { 12530813621bSBoris Brezillon read_abs_bbts(this, buf, td, md); 12541da177e4SLinus Torvalds } else { 12551da177e4SLinus Torvalds /* Search the bad block table using a pattern in oob */ 12560813621bSBoris Brezillon search_read_bbts(this, buf, td, md); 12571da177e4SLinus Torvalds } 12581da177e4SLinus Torvalds 12590813621bSBoris Brezillon res = check_create(this, buf, bd); 126083c59542SBrian Norris if (res) 126186aa04f4SWenwen Wang goto err_free_buf; 12621da177e4SLinus Torvalds 12631da177e4SLinus Torvalds /* Prevent the bbt regions from erasing / writing */ 12640813621bSBoris Brezillon mark_bbt_region(this, td); 12651da177e4SLinus Torvalds if (md) 12660813621bSBoris Brezillon mark_bbt_region(this, md); 12671da177e4SLinus Torvalds 1268c3f8abf4SDavid Woodhouse vfree(buf); 126983c59542SBrian Norris return 0; 127083c59542SBrian Norris 127186aa04f4SWenwen Wang err_free_buf: 127286aa04f4SWenwen Wang vfree(buf); 127386aa04f4SWenwen Wang err_free_bbt: 127483c59542SBrian Norris kfree(this->bbt); 127583c59542SBrian Norris this->bbt = NULL; 12761da177e4SLinus Torvalds return res; 12771da177e4SLinus Torvalds } 12781da177e4SLinus Torvalds 12798b6e50c9SBrian Norris /* 12808b6e50c9SBrian Norris * Define some generic bad / good block scan pattern which are used 12818b6e50c9SBrian Norris * while scanning a device for factory marked good / bad blocks. 12828b6e50c9SBrian Norris */ 12831da177e4SLinus Torvalds static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; 12841da177e4SLinus Torvalds 12857854d3f7SBrian Norris /* Generic flash bbt descriptors */ 12861da177e4SLinus Torvalds static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; 12871da177e4SLinus Torvalds static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; 12881da177e4SLinus Torvalds 12891da177e4SLinus Torvalds static struct nand_bbt_descr bbt_main_descr = { 12901da177e4SLinus Torvalds .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 12911da177e4SLinus Torvalds | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, 12921da177e4SLinus Torvalds .offs = 8, 12931da177e4SLinus Torvalds .len = 4, 12941da177e4SLinus Torvalds .veroffs = 12, 129561de9da6SRichard Genoud .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 12961da177e4SLinus Torvalds .pattern = bbt_pattern 12971da177e4SLinus Torvalds }; 12981da177e4SLinus Torvalds 12991da177e4SLinus Torvalds static struct nand_bbt_descr bbt_mirror_descr = { 13001da177e4SLinus Torvalds .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 13011da177e4SLinus Torvalds | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, 13021da177e4SLinus Torvalds .offs = 8, 13031da177e4SLinus Torvalds .len = 4, 13041da177e4SLinus Torvalds .veroffs = 12, 130561de9da6SRichard Genoud .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 13061da177e4SLinus Torvalds .pattern = mirror_pattern 13071da177e4SLinus Torvalds }; 13081da177e4SLinus Torvalds 13099fd6b37aSBrian Norris static struct nand_bbt_descr bbt_main_no_oob_descr = { 13107cba7b14SSebastian Andrzej Siewior .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 13117cba7b14SSebastian Andrzej Siewior | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP 13127cba7b14SSebastian Andrzej Siewior | NAND_BBT_NO_OOB, 13137cba7b14SSebastian Andrzej Siewior .len = 4, 13147cba7b14SSebastian Andrzej Siewior .veroffs = 4, 131561de9da6SRichard Genoud .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 13167cba7b14SSebastian Andrzej Siewior .pattern = bbt_pattern 13177cba7b14SSebastian Andrzej Siewior }; 13187cba7b14SSebastian Andrzej Siewior 13199fd6b37aSBrian Norris static struct nand_bbt_descr bbt_mirror_no_oob_descr = { 13207cba7b14SSebastian Andrzej Siewior .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 13217cba7b14SSebastian Andrzej Siewior | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP 13227cba7b14SSebastian Andrzej Siewior | NAND_BBT_NO_OOB, 13237cba7b14SSebastian Andrzej Siewior .len = 4, 13247cba7b14SSebastian Andrzej Siewior .veroffs = 4, 132561de9da6SRichard Genoud .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 13267cba7b14SSebastian Andrzej Siewior .pattern = mirror_pattern 13277cba7b14SSebastian Andrzej Siewior }; 13287cba7b14SSebastian Andrzej Siewior 1329752ed6c5SBrian Norris #define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) 133058373ff0SBrian Norris /** 1331752ed6c5SBrian Norris * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure 133258373ff0SBrian Norris * @this: NAND chip to create descriptor for 133358373ff0SBrian Norris * 133458373ff0SBrian Norris * This function allocates and initializes a nand_bbt_descr for BBM detection 1335752ed6c5SBrian Norris * based on the properties of @this. The new descriptor is stored in 133658373ff0SBrian Norris * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when 133758373ff0SBrian Norris * passed to this function. 133858373ff0SBrian Norris */ 1339752ed6c5SBrian Norris static int nand_create_badblock_pattern(struct nand_chip *this) 134058373ff0SBrian Norris { 134158373ff0SBrian Norris struct nand_bbt_descr *bd; 134258373ff0SBrian Norris if (this->badblock_pattern) { 1343752ed6c5SBrian Norris pr_warn("Bad block pattern already allocated; not replacing\n"); 134458373ff0SBrian Norris return -EINVAL; 134558373ff0SBrian Norris } 134658373ff0SBrian Norris bd = kzalloc(sizeof(*bd), GFP_KERNEL); 13470870066dSBrian Norris if (!bd) 134858373ff0SBrian Norris return -ENOMEM; 1349752ed6c5SBrian Norris bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; 135058373ff0SBrian Norris bd->offs = this->badblockpos; 135158373ff0SBrian Norris bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; 135258373ff0SBrian Norris bd->pattern = scan_ff_pattern; 135358373ff0SBrian Norris bd->options |= NAND_BBT_DYNAMICSTRUCT; 135458373ff0SBrian Norris this->badblock_pattern = bd; 135558373ff0SBrian Norris return 0; 135658373ff0SBrian Norris } 135758373ff0SBrian Norris 13581da177e4SLinus Torvalds /** 135944b07b92SBoris Brezillon * nand_create_bbt - [NAND Interface] Select a default bad block table for the device 136044b07b92SBoris Brezillon * @this: NAND chip object 13611da177e4SLinus Torvalds * 13628b6e50c9SBrian Norris * This function selects the default bad block table support for the device and 13638b6e50c9SBrian Norris * calls the nand_scan_bbt function. 13641da177e4SLinus Torvalds */ 136544b07b92SBoris Brezillon int nand_create_bbt(struct nand_chip *this) 13661da177e4SLinus Torvalds { 1367abb9cf78SBrian Norris int ret; 13681da177e4SLinus Torvalds 13691da177e4SLinus Torvalds /* Is a flash based bad block table requested? */ 1370bb9ebd4eSBrian Norris if (this->bbt_options & NAND_BBT_USE_FLASH) { 13711da177e4SLinus Torvalds /* Use the default pattern descriptors */ 13721da177e4SLinus Torvalds if (!this->bbt_td) { 1373a40f7341SBrian Norris if (this->bbt_options & NAND_BBT_NO_OOB) { 13749fd6b37aSBrian Norris this->bbt_td = &bbt_main_no_oob_descr; 13759fd6b37aSBrian Norris this->bbt_md = &bbt_mirror_no_oob_descr; 13767cba7b14SSebastian Andrzej Siewior } else { 13771da177e4SLinus Torvalds this->bbt_td = &bbt_main_descr; 13781da177e4SLinus Torvalds this->bbt_md = &bbt_mirror_descr; 13791da177e4SLinus Torvalds } 13807cba7b14SSebastian Andrzej Siewior } 13811da177e4SLinus Torvalds } else { 13821da177e4SLinus Torvalds this->bbt_td = NULL; 13831da177e4SLinus Torvalds this->bbt_md = NULL; 1384a2f812dfSBrian Norris } 1385a2f812dfSBrian Norris 1386abb9cf78SBrian Norris if (!this->badblock_pattern) { 1387abb9cf78SBrian Norris ret = nand_create_badblock_pattern(this); 1388abb9cf78SBrian Norris if (ret) 1389abb9cf78SBrian Norris return ret; 1390abb9cf78SBrian Norris } 1391a2f812dfSBrian Norris 13920813621bSBoris Brezillon return nand_scan_bbt(this, this->badblock_pattern); 13931da177e4SLinus Torvalds } 139444b07b92SBoris Brezillon EXPORT_SYMBOL(nand_create_bbt); 13951da177e4SLinus Torvalds 13961da177e4SLinus Torvalds /** 13978471bb73SEzequiel Garcia * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved 13985740d4c4SBoris Brezillon * @this: NAND chip object 13998471bb73SEzequiel Garcia * @offs: offset in the device 14008471bb73SEzequiel Garcia */ 14015740d4c4SBoris Brezillon int nand_isreserved_bbt(struct nand_chip *this, loff_t offs) 14028471bb73SEzequiel Garcia { 14038471bb73SEzequiel Garcia int block; 14048471bb73SEzequiel Garcia 14058471bb73SEzequiel Garcia block = (int)(offs >> this->bbt_erase_shift); 14068471bb73SEzequiel Garcia return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED; 14078471bb73SEzequiel Garcia } 14088471bb73SEzequiel Garcia 14098471bb73SEzequiel Garcia /** 14101da177e4SLinus Torvalds * nand_isbad_bbt - [NAND Interface] Check if a block is bad 14115740d4c4SBoris Brezillon * @this: NAND chip object 14121da177e4SLinus Torvalds * @offs: offset in the device 14131da177e4SLinus Torvalds * @allowbbt: allow access to bad block table region 14141da177e4SLinus Torvalds */ 14155740d4c4SBoris Brezillon int nand_isbad_bbt(struct nand_chip *this, loff_t offs, int allowbbt) 14161da177e4SLinus Torvalds { 141739dbb029SBrian Norris int block, res; 14181da177e4SLinus Torvalds 1419b4d20d60SBrian Norris block = (int)(offs >> this->bbt_erase_shift); 1420b4d20d60SBrian Norris res = bbt_get_entry(this, block); 14211da177e4SLinus Torvalds 14222ac63d90SRafał Miłecki pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", 1423b4d20d60SBrian Norris (unsigned int)offs, block, res); 14241da177e4SLinus Torvalds 142539dbb029SBrian Norris switch (res) { 1426771c568bSBrian Norris case BBT_BLOCK_GOOD: 1427e0c7d767SDavid Woodhouse return 0; 1428771c568bSBrian Norris case BBT_BLOCK_WORN: 1429e0c7d767SDavid Woodhouse return 1; 1430771c568bSBrian Norris case BBT_BLOCK_RESERVED: 1431e0c7d767SDavid Woodhouse return allowbbt ? 0 : 1; 14321da177e4SLinus Torvalds } 14331da177e4SLinus Torvalds return 1; 14341da177e4SLinus Torvalds } 14351da177e4SLinus Torvalds 1436b32843b7SBrian Norris /** 1437b32843b7SBrian Norris * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT 14385740d4c4SBoris Brezillon * @this: NAND chip object 1439b32843b7SBrian Norris * @offs: offset of the bad block 1440b32843b7SBrian Norris */ 14415740d4c4SBoris Brezillon int nand_markbad_bbt(struct nand_chip *this, loff_t offs) 1442b32843b7SBrian Norris { 1443b32843b7SBrian Norris int block, ret = 0; 1444b32843b7SBrian Norris 1445b32843b7SBrian Norris block = (int)(offs >> this->bbt_erase_shift); 1446b32843b7SBrian Norris 1447b32843b7SBrian Norris /* Mark bad block in memory */ 1448b32843b7SBrian Norris bbt_mark_entry(this, block, BBT_BLOCK_WORN); 1449b32843b7SBrian Norris 1450b32843b7SBrian Norris /* Update flash-based bad block table */ 1451b32843b7SBrian Norris if (this->bbt_options & NAND_BBT_USE_FLASH) 14520813621bSBoris Brezillon ret = nand_update_bbt(this, offs); 1453b32843b7SBrian Norris 1454b32843b7SBrian Norris return ret; 1455b32843b7SBrian Norris } 1456