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