18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Overview: 48c2ecf20Sopenharmony_ci * Bad block table support for the NAND driver 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de) 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Description: 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * When nand_scan_bbt is called, then it tries to find the bad block table 118c2ecf20Sopenharmony_ci * depending on the options in the BBT descriptor(s). If no flash based BBT 128c2ecf20Sopenharmony_ci * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory 138c2ecf20Sopenharmony_ci * marked good / bad blocks. This information is used to create a memory BBT. 148c2ecf20Sopenharmony_ci * Once a new bad block is discovered then the "factory" information is updated 158c2ecf20Sopenharmony_ci * on the device. 168c2ecf20Sopenharmony_ci * If a flash based BBT is specified then the function first tries to find the 178c2ecf20Sopenharmony_ci * BBT on flash. If a BBT is found then the contents are read and the memory 188c2ecf20Sopenharmony_ci * based BBT is created. If a mirrored BBT is selected then the mirror is 198c2ecf20Sopenharmony_ci * searched too and the versions are compared. If the mirror has a greater 208c2ecf20Sopenharmony_ci * version number, then the mirror BBT is used to build the memory based BBT. 218c2ecf20Sopenharmony_ci * If the tables are not versioned, then we "or" the bad block information. 228c2ecf20Sopenharmony_ci * If one of the BBTs is out of date or does not exist it is (re)created. 238c2ecf20Sopenharmony_ci * If no BBT exists at all then the device is scanned for factory marked 248c2ecf20Sopenharmony_ci * good / bad blocks and the bad block tables are created. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * For manufacturer created BBTs like the one found on M-SYS DOC devices 278c2ecf20Sopenharmony_ci * the BBT is searched and read but never created 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * The auto generated bad block table is located in the last good blocks 308c2ecf20Sopenharmony_ci * of the device. The table is mirrored, so it can be updated eventually. 318c2ecf20Sopenharmony_ci * The table is marked in the OOB area with an ident pattern and a version 328c2ecf20Sopenharmony_ci * number which indicates which of both tables is more up to date. If the NAND 338c2ecf20Sopenharmony_ci * controller needs the complete OOB area for the ECC information then the 348c2ecf20Sopenharmony_ci * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of 358c2ecf20Sopenharmony_ci * course): it moves the ident pattern and the version byte into the data area 368c2ecf20Sopenharmony_ci * and the OOB area will remain untouched. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * The table uses 2 bits per block 398c2ecf20Sopenharmony_ci * 11b: block is good 408c2ecf20Sopenharmony_ci * 00b: block is factory marked bad 418c2ecf20Sopenharmony_ci * 01b, 10b: block is marked bad due to wear 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * The memory bad block table uses the following scheme: 448c2ecf20Sopenharmony_ci * 00b: block is good 458c2ecf20Sopenharmony_ci * 01b: block is marked bad due to wear 468c2ecf20Sopenharmony_ci * 10b: block is reserved (to protect the bbt area) 478c2ecf20Sopenharmony_ci * 11b: block is factory marked bad 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * Multichip devices like DOC store the bad block info per floor. 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * Following assumptions are made: 528c2ecf20Sopenharmony_ci * - bbts start at a page boundary, if autolocated on a block boundary 538c2ecf20Sopenharmony_ci * - the space necessary for a bbt in FLASH does not exceed a block boundary 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#include <linux/slab.h> 578c2ecf20Sopenharmony_ci#include <linux/types.h> 588c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 598c2ecf20Sopenharmony_ci#include <linux/mtd/bbm.h> 608c2ecf20Sopenharmony_ci#include <linux/bitops.h> 618c2ecf20Sopenharmony_ci#include <linux/delay.h> 628c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 638c2ecf20Sopenharmony_ci#include <linux/export.h> 648c2ecf20Sopenharmony_ci#include <linux/string.h> 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#include "internals.h" 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define BBT_BLOCK_GOOD 0x00 698c2ecf20Sopenharmony_ci#define BBT_BLOCK_WORN 0x01 708c2ecf20Sopenharmony_ci#define BBT_BLOCK_RESERVED 0x02 718c2ecf20Sopenharmony_ci#define BBT_BLOCK_FACTORY_BAD 0x03 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define BBT_ENTRY_MASK 0x03 748c2ecf20Sopenharmony_ci#define BBT_ENTRY_SHIFT 2 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; 798c2ecf20Sopenharmony_ci entry >>= (block & BBT_ENTRY_MASK) * 2; 808c2ecf20Sopenharmony_ci return entry & BBT_ENTRY_MASK; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic inline void bbt_mark_entry(struct nand_chip *chip, int block, 848c2ecf20Sopenharmony_ci uint8_t mark) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); 878c2ecf20Sopenharmony_ci chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci if (memcmp(buf, td->pattern, td->len)) 938c2ecf20Sopenharmony_ci return -1; 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/** 988c2ecf20Sopenharmony_ci * check_pattern - [GENERIC] check if a pattern is in the buffer 998c2ecf20Sopenharmony_ci * @buf: the buffer to search 1008c2ecf20Sopenharmony_ci * @len: the length of buffer to search 1018c2ecf20Sopenharmony_ci * @paglen: the pagelength 1028c2ecf20Sopenharmony_ci * @td: search pattern descriptor 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * Check for a pattern at the given place. Used to search bad block tables and 1058c2ecf20Sopenharmony_ci * good / bad block identifiers. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_cistatic int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_NO_OOB) 1108c2ecf20Sopenharmony_ci return check_pattern_no_oob(buf, td); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Compare the pattern */ 1138c2ecf20Sopenharmony_ci if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) 1148c2ecf20Sopenharmony_ci return -1; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/** 1208c2ecf20Sopenharmony_ci * check_short_pattern - [GENERIC] check if a pattern is in the buffer 1218c2ecf20Sopenharmony_ci * @buf: the buffer to search 1228c2ecf20Sopenharmony_ci * @td: search pattern descriptor 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * Check for a pattern at the given place. Used to search bad block tables and 1258c2ecf20Sopenharmony_ci * good / bad block identifiers. Same as check_pattern, but no optional empty 1268c2ecf20Sopenharmony_ci * check. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_cistatic int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci /* Compare the pattern */ 1318c2ecf20Sopenharmony_ci if (memcmp(buf + td->offs, td->pattern, td->len)) 1328c2ecf20Sopenharmony_ci return -1; 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/** 1378c2ecf20Sopenharmony_ci * add_marker_len - compute the length of the marker in data area 1388c2ecf20Sopenharmony_ci * @td: BBT descriptor used for computation 1398c2ecf20Sopenharmony_ci * 1408c2ecf20Sopenharmony_ci * The length will be 0 if the marker is located in OOB area. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_cistatic u32 add_marker_len(struct nand_bbt_descr *td) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci u32 len; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (!(td->options & NAND_BBT_NO_OOB)) 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci len = td->len; 1508c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_VERSION) 1518c2ecf20Sopenharmony_ci len++; 1528c2ecf20Sopenharmony_ci return len; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/** 1568c2ecf20Sopenharmony_ci * read_bbt - [GENERIC] Read the bad block table starting from page 1578c2ecf20Sopenharmony_ci * @this: NAND chip object 1588c2ecf20Sopenharmony_ci * @buf: temporary buffer 1598c2ecf20Sopenharmony_ci * @page: the starting page 1608c2ecf20Sopenharmony_ci * @num: the number of bbt descriptors to read 1618c2ecf20Sopenharmony_ci * @td: the bbt describtion table 1628c2ecf20Sopenharmony_ci * @offs: block number offset in the table 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * Read the bad block table starting from page. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_cistatic int read_bbt(struct nand_chip *this, uint8_t *buf, int page, int num, 1678c2ecf20Sopenharmony_ci struct nand_bbt_descr *td, int offs) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 1708c2ecf20Sopenharmony_ci int res, ret = 0, i, j, act = 0; 1718c2ecf20Sopenharmony_ci size_t retlen, len, totlen; 1728c2ecf20Sopenharmony_ci loff_t from; 1738c2ecf20Sopenharmony_ci int bits = td->options & NAND_BBT_NRBITS_MSK; 1748c2ecf20Sopenharmony_ci uint8_t msk = (uint8_t)((1 << bits) - 1); 1758c2ecf20Sopenharmony_ci u32 marker_len; 1768c2ecf20Sopenharmony_ci int reserved_block_code = td->reserved_block_code; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci totlen = (num * bits) >> 3; 1798c2ecf20Sopenharmony_ci marker_len = add_marker_len(td); 1808c2ecf20Sopenharmony_ci from = ((loff_t)page) << this->page_shift; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci while (totlen) { 1838c2ecf20Sopenharmony_ci len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); 1848c2ecf20Sopenharmony_ci if (marker_len) { 1858c2ecf20Sopenharmony_ci /* 1868c2ecf20Sopenharmony_ci * In case the BBT marker is not in the OOB area it 1878c2ecf20Sopenharmony_ci * will be just in the first page. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci len -= marker_len; 1908c2ecf20Sopenharmony_ci from += marker_len; 1918c2ecf20Sopenharmony_ci marker_len = 0; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci res = mtd_read(mtd, from, len, &retlen, buf); 1948c2ecf20Sopenharmony_ci if (res < 0) { 1958c2ecf20Sopenharmony_ci if (mtd_is_eccerr(res)) { 1968c2ecf20Sopenharmony_ci pr_info("nand_bbt: ECC error in BBT at 0x%012llx\n", 1978c2ecf20Sopenharmony_ci from & ~mtd->writesize); 1988c2ecf20Sopenharmony_ci return res; 1998c2ecf20Sopenharmony_ci } else if (mtd_is_bitflip(res)) { 2008c2ecf20Sopenharmony_ci pr_info("nand_bbt: corrected error in BBT at 0x%012llx\n", 2018c2ecf20Sopenharmony_ci from & ~mtd->writesize); 2028c2ecf20Sopenharmony_ci ret = res; 2038c2ecf20Sopenharmony_ci } else { 2048c2ecf20Sopenharmony_ci pr_info("nand_bbt: error reading BBT\n"); 2058c2ecf20Sopenharmony_ci return res; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Analyse data */ 2108c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 2118c2ecf20Sopenharmony_ci uint8_t dat = buf[i]; 2128c2ecf20Sopenharmony_ci for (j = 0; j < 8; j += bits, act++) { 2138c2ecf20Sopenharmony_ci uint8_t tmp = (dat >> j) & msk; 2148c2ecf20Sopenharmony_ci if (tmp == msk) 2158c2ecf20Sopenharmony_ci continue; 2168c2ecf20Sopenharmony_ci if (reserved_block_code && (tmp == reserved_block_code)) { 2178c2ecf20Sopenharmony_ci pr_info("nand_read_bbt: reserved block at 0x%012llx\n", 2188c2ecf20Sopenharmony_ci (loff_t)(offs + act) << 2198c2ecf20Sopenharmony_ci this->bbt_erase_shift); 2208c2ecf20Sopenharmony_ci bbt_mark_entry(this, offs + act, 2218c2ecf20Sopenharmony_ci BBT_BLOCK_RESERVED); 2228c2ecf20Sopenharmony_ci mtd->ecc_stats.bbtblocks++; 2238c2ecf20Sopenharmony_ci continue; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci /* 2268c2ecf20Sopenharmony_ci * Leave it for now, if it's matured we can 2278c2ecf20Sopenharmony_ci * move this message to pr_debug. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci pr_info("nand_read_bbt: bad block at 0x%012llx\n", 2308c2ecf20Sopenharmony_ci (loff_t)(offs + act) << 2318c2ecf20Sopenharmony_ci this->bbt_erase_shift); 2328c2ecf20Sopenharmony_ci /* Factory marked bad or worn out? */ 2338c2ecf20Sopenharmony_ci if (tmp == 0) 2348c2ecf20Sopenharmony_ci bbt_mark_entry(this, offs + act, 2358c2ecf20Sopenharmony_ci BBT_BLOCK_FACTORY_BAD); 2368c2ecf20Sopenharmony_ci else 2378c2ecf20Sopenharmony_ci bbt_mark_entry(this, offs + act, 2388c2ecf20Sopenharmony_ci BBT_BLOCK_WORN); 2398c2ecf20Sopenharmony_ci mtd->ecc_stats.badblocks++; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci totlen -= len; 2438c2ecf20Sopenharmony_ci from += len; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci return ret; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/** 2498c2ecf20Sopenharmony_ci * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page 2508c2ecf20Sopenharmony_ci * @this: NAND chip object 2518c2ecf20Sopenharmony_ci * @buf: temporary buffer 2528c2ecf20Sopenharmony_ci * @td: descriptor for the bad block table 2538c2ecf20Sopenharmony_ci * @chip: read the table for a specific chip, -1 read all chips; applies only if 2548c2ecf20Sopenharmony_ci * NAND_BBT_PERCHIP option is set 2558c2ecf20Sopenharmony_ci * 2568c2ecf20Sopenharmony_ci * Read the bad block table for all chips starting at a given page. We assume 2578c2ecf20Sopenharmony_ci * that the bbt bits are in consecutive order. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_cistatic int read_abs_bbt(struct nand_chip *this, uint8_t *buf, 2608c2ecf20Sopenharmony_ci struct nand_bbt_descr *td, int chip) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 2638c2ecf20Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 2648c2ecf20Sopenharmony_ci int res = 0, i; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) { 2678c2ecf20Sopenharmony_ci int offs = 0; 2688c2ecf20Sopenharmony_ci for (i = 0; i < nanddev_ntargets(&this->base); i++) { 2698c2ecf20Sopenharmony_ci if (chip == -1 || chip == i) 2708c2ecf20Sopenharmony_ci res = read_bbt(this, buf, td->pages[i], 2718c2ecf20Sopenharmony_ci targetsize >> this->bbt_erase_shift, 2728c2ecf20Sopenharmony_ci td, offs); 2738c2ecf20Sopenharmony_ci if (res) 2748c2ecf20Sopenharmony_ci return res; 2758c2ecf20Sopenharmony_ci offs += targetsize >> this->bbt_erase_shift; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci } else { 2788c2ecf20Sopenharmony_ci res = read_bbt(this, buf, td->pages[0], 2798c2ecf20Sopenharmony_ci mtd->size >> this->bbt_erase_shift, td, 0); 2808c2ecf20Sopenharmony_ci if (res) 2818c2ecf20Sopenharmony_ci return res; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/* BBT marker is in the first page, no OOB */ 2878c2ecf20Sopenharmony_cistatic int scan_read_data(struct nand_chip *this, uint8_t *buf, loff_t offs, 2888c2ecf20Sopenharmony_ci struct nand_bbt_descr *td) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 2918c2ecf20Sopenharmony_ci size_t retlen; 2928c2ecf20Sopenharmony_ci size_t len; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci len = td->len; 2958c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_VERSION) 2968c2ecf20Sopenharmony_ci len++; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return mtd_read(mtd, offs, len, &retlen, buf); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/** 3028c2ecf20Sopenharmony_ci * scan_read_oob - [GENERIC] Scan data+OOB region to buffer 3038c2ecf20Sopenharmony_ci * @this: NAND chip object 3048c2ecf20Sopenharmony_ci * @buf: temporary buffer 3058c2ecf20Sopenharmony_ci * @offs: offset at which to scan 3068c2ecf20Sopenharmony_ci * @len: length of data region to read 3078c2ecf20Sopenharmony_ci * 3088c2ecf20Sopenharmony_ci * Scan read data from data+OOB. May traverse multiple pages, interleaving 3098c2ecf20Sopenharmony_ci * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" 3108c2ecf20Sopenharmony_ci * ECC condition (error or bitflip). May quit on the first (non-ECC) error. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_cistatic int scan_read_oob(struct nand_chip *this, uint8_t *buf, loff_t offs, 3138c2ecf20Sopenharmony_ci size_t len) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 3168c2ecf20Sopenharmony_ci struct mtd_oob_ops ops; 3178c2ecf20Sopenharmony_ci int res, ret = 0; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 3208c2ecf20Sopenharmony_ci ops.ooboffs = 0; 3218c2ecf20Sopenharmony_ci ops.ooblen = mtd->oobsize; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci while (len > 0) { 3248c2ecf20Sopenharmony_ci ops.datbuf = buf; 3258c2ecf20Sopenharmony_ci ops.len = min(len, (size_t)mtd->writesize); 3268c2ecf20Sopenharmony_ci ops.oobbuf = buf + ops.len; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci res = mtd_read_oob(mtd, offs, &ops); 3298c2ecf20Sopenharmony_ci if (res) { 3308c2ecf20Sopenharmony_ci if (!mtd_is_bitflip_or_eccerr(res)) 3318c2ecf20Sopenharmony_ci return res; 3328c2ecf20Sopenharmony_ci else if (mtd_is_eccerr(res) || !ret) 3338c2ecf20Sopenharmony_ci ret = res; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci buf += mtd->oobsize + mtd->writesize; 3378c2ecf20Sopenharmony_ci len -= mtd->writesize; 3388c2ecf20Sopenharmony_ci offs += mtd->writesize; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci return ret; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int scan_read(struct nand_chip *this, uint8_t *buf, loff_t offs, 3448c2ecf20Sopenharmony_ci size_t len, struct nand_bbt_descr *td) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_NO_OOB) 3478c2ecf20Sopenharmony_ci return scan_read_data(this, buf, offs, td); 3488c2ecf20Sopenharmony_ci else 3498c2ecf20Sopenharmony_ci return scan_read_oob(this, buf, offs, len); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/* Scan write data with oob to flash */ 3538c2ecf20Sopenharmony_cistatic int scan_write_bbt(struct nand_chip *this, loff_t offs, size_t len, 3548c2ecf20Sopenharmony_ci uint8_t *buf, uint8_t *oob) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 3578c2ecf20Sopenharmony_ci struct mtd_oob_ops ops; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 3608c2ecf20Sopenharmony_ci ops.ooboffs = 0; 3618c2ecf20Sopenharmony_ci ops.ooblen = mtd->oobsize; 3628c2ecf20Sopenharmony_ci ops.datbuf = buf; 3638c2ecf20Sopenharmony_ci ops.oobbuf = oob; 3648c2ecf20Sopenharmony_ci ops.len = len; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return mtd_write_oob(mtd, offs, &ops); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic u32 bbt_get_ver_offs(struct nand_chip *this, struct nand_bbt_descr *td) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 3728c2ecf20Sopenharmony_ci u32 ver_offs = td->veroffs; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (!(td->options & NAND_BBT_NO_OOB)) 3758c2ecf20Sopenharmony_ci ver_offs += mtd->writesize; 3768c2ecf20Sopenharmony_ci return ver_offs; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/** 3808c2ecf20Sopenharmony_ci * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page 3818c2ecf20Sopenharmony_ci * @this: NAND chip object 3828c2ecf20Sopenharmony_ci * @buf: temporary buffer 3838c2ecf20Sopenharmony_ci * @td: descriptor for the bad block table 3848c2ecf20Sopenharmony_ci * @md: descriptor for the bad block table mirror 3858c2ecf20Sopenharmony_ci * 3868c2ecf20Sopenharmony_ci * Read the bad block table(s) for all chips starting at a given page. We 3878c2ecf20Sopenharmony_ci * assume that the bbt bits are in consecutive order. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_cistatic void read_abs_bbts(struct nand_chip *this, uint8_t *buf, 3908c2ecf20Sopenharmony_ci struct nand_bbt_descr *td, struct nand_bbt_descr *md) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Read the primary version, if available */ 3958c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_VERSION) { 3968c2ecf20Sopenharmony_ci scan_read(this, buf, (loff_t)td->pages[0] << this->page_shift, 3978c2ecf20Sopenharmony_ci mtd->writesize, td); 3988c2ecf20Sopenharmony_ci td->version[0] = buf[bbt_get_ver_offs(this, td)]; 3998c2ecf20Sopenharmony_ci pr_info("Bad block table at page %d, version 0x%02X\n", 4008c2ecf20Sopenharmony_ci td->pages[0], td->version[0]); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Read the mirror version, if available */ 4048c2ecf20Sopenharmony_ci if (md && (md->options & NAND_BBT_VERSION)) { 4058c2ecf20Sopenharmony_ci scan_read(this, buf, (loff_t)md->pages[0] << this->page_shift, 4068c2ecf20Sopenharmony_ci mtd->writesize, md); 4078c2ecf20Sopenharmony_ci md->version[0] = buf[bbt_get_ver_offs(this, md)]; 4088c2ecf20Sopenharmony_ci pr_info("Bad block table at page %d, version 0x%02X\n", 4098c2ecf20Sopenharmony_ci md->pages[0], md->version[0]); 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/* Scan a given block partially */ 4148c2ecf20Sopenharmony_cistatic int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd, 4158c2ecf20Sopenharmony_ci loff_t offs, uint8_t *buf) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci struct mtd_oob_ops ops; 4208c2ecf20Sopenharmony_ci int ret, page_offset; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci ops.ooblen = mtd->oobsize; 4238c2ecf20Sopenharmony_ci ops.oobbuf = buf; 4248c2ecf20Sopenharmony_ci ops.ooboffs = 0; 4258c2ecf20Sopenharmony_ci ops.datbuf = NULL; 4268c2ecf20Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci page_offset = nand_bbm_get_next_page(this, 0); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci while (page_offset >= 0) { 4318c2ecf20Sopenharmony_ci /* 4328c2ecf20Sopenharmony_ci * Read the full oob until read_oob is fixed to handle single 4338c2ecf20Sopenharmony_ci * byte reads for 16 bit buswidth. 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci ret = mtd_read_oob(mtd, offs + (page_offset * mtd->writesize), 4368c2ecf20Sopenharmony_ci &ops); 4378c2ecf20Sopenharmony_ci /* Ignore ECC errors when checking for BBM */ 4388c2ecf20Sopenharmony_ci if (ret && !mtd_is_bitflip_or_eccerr(ret)) 4398c2ecf20Sopenharmony_ci return ret; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (check_short_pattern(buf, bd)) 4428c2ecf20Sopenharmony_ci return 1; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci page_offset = nand_bbm_get_next_page(this, page_offset + 1); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci/** 4518c2ecf20Sopenharmony_ci * create_bbt - [GENERIC] Create a bad block table by scanning the device 4528c2ecf20Sopenharmony_ci * @this: NAND chip object 4538c2ecf20Sopenharmony_ci * @buf: temporary buffer 4548c2ecf20Sopenharmony_ci * @bd: descriptor for the good/bad block search pattern 4558c2ecf20Sopenharmony_ci * @chip: create the table for a specific chip, -1 read all chips; applies only 4568c2ecf20Sopenharmony_ci * if NAND_BBT_PERCHIP option is set 4578c2ecf20Sopenharmony_ci * 4588c2ecf20Sopenharmony_ci * Create a bad block table by scanning the device for the given good/bad block 4598c2ecf20Sopenharmony_ci * identify pattern. 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_cistatic int create_bbt(struct nand_chip *this, uint8_t *buf, 4628c2ecf20Sopenharmony_ci struct nand_bbt_descr *bd, int chip) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 4658c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 4668c2ecf20Sopenharmony_ci int i, numblocks, startblock; 4678c2ecf20Sopenharmony_ci loff_t from; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci pr_info("Scanning device for bad blocks\n"); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (chip == -1) { 4728c2ecf20Sopenharmony_ci numblocks = mtd->size >> this->bbt_erase_shift; 4738c2ecf20Sopenharmony_ci startblock = 0; 4748c2ecf20Sopenharmony_ci from = 0; 4758c2ecf20Sopenharmony_ci } else { 4768c2ecf20Sopenharmony_ci if (chip >= nanddev_ntargets(&this->base)) { 4778c2ecf20Sopenharmony_ci pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n", 4788c2ecf20Sopenharmony_ci chip + 1, nanddev_ntargets(&this->base)); 4798c2ecf20Sopenharmony_ci return -EINVAL; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci numblocks = targetsize >> this->bbt_erase_shift; 4828c2ecf20Sopenharmony_ci startblock = chip * numblocks; 4838c2ecf20Sopenharmony_ci numblocks += startblock; 4848c2ecf20Sopenharmony_ci from = (loff_t)startblock << this->bbt_erase_shift; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci for (i = startblock; i < numblocks; i++) { 4888c2ecf20Sopenharmony_ci int ret; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci BUG_ON(bd->options & NAND_BBT_NO_OOB); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci ret = scan_block_fast(this, bd, from, buf); 4938c2ecf20Sopenharmony_ci if (ret < 0) 4948c2ecf20Sopenharmony_ci return ret; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (ret) { 4978c2ecf20Sopenharmony_ci bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); 4988c2ecf20Sopenharmony_ci pr_warn("Bad eraseblock %d at 0x%012llx\n", 4998c2ecf20Sopenharmony_ci i, (unsigned long long)from); 5008c2ecf20Sopenharmony_ci mtd->ecc_stats.badblocks++; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci from += (1 << this->bbt_erase_shift); 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci/** 5098c2ecf20Sopenharmony_ci * search_bbt - [GENERIC] scan the device for a specific bad block table 5108c2ecf20Sopenharmony_ci * @this: NAND chip object 5118c2ecf20Sopenharmony_ci * @buf: temporary buffer 5128c2ecf20Sopenharmony_ci * @td: descriptor for the bad block table 5138c2ecf20Sopenharmony_ci * 5148c2ecf20Sopenharmony_ci * Read the bad block table by searching for a given ident pattern. Search is 5158c2ecf20Sopenharmony_ci * preformed either from the beginning up or from the end of the device 5168c2ecf20Sopenharmony_ci * downwards. The search starts always at the start of a block. If the option 5178c2ecf20Sopenharmony_ci * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains 5188c2ecf20Sopenharmony_ci * the bad block information of this chip. This is necessary to provide support 5198c2ecf20Sopenharmony_ci * for certain DOC devices. 5208c2ecf20Sopenharmony_ci * 5218c2ecf20Sopenharmony_ci * The bbt ident pattern resides in the oob area of the first page in a block. 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_cistatic int search_bbt(struct nand_chip *this, uint8_t *buf, 5248c2ecf20Sopenharmony_ci struct nand_bbt_descr *td) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 5278c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 5288c2ecf20Sopenharmony_ci int i, chips; 5298c2ecf20Sopenharmony_ci int startblock, block, dir; 5308c2ecf20Sopenharmony_ci int scanlen = mtd->writesize + mtd->oobsize; 5318c2ecf20Sopenharmony_ci int bbtblocks; 5328c2ecf20Sopenharmony_ci int blocktopage = this->bbt_erase_shift - this->page_shift; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* Search direction top -> down? */ 5358c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_LASTBLOCK) { 5368c2ecf20Sopenharmony_ci startblock = (mtd->size >> this->bbt_erase_shift) - 1; 5378c2ecf20Sopenharmony_ci dir = -1; 5388c2ecf20Sopenharmony_ci } else { 5398c2ecf20Sopenharmony_ci startblock = 0; 5408c2ecf20Sopenharmony_ci dir = 1; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Do we have a bbt per chip? */ 5448c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) { 5458c2ecf20Sopenharmony_ci chips = nanddev_ntargets(&this->base); 5468c2ecf20Sopenharmony_ci bbtblocks = targetsize >> this->bbt_erase_shift; 5478c2ecf20Sopenharmony_ci startblock &= bbtblocks - 1; 5488c2ecf20Sopenharmony_ci } else { 5498c2ecf20Sopenharmony_ci chips = 1; 5508c2ecf20Sopenharmony_ci bbtblocks = mtd->size >> this->bbt_erase_shift; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci for (i = 0; i < chips; i++) { 5548c2ecf20Sopenharmony_ci /* Reset version information */ 5558c2ecf20Sopenharmony_ci td->version[i] = 0; 5568c2ecf20Sopenharmony_ci td->pages[i] = -1; 5578c2ecf20Sopenharmony_ci /* Scan the maximum number of blocks */ 5588c2ecf20Sopenharmony_ci for (block = 0; block < td->maxblocks; block++) { 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci int actblock = startblock + dir * block; 5618c2ecf20Sopenharmony_ci loff_t offs = (loff_t)actblock << this->bbt_erase_shift; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci /* Read first page */ 5648c2ecf20Sopenharmony_ci scan_read(this, buf, offs, mtd->writesize, td); 5658c2ecf20Sopenharmony_ci if (!check_pattern(buf, scanlen, mtd->writesize, td)) { 5668c2ecf20Sopenharmony_ci td->pages[i] = actblock << blocktopage; 5678c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_VERSION) { 5688c2ecf20Sopenharmony_ci offs = bbt_get_ver_offs(this, td); 5698c2ecf20Sopenharmony_ci td->version[i] = buf[offs]; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci break; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci startblock += targetsize >> this->bbt_erase_shift; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci /* Check, if we found a bbt for each requested chip */ 5778c2ecf20Sopenharmony_ci for (i = 0; i < chips; i++) { 5788c2ecf20Sopenharmony_ci if (td->pages[i] == -1) 5798c2ecf20Sopenharmony_ci pr_warn("Bad block table not found for chip %d\n", i); 5808c2ecf20Sopenharmony_ci else 5818c2ecf20Sopenharmony_ci pr_info("Bad block table found at page %d, version 0x%02X\n", 5828c2ecf20Sopenharmony_ci td->pages[i], td->version[i]); 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci return 0; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci/** 5888c2ecf20Sopenharmony_ci * search_read_bbts - [GENERIC] scan the device for bad block table(s) 5898c2ecf20Sopenharmony_ci * @this: NAND chip object 5908c2ecf20Sopenharmony_ci * @buf: temporary buffer 5918c2ecf20Sopenharmony_ci * @td: descriptor for the bad block table 5928c2ecf20Sopenharmony_ci * @md: descriptor for the bad block table mirror 5938c2ecf20Sopenharmony_ci * 5948c2ecf20Sopenharmony_ci * Search and read the bad block table(s). 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_cistatic void search_read_bbts(struct nand_chip *this, uint8_t *buf, 5978c2ecf20Sopenharmony_ci struct nand_bbt_descr *td, 5988c2ecf20Sopenharmony_ci struct nand_bbt_descr *md) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci /* Search the primary table */ 6018c2ecf20Sopenharmony_ci search_bbt(this, buf, td); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* Search the mirror table */ 6048c2ecf20Sopenharmony_ci if (md) 6058c2ecf20Sopenharmony_ci search_bbt(this, buf, md); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci/** 6098c2ecf20Sopenharmony_ci * get_bbt_block - Get the first valid eraseblock suitable to store a BBT 6108c2ecf20Sopenharmony_ci * @this: the NAND device 6118c2ecf20Sopenharmony_ci * @td: the BBT description 6128c2ecf20Sopenharmony_ci * @md: the mirror BBT descriptor 6138c2ecf20Sopenharmony_ci * @chip: the CHIP selector 6148c2ecf20Sopenharmony_ci * 6158c2ecf20Sopenharmony_ci * This functions returns a positive block number pointing a valid eraseblock 6168c2ecf20Sopenharmony_ci * suitable to store a BBT (i.e. in the range reserved for BBT), or -ENOSPC if 6178c2ecf20Sopenharmony_ci * all blocks are already used of marked bad. If td->pages[chip] was already 6188c2ecf20Sopenharmony_ci * pointing to a valid block we re-use it, otherwise we search for the next 6198c2ecf20Sopenharmony_ci * valid one. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_cistatic int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td, 6228c2ecf20Sopenharmony_ci struct nand_bbt_descr *md, int chip) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 6258c2ecf20Sopenharmony_ci int startblock, dir, page, numblocks, i; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* 6288c2ecf20Sopenharmony_ci * There was already a version of the table, reuse the page. This 6298c2ecf20Sopenharmony_ci * applies for absolute placement too, as we have the page number in 6308c2ecf20Sopenharmony_ci * td->pages. 6318c2ecf20Sopenharmony_ci */ 6328c2ecf20Sopenharmony_ci if (td->pages[chip] != -1) 6338c2ecf20Sopenharmony_ci return td->pages[chip] >> 6348c2ecf20Sopenharmony_ci (this->bbt_erase_shift - this->page_shift); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci numblocks = (int)(targetsize >> this->bbt_erase_shift); 6378c2ecf20Sopenharmony_ci if (!(td->options & NAND_BBT_PERCHIP)) 6388c2ecf20Sopenharmony_ci numblocks *= nanddev_ntargets(&this->base); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* 6418c2ecf20Sopenharmony_ci * Automatic placement of the bad block table. Search direction 6428c2ecf20Sopenharmony_ci * top -> down? 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_LASTBLOCK) { 6458c2ecf20Sopenharmony_ci startblock = numblocks * (chip + 1) - 1; 6468c2ecf20Sopenharmony_ci dir = -1; 6478c2ecf20Sopenharmony_ci } else { 6488c2ecf20Sopenharmony_ci startblock = chip * numblocks; 6498c2ecf20Sopenharmony_ci dir = 1; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci for (i = 0; i < td->maxblocks; i++) { 6538c2ecf20Sopenharmony_ci int block = startblock + dir * i; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* Check, if the block is bad */ 6568c2ecf20Sopenharmony_ci switch (bbt_get_entry(this, block)) { 6578c2ecf20Sopenharmony_ci case BBT_BLOCK_WORN: 6588c2ecf20Sopenharmony_ci case BBT_BLOCK_FACTORY_BAD: 6598c2ecf20Sopenharmony_ci continue; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci page = block << (this->bbt_erase_shift - this->page_shift); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci /* Check, if the block is used by the mirror table */ 6658c2ecf20Sopenharmony_ci if (!md || md->pages[chip] != page) 6668c2ecf20Sopenharmony_ci return block; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return -ENOSPC; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci/** 6738c2ecf20Sopenharmony_ci * mark_bbt_block_bad - Mark one of the block reserved for BBT bad 6748c2ecf20Sopenharmony_ci * @this: the NAND device 6758c2ecf20Sopenharmony_ci * @td: the BBT description 6768c2ecf20Sopenharmony_ci * @chip: the CHIP selector 6778c2ecf20Sopenharmony_ci * @block: the BBT block to mark 6788c2ecf20Sopenharmony_ci * 6798c2ecf20Sopenharmony_ci * Blocks reserved for BBT can become bad. This functions is an helper to mark 6808c2ecf20Sopenharmony_ci * such blocks as bad. It takes care of updating the in-memory BBT, marking the 6818c2ecf20Sopenharmony_ci * block as bad using a bad block marker and invalidating the associated 6828c2ecf20Sopenharmony_ci * td->pages[] entry. 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_cistatic void mark_bbt_block_bad(struct nand_chip *this, 6858c2ecf20Sopenharmony_ci struct nand_bbt_descr *td, 6868c2ecf20Sopenharmony_ci int chip, int block) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci loff_t to; 6898c2ecf20Sopenharmony_ci int res; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci bbt_mark_entry(this, block, BBT_BLOCK_WORN); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci to = (loff_t)block << this->bbt_erase_shift; 6948c2ecf20Sopenharmony_ci res = nand_markbad_bbm(this, to); 6958c2ecf20Sopenharmony_ci if (res) 6968c2ecf20Sopenharmony_ci pr_warn("nand_bbt: error %d while marking block %d bad\n", 6978c2ecf20Sopenharmony_ci res, block); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci td->pages[chip] = -1; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci/** 7038c2ecf20Sopenharmony_ci * write_bbt - [GENERIC] (Re)write the bad block table 7048c2ecf20Sopenharmony_ci * @this: NAND chip object 7058c2ecf20Sopenharmony_ci * @buf: temporary buffer 7068c2ecf20Sopenharmony_ci * @td: descriptor for the bad block table 7078c2ecf20Sopenharmony_ci * @md: descriptor for the bad block table mirror 7088c2ecf20Sopenharmony_ci * @chipsel: selector for a specific chip, -1 for all 7098c2ecf20Sopenharmony_ci * 7108c2ecf20Sopenharmony_ci * (Re)write the bad block table. 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_cistatic int write_bbt(struct nand_chip *this, uint8_t *buf, 7138c2ecf20Sopenharmony_ci struct nand_bbt_descr *td, struct nand_bbt_descr *md, 7148c2ecf20Sopenharmony_ci int chipsel) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 7178c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 7188c2ecf20Sopenharmony_ci struct erase_info einfo; 7198c2ecf20Sopenharmony_ci int i, res, chip = 0; 7208c2ecf20Sopenharmony_ci int bits, page, offs, numblocks, sft, sftmsk; 7218c2ecf20Sopenharmony_ci int nrchips, pageoffs, ooboffs; 7228c2ecf20Sopenharmony_ci uint8_t msk[4]; 7238c2ecf20Sopenharmony_ci uint8_t rcode = td->reserved_block_code; 7248c2ecf20Sopenharmony_ci size_t retlen, len = 0; 7258c2ecf20Sopenharmony_ci loff_t to; 7268c2ecf20Sopenharmony_ci struct mtd_oob_ops ops; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci ops.ooblen = mtd->oobsize; 7298c2ecf20Sopenharmony_ci ops.ooboffs = 0; 7308c2ecf20Sopenharmony_ci ops.datbuf = NULL; 7318c2ecf20Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (!rcode) 7348c2ecf20Sopenharmony_ci rcode = 0xff; 7358c2ecf20Sopenharmony_ci /* Write bad block table per chip rather than per device? */ 7368c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) { 7378c2ecf20Sopenharmony_ci numblocks = (int)(targetsize >> this->bbt_erase_shift); 7388c2ecf20Sopenharmony_ci /* Full device write or specific chip? */ 7398c2ecf20Sopenharmony_ci if (chipsel == -1) { 7408c2ecf20Sopenharmony_ci nrchips = nanddev_ntargets(&this->base); 7418c2ecf20Sopenharmony_ci } else { 7428c2ecf20Sopenharmony_ci nrchips = chipsel + 1; 7438c2ecf20Sopenharmony_ci chip = chipsel; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci } else { 7468c2ecf20Sopenharmony_ci numblocks = (int)(mtd->size >> this->bbt_erase_shift); 7478c2ecf20Sopenharmony_ci nrchips = 1; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci /* Loop through the chips */ 7518c2ecf20Sopenharmony_ci while (chip < nrchips) { 7528c2ecf20Sopenharmony_ci int block; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci block = get_bbt_block(this, td, md, chip); 7558c2ecf20Sopenharmony_ci if (block < 0) { 7568c2ecf20Sopenharmony_ci pr_err("No space left to write bad block table\n"); 7578c2ecf20Sopenharmony_ci res = block; 7588c2ecf20Sopenharmony_ci goto outerr; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* 7628c2ecf20Sopenharmony_ci * get_bbt_block() returns a block number, shift the value to 7638c2ecf20Sopenharmony_ci * get a page number. 7648c2ecf20Sopenharmony_ci */ 7658c2ecf20Sopenharmony_ci page = block << (this->bbt_erase_shift - this->page_shift); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* Set up shift count and masks for the flash table */ 7688c2ecf20Sopenharmony_ci bits = td->options & NAND_BBT_NRBITS_MSK; 7698c2ecf20Sopenharmony_ci msk[2] = ~rcode; 7708c2ecf20Sopenharmony_ci switch (bits) { 7718c2ecf20Sopenharmony_ci case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; 7728c2ecf20Sopenharmony_ci msk[3] = 0x01; 7738c2ecf20Sopenharmony_ci break; 7748c2ecf20Sopenharmony_ci case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; 7758c2ecf20Sopenharmony_ci msk[3] = 0x03; 7768c2ecf20Sopenharmony_ci break; 7778c2ecf20Sopenharmony_ci case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; 7788c2ecf20Sopenharmony_ci msk[3] = 0x0f; 7798c2ecf20Sopenharmony_ci break; 7808c2ecf20Sopenharmony_ci case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; 7818c2ecf20Sopenharmony_ci msk[3] = 0xff; 7828c2ecf20Sopenharmony_ci break; 7838c2ecf20Sopenharmony_ci default: return -EINVAL; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci to = ((loff_t)page) << this->page_shift; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* Must we save the block contents? */ 7898c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_SAVECONTENT) { 7908c2ecf20Sopenharmony_ci /* Make it block aligned */ 7918c2ecf20Sopenharmony_ci to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1); 7928c2ecf20Sopenharmony_ci len = 1 << this->bbt_erase_shift; 7938c2ecf20Sopenharmony_ci res = mtd_read(mtd, to, len, &retlen, buf); 7948c2ecf20Sopenharmony_ci if (res < 0) { 7958c2ecf20Sopenharmony_ci if (retlen != len) { 7968c2ecf20Sopenharmony_ci pr_info("nand_bbt: error reading block for writing the bad block table\n"); 7978c2ecf20Sopenharmony_ci return res; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci pr_warn("nand_bbt: ECC error while reading block for writing bad block table\n"); 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci /* Read oob data */ 8028c2ecf20Sopenharmony_ci ops.ooblen = (len >> this->page_shift) * mtd->oobsize; 8038c2ecf20Sopenharmony_ci ops.oobbuf = &buf[len]; 8048c2ecf20Sopenharmony_ci res = mtd_read_oob(mtd, to + mtd->writesize, &ops); 8058c2ecf20Sopenharmony_ci if (res < 0 || ops.oobretlen != ops.ooblen) 8068c2ecf20Sopenharmony_ci goto outerr; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* Calc the byte offset in the buffer */ 8098c2ecf20Sopenharmony_ci pageoffs = page - (int)(to >> this->page_shift); 8108c2ecf20Sopenharmony_ci offs = pageoffs << this->page_shift; 8118c2ecf20Sopenharmony_ci /* Preset the bbt area with 0xff */ 8128c2ecf20Sopenharmony_ci memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); 8138c2ecf20Sopenharmony_ci ooboffs = len + (pageoffs * mtd->oobsize); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci } else if (td->options & NAND_BBT_NO_OOB) { 8168c2ecf20Sopenharmony_ci ooboffs = 0; 8178c2ecf20Sopenharmony_ci offs = td->len; 8188c2ecf20Sopenharmony_ci /* The version byte */ 8198c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_VERSION) 8208c2ecf20Sopenharmony_ci offs++; 8218c2ecf20Sopenharmony_ci /* Calc length */ 8228c2ecf20Sopenharmony_ci len = (size_t)(numblocks >> sft); 8238c2ecf20Sopenharmony_ci len += offs; 8248c2ecf20Sopenharmony_ci /* Make it page aligned! */ 8258c2ecf20Sopenharmony_ci len = ALIGN(len, mtd->writesize); 8268c2ecf20Sopenharmony_ci /* Preset the buffer with 0xff */ 8278c2ecf20Sopenharmony_ci memset(buf, 0xff, len); 8288c2ecf20Sopenharmony_ci /* Pattern is located at the begin of first page */ 8298c2ecf20Sopenharmony_ci memcpy(buf, td->pattern, td->len); 8308c2ecf20Sopenharmony_ci } else { 8318c2ecf20Sopenharmony_ci /* Calc length */ 8328c2ecf20Sopenharmony_ci len = (size_t)(numblocks >> sft); 8338c2ecf20Sopenharmony_ci /* Make it page aligned! */ 8348c2ecf20Sopenharmony_ci len = ALIGN(len, mtd->writesize); 8358c2ecf20Sopenharmony_ci /* Preset the buffer with 0xff */ 8368c2ecf20Sopenharmony_ci memset(buf, 0xff, len + 8378c2ecf20Sopenharmony_ci (len >> this->page_shift)* mtd->oobsize); 8388c2ecf20Sopenharmony_ci offs = 0; 8398c2ecf20Sopenharmony_ci ooboffs = len; 8408c2ecf20Sopenharmony_ci /* Pattern is located in oob area of first page */ 8418c2ecf20Sopenharmony_ci memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_VERSION) 8458c2ecf20Sopenharmony_ci buf[ooboffs + td->veroffs] = td->version[chip]; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci /* Walk through the memory table */ 8488c2ecf20Sopenharmony_ci for (i = 0; i < numblocks; i++) { 8498c2ecf20Sopenharmony_ci uint8_t dat; 8508c2ecf20Sopenharmony_ci int sftcnt = (i << (3 - sft)) & sftmsk; 8518c2ecf20Sopenharmony_ci dat = bbt_get_entry(this, chip * numblocks + i); 8528c2ecf20Sopenharmony_ci /* Do not store the reserved bbt blocks! */ 8538c2ecf20Sopenharmony_ci buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci memset(&einfo, 0, sizeof(einfo)); 8578c2ecf20Sopenharmony_ci einfo.addr = to; 8588c2ecf20Sopenharmony_ci einfo.len = 1 << this->bbt_erase_shift; 8598c2ecf20Sopenharmony_ci res = nand_erase_nand(this, &einfo, 1); 8608c2ecf20Sopenharmony_ci if (res < 0) { 8618c2ecf20Sopenharmony_ci pr_warn("nand_bbt: error while erasing BBT block %d\n", 8628c2ecf20Sopenharmony_ci res); 8638c2ecf20Sopenharmony_ci mark_bbt_block_bad(this, td, chip, block); 8648c2ecf20Sopenharmony_ci continue; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci res = scan_write_bbt(this, to, len, buf, 8688c2ecf20Sopenharmony_ci td->options & NAND_BBT_NO_OOB ? 8698c2ecf20Sopenharmony_ci NULL : &buf[len]); 8708c2ecf20Sopenharmony_ci if (res < 0) { 8718c2ecf20Sopenharmony_ci pr_warn("nand_bbt: error while writing BBT block %d\n", 8728c2ecf20Sopenharmony_ci res); 8738c2ecf20Sopenharmony_ci mark_bbt_block_bad(this, td, chip, block); 8748c2ecf20Sopenharmony_ci continue; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", 8788c2ecf20Sopenharmony_ci (unsigned long long)to, td->version[chip]); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* Mark it as used */ 8818c2ecf20Sopenharmony_ci td->pages[chip++] = page; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci outerr: 8868c2ecf20Sopenharmony_ci pr_warn("nand_bbt: error while writing bad block table %d\n", res); 8878c2ecf20Sopenharmony_ci return res; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci/** 8918c2ecf20Sopenharmony_ci * nand_memory_bbt - [GENERIC] create a memory based bad block table 8928c2ecf20Sopenharmony_ci * @this: NAND chip object 8938c2ecf20Sopenharmony_ci * @bd: descriptor for the good/bad block search pattern 8948c2ecf20Sopenharmony_ci * 8958c2ecf20Sopenharmony_ci * The function creates a memory based bbt by scanning the device for 8968c2ecf20Sopenharmony_ci * manufacturer / software marked good / bad blocks. 8978c2ecf20Sopenharmony_ci */ 8988c2ecf20Sopenharmony_cistatic inline int nand_memory_bbt(struct nand_chip *this, 8998c2ecf20Sopenharmony_ci struct nand_bbt_descr *bd) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci u8 *pagebuf = nand_get_data_buf(this); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci return create_bbt(this, pagebuf, bd, -1); 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci/** 9078c2ecf20Sopenharmony_ci * check_create - [GENERIC] create and write bbt(s) if necessary 9088c2ecf20Sopenharmony_ci * @this: the NAND device 9098c2ecf20Sopenharmony_ci * @buf: temporary buffer 9108c2ecf20Sopenharmony_ci * @bd: descriptor for the good/bad block search pattern 9118c2ecf20Sopenharmony_ci * 9128c2ecf20Sopenharmony_ci * The function checks the results of the previous call to read_bbt and creates 9138c2ecf20Sopenharmony_ci * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found 9148c2ecf20Sopenharmony_ci * for the chip/device. Update is necessary if one of the tables is missing or 9158c2ecf20Sopenharmony_ci * the version nr. of one table is less than the other. 9168c2ecf20Sopenharmony_ci */ 9178c2ecf20Sopenharmony_cistatic int check_create(struct nand_chip *this, uint8_t *buf, 9188c2ecf20Sopenharmony_ci struct nand_bbt_descr *bd) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci int i, chips, writeops, create, chipsel, res, res2; 9218c2ecf20Sopenharmony_ci struct nand_bbt_descr *td = this->bbt_td; 9228c2ecf20Sopenharmony_ci struct nand_bbt_descr *md = this->bbt_md; 9238c2ecf20Sopenharmony_ci struct nand_bbt_descr *rd, *rd2; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci /* Do we have a bbt per chip? */ 9268c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) 9278c2ecf20Sopenharmony_ci chips = nanddev_ntargets(&this->base); 9288c2ecf20Sopenharmony_ci else 9298c2ecf20Sopenharmony_ci chips = 1; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci for (i = 0; i < chips; i++) { 9328c2ecf20Sopenharmony_ci writeops = 0; 9338c2ecf20Sopenharmony_ci create = 0; 9348c2ecf20Sopenharmony_ci rd = NULL; 9358c2ecf20Sopenharmony_ci rd2 = NULL; 9368c2ecf20Sopenharmony_ci res = res2 = 0; 9378c2ecf20Sopenharmony_ci /* Per chip or per device? */ 9388c2ecf20Sopenharmony_ci chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; 9398c2ecf20Sopenharmony_ci /* Mirrored table available? */ 9408c2ecf20Sopenharmony_ci if (md) { 9418c2ecf20Sopenharmony_ci if (td->pages[i] == -1 && md->pages[i] == -1) { 9428c2ecf20Sopenharmony_ci create = 1; 9438c2ecf20Sopenharmony_ci writeops = 0x03; 9448c2ecf20Sopenharmony_ci } else if (td->pages[i] == -1) { 9458c2ecf20Sopenharmony_ci rd = md; 9468c2ecf20Sopenharmony_ci writeops = 0x01; 9478c2ecf20Sopenharmony_ci } else if (md->pages[i] == -1) { 9488c2ecf20Sopenharmony_ci rd = td; 9498c2ecf20Sopenharmony_ci writeops = 0x02; 9508c2ecf20Sopenharmony_ci } else if (td->version[i] == md->version[i]) { 9518c2ecf20Sopenharmony_ci rd = td; 9528c2ecf20Sopenharmony_ci if (!(td->options & NAND_BBT_VERSION)) 9538c2ecf20Sopenharmony_ci rd2 = md; 9548c2ecf20Sopenharmony_ci } else if (((int8_t)(td->version[i] - md->version[i])) > 0) { 9558c2ecf20Sopenharmony_ci rd = td; 9568c2ecf20Sopenharmony_ci writeops = 0x02; 9578c2ecf20Sopenharmony_ci } else { 9588c2ecf20Sopenharmony_ci rd = md; 9598c2ecf20Sopenharmony_ci writeops = 0x01; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci } else { 9628c2ecf20Sopenharmony_ci if (td->pages[i] == -1) { 9638c2ecf20Sopenharmony_ci create = 1; 9648c2ecf20Sopenharmony_ci writeops = 0x01; 9658c2ecf20Sopenharmony_ci } else { 9668c2ecf20Sopenharmony_ci rd = td; 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (create) { 9718c2ecf20Sopenharmony_ci /* Create the bad block table by scanning the device? */ 9728c2ecf20Sopenharmony_ci if (!(td->options & NAND_BBT_CREATE)) 9738c2ecf20Sopenharmony_ci continue; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* Create the table in memory by scanning the chip(s) */ 9768c2ecf20Sopenharmony_ci if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) 9778c2ecf20Sopenharmony_ci create_bbt(this, buf, bd, chipsel); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci td->version[i] = 1; 9808c2ecf20Sopenharmony_ci if (md) 9818c2ecf20Sopenharmony_ci md->version[i] = 1; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci /* Read back first? */ 9858c2ecf20Sopenharmony_ci if (rd) { 9868c2ecf20Sopenharmony_ci res = read_abs_bbt(this, buf, rd, chipsel); 9878c2ecf20Sopenharmony_ci if (mtd_is_eccerr(res)) { 9888c2ecf20Sopenharmony_ci /* Mark table as invalid */ 9898c2ecf20Sopenharmony_ci rd->pages[i] = -1; 9908c2ecf20Sopenharmony_ci rd->version[i] = 0; 9918c2ecf20Sopenharmony_ci i--; 9928c2ecf20Sopenharmony_ci continue; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci /* If they weren't versioned, read both */ 9968c2ecf20Sopenharmony_ci if (rd2) { 9978c2ecf20Sopenharmony_ci res2 = read_abs_bbt(this, buf, rd2, chipsel); 9988c2ecf20Sopenharmony_ci if (mtd_is_eccerr(res2)) { 9998c2ecf20Sopenharmony_ci /* Mark table as invalid */ 10008c2ecf20Sopenharmony_ci rd2->pages[i] = -1; 10018c2ecf20Sopenharmony_ci rd2->version[i] = 0; 10028c2ecf20Sopenharmony_ci i--; 10038c2ecf20Sopenharmony_ci continue; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* Scrub the flash table(s)? */ 10088c2ecf20Sopenharmony_ci if (mtd_is_bitflip(res) || mtd_is_bitflip(res2)) 10098c2ecf20Sopenharmony_ci writeops = 0x03; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* Update version numbers before writing */ 10128c2ecf20Sopenharmony_ci if (md) { 10138c2ecf20Sopenharmony_ci td->version[i] = max(td->version[i], md->version[i]); 10148c2ecf20Sopenharmony_ci md->version[i] = td->version[i]; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* Write the bad block table to the device? */ 10188c2ecf20Sopenharmony_ci if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { 10198c2ecf20Sopenharmony_ci res = write_bbt(this, buf, td, md, chipsel); 10208c2ecf20Sopenharmony_ci if (res < 0) 10218c2ecf20Sopenharmony_ci return res; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* Write the mirror bad block table to the device? */ 10258c2ecf20Sopenharmony_ci if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { 10268c2ecf20Sopenharmony_ci res = write_bbt(this, buf, md, td, chipsel); 10278c2ecf20Sopenharmony_ci if (res < 0) 10288c2ecf20Sopenharmony_ci return res; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci return 0; 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci/** 10358c2ecf20Sopenharmony_ci * nand_update_bbt - update bad block table(s) 10368c2ecf20Sopenharmony_ci * @this: the NAND device 10378c2ecf20Sopenharmony_ci * @offs: the offset of the newly marked block 10388c2ecf20Sopenharmony_ci * 10398c2ecf20Sopenharmony_ci * The function updates the bad block table(s). 10408c2ecf20Sopenharmony_ci */ 10418c2ecf20Sopenharmony_cistatic int nand_update_bbt(struct nand_chip *this, loff_t offs) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 10448c2ecf20Sopenharmony_ci int len, res = 0; 10458c2ecf20Sopenharmony_ci int chip, chipsel; 10468c2ecf20Sopenharmony_ci uint8_t *buf; 10478c2ecf20Sopenharmony_ci struct nand_bbt_descr *td = this->bbt_td; 10488c2ecf20Sopenharmony_ci struct nand_bbt_descr *md = this->bbt_md; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (!this->bbt || !td) 10518c2ecf20Sopenharmony_ci return -EINVAL; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* Allocate a temporary buffer for one eraseblock incl. oob */ 10548c2ecf20Sopenharmony_ci len = (1 << this->bbt_erase_shift); 10558c2ecf20Sopenharmony_ci len += (len >> this->page_shift) * mtd->oobsize; 10568c2ecf20Sopenharmony_ci buf = kmalloc(len, GFP_KERNEL); 10578c2ecf20Sopenharmony_ci if (!buf) 10588c2ecf20Sopenharmony_ci return -ENOMEM; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* Do we have a bbt per chip? */ 10618c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) { 10628c2ecf20Sopenharmony_ci chip = (int)(offs >> this->chip_shift); 10638c2ecf20Sopenharmony_ci chipsel = chip; 10648c2ecf20Sopenharmony_ci } else { 10658c2ecf20Sopenharmony_ci chip = 0; 10668c2ecf20Sopenharmony_ci chipsel = -1; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci td->version[chip]++; 10708c2ecf20Sopenharmony_ci if (md) 10718c2ecf20Sopenharmony_ci md->version[chip]++; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci /* Write the bad block table to the device? */ 10748c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_WRITE) { 10758c2ecf20Sopenharmony_ci res = write_bbt(this, buf, td, md, chipsel); 10768c2ecf20Sopenharmony_ci if (res < 0) 10778c2ecf20Sopenharmony_ci goto out; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci /* Write the mirror bad block table to the device? */ 10808c2ecf20Sopenharmony_ci if (md && (md->options & NAND_BBT_WRITE)) { 10818c2ecf20Sopenharmony_ci res = write_bbt(this, buf, md, td, chipsel); 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci out: 10858c2ecf20Sopenharmony_ci kfree(buf); 10868c2ecf20Sopenharmony_ci return res; 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci/** 10908c2ecf20Sopenharmony_ci * mark_bbt_regions - [GENERIC] mark the bad block table regions 10918c2ecf20Sopenharmony_ci * @this: the NAND device 10928c2ecf20Sopenharmony_ci * @td: bad block table descriptor 10938c2ecf20Sopenharmony_ci * 10948c2ecf20Sopenharmony_ci * The bad block table regions are marked as "bad" to prevent accidental 10958c2ecf20Sopenharmony_ci * erasures / writes. The regions are identified by the mark 0x02. 10968c2ecf20Sopenharmony_ci */ 10978c2ecf20Sopenharmony_cistatic void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 11008c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 11018c2ecf20Sopenharmony_ci int i, j, chips, block, nrblocks, update; 11028c2ecf20Sopenharmony_ci uint8_t oldval; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci /* Do we have a bbt per chip? */ 11058c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_PERCHIP) { 11068c2ecf20Sopenharmony_ci chips = nanddev_ntargets(&this->base); 11078c2ecf20Sopenharmony_ci nrblocks = (int)(targetsize >> this->bbt_erase_shift); 11088c2ecf20Sopenharmony_ci } else { 11098c2ecf20Sopenharmony_ci chips = 1; 11108c2ecf20Sopenharmony_ci nrblocks = (int)(mtd->size >> this->bbt_erase_shift); 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci for (i = 0; i < chips; i++) { 11148c2ecf20Sopenharmony_ci if ((td->options & NAND_BBT_ABSPAGE) || 11158c2ecf20Sopenharmony_ci !(td->options & NAND_BBT_WRITE)) { 11168c2ecf20Sopenharmony_ci if (td->pages[i] == -1) 11178c2ecf20Sopenharmony_ci continue; 11188c2ecf20Sopenharmony_ci block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); 11198c2ecf20Sopenharmony_ci oldval = bbt_get_entry(this, block); 11208c2ecf20Sopenharmony_ci bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); 11218c2ecf20Sopenharmony_ci if ((oldval != BBT_BLOCK_RESERVED) && 11228c2ecf20Sopenharmony_ci td->reserved_block_code) 11238c2ecf20Sopenharmony_ci nand_update_bbt(this, (loff_t)block << 11248c2ecf20Sopenharmony_ci this->bbt_erase_shift); 11258c2ecf20Sopenharmony_ci continue; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci update = 0; 11288c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_LASTBLOCK) 11298c2ecf20Sopenharmony_ci block = ((i + 1) * nrblocks) - td->maxblocks; 11308c2ecf20Sopenharmony_ci else 11318c2ecf20Sopenharmony_ci block = i * nrblocks; 11328c2ecf20Sopenharmony_ci for (j = 0; j < td->maxblocks; j++) { 11338c2ecf20Sopenharmony_ci oldval = bbt_get_entry(this, block); 11348c2ecf20Sopenharmony_ci bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); 11358c2ecf20Sopenharmony_ci if (oldval != BBT_BLOCK_RESERVED) 11368c2ecf20Sopenharmony_ci update = 1; 11378c2ecf20Sopenharmony_ci block++; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci /* 11408c2ecf20Sopenharmony_ci * If we want reserved blocks to be recorded to flash, and some 11418c2ecf20Sopenharmony_ci * new ones have been marked, then we need to update the stored 11428c2ecf20Sopenharmony_ci * bbts. This should only happen once. 11438c2ecf20Sopenharmony_ci */ 11448c2ecf20Sopenharmony_ci if (update && td->reserved_block_code) 11458c2ecf20Sopenharmony_ci nand_update_bbt(this, (loff_t)(block - 1) << 11468c2ecf20Sopenharmony_ci this->bbt_erase_shift); 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci/** 11518c2ecf20Sopenharmony_ci * verify_bbt_descr - verify the bad block description 11528c2ecf20Sopenharmony_ci * @this: the NAND device 11538c2ecf20Sopenharmony_ci * @bd: the table to verify 11548c2ecf20Sopenharmony_ci * 11558c2ecf20Sopenharmony_ci * This functions performs a few sanity checks on the bad block description 11568c2ecf20Sopenharmony_ci * table. 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_cistatic void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci u64 targetsize = nanddev_target_size(&this->base); 11618c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 11628c2ecf20Sopenharmony_ci u32 pattern_len; 11638c2ecf20Sopenharmony_ci u32 bits; 11648c2ecf20Sopenharmony_ci u32 table_size; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci if (!bd) 11678c2ecf20Sopenharmony_ci return; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci pattern_len = bd->len; 11708c2ecf20Sopenharmony_ci bits = bd->options & NAND_BBT_NRBITS_MSK; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) && 11738c2ecf20Sopenharmony_ci !(this->bbt_options & NAND_BBT_USE_FLASH)); 11748c2ecf20Sopenharmony_ci BUG_ON(!bits); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci if (bd->options & NAND_BBT_VERSION) 11778c2ecf20Sopenharmony_ci pattern_len++; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci if (bd->options & NAND_BBT_NO_OOB) { 11808c2ecf20Sopenharmony_ci BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH)); 11818c2ecf20Sopenharmony_ci BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB)); 11828c2ecf20Sopenharmony_ci BUG_ON(bd->offs); 11838c2ecf20Sopenharmony_ci if (bd->options & NAND_BBT_VERSION) 11848c2ecf20Sopenharmony_ci BUG_ON(bd->veroffs != bd->len); 11858c2ecf20Sopenharmony_ci BUG_ON(bd->options & NAND_BBT_SAVECONTENT); 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (bd->options & NAND_BBT_PERCHIP) 11898c2ecf20Sopenharmony_ci table_size = targetsize >> this->bbt_erase_shift; 11908c2ecf20Sopenharmony_ci else 11918c2ecf20Sopenharmony_ci table_size = mtd->size >> this->bbt_erase_shift; 11928c2ecf20Sopenharmony_ci table_size >>= 3; 11938c2ecf20Sopenharmony_ci table_size *= bits; 11948c2ecf20Sopenharmony_ci if (bd->options & NAND_BBT_NO_OOB) 11958c2ecf20Sopenharmony_ci table_size += pattern_len; 11968c2ecf20Sopenharmony_ci BUG_ON(table_size > (1 << this->bbt_erase_shift)); 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci/** 12008c2ecf20Sopenharmony_ci * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) 12018c2ecf20Sopenharmony_ci * @this: the NAND device 12028c2ecf20Sopenharmony_ci * @bd: descriptor for the good/bad block search pattern 12038c2ecf20Sopenharmony_ci * 12048c2ecf20Sopenharmony_ci * The function checks, if a bad block table(s) is/are already available. If 12058c2ecf20Sopenharmony_ci * not it scans the device for manufacturer marked good / bad blocks and writes 12068c2ecf20Sopenharmony_ci * the bad block table(s) to the selected place. 12078c2ecf20Sopenharmony_ci * 12088c2ecf20Sopenharmony_ci * The bad block table memory is allocated here. It must be freed by calling 12098c2ecf20Sopenharmony_ci * the nand_free_bbt function. 12108c2ecf20Sopenharmony_ci */ 12118c2ecf20Sopenharmony_cistatic int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(this); 12148c2ecf20Sopenharmony_ci int len, res; 12158c2ecf20Sopenharmony_ci uint8_t *buf; 12168c2ecf20Sopenharmony_ci struct nand_bbt_descr *td = this->bbt_td; 12178c2ecf20Sopenharmony_ci struct nand_bbt_descr *md = this->bbt_md; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1; 12208c2ecf20Sopenharmony_ci /* 12218c2ecf20Sopenharmony_ci * Allocate memory (2bit per block) and clear the memory bad block 12228c2ecf20Sopenharmony_ci * table. 12238c2ecf20Sopenharmony_ci */ 12248c2ecf20Sopenharmony_ci this->bbt = kzalloc(len, GFP_KERNEL); 12258c2ecf20Sopenharmony_ci if (!this->bbt) 12268c2ecf20Sopenharmony_ci return -ENOMEM; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci /* 12298c2ecf20Sopenharmony_ci * If no primary table descriptor is given, scan the device to build a 12308c2ecf20Sopenharmony_ci * memory based bad block table. 12318c2ecf20Sopenharmony_ci */ 12328c2ecf20Sopenharmony_ci if (!td) { 12338c2ecf20Sopenharmony_ci if ((res = nand_memory_bbt(this, bd))) { 12348c2ecf20Sopenharmony_ci pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); 12358c2ecf20Sopenharmony_ci goto err_free_bbt; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci verify_bbt_descr(this, td); 12408c2ecf20Sopenharmony_ci verify_bbt_descr(this, md); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci /* Allocate a temporary buffer for one eraseblock incl. oob */ 12438c2ecf20Sopenharmony_ci len = (1 << this->bbt_erase_shift); 12448c2ecf20Sopenharmony_ci len += (len >> this->page_shift) * mtd->oobsize; 12458c2ecf20Sopenharmony_ci buf = vmalloc(len); 12468c2ecf20Sopenharmony_ci if (!buf) { 12478c2ecf20Sopenharmony_ci res = -ENOMEM; 12488c2ecf20Sopenharmony_ci goto err_free_bbt; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci /* Is the bbt at a given page? */ 12528c2ecf20Sopenharmony_ci if (td->options & NAND_BBT_ABSPAGE) { 12538c2ecf20Sopenharmony_ci read_abs_bbts(this, buf, td, md); 12548c2ecf20Sopenharmony_ci } else { 12558c2ecf20Sopenharmony_ci /* Search the bad block table using a pattern in oob */ 12568c2ecf20Sopenharmony_ci search_read_bbts(this, buf, td, md); 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci res = check_create(this, buf, bd); 12608c2ecf20Sopenharmony_ci if (res) 12618c2ecf20Sopenharmony_ci goto err_free_buf; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci /* Prevent the bbt regions from erasing / writing */ 12648c2ecf20Sopenharmony_ci mark_bbt_region(this, td); 12658c2ecf20Sopenharmony_ci if (md) 12668c2ecf20Sopenharmony_ci mark_bbt_region(this, md); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci vfree(buf); 12698c2ecf20Sopenharmony_ci return 0; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cierr_free_buf: 12728c2ecf20Sopenharmony_ci vfree(buf); 12738c2ecf20Sopenharmony_cierr_free_bbt: 12748c2ecf20Sopenharmony_ci kfree(this->bbt); 12758c2ecf20Sopenharmony_ci this->bbt = NULL; 12768c2ecf20Sopenharmony_ci return res; 12778c2ecf20Sopenharmony_ci} 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci/* 12808c2ecf20Sopenharmony_ci * Define some generic bad / good block scan pattern which are used 12818c2ecf20Sopenharmony_ci * while scanning a device for factory marked good / bad blocks. 12828c2ecf20Sopenharmony_ci */ 12838c2ecf20Sopenharmony_cistatic uint8_t scan_ff_pattern[] = { 0xff, 0xff }; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci/* Generic flash bbt descriptors */ 12868c2ecf20Sopenharmony_cistatic uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; 12878c2ecf20Sopenharmony_cistatic uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_main_descr = { 12908c2ecf20Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 12918c2ecf20Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, 12928c2ecf20Sopenharmony_ci .offs = 8, 12938c2ecf20Sopenharmony_ci .len = 4, 12948c2ecf20Sopenharmony_ci .veroffs = 12, 12958c2ecf20Sopenharmony_ci .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 12968c2ecf20Sopenharmony_ci .pattern = bbt_pattern 12978c2ecf20Sopenharmony_ci}; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_mirror_descr = { 13008c2ecf20Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 13018c2ecf20Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, 13028c2ecf20Sopenharmony_ci .offs = 8, 13038c2ecf20Sopenharmony_ci .len = 4, 13048c2ecf20Sopenharmony_ci .veroffs = 12, 13058c2ecf20Sopenharmony_ci .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 13068c2ecf20Sopenharmony_ci .pattern = mirror_pattern 13078c2ecf20Sopenharmony_ci}; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_main_no_oob_descr = { 13108c2ecf20Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 13118c2ecf20Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP 13128c2ecf20Sopenharmony_ci | NAND_BBT_NO_OOB, 13138c2ecf20Sopenharmony_ci .len = 4, 13148c2ecf20Sopenharmony_ci .veroffs = 4, 13158c2ecf20Sopenharmony_ci .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 13168c2ecf20Sopenharmony_ci .pattern = bbt_pattern 13178c2ecf20Sopenharmony_ci}; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_mirror_no_oob_descr = { 13208c2ecf20Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 13218c2ecf20Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP 13228c2ecf20Sopenharmony_ci | NAND_BBT_NO_OOB, 13238c2ecf20Sopenharmony_ci .len = 4, 13248c2ecf20Sopenharmony_ci .veroffs = 4, 13258c2ecf20Sopenharmony_ci .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, 13268c2ecf20Sopenharmony_ci .pattern = mirror_pattern 13278c2ecf20Sopenharmony_ci}; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) 13308c2ecf20Sopenharmony_ci/** 13318c2ecf20Sopenharmony_ci * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure 13328c2ecf20Sopenharmony_ci * @this: NAND chip to create descriptor for 13338c2ecf20Sopenharmony_ci * 13348c2ecf20Sopenharmony_ci * This function allocates and initializes a nand_bbt_descr for BBM detection 13358c2ecf20Sopenharmony_ci * based on the properties of @this. The new descriptor is stored in 13368c2ecf20Sopenharmony_ci * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when 13378c2ecf20Sopenharmony_ci * passed to this function. 13388c2ecf20Sopenharmony_ci */ 13398c2ecf20Sopenharmony_cistatic int nand_create_badblock_pattern(struct nand_chip *this) 13408c2ecf20Sopenharmony_ci{ 13418c2ecf20Sopenharmony_ci struct nand_bbt_descr *bd; 13428c2ecf20Sopenharmony_ci if (this->badblock_pattern) { 13438c2ecf20Sopenharmony_ci pr_warn("Bad block pattern already allocated; not replacing\n"); 13448c2ecf20Sopenharmony_ci return -EINVAL; 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci bd = kzalloc(sizeof(*bd), GFP_KERNEL); 13478c2ecf20Sopenharmony_ci if (!bd) 13488c2ecf20Sopenharmony_ci return -ENOMEM; 13498c2ecf20Sopenharmony_ci bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; 13508c2ecf20Sopenharmony_ci bd->offs = this->badblockpos; 13518c2ecf20Sopenharmony_ci bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; 13528c2ecf20Sopenharmony_ci bd->pattern = scan_ff_pattern; 13538c2ecf20Sopenharmony_ci bd->options |= NAND_BBT_DYNAMICSTRUCT; 13548c2ecf20Sopenharmony_ci this->badblock_pattern = bd; 13558c2ecf20Sopenharmony_ci return 0; 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci/** 13598c2ecf20Sopenharmony_ci * nand_create_bbt - [NAND Interface] Select a default bad block table for the device 13608c2ecf20Sopenharmony_ci * @this: NAND chip object 13618c2ecf20Sopenharmony_ci * 13628c2ecf20Sopenharmony_ci * This function selects the default bad block table support for the device and 13638c2ecf20Sopenharmony_ci * calls the nand_scan_bbt function. 13648c2ecf20Sopenharmony_ci */ 13658c2ecf20Sopenharmony_ciint nand_create_bbt(struct nand_chip *this) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci int ret; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci /* Is a flash based bad block table requested? */ 13708c2ecf20Sopenharmony_ci if (this->bbt_options & NAND_BBT_USE_FLASH) { 13718c2ecf20Sopenharmony_ci /* Use the default pattern descriptors */ 13728c2ecf20Sopenharmony_ci if (!this->bbt_td) { 13738c2ecf20Sopenharmony_ci if (this->bbt_options & NAND_BBT_NO_OOB) { 13748c2ecf20Sopenharmony_ci this->bbt_td = &bbt_main_no_oob_descr; 13758c2ecf20Sopenharmony_ci this->bbt_md = &bbt_mirror_no_oob_descr; 13768c2ecf20Sopenharmony_ci } else { 13778c2ecf20Sopenharmony_ci this->bbt_td = &bbt_main_descr; 13788c2ecf20Sopenharmony_ci this->bbt_md = &bbt_mirror_descr; 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci } else { 13828c2ecf20Sopenharmony_ci this->bbt_td = NULL; 13838c2ecf20Sopenharmony_ci this->bbt_md = NULL; 13848c2ecf20Sopenharmony_ci } 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (!this->badblock_pattern) { 13878c2ecf20Sopenharmony_ci ret = nand_create_badblock_pattern(this); 13888c2ecf20Sopenharmony_ci if (ret) 13898c2ecf20Sopenharmony_ci return ret; 13908c2ecf20Sopenharmony_ci } 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci return nand_scan_bbt(this, this->badblock_pattern); 13938c2ecf20Sopenharmony_ci} 13948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nand_create_bbt); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci/** 13978c2ecf20Sopenharmony_ci * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved 13988c2ecf20Sopenharmony_ci * @this: NAND chip object 13998c2ecf20Sopenharmony_ci * @offs: offset in the device 14008c2ecf20Sopenharmony_ci */ 14018c2ecf20Sopenharmony_ciint nand_isreserved_bbt(struct nand_chip *this, loff_t offs) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci int block; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci block = (int)(offs >> this->bbt_erase_shift); 14068c2ecf20Sopenharmony_ci return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci/** 14108c2ecf20Sopenharmony_ci * nand_isbad_bbt - [NAND Interface] Check if a block is bad 14118c2ecf20Sopenharmony_ci * @this: NAND chip object 14128c2ecf20Sopenharmony_ci * @offs: offset in the device 14138c2ecf20Sopenharmony_ci * @allowbbt: allow access to bad block table region 14148c2ecf20Sopenharmony_ci */ 14158c2ecf20Sopenharmony_ciint nand_isbad_bbt(struct nand_chip *this, loff_t offs, int allowbbt) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci int block, res; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci block = (int)(offs >> this->bbt_erase_shift); 14208c2ecf20Sopenharmony_ci res = bbt_get_entry(this, block); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", 14238c2ecf20Sopenharmony_ci (unsigned int)offs, block, res); 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci switch (res) { 14268c2ecf20Sopenharmony_ci case BBT_BLOCK_GOOD: 14278c2ecf20Sopenharmony_ci return 0; 14288c2ecf20Sopenharmony_ci case BBT_BLOCK_WORN: 14298c2ecf20Sopenharmony_ci return 1; 14308c2ecf20Sopenharmony_ci case BBT_BLOCK_RESERVED: 14318c2ecf20Sopenharmony_ci return allowbbt ? 0 : 1; 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci return 1; 14348c2ecf20Sopenharmony_ci} 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci/** 14378c2ecf20Sopenharmony_ci * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT 14388c2ecf20Sopenharmony_ci * @this: NAND chip object 14398c2ecf20Sopenharmony_ci * @offs: offset of the bad block 14408c2ecf20Sopenharmony_ci */ 14418c2ecf20Sopenharmony_ciint nand_markbad_bbt(struct nand_chip *this, loff_t offs) 14428c2ecf20Sopenharmony_ci{ 14438c2ecf20Sopenharmony_ci int block, ret = 0; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci block = (int)(offs >> this->bbt_erase_shift); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci /* Mark bad block in memory */ 14488c2ecf20Sopenharmony_ci bbt_mark_entry(this, block, BBT_BLOCK_WORN); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci /* Update flash-based bad block table */ 14518c2ecf20Sopenharmony_ci if (this->bbt_options & NAND_BBT_USE_FLASH) 14528c2ecf20Sopenharmony_ci ret = nand_update_bbt(this, offs); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci return ret; 14558c2ecf20Sopenharmony_ci} 1456