|
|
@@ -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;
|
|
|
|