[openwrt/openwrt] kernel: mtk_bmt: add support for the bbt format used on mt7621

LEDE Commits lede-commits at lists.infradead.org
Thu Jan 13 11:13:54 PST 2022


nbd pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/e2aa04d1e50104456902aea685e46c754b07ebf4

commit e2aa04d1e50104456902aea685e46c754b07ebf4
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Thu Jan 13 18:30:51 2022 +0100

    kernel: mtk_bmt: add support for the bbt format used on mt7621
    
    This does not have spare blocks for remapping, and it is also not suitable
    for random write access. It only skips over bad blocks on linear writes of an
    image to a partition. As such, it is really only suitable for the kernel
    partition, or other partitions with mostly static data
    
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
 .../linux/generic/files/drivers/mtd/nand/mtk_bmt.c | 208 ++++++++++++++++++++-
 1 file changed, 202 insertions(+), 6 deletions(-)

diff --git a/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c
index 7f55c1810d..7c06ee01f0 100644
--- a/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c
+++ b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c
@@ -469,18 +469,26 @@ static bool remap_block_v2(u16 block, u16 mapped_block, int copy_len)
 }
 
 static bool
-mapping_block_in_range(int block)
+mapping_block_in_range(int block, int *start, int *end)
 {
 	const __be32 *cur = bmtd.remap_range;
 	u32 addr = block << bmtd.blk_shift;
 	int i;
 
-	if (!cur || !bmtd.remap_range_len)
+	if (!cur || !bmtd.remap_range_len) {
+		*start = 0;
+		*end = bmtd.total_blks;
 		return true;
+	}
 
-	for (i = 0; i < bmtd.remap_range_len; i++, cur += 2)
-		if (addr >= be32_to_cpu(cur[0]) && addr < be32_to_cpu(cur[1]))
-			return true;
+	for (i = 0; i < bmtd.remap_range_len; i++, cur += 2) {
+		if (addr < be32_to_cpu(cur[0]) || addr >= be32_to_cpu(cur[1]))
+			continue;
+
+		*start = be32_to_cpu(cur[0]);
+		*end = be32_to_cpu(cur[1]);
+		return true;
+	}
 
 	return false;
 }
@@ -488,10 +496,12 @@ mapping_block_in_range(int block)
 static u16
 get_mapping_block_index_v2(int block)
 {
+	int start, end;
+
 	if (block >= bmtd.pool_lba)
 		return block;
 
-	if (!mapping_block_in_range(block))
+	if (!mapping_block_in_range(block, &start, &end))
 		return block;
 
 	return bmtd.bbt->bb_tbl[block];
@@ -938,6 +948,181 @@ static int mtk_bmt_init_v2(struct device_node *np)
 	return 0;
 }
 
+static bool
+bbt_block_is_bad(u16 block)
+{
+	u8 cur = nand_bbt_buf[block / 4];
+
+	return cur & (3 << ((block % 4) * 2));
+}
+
+static void
+bbt_set_block_state(u16 block, bool bad)
+{
+	u8 mask = (3 << ((block % 4) * 2));
+
+	if (bad)
+		nand_bbt_buf[block / 4] |= mask;
+	else
+		nand_bbt_buf[block / 4] &= ~mask;
+
+	bbt_nand_erase(bmtd.bmt_blk_idx);
+	write_bmt(bmtd.bmt_blk_idx, nand_bbt_buf);
+}
+
+static u16
+get_mapping_block_index_bbt(int block)
+{
+	int start, end, ofs;
+	int bad_blocks = 0;
+	int i;
+
+	if (!mapping_block_in_range(block, &start, &end))
+		return block;
+
+	start >>= bmtd.blk_shift;
+	end >>= bmtd.blk_shift;
+	/* skip bad blocks within the mapping range */
+	ofs = block - start;
+	for (i = start; i < end; i++) {
+		if (bbt_block_is_bad(i))
+			bad_blocks++;
+		else if (ofs)
+			ofs--;
+		else
+			break;
+	}
+
+	if (i < end)
+		return i;
+
+	/* when overflowing, remap remaining blocks to bad ones */
+	for (i = end - 1; bad_blocks > 0; i--) {
+		if (!bbt_block_is_bad(i))
+			continue;
+
+		bad_blocks--;
+		if (bad_blocks <= ofs)
+			return i;
+	}
+
+	return block;
+}
+
+static bool remap_block_bbt(u16 block, u16 mapped_blk, int copy_len)
+{
+	int start, end;
+	u16 new_blk;
+
+	if (!mapping_block_in_range(block, &start, &end))
+		return false;
+
+	bbt_set_block_state(mapped_blk, true);
+
+	new_blk = get_mapping_block_index_bbt(block);
+	bbt_nand_erase(new_blk);
+	if (copy_len > 0)
+		bbt_nand_copy(new_blk, mapped_blk, copy_len);
+
+	return false;
+}
+
+static void
+unmap_block_bbt(u16 block)
+{
+	bbt_set_block_state(block, false);
+}
+
+static int
+mtk_bmt_read_bbt(void)
+{
+	u8 oob_buf[8];
+	int i;
+
+	for (i = bmtd.total_blks - 1; i >= bmtd.total_blks - 5; i--) {
+		u32 page = i << (bmtd.blk_shift - bmtd.pg_shift);
+
+		if (bbt_nand_read(page, nand_bbt_buf, bmtd.pg_size,
+				  oob_buf, sizeof(oob_buf))) {
+			pr_info("read_bbt: could not read block %d\n", i);
+			continue;
+		}
+
+		if (oob_buf[0] != 0xff) {
+			pr_info("read_bbt: bad block at %d\n", i);
+			continue;
+		}
+
+		if (memcmp(&oob_buf[1], "mtknand", 7) != 0) {
+			pr_info("read_bbt: signature mismatch in block %d\n", i);
+			print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, oob_buf, 8, 1);
+			continue;
+		}
+
+		pr_info("read_bbt: found bbt at block %d\n", i);
+		bmtd.bmt_blk_idx = i;
+		return 0;
+	}
+
+	return -EIO;
+}
+
+
+static int
+mtk_bmt_init_bbt(struct device_node *np)
+{
+	int buf_size = round_up(bmtd.total_blks >> 2, bmtd.blk_size);
+	int ret;
+
+	nand_bbt_buf = kmalloc(buf_size, GFP_KERNEL);
+	if (!nand_bbt_buf)
+		return -ENOMEM;
+
+	memset(nand_bbt_buf, 0xff, buf_size);
+	bmtd.mtd->size -= 4 * bmtd.mtd->erasesize;
+
+	ret = mtk_bmt_read_bbt();
+	if (ret)
+		return ret;
+
+	bmtd.bmt_pgs = buf_size / bmtd.pg_size;
+
+	return 0;
+}
+
+static int mtk_bmt_debug_bbt(void *data, u64 val)
+{
+	char buf[5];
+	int i, k;
+
+	switch (val) {
+	case 0:
+		for (i = 0; i < bmtd.total_blks; i += 4) {
+			u8 cur = nand_bbt_buf[i / 4];
+
+			for (k = 0; k < 4; k++, cur >>= 2)
+				buf[k] = (cur & 3) ? 'B' : '.';
+
+			buf[4] = 0;
+			printk("[%06x] %s\n", i * bmtd.blk_size, buf);
+		}
+		break;
+	case 100:
+#if 0
+		for (i = bmtd.bmt_blk_idx; i < bmtd.total_blks - 1; i++)
+			bbt_nand_erase(bmtd.bmt_blk_idx);
+#endif
+
+		bmtd.bmt_blk_idx = bmtd.total_blks - 1;
+		bbt_nand_erase(bmtd.bmt_blk_idx);
+		write_bmt(bmtd.bmt_blk_idx, nand_bbt_buf);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
 int mtk_bmt_attach(struct mtd_info *mtd)
 {
 	static const struct mtk_bmt_ops v2_ops = {
@@ -949,6 +1134,15 @@ int mtk_bmt_attach(struct mtd_info *mtd)
 		.get_mapping_block = get_mapping_block_index_v2,
 		.debug = mtk_bmt_debug_v2,
 	};
+	static const struct mtk_bmt_ops bbt_ops = {
+		.sig = "mtknand",
+		.sig_len = 7,
+		.init = mtk_bmt_init_bbt,
+		.remap_block = remap_block_bbt,
+		.unmap_block = unmap_block_bbt,
+		.get_mapping_block = get_mapping_block_index_bbt,
+		.debug = mtk_bmt_debug_bbt,
+	};
 	struct device_node *np;
 	int ret = 0;
 
@@ -961,6 +1155,8 @@ int mtk_bmt_attach(struct mtd_info *mtd)
 
 	if (of_property_read_bool(np, "mediatek,bmt-v2"))
 		bmtd.ops = &v2_ops;
+	else if (of_property_read_bool(np, "mediatek,bbt"))
+		bmtd.ops = &bbt_ops;
 	else
 		return 0;
 



More information about the lede-commits mailing list