Squashed backup commit (no shallow-clone history issues) of xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream commit 9eece662e) plus all fixes applied to get everpal_defconfig building clean against a modern (GCC 15.2.0) toolchain -- see the previous commit message on the lineage-23.2 branch (also in this repo history is not available here since this is a squashed snapshot; full incremental history lives in the local DL380 checkout and the upstream GitHub remote). Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
688 lines
16 KiB
C
688 lines
16 KiB
C
/*
|
|
* Copyright (C) 2017 MediaTek Inc.
|
|
* Licensed under either
|
|
* BSD Licence, (see NOTICE for more details)
|
|
* GNU General Public License, version 2.0, (see NOTICE for more details)
|
|
*/
|
|
#include "nandx_util.h"
|
|
#include "nandx_errno.h"
|
|
#include "nandx_info.h"
|
|
#include "nandx_core.h"
|
|
#include "nandx_ops.h"
|
|
#include "nandx_bmt.h"
|
|
|
|
#define BMT_VERSION (1) /* initial version */
|
|
#define MAIN_BMT_SIZE (0x200)
|
|
#define SIGNATURE_SIZE (3)
|
|
#define MAIN_SIGNATURE_OFFSET (0)
|
|
#define OOB_SIGNATURE_OFFSET (1)
|
|
#define OOB_INDEX_OFFSET (29)
|
|
#define OOB_INDEX_SIZE (2)
|
|
#define FAKE_INDEX (0xAAAA)
|
|
|
|
struct bmt_header {
|
|
char signature[SIGNATURE_SIZE];
|
|
u8 version;
|
|
u8 bad_count; /* bad block count in pool */
|
|
u8 mapped_count; /* mapped block count in pool */
|
|
u8 checksum;
|
|
u8 reseverd[13];
|
|
};
|
|
|
|
struct bmt_entry {
|
|
u16 bad_index; /* bad block index */
|
|
u16 mapped_index; /* mapping block index in the replace pool */
|
|
};
|
|
|
|
struct bmt_data_info {
|
|
struct bmt_header header;
|
|
struct bmt_entry table[MAIN_BMT_SIZE];
|
|
struct data_bmt_struct data_bmt;
|
|
};
|
|
|
|
struct bmt_handler {
|
|
struct nandx_chip_info *dev_info;
|
|
u32 start_block;
|
|
u32 block_count;
|
|
u32 current_block;
|
|
u8 *data;
|
|
u8 *oob;
|
|
struct bmt_data_info data_info;
|
|
};
|
|
|
|
static const char MAIN_SIGNATURE[] = "BMT";
|
|
static const char OOB_SIGNATURE[] = "bmt";
|
|
|
|
static struct bmt_handler *nandx_bmt;
|
|
|
|
static bool bmt_read_page(struct bmt_handler *bmt, u32 page)
|
|
{
|
|
int ret;
|
|
struct nandx_ops ops;
|
|
|
|
nandx_get_device(FL_READING);
|
|
|
|
ops.row = page;
|
|
ops.col = 0;
|
|
ops.len = bmt->dev_info->page_size;
|
|
ops.data = bmt->data;
|
|
ops.oob = bmt->oob;
|
|
ret = nandx_core_read(&ops, 1, MODE_SLC);
|
|
if (ret == -ENANDFLIPS)
|
|
ret = 0;
|
|
|
|
nandx_release_device();
|
|
return ret < 0 ? false : true;
|
|
}
|
|
|
|
static bool bmt_write_page(struct bmt_handler *bmt, u32 page)
|
|
{
|
|
int ret;
|
|
struct nandx_ops ops;
|
|
|
|
nandx_get_device(FL_WRITING);
|
|
|
|
ops.row = page;
|
|
ops.col = 0;
|
|
ops.len = bmt->dev_info->page_size;
|
|
ops.data = bmt->data;
|
|
ops.oob = bmt->oob;
|
|
ret = nandx_core_write(&ops, 1, MODE_SLC);
|
|
|
|
nandx_release_device();
|
|
return ret < 0 ? false : true;
|
|
}
|
|
|
|
static bool bmt_erase_block(struct bmt_handler *bmt, u32 block)
|
|
{
|
|
int ret;
|
|
u32 row;
|
|
|
|
nandx_get_device(FL_ERASING);
|
|
|
|
row = block * (bmt->dev_info->block_size / bmt->dev_info->page_size);
|
|
ret = nandx_core_erase(&row, 1, MODE_SLC);
|
|
|
|
nandx_release_device();
|
|
return ret < 0 ? false : true;
|
|
}
|
|
|
|
static bool bmt_page_is_empty(struct bmt_handler *bmt, u8 *page)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < bmt->dev_info->page_size; i++) {
|
|
if (page[i] != 0xff)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool bmt_block_isbad(struct bmt_handler *bmt, u32 block)
|
|
{
|
|
bool ret;
|
|
u32 row;
|
|
bool empty_page = true;
|
|
|
|
row = block * (bmt->dev_info->block_size / bmt->dev_info->page_size);
|
|
|
|
memset(bmt->data, 0, bmt->dev_info->page_size);
|
|
memset(bmt->oob, 0, bmt->dev_info->oob_size);
|
|
|
|
ret = bmt_read_page(bmt, row);
|
|
if (ret)
|
|
empty_page = bmt_page_is_empty(bmt, bmt->data);
|
|
|
|
if (!empty_page && bmt->oob[0] == 0xff)
|
|
return false;
|
|
|
|
pr_info("Check bmt block vendor mark %u\n", block);
|
|
|
|
nandx_get_device(FL_READING);
|
|
ret = nandx_core_is_bad(row);
|
|
nandx_release_device();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool bmt_mark_block_bad(struct bmt_handler *bmt, u32 block)
|
|
{
|
|
int ret, i, num;
|
|
struct nandx_ops *ops;
|
|
u32 row;
|
|
|
|
nandx_get_device(FL_WRITING);
|
|
|
|
num = bmt->dev_info->wl_page_num * bmt->dev_info->wl_page_num;
|
|
row = block * (bmt->dev_info->block_size / bmt->dev_info->page_size);
|
|
|
|
ops = mem_alloc(num, sizeof(struct nandx_ops));
|
|
if (!ops) {
|
|
nandx_release_device();
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memset(ops, 0, num * sizeof(struct nandx_ops));
|
|
memset(bmt->data, 0, bmt->dev_info->page_size);
|
|
memset(bmt->oob, 0, bmt->dev_info->oob_size);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
ops[i].row = row + i;
|
|
ops[i].col = 0;
|
|
ops[i].len = bmt->dev_info->page_size;
|
|
ops[i].data = bmt->data;
|
|
ops[i].oob = bmt->oob;
|
|
}
|
|
|
|
ret = nandx_core_write(ops, num, MODE_SLC);
|
|
mem_free(ops);
|
|
|
|
nandx_release_device();
|
|
|
|
return ret < 0 ? false : true;
|
|
}
|
|
|
|
int nandx_bmt_block_isbad(u32 block)
|
|
{
|
|
if (bmt_block_isbad(nandx_bmt, block))
|
|
return -ENANDBAD;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int nandx_bmt_block_markbad(u32 block)
|
|
{
|
|
if (bmt_mark_block_bad(nandx_bmt, block))
|
|
return -ENANDWRITE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void bmt_dump_info(struct bmt_data_info *data_info)
|
|
{
|
|
int i;
|
|
|
|
pr_info("BMT v%d. total %d mapping:\n",
|
|
data_info->header.version, data_info->header.mapped_count);
|
|
for (i = 0; i < data_info->header.mapped_count; i++) {
|
|
pr_info("block[%d] map to block[%d]\n",
|
|
data_info->table[i].bad_index,
|
|
data_info->table[i].mapped_index);
|
|
}
|
|
}
|
|
|
|
static bool bmt_match_signature(struct bmt_handler *bmt)
|
|
{
|
|
|
|
return memcmp(bmt->data + MAIN_SIGNATURE_OFFSET, MAIN_SIGNATURE,
|
|
SIGNATURE_SIZE) ? false : true;
|
|
}
|
|
|
|
static u8 bmt_calculate_checksum(struct bmt_handler *bmt)
|
|
{
|
|
struct bmt_data_info *data_info = &bmt->data_info;
|
|
u32 i, size = bmt->block_count * sizeof(struct bmt_entry);
|
|
u8 checksum = 0;
|
|
u8 *data = (u8 *)data_info->table;
|
|
|
|
checksum += data_info->header.version;
|
|
checksum += data_info->header.mapped_count;
|
|
|
|
for (i = 0; i < size; i++)
|
|
checksum += data[i];
|
|
|
|
return checksum;
|
|
}
|
|
|
|
static bool bmt_block_is_mapped(struct bmt_data_info *data_info, u32 index)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < data_info->header.mapped_count; i++) {
|
|
/* bmt block mapped to another */
|
|
if (index == data_info->table[i].mapped_index)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool bmt_data_is_valid(struct bmt_handler *bmt)
|
|
{
|
|
u8 checksum;
|
|
struct bmt_data_info *data_info = &bmt->data_info;
|
|
|
|
checksum = bmt_calculate_checksum(bmt);
|
|
|
|
pr_debug("BMT Checksum is: 0x%x\n", data_info->header.checksum);
|
|
/* checksum correct? */
|
|
if (data_info->header.checksum != checksum) {
|
|
pr_err("BMT Data checksum error: %x %x\n",
|
|
data_info->header.checksum, checksum);
|
|
return false;
|
|
}
|
|
|
|
/* pass check, valid bmt. */
|
|
pr_debug("Valid BMT, version v%d\n", data_info->header.version);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void bmt_fill_buffer(struct bmt_handler *bmt)
|
|
{
|
|
struct bmt_data_info *data_info = &bmt->data_info;
|
|
|
|
bmt_dump_info(&bmt->data_info);
|
|
|
|
memcpy(data_info->header.signature, MAIN_SIGNATURE, SIGNATURE_SIZE);
|
|
data_info->header.version = BMT_VERSION;
|
|
|
|
data_info->header.checksum = bmt_calculate_checksum(bmt);
|
|
|
|
memcpy(bmt->data + MAIN_SIGNATURE_OFFSET, data_info,
|
|
sizeof(struct bmt_data_info));
|
|
memcpy(bmt->oob + OOB_SIGNATURE_OFFSET, OOB_SIGNATURE,
|
|
SIGNATURE_SIZE);
|
|
}
|
|
|
|
static int bmt_load_data(struct bmt_handler *bmt)
|
|
{
|
|
u32 row;
|
|
struct nandx_chip_info *info = bmt->dev_info;
|
|
u32 bmt_index = bmt->start_block + bmt->block_count - 1;
|
|
|
|
for (; bmt_index >= bmt->start_block; bmt_index--) {
|
|
if (bmt_block_isbad(bmt, bmt_index)) {
|
|
pr_err("Skip bad block: %d\n", bmt_index);
|
|
continue;
|
|
}
|
|
|
|
row = bmt_index * (info->block_size / info->page_size);
|
|
if (!bmt_read_page(bmt, row)) {
|
|
pr_err("read block %d error\n", bmt_index);
|
|
continue;
|
|
}
|
|
|
|
memcpy(&bmt->data_info, bmt->data + MAIN_SIGNATURE_OFFSET,
|
|
sizeof(struct bmt_data_info));
|
|
|
|
if (!bmt_match_signature(bmt)) {
|
|
pr_err("signature not match at %u\n", bmt_index);
|
|
continue;
|
|
}
|
|
|
|
pr_info("Match bmt signature @ block 0x%x\n", bmt_index);
|
|
|
|
if (!bmt_data_is_valid(bmt)) {
|
|
pr_err("BMT data not correct %d\n", bmt_index);
|
|
continue;
|
|
}
|
|
|
|
bmt_dump_info(&bmt->data_info);
|
|
return bmt_index;
|
|
}
|
|
|
|
pr_err("Bmt block not found!\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int find_available_block(struct bmt_handler *bmt, bool start_from_end)
|
|
{
|
|
u32 i, block;
|
|
int direct, retry;
|
|
|
|
pr_debug("Try to find available block\n");
|
|
|
|
block = start_from_end ? (bmt->dev_info->block_num - 1) :
|
|
bmt->start_block;
|
|
direct = start_from_end ? -1 : 1;
|
|
retry = start_from_end ? 1 : 0;
|
|
|
|
for (i = 0; i < bmt->block_count; i++, block += direct) {
|
|
if (block == bmt->current_block) {
|
|
pr_debug("Skip bmt block 0x%x\n", block);
|
|
continue;
|
|
}
|
|
|
|
if (bmt_block_isbad(bmt, block)) {
|
|
pr_debug("Skip bad block 0x%x\n", block);
|
|
continue;
|
|
}
|
|
|
|
if (bmt_block_is_mapped(&bmt->data_info, block)) {
|
|
pr_debug("Skip mapped block 0x%x\n", block);
|
|
continue;
|
|
}
|
|
|
|
pr_debug("Find block 0x%x available\n", block);
|
|
|
|
if (retry) {
|
|
/* fix block is good, but erase fail */
|
|
if (bmt_erase_block(bmt, block)) {
|
|
pr_info
|
|
("Reserved 0x%x for DL_INFO, find next\n",
|
|
block);
|
|
retry -= 1;
|
|
}
|
|
pr_info("Erase block %u fail, retry\n", block);
|
|
continue;
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u16 get_bad_index_from_oob(struct bmt_handler *bmt, u8 *oob)
|
|
{
|
|
u16 index;
|
|
u32 offset = OOB_INDEX_OFFSET;
|
|
|
|
if (KB(2) == bmt->dev_info->page_size)
|
|
offset = 13;
|
|
|
|
memcpy(&index, oob + offset, OOB_INDEX_SIZE);
|
|
|
|
return index;
|
|
}
|
|
|
|
static void set_bad_index_to_oob(struct bmt_handler *bmt, u8 *oob, u16 index)
|
|
{
|
|
u32 offset = OOB_INDEX_OFFSET;
|
|
|
|
if (KB(2) == bmt->dev_info->page_size)
|
|
offset = 13;
|
|
|
|
memcpy(oob + offset, &index, sizeof(index));
|
|
}
|
|
|
|
static int bmt_write_to_flash(struct bmt_handler *bmt)
|
|
{
|
|
bool ret;
|
|
u32 page, page_size, block_size;
|
|
|
|
page_size = bmt->dev_info->page_size;
|
|
block_size = bmt->dev_info->block_size;
|
|
|
|
pr_debug("Try to write BMT\n");
|
|
|
|
while (1) {
|
|
if (bmt->current_block == 0) {
|
|
bmt->current_block = find_available_block(bmt, true);
|
|
if (!bmt->current_block) {
|
|
pr_err("no valid block\n");
|
|
return -ENOSPC;
|
|
}
|
|
}
|
|
|
|
pr_info("Find BMT block: 0x%x\n", bmt->current_block);
|
|
|
|
/* write bmt to flash */
|
|
ret = bmt_erase_block(bmt, bmt->current_block);
|
|
if (ret) {
|
|
/* fill NAND BMT buffer */
|
|
memset(bmt->oob, 0xFF, bmt->dev_info->oob_size);
|
|
bmt_fill_buffer(bmt);
|
|
page = bmt->current_block * (block_size / page_size);
|
|
ret = bmt_write_page(bmt, page);
|
|
if (ret)
|
|
break;
|
|
pr_err("write failed 0x%x\n", bmt->current_block);
|
|
}
|
|
|
|
pr_err("erase fail, mark bad 0x%x\n", bmt->current_block);
|
|
bmt_mark_block_bad(bmt, bmt->current_block);
|
|
|
|
bmt->current_block = 0;
|
|
}
|
|
|
|
pr_debug("Write BMT data to block 0x%x success\n",
|
|
bmt->current_block);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bmt_construct(struct bmt_handler *bmt)
|
|
{
|
|
u32 index, bad_index, row, count = 0;
|
|
struct nandx_chip_info *dev_info = bmt->dev_info;
|
|
struct bmt_data_info *data_info = &bmt->data_info;
|
|
u32 end_block = bmt->start_block + bmt->block_count;
|
|
u32 pages_per_block = dev_info->block_size / dev_info->page_size;
|
|
|
|
/* init everything in BMT struct */
|
|
data_info->header.version = BMT_VERSION;
|
|
data_info->header.bad_count = 0;
|
|
data_info->header.mapped_count = 0;
|
|
|
|
memset(data_info->table, 0,
|
|
bmt->block_count * sizeof(struct bmt_entry));
|
|
|
|
/* TODO: cannot scan ftl partition for construct */
|
|
for (index = bmt->start_block; index < end_block; index++) {
|
|
if (bmt_block_isbad(bmt, index)) {
|
|
pr_err("Skip bad block: 0x%x\n", index);
|
|
continue;
|
|
}
|
|
row = index * pages_per_block;
|
|
if (bmt_read_page(bmt, row)) {
|
|
bad_index = get_bad_index_from_oob(bmt, bmt->oob);
|
|
if (bad_index >= bmt->start_block) {
|
|
pr_err("get bad index: 0x%x\n", bad_index);
|
|
if (bad_index != 0xFFFF)
|
|
pr_err("Invalid index\n");
|
|
continue;
|
|
}
|
|
pr_debug("0x%x mapped to bad block: 0x%x\n",
|
|
index, bad_index);
|
|
/* add mapping to BMT */
|
|
data_info->table[count].bad_index = bad_index;
|
|
data_info->table[count].mapped_index = index;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
data_info->header.mapped_count = count;
|
|
pr_debug("Scan replace pool done, mapped block: %d\n",
|
|
data_info->header.mapped_count);
|
|
|
|
/* write BMT back */
|
|
return bmt_write_to_flash(bmt);
|
|
}
|
|
|
|
int nandx_bmt_init(struct nandx_chip_info *dev_info, u32 block_num,
|
|
bool rebuild)
|
|
{
|
|
if (block_num > MAIN_BMT_SIZE) {
|
|
pr_err("bmt size %d over %d\n", block_num, MAIN_BMT_SIZE);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
nandx_bmt = mem_alloc(1, sizeof(struct bmt_handler));
|
|
if (!nandx_bmt)
|
|
return -ENOMEM;
|
|
|
|
nandx_bmt->dev_info = dev_info;
|
|
nandx_bmt->data = mem_alloc(1, dev_info->page_size);
|
|
nandx_bmt->oob = mem_alloc(1, dev_info->oob_size);
|
|
nandx_bmt->start_block = dev_info->block_num - block_num;
|
|
nandx_bmt->block_count = block_num;
|
|
|
|
pr_info("bmt start block: %d, bmt block count: %d\n",
|
|
nandx_bmt->start_block, nandx_bmt->block_count);
|
|
|
|
memset(nandx_bmt->data_info.table, 0,
|
|
block_num * sizeof(struct bmt_entry));
|
|
|
|
nandx_bmt->current_block = bmt_load_data(nandx_bmt);
|
|
if (!nandx_bmt->current_block) {
|
|
if (rebuild) {
|
|
pr_info("Not found bmt, do bmt construct...\n");
|
|
return bmt_construct(nandx_bmt);
|
|
}
|
|
pr_info("Not found bmt, need construct data bmt info\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void nandx_bmt_reset(void)
|
|
{
|
|
nandx_bmt->current_block = 0;
|
|
memset(&nandx_bmt->data_info, 0, sizeof(struct bmt_data_info));
|
|
}
|
|
|
|
void nandx_bmt_exit(void)
|
|
{
|
|
mem_free(nandx_bmt->data);
|
|
mem_free(nandx_bmt->oob);
|
|
mem_free(nandx_bmt);
|
|
}
|
|
|
|
static u32 nandx_bmt_update_block(struct bmt_handler *bmt, u32 bad_block,
|
|
enum UPDATE_REASON reason)
|
|
{
|
|
u32 i, map_index = 0, map_count = 0;
|
|
struct bmt_entry *entry;
|
|
struct bmt_data_info *data_info = &bmt->data_info;
|
|
struct data_bmt_struct *info = &(bmt->data_info.data_bmt);
|
|
|
|
if (bad_block >= info->start_block && bad_block < info->end_block) {
|
|
if (nandx_bmt_get_mapped_block(bad_block) != DATA_BAD_BLK) {
|
|
pr_debug("update_DATA bad block %d\n", bad_block);
|
|
info->entry[info->bad_count].bad_index = bad_block;
|
|
info->entry[info->bad_count].flag = reason;
|
|
info->bad_count++;
|
|
} else {
|
|
pr_err("block(%d) in bad list\n", bad_block);
|
|
return bad_block;
|
|
}
|
|
} else {
|
|
map_index = find_available_block(bmt, false);
|
|
if (!map_index) {
|
|
pr_err("no good bmt block to map\n");
|
|
return bad_block;
|
|
}
|
|
|
|
/* now let's update BMT */
|
|
if (bad_block >= bmt->start_block) {
|
|
/* mapped block become bad, find original bad block */
|
|
for (i = 0; i < bmt->block_count; i++) {
|
|
entry = &data_info->table[i];
|
|
if (entry->mapped_index == bad_block) {
|
|
pr_debug("block %d is bad\n",
|
|
entry->bad_index);
|
|
entry->mapped_index = map_index;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
map_count = data_info->header.mapped_count;
|
|
entry = &data_info->table[map_count];
|
|
entry->mapped_index = map_index;
|
|
entry->bad_index = bad_block;
|
|
data_info->header.mapped_count++;
|
|
}
|
|
}
|
|
return map_index;
|
|
}
|
|
|
|
u32 nandx_bmt_update(u32 bad_block, enum UPDATE_REASON reason)
|
|
{
|
|
u32 map_index;
|
|
|
|
map_index = nandx_bmt_update_block(nandx_bmt, bad_block, reason);
|
|
|
|
if (bmt_write_to_flash(nandx_bmt)) {
|
|
NANDX_ASSERT(0);
|
|
return bad_block;
|
|
}
|
|
|
|
return map_index;
|
|
}
|
|
|
|
int nandx_bmt_remark(u32 *blocks, int count, enum UPDATE_REASON reason)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (blocks[i] >= nandx_bmt->start_block) {
|
|
pr_info("skip remark bmt bad %u\n", blocks[i]);
|
|
continue;
|
|
}
|
|
|
|
pr_info("remark bad block %u\n", blocks[i]);
|
|
nandx_bmt_update_block(nandx_bmt, blocks[i], reason);
|
|
}
|
|
|
|
return bmt_write_to_flash(nandx_bmt);
|
|
}
|
|
|
|
void nandx_bmt_update_oob(u32 block, u8 *oob)
|
|
{
|
|
u16 index;
|
|
u32 mapped_block;
|
|
|
|
mapped_block = nandx_bmt_get_mapped_block(block);
|
|
index = mapped_block == block ? FAKE_INDEX : block;
|
|
|
|
set_bad_index_to_oob(nandx_bmt, oob, index);
|
|
}
|
|
|
|
u32 nandx_bmt_get_mapped_block(u32 block)
|
|
{
|
|
int i;
|
|
struct bmt_data_info *data_info = &nandx_bmt->data_info;
|
|
struct data_bmt_struct *data_bmt_info =
|
|
&(nandx_bmt->data_info.data_bmt);
|
|
|
|
if ((block >= data_bmt_info->start_block) &&
|
|
(block < data_bmt_info->end_block)) {
|
|
for (i = 0; i < data_bmt_info->bad_count; i++) {
|
|
if (data_bmt_info->entry[i].bad_index == block) {
|
|
pr_debug
|
|
("FTL bad block at 0x%x, bad_count:%d\n",
|
|
block, data_bmt_info->bad_count);
|
|
return DATA_BAD_BLK;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < data_info->header.mapped_count; i++) {
|
|
if (data_info->table[i].bad_index == block)
|
|
return data_info->table[i].mapped_index;
|
|
}
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
int nandx_bmt_get_data_bmt(struct data_bmt_struct *data_bmt)
|
|
{
|
|
struct data_bmt_struct *data_bmt_info =
|
|
&(nandx_bmt->data_info.data_bmt);
|
|
|
|
if (data_bmt_info->version == DATA_BMT_VERSION) {
|
|
memcpy(data_bmt, data_bmt_info,
|
|
sizeof(struct data_bmt_struct));
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int nandx_bmt_init_data_bmt(u32 start_block, u32 end_block)
|
|
{
|
|
struct data_bmt_struct *data_bmt = &nandx_bmt->data_info.data_bmt;
|
|
|
|
data_bmt->start_block = start_block;
|
|
data_bmt->end_block = end_block;
|
|
data_bmt->version = DATA_BMT_VERSION;
|
|
data_bmt->bad_count = 0;
|
|
|
|
return 0;
|
|
}
|