xref: /rk3399_rockchip-uboot/drivers/mtd/nand/bbt.c (revision ad0301ff2ed4230485778ac43e34289e25c608eb)
1ed99f773SBoris Brezillon // SPDX-License-Identifier: GPL-2.0
2ed99f773SBoris Brezillon /*
3ed99f773SBoris Brezillon  * Copyright (c) 2017 Free Electrons
4ed99f773SBoris Brezillon  *
5ed99f773SBoris Brezillon  * Authors:
6ed99f773SBoris Brezillon  *	Boris Brezillon <boris.brezillon@free-electrons.com>
7ed99f773SBoris Brezillon  *	Peter Pan <peterpandong@micron.com>
8ed99f773SBoris Brezillon  */
9ed99f773SBoris Brezillon 
10ed99f773SBoris Brezillon #define pr_fmt(fmt)	"nand-bbt: " fmt
11ed99f773SBoris Brezillon 
12ed99f773SBoris Brezillon #include <linux/mtd/nand.h>
13ed99f773SBoris Brezillon #ifndef __UBOOT__
14ed99f773SBoris Brezillon #include <linux/slab.h>
15ed99f773SBoris Brezillon #endif
16ed99f773SBoris Brezillon 
1753bfae03SJon Lin #ifdef CONFIG_MTD_NAND_BBT_USING_FLASH
1853bfae03SJon Lin 
1953bfae03SJon Lin #ifdef BBT_DEBUG
20*ad0301ffSJon Lin #define bbt_dbg pr_err
2153bfae03SJon Lin #else
22*ad0301ffSJon Lin #define bbt_dbg(args...)
2353bfae03SJon Lin #endif
2453bfae03SJon Lin 
25*ad0301ffSJon Lin #define BBT_VERSION_INVALID		(0xFFFFFFFFU)
26*ad0301ffSJon Lin #define BBT_VERSION_BLOCK_ABNORMAL	(BBT_VERSION_INVALID - 1)
27*ad0301ffSJon Lin #define BBT_VERSION_MAX			(BBT_VERSION_INVALID - 8)
28*ad0301ffSJon Lin 
2953bfae03SJon Lin struct nanddev_bbt_info {
3053bfae03SJon Lin 	u8 pattern[4];
3153bfae03SJon Lin 	unsigned int version;
32*ad0301ffSJon Lin 	u32 hash;
3353bfae03SJon Lin };
3453bfae03SJon Lin 
3553bfae03SJon Lin static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
3653bfae03SJon Lin 
37*ad0301ffSJon Lin #if defined(BBT_DEBUG) && defined(BBT_DEBUG_DUMP)
bbt_dbg_hex(char * s,void * buf,u32 len)38*ad0301ffSJon Lin static void bbt_dbg_hex(char *s, void *buf, u32 len)
39*ad0301ffSJon Lin {
40*ad0301ffSJon Lin 	u32 i, j = 0;
41*ad0301ffSJon Lin 	u32 *p32 = (u32 *)buf;
42*ad0301ffSJon Lin 
43*ad0301ffSJon Lin 	for (i = 0; i < len / 4; i++) {
44*ad0301ffSJon Lin 		if (j == 0)
45*ad0301ffSJon Lin 			printf("%s %p + 0x%04x: ", s, buf, i * 4);
46*ad0301ffSJon Lin 
47*ad0301ffSJon Lin 		printf("0x%08x,", p32[i]);
48*ad0301ffSJon Lin 
49*ad0301ffSJon Lin 		if (++j >= (4)) {
50*ad0301ffSJon Lin 			j = 0;
51*ad0301ffSJon Lin 			printf("\n");
52*ad0301ffSJon Lin 		}
53*ad0301ffSJon Lin 	}
54*ad0301ffSJon Lin 	printf("\n");
55*ad0301ffSJon Lin }
56*ad0301ffSJon Lin #endif
57*ad0301ffSJon Lin 
js_hash(u8 * buf,u32 len)58*ad0301ffSJon Lin static u32 js_hash(u8 *buf, u32 len)
59*ad0301ffSJon Lin {
60*ad0301ffSJon Lin 	u32 hash = 0x47C6A7E6;
61*ad0301ffSJon Lin 	u32 i;
62*ad0301ffSJon Lin 
63*ad0301ffSJon Lin 	for (i = 0; i < len; i++)
64*ad0301ffSJon Lin 		hash ^= ((hash << 5) + buf[i] + (hash >> 2));
65*ad0301ffSJon Lin 
66*ad0301ffSJon Lin 	return hash;
67*ad0301ffSJon Lin }
68*ad0301ffSJon Lin 
bbt_check_hash(u8 * buf,u32 len,u32 hash_cmp)69*ad0301ffSJon Lin static bool bbt_check_hash(u8 *buf, u32 len, u32 hash_cmp)
70*ad0301ffSJon Lin {
71*ad0301ffSJon Lin 	u32 hash;
72*ad0301ffSJon Lin 
73*ad0301ffSJon Lin 	/* compatible with no-hash version */
74*ad0301ffSJon Lin 	if (hash_cmp == 0 || hash_cmp == 0xFFFFFFFF)
75*ad0301ffSJon Lin 		return 1;
76*ad0301ffSJon Lin 
77*ad0301ffSJon Lin 	hash = js_hash(buf, len);
78*ad0301ffSJon Lin 	if (hash != hash_cmp)
79*ad0301ffSJon Lin 		return 0;
80*ad0301ffSJon Lin 
81*ad0301ffSJon Lin 	return 1;
82*ad0301ffSJon Lin }
83*ad0301ffSJon Lin 
bbt_nand_isbad_bypass(struct nand_device * nand,u32 block)84*ad0301ffSJon Lin static u32 bbt_nand_isbad_bypass(struct nand_device *nand, u32 block)
85*ad0301ffSJon Lin {
86*ad0301ffSJon Lin 	struct mtd_info *mtd = nanddev_to_mtd(nand);
87*ad0301ffSJon Lin 	struct nand_pos pos;
88*ad0301ffSJon Lin 
89*ad0301ffSJon Lin 	nanddev_bbt_set_block_status(nand, block, NAND_BBT_BLOCK_STATUS_UNKNOWN);
90*ad0301ffSJon Lin 	nanddev_offs_to_pos(nand, block * mtd->erasesize, &pos);
91*ad0301ffSJon Lin 
92*ad0301ffSJon Lin 	return nanddev_isbad(nand, &pos);
93*ad0301ffSJon Lin }
94*ad0301ffSJon Lin 
9553bfae03SJon Lin /**
9653bfae03SJon Lin  * nanddev_read_bbt() - Read the BBT (Bad Block Table)
9753bfae03SJon Lin  * @nand: NAND device
9853bfae03SJon Lin  * @block: bbt block address
9953bfae03SJon Lin  * @update: true - get version and overwrite bbt.cache with new version;
10053bfae03SJon Lin  *	false - get bbt version only;
10153bfae03SJon Lin  *
10253bfae03SJon Lin  * Initialize the in-memory BBT.
10353bfae03SJon Lin  *
10453bfae03SJon Lin  * Return: 0 in case of success, a negative error code otherwise.
10553bfae03SJon Lin  */
nanddev_read_bbt(struct nand_device * nand,u32 block,bool update)10653bfae03SJon Lin static int nanddev_read_bbt(struct nand_device *nand, u32 block, bool update)
10753bfae03SJon Lin {
10853bfae03SJon Lin 	unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
10953bfae03SJon Lin 	unsigned int nblocks = nanddev_neraseblocks(nand);
11053bfae03SJon Lin 	unsigned int nbytes = DIV_ROUND_UP(nblocks * bits_per_block,
1112f0bb0e6SJon Lin 					   BITS_PER_LONG) * sizeof(*nand->bbt.cache);
11253bfae03SJon Lin 	struct mtd_info *mtd = nanddev_to_mtd(nand);
11353bfae03SJon Lin 	u8 *data_buf, *oob_buf;
11453bfae03SJon Lin 	struct nanddev_bbt_info *bbt_info;
11553bfae03SJon Lin 	struct mtd_oob_ops ops;
116*ad0301ffSJon Lin 	u32 bbt_page_num;
11753bfae03SJon Lin 	int ret = 0;
11853bfae03SJon Lin 	unsigned int version = 0;
11953bfae03SJon Lin 
12053bfae03SJon Lin 	if (!nand->bbt.cache)
12153bfae03SJon Lin 		return -ENOMEM;
12253bfae03SJon Lin 
12353bfae03SJon Lin 	if (block >= nblocks)
12453bfae03SJon Lin 		return -EINVAL;
12553bfae03SJon Lin 
12653bfae03SJon Lin 	/* aligned to page size, and even pages is better */
12753bfae03SJon Lin 	bbt_page_num = (sizeof(struct nanddev_bbt_info) + nbytes +
12853bfae03SJon Lin 		mtd->writesize - 1) >> mtd->writesize_shift;
12953bfae03SJon Lin 	bbt_page_num = (bbt_page_num + 1) / 2 * 2;
13053bfae03SJon Lin 	data_buf = kzalloc(bbt_page_num * mtd->writesize, GFP_KERNEL);
13153bfae03SJon Lin 	if (!data_buf)
13253bfae03SJon Lin 		return -ENOMEM;
13353bfae03SJon Lin 	oob_buf = kzalloc(bbt_page_num * mtd->oobsize, GFP_KERNEL);
13453bfae03SJon Lin 	if (!oob_buf) {
13553bfae03SJon Lin 		kfree(data_buf);
13653bfae03SJon Lin 
13753bfae03SJon Lin 		return -ENOMEM;
13853bfae03SJon Lin 	}
13953bfae03SJon Lin 
14053bfae03SJon Lin 	bbt_info = (struct nanddev_bbt_info *)(data_buf + nbytes);
14153bfae03SJon Lin 
14253bfae03SJon Lin 	memset(&ops, 0, sizeof(struct mtd_oob_ops));
143be6c00c0SJon Lin 	ops.mode = MTD_OPS_PLACE_OOB;
14453bfae03SJon Lin 	ops.datbuf = data_buf;
14553bfae03SJon Lin 	ops.len = bbt_page_num * mtd->writesize;
14653bfae03SJon Lin 	ops.oobbuf = oob_buf;
14753bfae03SJon Lin 	ops.ooblen = bbt_page_num * mtd->oobsize;
14853bfae03SJon Lin 	ops.ooboffs = 0;
14953bfae03SJon Lin 
15053bfae03SJon Lin 	/* Store one entry for each block */
15153bfae03SJon Lin 	ret = mtd_read_oob(mtd, block * mtd->erasesize, &ops);
15253bfae03SJon Lin 	if (ret && ret != -EUCLEAN) {
153*ad0301ffSJon Lin 		pr_err("read_bbt blk=%d fail=%d update=%d\n", block, ret, update);
154*ad0301ffSJon Lin 		ret = 0;
155*ad0301ffSJon Lin 		version = BBT_VERSION_BLOCK_ABNORMAL;
15653bfae03SJon Lin 		goto out;
15753bfae03SJon Lin 	} else {
15853bfae03SJon Lin 		ret = 0;
15953bfae03SJon Lin 	}
16053bfae03SJon Lin 
161*ad0301ffSJon Lin 	/* bad block or good block without bbt */
162*ad0301ffSJon Lin 	if (memcmp(bbt_pattern, bbt_info->pattern, 4)) {
163*ad0301ffSJon Lin 		ret = 0;
164*ad0301ffSJon Lin 		goto out;
165*ad0301ffSJon Lin 	}
16653bfae03SJon Lin 
167*ad0301ffSJon Lin 	/* good block with abnornal bbt */
168*ad0301ffSJon Lin 	if (oob_buf[0] == 0xff ||
169*ad0301ffSJon Lin 	    !bbt_check_hash(data_buf, nbytes + sizeof(struct nanddev_bbt_info) - 4, bbt_info->hash)) {
170*ad0301ffSJon Lin 		pr_err("read_bbt check fail blk=%d ret=%d update=%d\n", block, ret, update);
171*ad0301ffSJon Lin 		ret = 0;
172*ad0301ffSJon Lin 		version = BBT_VERSION_BLOCK_ABNORMAL;
173*ad0301ffSJon Lin 		goto out;
174*ad0301ffSJon Lin 	}
175*ad0301ffSJon Lin 
176*ad0301ffSJon Lin 	/* good block with good bbt */
177*ad0301ffSJon Lin 	version = bbt_info->version;
178*ad0301ffSJon Lin 	bbt_dbg("read_bbt from blk=%d ver=%d update=%d\n", block, version, update);
17953bfae03SJon Lin 	if (update && version > nand->bbt.version) {
18053bfae03SJon Lin 		memcpy(nand->bbt.cache, data_buf, nbytes);
18153bfae03SJon Lin 		nand->bbt.version = version;
18253bfae03SJon Lin 	}
18353bfae03SJon Lin 
184*ad0301ffSJon Lin #if defined(BBT_DEBUG) && defined(BBT_DEBUG_DUMP)
185*ad0301ffSJon Lin 	bbt_dbg_hex("bbt", data_buf, nbytes + sizeof(struct nanddev_bbt_info));
186c98030d8SJon Lin 	if (version) {
187c98030d8SJon Lin 		u8 *temp_buf = kzalloc(bbt_page_num * mtd->writesize, GFP_KERNEL);
188*ad0301ffSJon Lin 		bool in_scan = nand->bbt.option & NANDDEV_BBT_SCANNED;
189*ad0301ffSJon Lin 
190*ad0301ffSJon Lin 		if (!temp_buf)
191*ad0301ffSJon Lin 			goto out;
192c98030d8SJon Lin 
193c98030d8SJon Lin 		memcpy(temp_buf, nand->bbt.cache, nbytes);
194c98030d8SJon Lin 		memcpy(nand->bbt.cache, data_buf, nbytes);
195c98030d8SJon Lin 
196*ad0301ffSJon Lin 		if (!in_scan)
197c98030d8SJon Lin 			nand->bbt.option |= NANDDEV_BBT_SCANNED;
198c98030d8SJon Lin 		for (block = 0; block < nblocks; block++) {
199c98030d8SJon Lin 			ret = nanddev_bbt_get_block_status(nand, block);
200c98030d8SJon Lin 			if (ret != NAND_BBT_BLOCK_GOOD)
201*ad0301ffSJon Lin 				bbt_dbg("bad block[0x%x], ret=%d\n", block, ret);
202c98030d8SJon Lin 		}
203*ad0301ffSJon Lin 		if (!in_scan)
204c98030d8SJon Lin 			nand->bbt.option &= ~NANDDEV_BBT_SCANNED;
205c98030d8SJon Lin 		memcpy(nand->bbt.cache, temp_buf, nbytes);
206c98030d8SJon Lin 		kfree(temp_buf);
207*ad0301ffSJon Lin 		ret = 0;
208c98030d8SJon Lin 	}
209c98030d8SJon Lin #endif
210c98030d8SJon Lin 
21153bfae03SJon Lin out:
21253bfae03SJon Lin 	kfree(data_buf);
21353bfae03SJon Lin 	kfree(oob_buf);
21453bfae03SJon Lin 
215*ad0301ffSJon Lin 	return ret < 0 ? -EIO : (int)version;
21653bfae03SJon Lin }
21753bfae03SJon Lin 
nanddev_write_bbt(struct nand_device * nand,u32 block)21853bfae03SJon Lin static int nanddev_write_bbt(struct nand_device *nand, u32 block)
21953bfae03SJon Lin {
22053bfae03SJon Lin 	unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
22153bfae03SJon Lin 	unsigned int nblocks = nanddev_neraseblocks(nand);
22253bfae03SJon Lin 	unsigned int nbytes = DIV_ROUND_UP(nblocks * bits_per_block,
2232f0bb0e6SJon Lin 					   BITS_PER_LONG) * sizeof(*nand->bbt.cache);
22453bfae03SJon Lin 	struct mtd_info *mtd = nanddev_to_mtd(nand);
22553bfae03SJon Lin 	u8 *data_buf, *oob_buf;
22653bfae03SJon Lin 	struct nanddev_bbt_info *bbt_info;
22753bfae03SJon Lin 	struct mtd_oob_ops ops;
228*ad0301ffSJon Lin 	u32 bbt_page_num;
229*ad0301ffSJon Lin 	int ret = 0, version;
23053bfae03SJon Lin 	struct nand_pos pos;
23153bfae03SJon Lin 
232*ad0301ffSJon Lin 	bbt_dbg("write_bbt to blk=%d ver=%d\n", block, nand->bbt.version);
23353bfae03SJon Lin 	if (!nand->bbt.cache)
23453bfae03SJon Lin 		return -ENOMEM;
23553bfae03SJon Lin 
23653bfae03SJon Lin 	if (block >= nblocks)
23753bfae03SJon Lin 		return -EINVAL;
23853bfae03SJon Lin 
23953bfae03SJon Lin 	/* aligned to page size, and even pages is better */
24053bfae03SJon Lin 	bbt_page_num = (sizeof(struct nanddev_bbt_info) + nbytes +
24153bfae03SJon Lin 		mtd->writesize - 1) >> mtd->writesize_shift;
24253bfae03SJon Lin 	bbt_page_num = (bbt_page_num + 1) / 2 * 2;
24353bfae03SJon Lin 
24453bfae03SJon Lin 	data_buf = kzalloc(bbt_page_num * mtd->writesize, GFP_KERNEL);
24553bfae03SJon Lin 	if (!data_buf)
24653bfae03SJon Lin 		return -ENOMEM;
24753bfae03SJon Lin 	oob_buf = kzalloc(bbt_page_num * mtd->oobsize, GFP_KERNEL);
24853bfae03SJon Lin 	if (!oob_buf) {
24953bfae03SJon Lin 		kfree(data_buf);
25053bfae03SJon Lin 
25153bfae03SJon Lin 		return -ENOMEM;
25253bfae03SJon Lin 	}
25353bfae03SJon Lin 
25453bfae03SJon Lin 	bbt_info = (struct nanddev_bbt_info *)(data_buf + nbytes);
25553bfae03SJon Lin 
25653bfae03SJon Lin 	memcpy(data_buf, nand->bbt.cache, nbytes);
25753bfae03SJon Lin 	memcpy(bbt_info, bbt_pattern, 4);
25853bfae03SJon Lin 	bbt_info->version = nand->bbt.version;
259*ad0301ffSJon Lin 	bbt_info->hash = js_hash(data_buf, nbytes + sizeof(struct nanddev_bbt_info) - 4);
26053bfae03SJon Lin 
26153bfae03SJon Lin 	/* Store one entry for each block */
26253bfae03SJon Lin 	nanddev_offs_to_pos(nand, block * mtd->erasesize, &pos);
26353bfae03SJon Lin 	ret = nand->ops->erase(nand, &pos);
26453bfae03SJon Lin 	if (ret)
26553bfae03SJon Lin 		goto out;
26653bfae03SJon Lin 
26753bfae03SJon Lin 	memset(&ops, 0, sizeof(struct mtd_oob_ops));
268be6c00c0SJon Lin 	ops.mode = MTD_OPS_PLACE_OOB;
26953bfae03SJon Lin 	ops.datbuf = data_buf;
27053bfae03SJon Lin 	ops.len = bbt_page_num * mtd->writesize;
27153bfae03SJon Lin 	ops.oobbuf = oob_buf;
27253bfae03SJon Lin 	ops.ooblen = bbt_page_num * mtd->oobsize;
27353bfae03SJon Lin 	ops.ooboffs = 0;
27453bfae03SJon Lin 	ret = mtd_write_oob(mtd, block * mtd->erasesize, &ops);
275*ad0301ffSJon Lin 	if (ret) {
276*ad0301ffSJon Lin 		nand->ops->erase(nand, &pos);
277*ad0301ffSJon Lin 		goto out;
278*ad0301ffSJon Lin 	}
27953bfae03SJon Lin 
280*ad0301ffSJon Lin 	version = nanddev_read_bbt(nand, block, false);
281*ad0301ffSJon Lin 	if (version != bbt_info->version) {
282*ad0301ffSJon Lin 		pr_err("bbt_write fail, blk=%d recheck fail %d-%d\n",
283*ad0301ffSJon Lin 		       block, version, bbt_info->version);
284*ad0301ffSJon Lin 		nand->ops->erase(nand, &pos);
285*ad0301ffSJon Lin 		ret = -EIO;
286*ad0301ffSJon Lin 	} else {
287*ad0301ffSJon Lin 		ret = 0;
288*ad0301ffSJon Lin 	}
28953bfae03SJon Lin out:
29053bfae03SJon Lin 	kfree(data_buf);
29153bfae03SJon Lin 	kfree(oob_buf);
29253bfae03SJon Lin 
29353bfae03SJon Lin 	return ret;
29453bfae03SJon Lin }
29553bfae03SJon Lin 
nanddev_bbt_format(struct nand_device * nand)29653bfae03SJon Lin static __maybe_unused int nanddev_bbt_format(struct nand_device *nand)
29753bfae03SJon Lin {
29853bfae03SJon Lin 	unsigned int nblocks = nanddev_neraseblocks(nand);
29953bfae03SJon Lin 	struct mtd_info *mtd = nanddev_to_mtd(nand);
30053bfae03SJon Lin 	struct nand_pos pos;
30153bfae03SJon Lin 	u32 start_block, block;
302*ad0301ffSJon Lin 	unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
303*ad0301ffSJon Lin 	unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
304*ad0301ffSJon Lin 					   BITS_PER_LONG);
30553bfae03SJon Lin 
30653bfae03SJon Lin 	start_block = nblocks - NANDDEV_BBT_SCAN_MAXBLOCKS;
30753bfae03SJon Lin 
308*ad0301ffSJon Lin 	for (block = 0; block < nblocks; block++) {
30953bfae03SJon Lin 		nanddev_offs_to_pos(nand, block * mtd->erasesize, &pos);
310*ad0301ffSJon Lin 		if (nanddev_isbad(nand, &pos)) {
311*ad0301ffSJon Lin 			if (bbt_nand_isbad_bypass(nand, 0)) {
312*ad0301ffSJon Lin 				memset(nand->bbt.cache, 0, nwords * sizeof(*nand->bbt.cache));
313*ad0301ffSJon Lin 				pr_err("bbt_format fail, test good block %d fail\n", 0);
314*ad0301ffSJon Lin 				return -EIO;
315*ad0301ffSJon Lin 			}
316*ad0301ffSJon Lin 
317*ad0301ffSJon Lin 			if (!bbt_nand_isbad_bypass(nand, block)) {
318*ad0301ffSJon Lin 				memset(nand->bbt.cache, 0, nwords * sizeof(*nand->bbt.cache));
319*ad0301ffSJon Lin 				pr_err("bbt_format fail, test bad block %d fail\n", block);
320*ad0301ffSJon Lin 				return -EIO;
321*ad0301ffSJon Lin 			}
322*ad0301ffSJon Lin 
32353bfae03SJon Lin 			nanddev_bbt_set_block_status(nand, block,
32453bfae03SJon Lin 						     NAND_BBT_BLOCK_FACTORY_BAD);
32553bfae03SJon Lin 		}
326*ad0301ffSJon Lin 	}
32753bfae03SJon Lin 
32853bfae03SJon Lin 	for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
32953bfae03SJon Lin 		if (nanddev_bbt_get_block_status(nand, start_block + block) ==
33053bfae03SJon Lin 			NAND_BBT_BLOCK_GOOD)
33153bfae03SJon Lin 			nanddev_bbt_set_block_status(nand, start_block + block,
33253bfae03SJon Lin 						     NAND_BBT_BLOCK_WORN);
33353bfae03SJon Lin 	}
33453bfae03SJon Lin 
33553bfae03SJon Lin 	return 0;
33653bfae03SJon Lin }
33753bfae03SJon Lin 
nanddev_scan_bbt(struct nand_device * nand)33853bfae03SJon Lin static int nanddev_scan_bbt(struct nand_device *nand)
33953bfae03SJon Lin {
34053bfae03SJon Lin 	unsigned int nblocks = nanddev_neraseblocks(nand);
34153bfae03SJon Lin 	u32 start_block, block;
34253bfae03SJon Lin 	int ret = 0;
34353bfae03SJon Lin 
34453bfae03SJon Lin 	nand->bbt.version = 0;
34553bfae03SJon Lin 	start_block = nblocks - NANDDEV_BBT_SCAN_MAXBLOCKS;
34653bfae03SJon Lin 	for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++)
34753bfae03SJon Lin 		nanddev_read_bbt(nand, start_block + block, true);
34853bfae03SJon Lin 
34953bfae03SJon Lin 	nand->bbt.option |= NANDDEV_BBT_SCANNED;
35053bfae03SJon Lin #ifndef CONFIG_SPL_BUILD
35153bfae03SJon Lin 	if (nand->bbt.version == 0) {
352*ad0301ffSJon Lin 		ret = nanddev_bbt_format(nand);
353*ad0301ffSJon Lin 		if (ret) {
354*ad0301ffSJon Lin 			nand->bbt.option = 0;
355*ad0301ffSJon Lin 			pr_err("%s format fail\n", __func__);
356*ad0301ffSJon Lin 
357*ad0301ffSJon Lin 			return ret;
358*ad0301ffSJon Lin 		}
359*ad0301ffSJon Lin 
36053bfae03SJon Lin 		ret = nanddev_bbt_update(nand);
36178cac1dfSJon Lin 		if (ret) {
36278cac1dfSJon Lin 			nand->bbt.option = 0;
363*ad0301ffSJon Lin 			pr_err("%s update fail\n", __func__);
364*ad0301ffSJon Lin 
365*ad0301ffSJon Lin 			return ret;
366*ad0301ffSJon Lin 		}
367*ad0301ffSJon Lin 	}
368*ad0301ffSJon Lin #endif
369*ad0301ffSJon Lin 
370*ad0301ffSJon Lin #if defined(BBT_DEBUG)
371*ad0301ffSJon Lin 	pr_err("scan_bbt success\n");
372*ad0301ffSJon Lin 	if (nand->bbt.version) {
373*ad0301ffSJon Lin 		for (block = 0; block < nblocks; block++) {
374*ad0301ffSJon Lin 			ret = nanddev_bbt_get_block_status(nand, block);
375*ad0301ffSJon Lin 			if (ret != NAND_BBT_BLOCK_GOOD)
376*ad0301ffSJon Lin 				bbt_dbg("bad block[0x%x], ret=%d\n", block, ret);
37753bfae03SJon Lin 		}
37878cac1dfSJon Lin 	}
37953bfae03SJon Lin #endif
38053bfae03SJon Lin 
38153bfae03SJon Lin 	return ret;
38253bfae03SJon Lin }
38353bfae03SJon Lin #endif
38453bfae03SJon Lin 
385ed99f773SBoris Brezillon /**
386ed99f773SBoris Brezillon  * nanddev_bbt_init() - Initialize the BBT (Bad Block Table)
387ed99f773SBoris Brezillon  * @nand: NAND device
388ed99f773SBoris Brezillon  *
389ed99f773SBoris Brezillon  * Initialize the in-memory BBT.
390ed99f773SBoris Brezillon  *
391ed99f773SBoris Brezillon  * Return: 0 in case of success, a negative error code otherwise.
392ed99f773SBoris Brezillon  */
nanddev_bbt_init(struct nand_device * nand)393ed99f773SBoris Brezillon int nanddev_bbt_init(struct nand_device *nand)
394ed99f773SBoris Brezillon {
395ed99f773SBoris Brezillon 	unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
396ed99f773SBoris Brezillon 	unsigned int nblocks = nanddev_neraseblocks(nand);
397ed99f773SBoris Brezillon 	unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
398ed99f773SBoris Brezillon 					   BITS_PER_LONG);
399ed99f773SBoris Brezillon 
400bdf7b34bSJon Lin 	nand->bbt.cache = kcalloc(nwords, sizeof(*nand->bbt.cache),
401bdf7b34bSJon Lin 				  GFP_KERNEL);
402ed99f773SBoris Brezillon 	if (!nand->bbt.cache)
403ed99f773SBoris Brezillon 		return -ENOMEM;
404ed99f773SBoris Brezillon 
405ed99f773SBoris Brezillon 	return 0;
406ed99f773SBoris Brezillon }
407ed99f773SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_bbt_init);
408ed99f773SBoris Brezillon 
409ed99f773SBoris Brezillon /**
410ed99f773SBoris Brezillon  * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table)
411ed99f773SBoris Brezillon  * @nand: NAND device
412ed99f773SBoris Brezillon  *
413ed99f773SBoris Brezillon  * Undoes what has been done in nanddev_bbt_init()
414ed99f773SBoris Brezillon  */
nanddev_bbt_cleanup(struct nand_device * nand)415ed99f773SBoris Brezillon void nanddev_bbt_cleanup(struct nand_device *nand)
416ed99f773SBoris Brezillon {
417ed99f773SBoris Brezillon 	kfree(nand->bbt.cache);
418ed99f773SBoris Brezillon }
419ed99f773SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup);
420ed99f773SBoris Brezillon 
421ed99f773SBoris Brezillon /**
422ed99f773SBoris Brezillon  * nanddev_bbt_update() - Update a BBT
423ed99f773SBoris Brezillon  * @nand: nand device
424ed99f773SBoris Brezillon  *
425ed99f773SBoris Brezillon  * Update the BBT. Currently a NOP function since on-flash bbt is not yet
426ed99f773SBoris Brezillon  * supported.
427ed99f773SBoris Brezillon  *
428ed99f773SBoris Brezillon  * Return: 0 in case of success, a negative error code otherwise.
429ed99f773SBoris Brezillon  */
nanddev_bbt_update(struct nand_device * nand)430ed99f773SBoris Brezillon int nanddev_bbt_update(struct nand_device *nand)
431ed99f773SBoris Brezillon {
43253bfae03SJon Lin #ifdef CONFIG_MTD_NAND_BBT_USING_FLASH
433*ad0301ffSJon Lin 	struct nand_pos pos;
434*ad0301ffSJon Lin 	struct mtd_info *mtd = nanddev_to_mtd(nand);
435*ad0301ffSJon Lin 
43653bfae03SJon Lin 	if (nand->bbt.cache &&
43753bfae03SJon Lin 	    nand->bbt.option & NANDDEV_BBT_USE_FLASH) {
43853bfae03SJon Lin 		unsigned int nblocks = nanddev_neraseblocks(nand);
43953bfae03SJon Lin 		u32 bbt_version[NANDDEV_BBT_SCAN_MAXBLOCKS];
44053bfae03SJon Lin 		int start_block, block;
44153bfae03SJon Lin 		u32 min_version, block_des;
442*ad0301ffSJon Lin 		int ret, count = 0, status;
44353bfae03SJon Lin 
44453bfae03SJon Lin 		start_block = nblocks - NANDDEV_BBT_SCAN_MAXBLOCKS;
44553bfae03SJon Lin 		for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
446*ad0301ffSJon Lin 			status = nanddev_bbt_get_block_status(nand, start_block + block);
447*ad0301ffSJon Lin 			ret = nanddev_read_bbt(nand, start_block + block, false);
448*ad0301ffSJon Lin 			if (ret == 0 && status == NAND_BBT_BLOCK_FACTORY_BAD)
449*ad0301ffSJon Lin 				bbt_version[block] = BBT_VERSION_INVALID;
450*ad0301ffSJon Lin 			else if (ret == -EIO)
451*ad0301ffSJon Lin 				bbt_version[block] = BBT_VERSION_INVALID;
452*ad0301ffSJon Lin 			else if (ret == BBT_VERSION_BLOCK_ABNORMAL)
453*ad0301ffSJon Lin 				bbt_version[block] = ret;
45453bfae03SJon Lin 			else
45553bfae03SJon Lin 				bbt_version[block] = ret;
45653bfae03SJon Lin 		}
45753bfae03SJon Lin get_min_ver:
458*ad0301ffSJon Lin 		min_version = BBT_VERSION_MAX;
45953bfae03SJon Lin 		block_des = 0;
46053bfae03SJon Lin 		for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
46153bfae03SJon Lin 			if (bbt_version[block] < min_version) {
46253bfae03SJon Lin 				min_version = bbt_version[block];
46353bfae03SJon Lin 				block_des = start_block + block;
46453bfae03SJon Lin 			}
46553bfae03SJon Lin 		}
46653bfae03SJon Lin 
467*ad0301ffSJon Lin 		/* Overwrite the BBT_VERSION_BLOCK_ABNORMAL block */
468*ad0301ffSJon Lin 		if (nand->bbt.version < min_version)
469*ad0301ffSJon Lin 			nand->bbt.version = min_version + 4;
470*ad0301ffSJon Lin 
47153bfae03SJon Lin 		if (block_des > 0) {
47253bfae03SJon Lin 			nand->bbt.version++;
47353bfae03SJon Lin 			ret = nanddev_write_bbt(nand, block_des);
47453bfae03SJon Lin 			if (ret) {
475*ad0301ffSJon Lin 				pr_err("bbt_update fail, blk=%d ret= %d\n", block_des, ret);
47653bfae03SJon Lin 
47753bfae03SJon Lin 				return -1;
47853bfae03SJon Lin 			}
479*ad0301ffSJon Lin 
480*ad0301ffSJon Lin 			bbt_version[block_des - start_block] = BBT_VERSION_INVALID;
481*ad0301ffSJon Lin 			count++;
482*ad0301ffSJon Lin 			if (count < 2)
483*ad0301ffSJon Lin 				goto get_min_ver;
484*ad0301ffSJon Lin 			bbt_dbg("bbt_update success\n");
485*ad0301ffSJon Lin 		} else {
486*ad0301ffSJon Lin 			pr_err("bbt_update failed\n");
487*ad0301ffSJon Lin 			ret = -1;
488*ad0301ffSJon Lin 		}
489*ad0301ffSJon Lin 
490*ad0301ffSJon Lin 		for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
491*ad0301ffSJon Lin 			if (bbt_version[block] == BBT_VERSION_BLOCK_ABNORMAL) {
492*ad0301ffSJon Lin 				block_des = start_block + block;
493*ad0301ffSJon Lin 				nanddev_offs_to_pos(nand, block_des * mtd->erasesize, &pos);
494*ad0301ffSJon Lin 				nand->ops->erase(nand, &pos);
495*ad0301ffSJon Lin 			}
496*ad0301ffSJon Lin 		}
497*ad0301ffSJon Lin 
498*ad0301ffSJon Lin 		return ret;
49953bfae03SJon Lin 	}
50053bfae03SJon Lin #endif
501ed99f773SBoris Brezillon 	return 0;
502ed99f773SBoris Brezillon }
503ed99f773SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_bbt_update);
504ed99f773SBoris Brezillon 
505ed99f773SBoris Brezillon /**
506ed99f773SBoris Brezillon  * nanddev_bbt_get_block_status() - Return the status of an eraseblock
507ed99f773SBoris Brezillon  * @nand: nand device
508ed99f773SBoris Brezillon  * @entry: the BBT entry
509ed99f773SBoris Brezillon  *
510ed99f773SBoris Brezillon  * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry
511ed99f773SBoris Brezillon  *	   is bigger than the BBT size.
512ed99f773SBoris Brezillon  */
nanddev_bbt_get_block_status(const struct nand_device * nand,unsigned int entry)513ed99f773SBoris Brezillon int nanddev_bbt_get_block_status(const struct nand_device *nand,
514ed99f773SBoris Brezillon 				 unsigned int entry)
515ed99f773SBoris Brezillon {
516ed99f773SBoris Brezillon 	unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
517ed99f773SBoris Brezillon 	unsigned long *pos = nand->bbt.cache +
518ed99f773SBoris Brezillon 			     ((entry * bits_per_block) / BITS_PER_LONG);
519ed99f773SBoris Brezillon 	unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
520ed99f773SBoris Brezillon 	unsigned long status;
521ed99f773SBoris Brezillon 
52253bfae03SJon Lin #ifdef CONFIG_MTD_NAND_BBT_USING_FLASH
52353bfae03SJon Lin 	if (nand->bbt.option & NANDDEV_BBT_USE_FLASH &&
52453bfae03SJon Lin 	    !(nand->bbt.option & NANDDEV_BBT_SCANNED))
52553bfae03SJon Lin 		nanddev_scan_bbt((struct nand_device *)nand);
52653bfae03SJon Lin #endif
52753bfae03SJon Lin 
528ed99f773SBoris Brezillon 	if (entry >= nanddev_neraseblocks(nand))
529ed99f773SBoris Brezillon 		return -ERANGE;
530ed99f773SBoris Brezillon 
531ed99f773SBoris Brezillon 	status = pos[0] >> offs;
532ed99f773SBoris Brezillon 	if (bits_per_block + offs > BITS_PER_LONG)
533ed99f773SBoris Brezillon 		status |= pos[1] << (BITS_PER_LONG - offs);
534ed99f773SBoris Brezillon 
535ed99f773SBoris Brezillon 	return status & GENMASK(bits_per_block - 1, 0);
536ed99f773SBoris Brezillon }
537ed99f773SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status);
538ed99f773SBoris Brezillon 
539ed99f773SBoris Brezillon /**
540ed99f773SBoris Brezillon  * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the
541ed99f773SBoris Brezillon  *				    in-memory BBT
542ed99f773SBoris Brezillon  * @nand: nand device
543ed99f773SBoris Brezillon  * @entry: the BBT entry to update
544ed99f773SBoris Brezillon  * @status: the new status
545ed99f773SBoris Brezillon  *
546ed99f773SBoris Brezillon  * Update an entry of the in-memory BBT. If you want to push the updated BBT
547ed99f773SBoris Brezillon  * the NAND you should call nanddev_bbt_update().
548ed99f773SBoris Brezillon  *
549ed99f773SBoris Brezillon  * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT
550ed99f773SBoris Brezillon  *	   size.
551ed99f773SBoris Brezillon  */
nanddev_bbt_set_block_status(struct nand_device * nand,unsigned int entry,enum nand_bbt_block_status status)552ed99f773SBoris Brezillon int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry,
553ed99f773SBoris Brezillon 				 enum nand_bbt_block_status status)
554ed99f773SBoris Brezillon {
555ed99f773SBoris Brezillon 	unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
556ed99f773SBoris Brezillon 	unsigned long *pos = nand->bbt.cache +
557ed99f773SBoris Brezillon 			     ((entry * bits_per_block) / BITS_PER_LONG);
558ed99f773SBoris Brezillon 	unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
559ed99f773SBoris Brezillon 	unsigned long val = status & GENMASK(bits_per_block - 1, 0);
560ed99f773SBoris Brezillon 
561ed99f773SBoris Brezillon 	if (entry >= nanddev_neraseblocks(nand))
562ed99f773SBoris Brezillon 		return -ERANGE;
563ed99f773SBoris Brezillon 
5642f0bb0e6SJon Lin 	if (offs + bits_per_block - 1 > (BITS_PER_LONG - 1))
5652f0bb0e6SJon Lin 		pos[0] &= ~GENMASK(BITS_PER_LONG - 1, offs);
566fd817f1dSJon Lin 	else
567ed99f773SBoris Brezillon 		pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs);
568ed99f773SBoris Brezillon 	pos[0] |= val << offs;
569ed99f773SBoris Brezillon 
570ed99f773SBoris Brezillon 	if (bits_per_block + offs > BITS_PER_LONG) {
571fd817f1dSJon Lin 		unsigned int rbits = BITS_PER_LONG - offs;
572ed99f773SBoris Brezillon 
573fd817f1dSJon Lin 		pos[1] &= ~GENMASK(bits_per_block - rbits - 1, 0);
574ed99f773SBoris Brezillon 		pos[1] |= val >> rbits;
575ed99f773SBoris Brezillon 	}
576ed99f773SBoris Brezillon 
577ed99f773SBoris Brezillon 	return 0;
578ed99f773SBoris Brezillon }
579ed99f773SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status);
580