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