uboot-mediatek: additions from MTK SDK
[openwrt/staging/dedeckeh.git] / package / boot / uboot-mediatek / patches / 100-06-mtd-add-core-facility-code-of-NMBM.patch
diff --git a/package/boot/uboot-mediatek/patches/100-06-mtd-add-core-facility-code-of-NMBM.patch b/package/boot/uboot-mediatek/patches/100-06-mtd-add-core-facility-code-of-NMBM.patch
new file mode 100644 (file)
index 0000000..9f80deb
--- /dev/null
@@ -0,0 +1,3422 @@
+From 690479081fb6a0c0f77f10fb457ad69e71390f15 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 10:26:35 +0800
+Subject: [PATCH 40/71] mtd: add core facility code of NMBM
+
+This patch adds a NAND bad block management named NMBM (NAND mapping block
+management) which supports using a mapping table to deal with bad blocks
+before factory shipping and during use.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mtd/Kconfig             |    2 +
+ drivers/mtd/Makefile            |    1 +
+ drivers/mtd/nmbm/Kconfig        |   29 +
+ drivers/mtd/nmbm/Makefile       |    5 +
+ drivers/mtd/nmbm/nmbm-core.c    | 2936 +++++++++++++++++++++++++++++++
+ drivers/mtd/nmbm/nmbm-debug.h   |   37 +
+ drivers/mtd/nmbm/nmbm-debug.inl |   39 +
+ drivers/mtd/nmbm/nmbm-private.h |  137 ++
+ include/nmbm/nmbm-os.h          |   66 +
+ include/nmbm/nmbm.h             |  102 ++
+ 10 files changed, 3354 insertions(+)
+ create mode 100644 drivers/mtd/nmbm/Kconfig
+ create mode 100644 drivers/mtd/nmbm/Makefile
+ create mode 100644 drivers/mtd/nmbm/nmbm-core.c
+ create mode 100644 drivers/mtd/nmbm/nmbm-debug.h
+ create mode 100644 drivers/mtd/nmbm/nmbm-debug.inl
+ create mode 100644 drivers/mtd/nmbm/nmbm-private.h
+ create mode 100644 include/nmbm/nmbm-os.h
+ create mode 100644 include/nmbm/nmbm.h
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -174,4 +174,6 @@ source "drivers/mtd/spi/Kconfig"
+ source "drivers/mtd/ubi/Kconfig"
++source "drivers/mtd/nmbm/Kconfig"
++
+ endmenu
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -41,3 +41,4 @@ obj-$(CONFIG_SPL_UBI) += ubispl/
+ endif
+ obj-$(CONFIG_MTK_SPI_NAND) += mtk-snand/
++obj-$(CONFIG_NMBM) += nmbm/
+--- /dev/null
++++ b/drivers/mtd/nmbm/Kconfig
+@@ -0,0 +1,29 @@
++
++config NMBM
++      bool "Enable NAND mapping block management"
++      default n
++
++choice
++      prompt "Default log level"
++      depends on NMBM
++      default NMBM_LOG_LEVEL_INFO
++
++config NMBM_LOG_LEVEL_DEBUG
++      bool "0 - Debug"
++
++config NMBM_LOG_LEVEL_INFO
++      bool "1 - Info"
++
++config NMBM_LOG_LEVEL_WARN
++      bool "2 - Warn"
++
++config NMBM_LOG_LEVEL_ERR
++      bool "3 - Error"
++
++config NMBM_LOG_LEVEL_EMERG
++      bool "4 - Emergency"
++
++config NMBM_LOG_LEVEL_NONE
++      bool "5 - None"
++
++endchoice
+--- /dev/null
++++ b/drivers/mtd/nmbm/Makefile
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# (C) Copyright 2020 MediaTek Inc. All rights reserved.
++
++obj-$(CONFIG_NMBM) += nmbm-core.o
+--- /dev/null
++++ b/drivers/mtd/nmbm/nmbm-core.c
+@@ -0,0 +1,2936 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#include "nmbm-private.h"
++
++#include "nmbm-debug.h"
++
++#define NMBM_VER_MAJOR                        1
++#define NMBM_VER_MINOR                        0
++#define NMBM_VER                      NMBM_VERSION_MAKE(NMBM_VER_MAJOR, \
++                                                        NMBM_VER_MINOR)
++
++#define NMBM_ALIGN(v, a)              (((v) + (a) - 1) & ~((a) - 1))
++
++/*****************************************************************************/
++/* Logging related functions */
++/*****************************************************************************/
++
++/*
++ * nmbm_log_lower - Print log using OS specific routine
++ * @nld: NMBM lower device structure
++ * @level: log level
++ * @fmt: format string
++ */
++static void nmbm_log_lower(struct nmbm_lower_device *nld,
++                         enum nmbm_log_category level, const char *fmt, ...)
++{
++      va_list ap;
++
++      if (!nld->logprint)
++              return;
++
++      va_start(ap, fmt);
++      nld->logprint(nld->arg, level, fmt, ap);
++      va_end(ap);
++}
++
++/*
++ * nmbm_log - Print log using OS specific routine
++ * @ni: NMBM instance structure
++ * @level: log level
++ * @fmt: format string
++ */
++static void nmbm_log(struct nmbm_instance *ni, enum nmbm_log_category level,
++                   const char *fmt, ...)
++{
++      va_list ap;
++
++      if (!ni)
++              return;
++
++      if (!ni->lower.logprint || level < ni->log_display_level)
++              return;
++
++      va_start(ap, fmt);
++      ni->lower.logprint(ni->lower.arg, level, fmt, ap);
++      va_end(ap);
++}
++
++/*
++ * nmbm_set_log_level - Set log display level
++ * @ni: NMBM instance structure
++ * @level: log display level
++ */
++enum nmbm_log_category nmbm_set_log_level(struct nmbm_instance *ni,
++                                        enum nmbm_log_category level)
++{
++      enum nmbm_log_category old;
++
++      if (!ni)
++              return __NMBM_LOG_MAX;
++
++      old = ni->log_display_level;
++      ni->log_display_level = level;
++      return old;
++}
++
++/*
++ * nlog_table_creation - Print log of table creation event
++ * @ni: NMBM instance structure
++ * @main_table: whether the table is main info table
++ * @start_ba: start block address of the table
++ * @end_ba: block address after the end of the table
++ */
++static void nlog_table_creation(struct nmbm_instance *ni, bool main_table,
++                             uint32_t start_ba, uint32_t end_ba)
++{
++      if (start_ba == end_ba - 1)
++              nlog_info(ni, "%s info table has been written to block %u\n",
++                       main_table ? "Main" : "Backup", start_ba);
++      else
++              nlog_info(ni, "%s info table has been written to block %u-%u\n",
++                       main_table ? "Main" : "Backup", start_ba, end_ba - 1);
++
++      nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1);
++}
++
++/*
++ * nlog_table_update - Print log of table update event
++ * @ni: NMBM instance structure
++ * @main_table: whether the table is main info table
++ * @start_ba: start block address of the table
++ * @end_ba: block address after the end of the table
++ */
++static void nlog_table_update(struct nmbm_instance *ni, bool main_table,
++                           uint32_t start_ba, uint32_t end_ba)
++{
++      if (start_ba == end_ba - 1)
++              nlog_debug(ni, "%s info table has been updated in block %u\n",
++                        main_table ? "Main" : "Backup", start_ba);
++      else
++              nlog_debug(ni, "%s info table has been updated in block %u-%u\n",
++                        main_table ? "Main" : "Backup", start_ba, end_ba - 1);
++
++      nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1);
++}
++
++/*
++ * nlog_table_found - Print log of table found event
++ * @ni: NMBM instance structure
++ * @first_table: whether the table is first found info table
++ * @write_count: write count of the info table
++ * @start_ba: start block address of the table
++ * @end_ba: block address after the end of the table
++ */
++static void nlog_table_found(struct nmbm_instance *ni, bool first_table,
++                          uint32_t write_count, uint32_t start_ba,
++                          uint32_t end_ba)
++{
++      if (start_ba == end_ba - 1)
++              nlog_info(ni, "%s info table with writecount %u found in block %u\n",
++                       first_table ? "First" : "Second", write_count,
++                       start_ba);
++      else
++              nlog_info(ni, "%s info table with writecount %u found in block %u-%u\n",
++                       first_table ? "First" : "Second", write_count,
++                       start_ba, end_ba - 1);
++
++      nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1);
++}
++
++/*****************************************************************************/
++/* Address conversion functions */
++/*****************************************************************************/
++
++/*
++ * addr2ba - Convert a linear address to block address
++ * @ni: NMBM instance structure
++ * @addr: Linear address
++ */
++static uint32_t addr2ba(struct nmbm_instance *ni, uint64_t addr)
++{
++      return addr >> ni->erasesize_shift;
++}
++
++/*
++ * ba2addr - Convert a block address to linear address
++ * @ni: NMBM instance structure
++ * @ba: Block address
++ */
++static uint64_t ba2addr(struct nmbm_instance *ni, uint32_t ba)
++{
++      return (uint64_t)ba << ni->erasesize_shift;
++}
++/*
++ * size2blk - Get minimum required blocks for storing specific size of data
++ * @ni: NMBM instance structure
++ * @size: size for storing
++ */
++static uint32_t size2blk(struct nmbm_instance *ni, uint64_t size)
++{
++      return (size + ni->lower.erasesize - 1) >> ni->erasesize_shift;
++}
++
++/*****************************************************************************/
++/* High level NAND chip APIs */
++/*****************************************************************************/
++
++/*
++ * nmbm_reset_chip - Reset NAND device
++ * @nld: Lower NAND chip structure
++ */
++static void nmbm_reset_chip(struct nmbm_instance *ni)
++{
++      if (ni->lower.reset_chip)
++              ni->lower.reset_chip(ni->lower.arg);
++}
++
++/*
++ * nmbm_read_phys_page - Read page with retry
++ * @ni: NMBM instance structure
++ * @addr: linear address where the data will be read from
++ * @data: the main data to be read
++ * @oob: the oob data to be read
++ * @mode: mode for processing oob data
++ *
++ * Read a page for at most NMBM_TRY_COUNT times.
++ *
++ * Return 0 for success, positive value for corrected bitflip count,
++ * -EBADMSG for ecc error, other negative values for other errors
++ */
++static int nmbm_read_phys_page(struct nmbm_instance *ni, uint64_t addr,
++                             void *data, void *oob, enum nmbm_oob_mode mode)
++{
++      int tries, ret;
++
++      for (tries = 0; tries < NMBM_TRY_COUNT; tries++) {
++              ret = ni->lower.read_page(ni->lower.arg, addr, data, oob, mode);
++              if (ret >= 0)
++                      return ret;
++
++              nmbm_reset_chip(ni);
++      }
++
++      if (ret != -EBADMSG)
++              nlog_err(ni, "Page read failed at address 0x%08llx\n", addr);
++
++      return ret;
++}
++
++/*
++ * nmbm_write_phys_page - Write page with retry
++ * @ni: NMBM instance structure
++ * @addr: linear address where the data will be written to
++ * @data: the main data to be written
++ * @oob: the oob data to be written
++ * @mode: mode for processing oob data
++ *
++ * Write a page for at most NMBM_TRY_COUNT times.
++ */
++static bool nmbm_write_phys_page(struct nmbm_instance *ni, uint64_t addr,
++                               const void *data, const void *oob,
++                               enum nmbm_oob_mode mode)
++{
++      int tries, ret;
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY) {
++              nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr);
++              return false;
++      }
++
++      for (tries = 0; tries < NMBM_TRY_COUNT; tries++) {
++              ret = ni->lower.write_page(ni->lower.arg, addr, data, oob, mode);
++              if (!ret)
++                      return true;
++
++              nmbm_reset_chip(ni);
++      }
++
++      nlog_err(ni, "Page write failed at address 0x%08llx\n", addr);
++
++      return false;
++}
++
++/*
++ * nmbm_erase_phys_block - Erase a block with retry
++ * @ni: NMBM instance structure
++ * @addr: Linear address
++ *
++ * Erase a block for at most NMBM_TRY_COUNT times.
++ */
++static bool nmbm_erase_phys_block(struct nmbm_instance *ni, uint64_t addr)
++{
++      int tries, ret;
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY) {
++              nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr);
++              return false;
++      }
++
++      for (tries = 0; tries < NMBM_TRY_COUNT; tries++) {
++              ret = ni->lower.erase_block(ni->lower.arg, addr);
++              if (!ret)
++                      return true;
++
++              nmbm_reset_chip(ni);
++      }
++
++      nlog_err(ni, "Block erasure failed at address 0x%08llx\n", addr);
++
++      return false;
++}
++
++/*
++ * nmbm_check_bad_phys_block - Check whether a block is marked bad in OOB
++ * @ni: NMBM instance structure
++ * @ba: block address
++ */
++static bool nmbm_check_bad_phys_block(struct nmbm_instance *ni, uint32_t ba)
++{
++      uint64_t addr = ba2addr(ni, ba);
++      int ret;
++
++      if (ni->lower.is_bad_block)
++              return ni->lower.is_bad_block(ni->lower.arg, addr);
++
++      /* Treat ECC error as read success */
++      ret = nmbm_read_phys_page(ni, addr, NULL,
++                                ni->page_cache + ni->lower.writesize,
++                                NMBM_MODE_RAW);
++      if (ret < 0 && ret != -EBADMSG)
++              return true;
++
++      return ni->page_cache[ni->lower.writesize] != 0xff;
++}
++
++/*
++ * nmbm_mark_phys_bad_block - Mark a block bad
++ * @ni: NMBM instance structure
++ * @addr: Linear address
++ */
++static int nmbm_mark_phys_bad_block(struct nmbm_instance *ni, uint32_t ba)
++{
++      uint64_t addr = ba2addr(ni, ba);
++      enum nmbm_log_category level;
++      uint32_t off;
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY) {
++              nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr);
++              return false;
++      }
++
++      nlog_info(ni, "Block %u [0x%08llx] will be marked bad\n", ba, addr);
++
++      if (ni->lower.mark_bad_block)
++              return ni->lower.mark_bad_block(ni->lower.arg, addr);
++
++      /* Whole page set to 0x00 */
++      memset(ni->page_cache, 0, ni->rawpage_size);
++
++      /* Write to all pages within this block, disable all errors */
++      level = nmbm_set_log_level(ni, __NMBM_LOG_MAX);
++
++      for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) {
++              nmbm_write_phys_page(ni, addr + off, ni->page_cache,
++                                   ni->page_cache + ni->lower.writesize,
++                                   NMBM_MODE_RAW);
++      }
++
++      nmbm_set_log_level(ni, level);
++
++      return 0;
++}
++
++/*****************************************************************************/
++/* NMBM related functions */
++/*****************************************************************************/
++
++/*
++ * nmbm_check_header - Check whether a NMBM structure is valid
++ * @data: pointer to a NMBM structure with a NMBM header at beginning
++ * @size: Size of the buffer pointed by @header
++ *
++ * The size of the NMBM structure may be larger than NMBM header,
++ * e.g. block mapping table and block state table.
++ */
++static bool nmbm_check_header(const void *data, uint32_t size)
++{
++      const struct nmbm_header *header = data;
++      struct nmbm_header nhdr;
++      uint32_t new_checksum;
++
++      /*
++       * Make sure expected structure size is equal or smaller than
++       * buffer size.
++       */
++      if (header->size > size)
++              return false;
++
++      memcpy(&nhdr, data, sizeof(nhdr));
++
++      nhdr.checksum = 0;
++      new_checksum = nmbm_crc32(0, &nhdr, sizeof(nhdr));
++      if (header->size > sizeof(nhdr))
++              new_checksum = nmbm_crc32(new_checksum,
++                      (const uint8_t *)data + sizeof(nhdr),
++                      header->size - sizeof(nhdr));
++
++      if (header->checksum != new_checksum)
++              return false;
++
++      return true;
++}
++
++/*
++ * nmbm_update_checksum - Update checksum of a NMBM structure
++ * @header: pointer to a NMBM structure with a NMBM header at beginning
++ *
++ * The size of the NMBM structure must be specified by @header->size
++ */
++static void nmbm_update_checksum(struct nmbm_header *header)
++{
++      header->checksum = 0;
++      header->checksum = nmbm_crc32(0, header, header->size);
++}
++
++/*
++ * nmbm_get_spare_block_count - Calculate number of blocks should be reserved
++ * @block_count: number of blocks of data
++ *
++ * Calculate number of blocks should be reserved for data
++ */
++static uint32_t nmbm_get_spare_block_count(uint32_t block_count)
++{
++      uint32_t val;
++
++      val = (block_count + NMBM_SPARE_BLOCK_DIV / 2) / NMBM_SPARE_BLOCK_DIV;
++      val *= NMBM_SPARE_BLOCK_MULTI;
++
++      if (val < NMBM_SPARE_BLOCK_MIN)
++              val = NMBM_SPARE_BLOCK_MIN;
++
++      return val;
++}
++
++/*
++ * nmbm_get_block_state_raw - Get state of a block from raw block state table
++ * @block_state: pointer to raw block state table (bitmap)
++ * @ba: block address
++ */
++static uint32_t nmbm_get_block_state_raw(nmbm_bitmap_t *block_state,
++                                       uint32_t ba)
++{
++      uint32_t unit, shift;
++
++      unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT;
++      shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK;
++
++      return (block_state[unit] >> shift) & BLOCK_ST_MASK;
++}
++
++/*
++ * nmbm_get_block_state - Get state of a block from block state table
++ * @ni: NMBM instance structure
++ * @ba: block address
++ */
++static uint32_t nmbm_get_block_state(struct nmbm_instance *ni, uint32_t ba)
++{
++      return nmbm_get_block_state_raw(ni->block_state, ba);
++}
++
++/*
++ * nmbm_set_block_state - Set state of a block to block state table
++ * @ni: NMBM instance structure
++ * @ba: block address
++ * @state: block state
++ *
++ * Set state of a block. If the block state changed, ni->block_state_changed
++ * will be increased.
++ */
++static bool nmbm_set_block_state(struct nmbm_instance *ni, uint32_t ba,
++                               uint32_t state)
++{
++      uint32_t unit, shift, orig;
++      nmbm_bitmap_t uv;
++
++      unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT;
++      shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK;
++
++      orig = (ni->block_state[unit] >> shift) & BLOCK_ST_MASK;
++      state &= BLOCK_ST_MASK;
++
++      uv = ni->block_state[unit] & (~(BLOCK_ST_MASK << shift));
++      uv |= state << shift;
++      ni->block_state[unit] = uv;
++
++      if (state == BLOCK_ST_BAD)
++              nmbm_mark_block_color_bad(ni, ba);
++
++      if (orig != state) {
++              ni->block_state_changed++;
++              return true;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_block_walk_asc - Skip specified number of good blocks, ascending addr.
++ * @ni: NMBM instance structure
++ * @ba: start physical block address
++ * @nba: return physical block address after walk
++ * @count: number of good blocks to be skipped
++ * @limit: highest block address allowed for walking
++ *
++ * Start from @ba, skipping any bad blocks, counting @count good blocks, and
++ * return the next good block address.
++ *
++ * If no enough good blocks counted while @limit reached, false will be returned.
++ *
++ * If @count == 0, nearest good block address will be returned.
++ * @limit is not counted in walking.
++ */
++static bool nmbm_block_walk_asc(struct nmbm_instance *ni, uint32_t ba,
++                              uint32_t *nba, uint32_t count,
++                              uint32_t limit)
++{
++      int32_t nblock = count;
++
++      if (limit >= ni->block_count)
++              limit = ni->block_count - 1;
++
++      while (ba < limit) {
++              if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD)
++                      nblock--;
++
++              if (nblock < 0) {
++                      *nba = ba;
++                      return true;
++              }
++
++              ba++;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_block_walk_desc - Skip specified number of good blocks, descending addr
++ * @ni: NMBM instance structure
++ * @ba: start physical block address
++ * @nba: return physical block address after walk
++ * @count: number of good blocks to be skipped
++ * @limit: lowest block address allowed for walking
++ *
++ * Start from @ba, skipping any bad blocks, counting @count good blocks, and
++ * return the next good block address.
++ *
++ * If no enough good blocks counted while @limit reached, false will be returned.
++ *
++ * If @count == 0, nearest good block address will be returned.
++ * @limit is not counted in walking.
++ */
++static bool nmbm_block_walk_desc(struct nmbm_instance *ni, uint32_t ba,
++                               uint32_t *nba, uint32_t count, uint32_t limit)
++{
++      int32_t nblock = count;
++
++      if (limit >= ni->block_count)
++              limit = ni->block_count - 1;
++
++      while (ba > limit) {
++              if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD)
++                      nblock--;
++
++              if (nblock < 0) {
++                      *nba = ba;
++                      return true;
++              }
++
++              ba--;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_block_walk - Skip specified number of good blocks from curr. block addr
++ * @ni: NMBM instance structure
++ * @ascending: whether to walk ascending
++ * @ba: start physical block address
++ * @nba: return physical block address after walk
++ * @count: number of good blocks to be skipped
++ * @limit: highest/lowest block address allowed for walking
++ *
++ * Start from @ba, skipping any bad blocks, counting @count good blocks, and
++ * return the next good block address.
++ *
++ * If no enough good blocks counted while @limit reached, false will be returned.
++ *
++ * If @count == 0, nearest good block address will be returned.
++ * @limit can be set to negative if no limit required.
++ * @limit is not counted in walking.
++ */
++static bool nmbm_block_walk(struct nmbm_instance *ni, bool ascending,
++                          uint32_t ba, uint32_t *nba, int32_t count,
++                          int32_t limit)
++{
++      if (ascending)
++              return nmbm_block_walk_asc(ni, ba, nba, count, limit);
++
++      return nmbm_block_walk_desc(ni, ba, nba, count, limit);
++}
++
++/*
++ * nmbm_scan_badblocks - Scan and record all bad blocks
++ * @ni: NMBM instance structure
++ *
++ * Scan the entire lower NAND chip and record all bad blocks in to block state
++ * table.
++ */
++static void nmbm_scan_badblocks(struct nmbm_instance *ni)
++{
++      uint32_t ba;
++
++      for (ba = 0; ba < ni->block_count; ba++) {
++              if (nmbm_check_bad_phys_block(ni, ba)) {
++                      nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++                      nlog_info(ni, "Bad block %u [0x%08llx]\n", ba,
++                               ba2addr(ni, ba));
++              }
++      }
++}
++
++/*
++ * nmbm_build_mapping_table - Build initial block mapping table
++ * @ni: NMBM instance structure
++ *
++ * The initial mapping table will be compatible with the stratage of
++ * factory production.
++ */
++static void nmbm_build_mapping_table(struct nmbm_instance *ni)
++{
++      uint32_t pb, lb;
++
++      for (pb = 0, lb = 0; pb < ni->mgmt_start_ba; pb++) {
++              if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
++                      continue;
++
++              /* Always map to the next good block */
++              ni->block_mapping[lb++] = pb;
++      }
++
++      ni->data_block_count = lb;
++
++      /* Unusable/Management blocks */
++      for (pb = lb; pb < ni->block_count; pb++)
++              ni->block_mapping[pb] = -1;
++}
++
++/*
++ * nmbm_erase_block_and_check - Erase a block and check its usability
++ * @ni: NMBM instance structure
++ * @ba: block address to be erased
++ *
++ * Erase a block anc check its usability
++ *
++ * Return true if the block is usable, false if erasure failure or the block
++ * has too many bitflips.
++ */
++static bool nmbm_erase_block_and_check(struct nmbm_instance *ni, uint32_t ba)
++{
++      uint64_t addr, off;
++      bool success;
++      int ret;
++
++      success = nmbm_erase_phys_block(ni, ba2addr(ni, ba));
++      if (!success)
++              return false;
++
++      if (!(ni->lower.flags & NMBM_F_EMPTY_PAGE_ECC_OK))
++              return true;
++
++      /* Check every page to make sure there aren't too many bitflips */
++
++      addr = ba2addr(ni, ba);
++
++      for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) {
++              WATCHDOG_RESET();
++
++              ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL,
++                                        NMBM_MODE_PLACE_OOB);
++              if (ret == -EBADMSG) {
++                      /*
++                       * NMBM_F_EMPTY_PAGE_ECC_OK means the empty page is
++                       * still protected by ECC. So reading pages with ECC
++                       * enabled and -EBADMSG means there are too many
++                       * bitflips that can't be recovered, and the block
++                       * containing the page should be marked bad.
++                       */
++                      nlog_err(ni,
++                               "Too many bitflips in empty page at 0x%llx\n",
++                               addr + off);
++                      return false;
++              }
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_erase_range - Erase a range of blocks
++ * @ni: NMBM instance structure
++ * @ba: block address where the erasure will start
++ * @limit: top block address allowed for erasure
++ *
++ * Erase blocks within the specific range. Newly-found bad blocks will be
++ * marked.
++ *
++ * @limit is not counted into the allowed erasure address.
++ */
++static void nmbm_erase_range(struct nmbm_instance *ni, uint32_t ba,
++                           uint32_t limit)
++{
++      bool success;
++
++      while (ba < limit) {
++              WATCHDOG_RESET();
++
++              if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
++                      goto next_block;
++
++              /* Insurance to detect unexpected bad block marked by user */
++              if (nmbm_check_bad_phys_block(ni, ba)) {
++                      nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++                      goto next_block;
++              }
++
++              success = nmbm_erase_block_and_check(ni, ba);
++              if (success)
++                      goto next_block;
++
++              nmbm_mark_phys_bad_block(ni, ba);
++              nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++
++      next_block:
++              ba++;
++      }
++}
++
++/*
++ * nmbm_write_repeated_data - Write critical data to a block with retry
++ * @ni: NMBM instance structure
++ * @ba: block address where the data will be written to
++ * @data: the data to be written
++ * @size: size of the data
++ *
++ * Write data to every page of the block. Success only if all pages within
++ * this block have been successfully written.
++ *
++ * Make sure data size is not bigger than one page.
++ *
++ * This function will write and verify every page for at most
++ * NMBM_TRY_COUNT times.
++ */
++static bool nmbm_write_repeated_data(struct nmbm_instance *ni, uint32_t ba,
++                                   const void *data, uint32_t size)
++{
++      uint64_t addr, off;
++      bool success;
++      int ret;
++
++      if (size > ni->lower.writesize)
++              return false;
++
++      addr = ba2addr(ni, ba);
++
++      for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) {
++              WATCHDOG_RESET();
++
++              /* Prepare page data. fill 0xff to unused region */
++              memcpy(ni->page_cache, data, size);
++              memset(ni->page_cache + size, 0xff, ni->rawpage_size - size);
++
++              success = nmbm_write_phys_page(ni, addr + off, ni->page_cache,
++                                             NULL, NMBM_MODE_PLACE_OOB);
++              if (!success)
++                      return false;
++
++              /* Verify the data just written. ECC error indicates failure */
++              ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL,
++                                        NMBM_MODE_PLACE_OOB);
++              if (ret < 0)
++                      return false;
++
++              if (memcmp(ni->page_cache, data, size))
++                      return false;
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_write_signature - Write signature to NAND chip
++ * @ni: NMBM instance structure
++ * @limit: top block address allowed for writing
++ * @signature: the signature to be written
++ * @signature_ba: the actual block address where signature is written to
++ *
++ * Write signature within a specific range, from chip bottom to limit.
++ * At most one block will be written.
++ *
++ * @limit is not counted into the allowed write address.
++ */
++static bool nmbm_write_signature(struct nmbm_instance *ni, uint32_t limit,
++                               const struct nmbm_signature *signature,
++                               uint32_t *signature_ba)
++{
++      uint32_t ba = ni->block_count - 1;
++      bool success;
++
++      while (ba > limit) {
++              WATCHDOG_RESET();
++
++              if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
++                      goto next_block;
++
++              /* Insurance to detect unexpected bad block marked by user */
++              if (nmbm_check_bad_phys_block(ni, ba)) {
++                      nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++                      goto next_block;
++              }
++
++              success = nmbm_erase_block_and_check(ni, ba);
++              if (!success)
++                      goto skip_bad_block;
++
++              success = nmbm_write_repeated_data(ni, ba, signature,
++                                                 sizeof(*signature));
++              if (success) {
++                      *signature_ba = ba;
++                      return true;
++              }
++
++      skip_bad_block:
++              nmbm_mark_phys_bad_block(ni, ba);
++              nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++
++      next_block:
++              ba--;
++      };
++
++      return false;
++}
++
++/*
++ * nmbn_read_data - Read data
++ * @ni: NMBM instance structure
++ * @addr: linear address where the data will be read from
++ * @data: the data to be read
++ * @size: the size of data
++ *
++ * Read data range.
++ * Every page will be tried for at most NMBM_TRY_COUNT times.
++ *
++ * Return 0 for success, positive value for corrected bitflip count,
++ * -EBADMSG for ecc error, other negative values for other errors
++ */
++static int nmbn_read_data(struct nmbm_instance *ni, uint64_t addr, void *data,
++                        uint32_t size)
++{
++      uint64_t off = addr;
++      uint8_t *ptr = data;
++      uint32_t sizeremain = size, chunksize, leading;
++      int ret;
++
++      while (sizeremain) {
++              WATCHDOG_RESET();
++
++              leading = off & ni->writesize_mask;
++              chunksize = ni->lower.writesize - leading;
++              if (chunksize > sizeremain)
++                      chunksize = sizeremain;
++
++              if (chunksize == ni->lower.writesize) {
++                      ret = nmbm_read_phys_page(ni, off - leading, ptr, NULL,
++                                                NMBM_MODE_PLACE_OOB);
++                      if (ret < 0)
++                              return ret;
++              } else {
++                      ret = nmbm_read_phys_page(ni, off - leading,
++                                                ni->page_cache, NULL,
++                                                NMBM_MODE_PLACE_OOB);
++                      if (ret < 0)
++                              return ret;
++
++                      memcpy(ptr, ni->page_cache + leading, chunksize);
++              }
++
++              off += chunksize;
++              ptr += chunksize;
++              sizeremain -= chunksize;
++      }
++
++      return 0;
++}
++
++/*
++ * nmbn_write_verify_data - Write data with validation
++ * @ni: NMBM instance structure
++ * @addr: linear address where the data will be written to
++ * @data: the data to be written
++ * @size: the size of data
++ *
++ * Write data and verify.
++ * Every page will be tried for at most NMBM_TRY_COUNT times.
++ */
++static bool nmbn_write_verify_data(struct nmbm_instance *ni, uint64_t addr,
++                                 const void *data, uint32_t size)
++{
++      uint64_t off = addr;
++      const uint8_t *ptr = data;
++      uint32_t sizeremain = size, chunksize, leading;
++      bool success;
++      int ret;
++
++      while (sizeremain) {
++              WATCHDOG_RESET();
++
++              leading = off & ni->writesize_mask;
++              chunksize = ni->lower.writesize - leading;
++              if (chunksize > sizeremain)
++                      chunksize = sizeremain;
++
++              /* Prepare page data. fill 0xff to unused region */
++              memset(ni->page_cache, 0xff, ni->rawpage_size);
++              memcpy(ni->page_cache + leading, ptr, chunksize);
++
++              success = nmbm_write_phys_page(ni, off - leading,
++                                             ni->page_cache, NULL,
++                                             NMBM_MODE_PLACE_OOB);
++              if (!success)
++                      return false;
++
++              /* Verify the data just written. ECC error indicates failure */
++              ret = nmbm_read_phys_page(ni, off - leading, ni->page_cache,
++                                        NULL, NMBM_MODE_PLACE_OOB);
++              if (ret < 0)
++                      return false;
++
++              if (memcmp(ni->page_cache + leading, ptr, chunksize))
++                      return false;
++
++              off += chunksize;
++              ptr += chunksize;
++              sizeremain -= chunksize;
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_write_mgmt_range - Write management data into NAND within a range
++ * @ni: NMBM instance structure
++ * @addr: preferred start block address for writing
++ * @limit: highest block address allowed for writing
++ * @data: the data to be written
++ * @size: the size of data
++ * @actual_start_ba: actual start block address of data
++ * @actual_end_ba: block address after the end of data
++ *
++ * @limit is not counted into the allowed write address.
++ */
++static bool nmbm_write_mgmt_range(struct nmbm_instance *ni, uint32_t ba,
++                                uint32_t limit, const void *data,
++                                uint32_t size, uint32_t *actual_start_ba,
++                                uint32_t *actual_end_ba)
++{
++      const uint8_t *ptr = data;
++      uint32_t sizeremain = size, chunksize;
++      bool success;
++
++      while (sizeremain && ba < limit) {
++              WATCHDOG_RESET();
++
++              chunksize = sizeremain;
++              if (chunksize > ni->lower.erasesize)
++                      chunksize = ni->lower.erasesize;
++
++              if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
++                      goto next_block;
++
++              /* Insurance to detect unexpected bad block marked by user */
++              if (nmbm_check_bad_phys_block(ni, ba)) {
++                      nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++                      goto next_block;
++              }
++
++              success = nmbm_erase_block_and_check(ni, ba);
++              if (!success)
++                      goto skip_bad_block;
++
++              success = nmbn_write_verify_data(ni, ba2addr(ni, ba), ptr,
++                                               chunksize);
++              if (!success)
++                      goto skip_bad_block;
++
++              if (sizeremain == size)
++                      *actual_start_ba = ba;
++
++              ptr += chunksize;
++              sizeremain -= chunksize;
++
++              goto next_block;
++
++      skip_bad_block:
++              nmbm_mark_phys_bad_block(ni, ba);
++              nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++
++      next_block:
++              ba++;
++      }
++
++      if (sizeremain)
++              return false;
++
++      *actual_end_ba = ba;
++
++      return true;
++}
++
++/*
++ * nmbm_generate_info_table_cache - Generate info table cache data
++ * @ni: NMBM instance structure
++ *
++ * Generate info table cache data to be written into flash.
++ */
++static bool nmbm_generate_info_table_cache(struct nmbm_instance *ni)
++{
++      bool changed = false;
++
++      memset(ni->info_table_cache, 0xff, ni->info_table_size);
++
++      memcpy(ni->info_table_cache + ni->info_table.state_table_off,
++             ni->block_state, ni->state_table_size);
++
++      memcpy(ni->info_table_cache + ni->info_table.mapping_table_off,
++              ni->block_mapping, ni->mapping_table_size);
++
++      ni->info_table.header.magic = NMBM_MAGIC_INFO_TABLE;
++      ni->info_table.header.version = NMBM_VER;
++      ni->info_table.header.size = ni->info_table_size;
++
++      if (ni->block_state_changed || ni->block_mapping_changed) {
++              ni->info_table.write_count++;
++              changed = true;
++      }
++
++      memcpy(ni->info_table_cache, &ni->info_table, sizeof(ni->info_table));
++
++      nmbm_update_checksum((struct nmbm_header *)ni->info_table_cache);
++
++      return changed;
++}
++
++/*
++ * nmbm_write_info_table - Write info table into NAND within a range
++ * @ni: NMBM instance structure
++ * @ba: preferred start block address for writing
++ * @limit: highest block address allowed for writing
++ * @actual_start_ba: actual start block address of info table
++ * @actual_end_ba: block address after the end of info table
++ *
++ * @limit is counted into the allowed write address.
++ */
++static bool nmbm_write_info_table(struct nmbm_instance *ni, uint32_t ba,
++                                uint32_t limit, uint32_t *actual_start_ba,
++                                uint32_t *actual_end_ba)
++{
++      return nmbm_write_mgmt_range(ni, ba, limit, ni->info_table_cache,
++                                   ni->info_table_size, actual_start_ba,
++                                   actual_end_ba);
++}
++
++/*
++ * nmbm_mark_tables_clean - Mark info table `clean'
++ * @ni: NMBM instance structure
++ */
++static void nmbm_mark_tables_clean(struct nmbm_instance *ni)
++{
++      ni->block_state_changed = 0;
++      ni->block_mapping_changed = 0;
++}
++
++/*
++ * nmbm_try_reserve_blocks - Reserve blocks with compromisation
++ * @ni: NMBM instance structure
++ * @ba: start physical block address
++ * @nba: return physical block address after reservation
++ * @count: number of good blocks to be skipped
++ * @min_count: minimum number of good blocks to be skipped
++ * @limit: highest/lowest block address allowed for walking
++ *
++ * Reserve specific blocks. If failed, try to reserve as many as possible.
++ */
++static bool nmbm_try_reserve_blocks(struct nmbm_instance *ni, uint32_t ba,
++                                  uint32_t *nba, uint32_t count,
++                                  int32_t min_count, int32_t limit)
++{
++      int32_t nblocks = count;
++      bool success;
++
++      while (nblocks >= min_count) {
++              success = nmbm_block_walk(ni, true, ba, nba, nblocks, limit);
++              if (success)
++                      return true;
++
++              nblocks--;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_rebuild_info_table - Build main & backup info table from scratch
++ * @ni: NMBM instance structure
++ * @allow_no_gap: allow no spare blocks between two tables
++ */
++static bool nmbm_rebuild_info_table(struct nmbm_instance *ni)
++{
++      uint32_t table_start_ba, table_end_ba, next_start_ba;
++      uint32_t main_table_end_ba;
++      bool success;
++
++      /* Set initial value */
++      ni->main_table_ba = 0;
++      ni->backup_table_ba = 0;
++      ni->mapping_blocks_ba = ni->mapping_blocks_top_ba;
++
++      /* Write main table */
++      success = nmbm_write_info_table(ni, ni->mgmt_start_ba,
++                                      ni->mapping_blocks_top_ba,
++                                      &table_start_ba, &table_end_ba);
++      if (!success) {
++              /* Failed to write main table, data will be lost */
++              nlog_emerg(ni, "Unable to write at least one info table!\n");
++              nlog_emerg(ni, "Please save your data before power off!\n");
++              ni->protected = 1;
++              return false;
++      }
++
++      /* Main info table is successfully written, record its offset */
++      ni->main_table_ba = table_start_ba;
++      main_table_end_ba = table_end_ba;
++
++      /* Adjust mapping_blocks_ba */
++      ni->mapping_blocks_ba = table_end_ba;
++
++      nmbm_mark_tables_clean(ni);
++
++      nlog_table_creation(ni, true, table_start_ba, table_end_ba);
++
++      /* Reserve spare blocks for main info table. */
++      success = nmbm_try_reserve_blocks(ni, table_end_ba,
++                                        &next_start_ba,
++                                        ni->info_table_spare_blocks, 0,
++                                        ni->mapping_blocks_top_ba -
++                                        size2blk(ni, ni->info_table_size));
++      if (!success) {
++              /* There is no spare block. */
++              nlog_debug(ni, "No room for backup info table\n");
++              return true;
++      }
++
++      /* Write backup info table. */
++      success = nmbm_write_info_table(ni, next_start_ba,
++                                      ni->mapping_blocks_top_ba,
++                                      &table_start_ba, &table_end_ba);
++      if (!success) {
++              /* There is no enough blocks for backup table. */
++              nlog_debug(ni, "No room for backup info table\n");
++              return true;
++      }
++
++      /* Backup table is successfully written, record its offset */
++      ni->backup_table_ba = table_start_ba;
++
++      /* Adjust mapping_blocks_off */
++      ni->mapping_blocks_ba = table_end_ba;
++
++      /* Erase spare blocks of main table to clean possible interference data */
++      nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba);
++
++      nlog_table_creation(ni, false, table_start_ba, table_end_ba);
++
++      return true;
++}
++
++/*
++ * nmbm_rescue_single_info_table - Rescue when there is only one info table
++ * @ni: NMBM instance structure
++ *
++ * This function is called when there is only one info table exists.
++ * This function may fail if we can't write new info table
++ */
++static bool nmbm_rescue_single_info_table(struct nmbm_instance *ni)
++{
++      uint32_t table_start_ba, table_end_ba, write_ba;
++      bool success;
++
++      /* Try to write new info table in front of existing table */
++      success = nmbm_write_info_table(ni, ni->mgmt_start_ba,
++                                      ni->main_table_ba,
++                                      &table_start_ba,
++                                      &table_end_ba);
++      if (success) {
++              /*
++               * New table becomes the main table, existing table becomes
++               * the backup table.
++               */
++              ni->backup_table_ba = ni->main_table_ba;
++              ni->main_table_ba = table_start_ba;
++
++              nmbm_mark_tables_clean(ni);
++
++              /* Erase spare blocks of main table to clean possible interference data */
++              nmbm_erase_range(ni, table_end_ba, ni->backup_table_ba);
++
++              nlog_table_creation(ni, true, table_start_ba, table_end_ba);
++
++              return true;
++      }
++
++      /* Try to reserve spare blocks for existing table */
++      success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba,
++                                        ni->info_table_spare_blocks, 0,
++                                        ni->mapping_blocks_top_ba -
++                                        size2blk(ni, ni->info_table_size));
++      if (!success) {
++              nlog_warn(ni, "Failed to rescue single info table\n");
++              return false;
++      }
++
++      /* Try to write new info table next to the existing table */
++      while (write_ba >= ni->mapping_blocks_ba) {
++              WATCHDOG_RESET();
++
++              success = nmbm_write_info_table(ni, write_ba,
++                                              ni->mapping_blocks_top_ba,
++                                              &table_start_ba,
++                                              &table_end_ba);
++              if (success)
++                      break;
++
++              write_ba--;
++      }
++
++      if (success) {
++              /* Erase spare blocks of main table to clean possible interference data */
++              nmbm_erase_range(ni, ni->mapping_blocks_ba, table_start_ba);
++
++              /* New table becomes the backup table */
++              ni->backup_table_ba = table_start_ba;
++              ni->mapping_blocks_ba = table_end_ba;
++
++              nmbm_mark_tables_clean(ni);
++
++              nlog_table_creation(ni, false, table_start_ba, table_end_ba);
++
++              return true;
++      }
++
++      nlog_warn(ni, "Failed to rescue single info table\n");
++      return false;
++}
++
++/*
++ * nmbm_update_single_info_table - Update specific one info table
++ * @ni: NMBM instance structure
++ */
++static bool nmbm_update_single_info_table(struct nmbm_instance *ni,
++                                        bool update_main_table)
++{
++      uint32_t write_start_ba, write_limit, table_start_ba, table_end_ba;
++      bool success;
++
++      /* Determine the write range */
++      if (update_main_table) {
++              write_start_ba = ni->main_table_ba;
++              write_limit = ni->backup_table_ba;
++      } else {
++              write_start_ba = ni->backup_table_ba;
++              write_limit = ni->mapping_blocks_top_ba;
++      }
++
++      nmbm_mark_block_color_mgmt(ni, write_start_ba, write_limit - 1);
++
++      success = nmbm_write_info_table(ni, write_start_ba, write_limit,
++                                      &table_start_ba, &table_end_ba);
++      if (success) {
++              if (update_main_table) {
++                      ni->main_table_ba = table_start_ba;
++              } else {
++                      ni->backup_table_ba = table_start_ba;
++                      ni->mapping_blocks_ba = table_end_ba;
++              }
++
++              nmbm_mark_tables_clean(ni);
++
++              nlog_table_update(ni, update_main_table, table_start_ba,
++                               table_end_ba);
++
++              return true;
++      }
++
++      if (update_main_table) {
++              /*
++               * If failed to update main table, make backup table the new
++               * main table, and call nmbm_rescue_single_info_table()
++               */
++              nlog_warn(ni, "Unable to update %s info table\n",
++                       update_main_table ? "Main" : "Backup");
++
++              ni->main_table_ba = ni->backup_table_ba;
++              ni->backup_table_ba = 0;
++              return nmbm_rescue_single_info_table(ni);
++      }
++
++      /* Only one table left */
++      ni->mapping_blocks_ba = ni->backup_table_ba;
++      ni->backup_table_ba = 0;
++
++      return false;
++}
++
++/*
++ * nmbm_rescue_main_info_table - Rescue when failed to write main info table
++ * @ni: NMBM instance structure
++ *
++ * This function is called when main info table failed to be written, and
++ *    backup info table exists.
++ */
++static bool nmbm_rescue_main_info_table(struct nmbm_instance *ni)
++{
++      uint32_t tmp_table_start_ba, tmp_table_end_ba, main_table_start_ba;
++      uint32_t main_table_end_ba, write_ba;
++      uint32_t info_table_erasesize = size2blk(ni, ni->info_table_size);
++      bool success;
++
++      /* Try to reserve spare blocks for existing backup info table */
++      success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba,
++                                        ni->info_table_spare_blocks, 0,
++                                        ni->mapping_blocks_top_ba -
++                                        info_table_erasesize);
++      if (!success) {
++              /* There is no spare block. Backup info table becomes the main table. */
++              nlog_err(ni, "No room for temporary info table\n");
++              ni->main_table_ba = ni->backup_table_ba;
++              ni->backup_table_ba = 0;
++              return true;
++      }
++
++      /* Try to write temporary info table into spare unmapped blocks */
++      while (write_ba >= ni->mapping_blocks_ba) {
++              WATCHDOG_RESET();
++
++              success = nmbm_write_info_table(ni, write_ba,
++                                              ni->mapping_blocks_top_ba,
++                                              &tmp_table_start_ba,
++                                              &tmp_table_end_ba);
++              if (success)
++                      break;
++
++              write_ba--;
++      }
++
++      if (!success) {
++              /* Backup info table becomes the main table */
++              nlog_err(ni, "Failed to update main info table\n");
++              ni->main_table_ba = ni->backup_table_ba;
++              ni->backup_table_ba = 0;
++              return true;
++      }
++
++      /* Adjust mapping_blocks_off */
++      ni->mapping_blocks_ba = tmp_table_end_ba;
++
++      nmbm_mark_block_color_mgmt(ni, ni->backup_table_ba,
++                                 tmp_table_end_ba - 1);
++
++      /*
++       * Now write main info table at the beginning of management area.
++       * This operation will generally destroy the original backup info
++       * table.
++       */
++      success = nmbm_write_info_table(ni, ni->mgmt_start_ba,
++                                      tmp_table_start_ba,
++                                      &main_table_start_ba,
++                                      &main_table_end_ba);
++      if (!success) {
++              /* Temporary info table becomes the main table */
++              ni->main_table_ba = tmp_table_start_ba;
++              ni->backup_table_ba = 0;
++
++              nmbm_mark_tables_clean(ni);
++
++              nlog_err(ni, "Failed to update main info table\n");
++              nmbm_mark_block_color_info_table(ni, tmp_table_start_ba,
++                                               tmp_table_end_ba - 1);
++
++              return true;
++      }
++
++      /* Main info table has been successfully written, record its offset */
++      ni->main_table_ba = main_table_start_ba;
++
++      nmbm_mark_tables_clean(ni);
++
++      nlog_table_creation(ni, true, main_table_start_ba, main_table_end_ba);
++
++      /*
++       * Temporary info table becomes the new backup info table if it's
++       * not overwritten.
++       */
++      if (main_table_end_ba <= tmp_table_start_ba) {
++              ni->backup_table_ba = tmp_table_start_ba;
++
++              nlog_table_creation(ni, false, tmp_table_start_ba,
++                                 tmp_table_end_ba);
++
++              return true;
++      }
++
++      /* Adjust mapping_blocks_off */
++      ni->mapping_blocks_ba = main_table_end_ba;
++
++      /* Try to reserve spare blocks for new main info table */
++      success = nmbm_try_reserve_blocks(ni, main_table_end_ba, &write_ba,
++                                        ni->info_table_spare_blocks, 0,
++                                        ni->mapping_blocks_top_ba -
++                                        info_table_erasesize);
++      if (!success) {
++              /* There is no spare block. Only main table exists. */
++              nlog_err(ni, "No room for backup info table\n");
++              ni->backup_table_ba = 0;
++              return true;
++      }
++
++      /* Write new backup info table. */
++      while (write_ba >= main_table_end_ba) {
++              WATCHDOG_RESET();
++
++              success = nmbm_write_info_table(ni, write_ba,
++                                              ni->mapping_blocks_top_ba,
++                                              &tmp_table_start_ba,
++                                              &tmp_table_end_ba);
++              if (success)
++                      break;
++
++              write_ba--;
++      }
++
++      if (!success) {
++              nlog_err(ni, "No room for backup info table\n");
++              ni->backup_table_ba = 0;
++              return true;
++      }
++
++      /* Backup info table has been successfully written, record its offset */
++      ni->backup_table_ba = tmp_table_start_ba;
++
++      /* Adjust mapping_blocks_off */
++      ni->mapping_blocks_ba = tmp_table_end_ba;
++
++      /* Erase spare blocks of main table to clean possible interference data */
++      nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba);
++
++      nlog_table_creation(ni, false, tmp_table_start_ba, tmp_table_end_ba);
++
++      return true;
++}
++
++/*
++ * nmbm_update_info_table_once - Update info table once
++ * @ni: NMBM instance structure
++ * @force: force update
++ *
++ * Update both main and backup info table. Return true if at least one info
++ * table has been successfully written.
++ * This function only try to update info table once regard less of the result.
++ */
++static bool nmbm_update_info_table_once(struct nmbm_instance *ni, bool force)
++{
++      uint32_t table_start_ba, table_end_ba;
++      uint32_t main_table_limit;
++      bool success;
++
++      /* Do nothing if there is no change */
++      if (!nmbm_generate_info_table_cache(ni) && !force)
++              return true;
++
++      /* Check whether both two tables exist */
++      if (!ni->backup_table_ba) {
++              main_table_limit = ni->mapping_blocks_top_ba;
++              goto write_main_table;
++      }
++
++      nmbm_mark_block_color_mgmt(ni, ni->backup_table_ba,
++                                 ni->mapping_blocks_ba - 1);
++
++      /*
++       * Write backup info table in its current range.
++       * Note that limit is set to mapping_blocks_top_off to provide as many
++       * spare blocks as possible for the backup table. If at last
++       * unmapped blocks are used by backup table, mapping_blocks_off will
++       * be adjusted.
++       */
++      success = nmbm_write_info_table(ni, ni->backup_table_ba,
++                                      ni->mapping_blocks_top_ba,
++                                      &table_start_ba, &table_end_ba);
++      if (!success) {
++              /*
++               * There is nothing to do if failed to write backup table.
++               * Write the main table now.
++               */
++              nlog_err(ni, "No room for backup table\n");
++              ni->mapping_blocks_ba = ni->backup_table_ba;
++              ni->backup_table_ba = 0;
++              main_table_limit = ni->mapping_blocks_top_ba;
++              goto write_main_table;
++      }
++
++      /* Backup table is successfully written, record its offset */
++      ni->backup_table_ba = table_start_ba;
++
++      /* Adjust mapping_blocks_off */
++      ni->mapping_blocks_ba = table_end_ba;
++
++      nmbm_mark_tables_clean(ni);
++
++      /* The normal limit of main table */
++      main_table_limit = ni->backup_table_ba;
++
++      nlog_table_update(ni, false, table_start_ba, table_end_ba);
++
++write_main_table:
++      if (!ni->main_table_ba)
++              goto rebuild_tables;
++
++      if (!ni->backup_table_ba)
++              nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba,
++                                         ni->mapping_blocks_ba - 1);
++      else
++              nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba,
++                                         ni->backup_table_ba - 1);
++
++      /* Write main info table in its current range */
++      success = nmbm_write_info_table(ni, ni->main_table_ba,
++                                      main_table_limit, &table_start_ba,
++                                      &table_end_ba);
++      if (!success) {
++              /* If failed to write main table, go rescue procedure */
++              if (!ni->backup_table_ba)
++                      goto rebuild_tables;
++
++              return nmbm_rescue_main_info_table(ni);
++      }
++
++      /* Main info table is successfully written, record its offset */
++      ni->main_table_ba = table_start_ba;
++
++      /* Adjust mapping_blocks_off */
++      if (!ni->backup_table_ba)
++              ni->mapping_blocks_ba = table_end_ba;
++
++      nmbm_mark_tables_clean(ni);
++
++      nlog_table_update(ni, true, table_start_ba, table_end_ba);
++
++      return true;
++
++rebuild_tables:
++      return nmbm_rebuild_info_table(ni);
++}
++
++/*
++ * nmbm_update_info_table - Update info table
++ * @ni: NMBM instance structure
++ *
++ * Update both main and backup info table. Return true if at least one table
++ * has been successfully written.
++ * This function will try to update info table repeatedly until no new bad
++ * block found during updating.
++ */
++static bool nmbm_update_info_table(struct nmbm_instance *ni)
++{
++      bool success;
++
++      if (ni->protected)
++              return true;
++
++      while (ni->block_state_changed || ni->block_mapping_changed) {
++              success = nmbm_update_info_table_once(ni, false);
++              if (!success) {
++                      nlog_err(ni, "Failed to update info table\n");
++                      return false;
++              }
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_map_block - Map a bad block to a unused spare block
++ * @ni: NMBM instance structure
++ * @lb: logic block addr to map
++ */
++static bool nmbm_map_block(struct nmbm_instance *ni, uint32_t lb)
++{
++      uint32_t pb;
++      bool success;
++
++      if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) {
++              nlog_warn(ni, "No spare unmapped blocks.\n");
++              return false;
++      }
++
++      success = nmbm_block_walk(ni, false, ni->mapping_blocks_top_ba, &pb, 0,
++                                ni->mapping_blocks_ba);
++      if (!success) {
++              nlog_warn(ni, "No spare unmapped blocks.\n");
++              nmbm_update_info_table(ni);
++              ni->mapping_blocks_top_ba = ni->mapping_blocks_ba;
++              return false;
++      }
++
++      ni->block_mapping[lb] = pb;
++      ni->mapping_blocks_top_ba--;
++      ni->block_mapping_changed++;
++
++      nlog_info(ni, "Logic block %u mapped to physical blcok %u\n", lb, pb);
++      nmbm_mark_block_color_mapped(ni, pb);
++
++      return true;
++}
++
++/*
++ * nmbm_create_info_table - Create info table(s)
++ * @ni: NMBM instance structure
++ *
++ * This function assumes that the chip has no existing info table(s)
++ */
++static bool nmbm_create_info_table(struct nmbm_instance *ni)
++{
++      uint32_t lb;
++      bool success;
++
++      /* Set initial mapping_blocks_top_off  */
++      success = nmbm_block_walk(ni, false, ni->signature_ba,
++                                &ni->mapping_blocks_top_ba, 1,
++                                ni->mgmt_start_ba);
++      if (!success) {
++              nlog_err(ni, "No room for spare blocks\n");
++              return false;
++      }
++
++      /* Generate info table cache */
++      nmbm_generate_info_table_cache(ni);
++
++      /* Write info table */
++      success = nmbm_rebuild_info_table(ni);
++      if (!success) {
++              nlog_err(ni, "Failed to build info tables\n");
++              return false;
++      }
++
++      /* Remap bad block(s) at end of data area */
++      for (lb = ni->data_block_count; lb < ni->mgmt_start_ba; lb++) {
++              success = nmbm_map_block(ni, lb);
++              if (!success)
++                      break;
++
++              ni->data_block_count++;
++      }
++
++      /* If state table and/or mapping table changed, update info table. */
++      success = nmbm_update_info_table(ni);
++      if (!success)
++              return false;
++
++      return true;
++}
++
++/*
++ * nmbm_create_new - Create NMBM on a new chip
++ * @ni: NMBM instance structure
++ */
++static bool nmbm_create_new(struct nmbm_instance *ni)
++{
++      bool success;
++
++      /* Determine the boundary of management blocks */
++      ni->mgmt_start_ba = ni->block_count * (NMBM_MGMT_DIV - ni->lower.max_ratio) / NMBM_MGMT_DIV;
++
++      if (ni->lower.max_reserved_blocks && ni->block_count - ni->mgmt_start_ba > ni->lower.max_reserved_blocks)
++              ni->mgmt_start_ba = ni->block_count - ni->lower.max_reserved_blocks;
++
++      nlog_info(ni, "NMBM management region starts at block %u [0x%08llx]\n",
++                ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba));
++      nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, ni->block_count - 1);
++
++      /* Fill block state table & mapping table */
++      nmbm_scan_badblocks(ni);
++      nmbm_build_mapping_table(ni);
++
++      /* Write signature */
++      ni->signature.header.magic = NMBM_MAGIC_SIGNATURE;
++      ni->signature.header.version = NMBM_VER;
++      ni->signature.header.size = sizeof(ni->signature);
++      ni->signature.nand_size = ni->lower.size;
++      ni->signature.block_size = ni->lower.erasesize;
++      ni->signature.page_size = ni->lower.writesize;
++      ni->signature.spare_size = ni->lower.oobsize;
++      ni->signature.mgmt_start_pb = ni->mgmt_start_ba;
++      ni->signature.max_try_count = NMBM_TRY_COUNT;
++      nmbm_update_checksum(&ni->signature.header);
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY) {
++              nlog_info(ni, "NMBM has been initialized in read-only mode\n");
++              return true;
++      }
++
++      success = nmbm_write_signature(ni, ni->mgmt_start_ba,
++                                     &ni->signature, &ni->signature_ba);
++      if (!success) {
++              nlog_err(ni, "Failed to write signature to a proper offset\n");
++              return false;
++      }
++
++      nlog_info(ni, "Signature has been written to block %u [0x%08llx]\n",
++               ni->signature_ba, ba2addr(ni, ni->signature_ba));
++      nmbm_mark_block_color_signature(ni, ni->signature_ba);
++
++      /* Write info table(s) */
++      success = nmbm_create_info_table(ni);
++      if (success) {
++              nlog_info(ni, "NMBM has been successfully created\n");
++              return true;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_check_info_table_header - Check if a info table header is valid
++ * @ni: NMBM instance structure
++ * @data: pointer to the info table header
++ */
++static bool nmbm_check_info_table_header(struct nmbm_instance *ni, void *data)
++{
++      struct nmbm_info_table_header *ifthdr = data;
++
++      if (ifthdr->header.magic != NMBM_MAGIC_INFO_TABLE)
++              return false;
++
++      if (ifthdr->header.size != ni->info_table_size)
++              return false;
++
++      if (ifthdr->mapping_table_off - ifthdr->state_table_off < ni->state_table_size)
++              return false;
++
++      if (ni->info_table_size - ifthdr->mapping_table_off < ni->mapping_table_size)
++              return false;
++
++      return true;
++}
++
++/*
++ * nmbm_check_info_table - Check if a whole info table is valid
++ * @ni: NMBM instance structure
++ * @start_ba: start block address of this table
++ * @end_ba: end block address of this table
++ * @data: pointer to the info table header
++ * @mapping_blocks_top_ba: return the block address of top remapped block
++ */
++static bool nmbm_check_info_table(struct nmbm_instance *ni, uint32_t start_ba,
++                                uint32_t end_ba, void *data,
++                                uint32_t *mapping_blocks_top_ba)
++{
++      struct nmbm_info_table_header *ifthdr = data;
++      int32_t *block_mapping = (int32_t *)((uintptr_t)data + ifthdr->mapping_table_off);
++      nmbm_bitmap_t *block_state = (nmbm_bitmap_t *)((uintptr_t)data + ifthdr->state_table_off);
++      uint32_t minimum_mapping_pb = ni->signature_ba;
++      uint32_t ba;
++
++      for (ba = 0; ba < ni->data_block_count; ba++) {
++              if ((block_mapping[ba] >= ni->data_block_count && block_mapping[ba] < end_ba) ||
++                  block_mapping[ba] == ni->signature_ba)
++                      return false;
++
++              if (block_mapping[ba] >= end_ba && block_mapping[ba] < minimum_mapping_pb)
++                      minimum_mapping_pb = block_mapping[ba];
++      }
++
++      for (ba = start_ba; ba < end_ba; ba++) {
++              if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
++                      continue;
++
++              if (nmbm_get_block_state_raw(block_state, ba) != BLOCK_ST_GOOD)
++                      return false;
++      }
++
++      *mapping_blocks_top_ba = minimum_mapping_pb - 1;
++
++      return true;
++}
++
++/*
++ * nmbm_try_load_info_table - Try to load info table from a address
++ * @ni: NMBM instance structure
++ * @ba: start block address of the info table
++ * @eba: return the block address after end of the table
++ * @write_count: return the write count of this table
++ * @mapping_blocks_top_ba: return the block address of top remapped block
++ * @table_loaded: used to record whether ni->info_table has valid data
++ */
++static bool nmbm_try_load_info_table(struct nmbm_instance *ni, uint32_t ba,
++                                   uint32_t *eba, uint32_t *write_count,
++                                   uint32_t *mapping_blocks_top_ba,
++                                   bool table_loaded)
++{
++      struct nmbm_info_table_header *ifthdr = (void *)ni->info_table_cache;
++      uint8_t *off = ni->info_table_cache;
++      uint32_t limit = ba + size2blk(ni, ni->info_table_size);
++      uint32_t start_ba = 0, chunksize, sizeremain = ni->info_table_size;
++      bool success, checkhdr = true;
++      int ret;
++
++      while (sizeremain && ba < limit) {
++              WATCHDOG_RESET();
++
++              if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
++                      goto next_block;
++
++              if (nmbm_check_bad_phys_block(ni, ba)) {
++                      nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++                      goto next_block;
++              }
++
++              chunksize = sizeremain;
++              if (chunksize > ni->lower.erasesize)
++                      chunksize = ni->lower.erasesize;
++
++              /* Assume block with ECC error has no info table data */
++              ret = nmbn_read_data(ni, ba2addr(ni, ba), off, chunksize);
++              if (ret < 0)
++                      goto skip_bad_block;
++              else if (ret > 0)
++                      return false;
++
++              if (checkhdr) {
++                      success = nmbm_check_info_table_header(ni, off);
++                      if (!success)
++                              return false;
++
++                      start_ba = ba;
++                      checkhdr = false;
++              }
++
++              off += chunksize;
++              sizeremain -= chunksize;
++
++              goto next_block;
++
++      skip_bad_block:
++              /* Only mark bad in memory */
++              nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++
++      next_block:
++              ba++;
++      }
++
++      if (sizeremain)
++              return false;
++
++      success = nmbm_check_header(ni->info_table_cache, ni->info_table_size);
++      if (!success)
++              return false;
++
++      *eba = ba;
++      *write_count = ifthdr->write_count;
++
++      success = nmbm_check_info_table(ni, start_ba, ba, ni->info_table_cache,
++                                      mapping_blocks_top_ba);
++      if (!success)
++              return false;
++
++      if (!table_loaded || ifthdr->write_count > ni->info_table.write_count) {
++              memcpy(&ni->info_table, ifthdr, sizeof(ni->info_table));
++              memcpy(ni->block_state,
++                     (uint8_t *)ifthdr + ifthdr->state_table_off,
++                     ni->state_table_size);
++              memcpy(ni->block_mapping,
++                     (uint8_t *)ifthdr + ifthdr->mapping_table_off,
++                     ni->mapping_table_size);
++              ni->info_table.write_count = ifthdr->write_count;
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_search_info_table - Search info table from specific address
++ * @ni: NMBM instance structure
++ * @ba: start block address to search
++ * @limit: highest block address allowed for searching
++ * @table_start_ba: return the start block address of this table
++ * @table_end_ba: return the block address after end of this table
++ * @write_count: return the write count of this table
++ * @mapping_blocks_top_ba: return the block address of top remapped block
++ * @table_loaded: used to record whether ni->info_table has valid data
++ */
++static bool nmbm_search_info_table(struct nmbm_instance *ni, uint32_t ba,
++                                 uint32_t limit, uint32_t *table_start_ba,
++                                 uint32_t *table_end_ba,
++                                 uint32_t *write_count,
++                                 uint32_t *mapping_blocks_top_ba,
++                                 bool table_loaded)
++{
++      bool success;
++
++      while (ba < limit - size2blk(ni, ni->info_table_size)) {
++              WATCHDOG_RESET();
++
++              success = nmbm_try_load_info_table(ni, ba, table_end_ba,
++                                                 write_count,
++                                                 mapping_blocks_top_ba,
++                                                 table_loaded);
++              if (success) {
++                      *table_start_ba = ba;
++                      return true;
++              }
++
++              ba++;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_load_info_table - Load info table(s) from a chip
++ * @ni: NMBM instance structure
++ * @ba: start block address to search info table
++ * @limit: highest block address allowed for searching
++ */
++static bool nmbm_load_info_table(struct nmbm_instance *ni, uint32_t ba,
++                               uint32_t limit)
++{
++      uint32_t main_table_end_ba, backup_table_end_ba, table_end_ba;
++      uint32_t main_mapping_blocks_top_ba, backup_mapping_blocks_top_ba;
++      uint32_t main_table_write_count, backup_table_write_count;
++      uint32_t i;
++      bool success;
++
++      /* Set initial value */
++      ni->main_table_ba = 0;
++      ni->backup_table_ba = 0;
++      ni->info_table.write_count = 0;
++      ni->mapping_blocks_top_ba = ni->signature_ba - 1;
++      ni->data_block_count = ni->signature.mgmt_start_pb;
++
++      /* Find first info table */
++      success = nmbm_search_info_table(ni, ba, limit, &ni->main_table_ba,
++              &main_table_end_ba, &main_table_write_count,
++              &main_mapping_blocks_top_ba, false);
++      if (!success) {
++              nlog_warn(ni, "No valid info table found\n");
++              return false;
++      }
++
++      table_end_ba = main_table_end_ba;
++
++      nlog_table_found(ni, true, main_table_write_count, ni->main_table_ba,
++                      main_table_end_ba);
++
++      /* Find second info table */
++      success = nmbm_search_info_table(ni, main_table_end_ba, limit,
++              &ni->backup_table_ba, &backup_table_end_ba,
++              &backup_table_write_count, &backup_mapping_blocks_top_ba, true);
++      if (!success) {
++              nlog_warn(ni, "Second info table not found\n");
++      } else {
++              table_end_ba = backup_table_end_ba;
++
++              nlog_table_found(ni, false, backup_table_write_count,
++                              ni->backup_table_ba, backup_table_end_ba);
++      }
++
++      /* Pick mapping_blocks_top_ba */
++      if (!ni->backup_table_ba) {
++              ni->mapping_blocks_top_ba= main_mapping_blocks_top_ba;
++      } else {
++              if (main_table_write_count >= backup_table_write_count)
++                      ni->mapping_blocks_top_ba = main_mapping_blocks_top_ba;
++              else
++                      ni->mapping_blocks_top_ba = backup_mapping_blocks_top_ba;
++      }
++
++      /* Set final mapping_blocks_ba */
++      ni->mapping_blocks_ba = table_end_ba;
++
++      /* Set final data_block_count */
++      for (i = ni->signature.mgmt_start_pb; i > 0; i--) {
++              if (ni->block_mapping[i - 1] >= 0) {
++                      ni->data_block_count = i;
++                      break;
++              }
++      }
++
++      /* Debug purpose: mark mapped blocks and bad blocks */
++      for (i = 0; i < ni->data_block_count; i++) {
++              if (ni->block_mapping[i] > ni->mapping_blocks_top_ba)
++                      nmbm_mark_block_color_mapped(ni, ni->block_mapping[i]);
++      }
++
++      for (i = 0; i < ni->block_count; i++) {
++              if (nmbm_get_block_state(ni, i) == BLOCK_ST_BAD)
++                      nmbm_mark_block_color_bad(ni, i);
++      }
++
++      /* Regenerate the info table cache from the final selected info table */
++      nmbm_generate_info_table_cache(ni);
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY)
++              return true;
++
++      /*
++       * If only one table exists, try to write another table.
++       * If two tables have different write count, try to update info table
++       */
++      if (!ni->backup_table_ba) {
++              success = nmbm_rescue_single_info_table(ni);
++      } else if (main_table_write_count != backup_table_write_count) {
++              /* Mark state & mapping tables changed */
++              ni->block_state_changed = 1;
++              ni->block_mapping_changed = 1;
++
++              success = nmbm_update_single_info_table(ni,
++                      main_table_write_count < backup_table_write_count);
++      } else {
++              success = true;
++      }
++
++      /*
++       * If there is no spare unmapped blocks, or still only one table
++       * exists, set the chip to read-only
++       */
++      if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) {
++              nlog_warn(ni, "No spare unmapped blocks. Device is now read-only\n");
++              ni->protected = 1;
++      } else if (!success) {
++              nlog_warn(ni, "Only one info table found. Device is now read-only\n");
++              ni->protected = 1;
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_load_existing - Load NMBM from a new chip
++ * @ni: NMBM instance structure
++ */
++static bool nmbm_load_existing(struct nmbm_instance *ni)
++{
++      bool success;
++
++      /* Calculate the boundary of management blocks */
++      ni->mgmt_start_ba = ni->signature.mgmt_start_pb;
++
++      nlog_debug(ni, "NMBM management region starts at block %u [0x%08llx]\n",
++                ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba));
++      nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba,
++                                 ni->signature_ba - 1);
++
++      /* Look for info table(s) */
++      success = nmbm_load_info_table(ni, ni->mgmt_start_ba,
++              ni->signature_ba);
++      if (success) {
++              nlog_info(ni, "NMBM has been successfully attached %s\n",
++                        (ni->lower.flags & NMBM_F_READ_ONLY) ? "in read-only mode" : "");
++              return true;
++      }
++
++      if (!(ni->lower.flags & NMBM_F_CREATE))
++              return false;
++
++      /* Fill block state table & mapping table */
++      nmbm_scan_badblocks(ni);
++      nmbm_build_mapping_table(ni);
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY) {
++              nlog_info(ni, "NMBM has been initialized in read-only mode\n");
++              return true;
++      }
++
++      /* Write info table(s) */
++      success = nmbm_create_info_table(ni);
++      if (success) {
++              nlog_info(ni, "NMBM has been successfully created\n");
++              return true;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_find_signature - Find signature in the lower NAND chip
++ * @ni: NMBM instance structure
++ * @signature_ba: used for storing block address of the signature
++ * @signature_ba: return the actual block address of signature block
++ *
++ * Find a valid signature from a specific range in the lower NAND chip,
++ * from bottom (highest address) to top (lowest address)
++ *
++ * Return true if found.
++ */
++static bool nmbm_find_signature(struct nmbm_instance *ni,
++                              struct nmbm_signature *signature,
++                              uint32_t *signature_ba)
++{
++      struct nmbm_signature sig;
++      uint64_t off, addr;
++      uint32_t block_count, ba, limit;
++      bool success;
++      int ret;
++
++      /* Calculate top and bottom block address */
++      block_count = ni->lower.size >> ni->erasesize_shift;
++      ba = block_count;
++      limit = (block_count / NMBM_MGMT_DIV) * (NMBM_MGMT_DIV - ni->lower.max_ratio);
++      if (ni->lower.max_reserved_blocks && block_count - limit > ni->lower.max_reserved_blocks)
++              limit = block_count - ni->lower.max_reserved_blocks;
++
++      while (ba >= limit) {
++              WATCHDOG_RESET();
++
++              ba--;
++              addr = ba2addr(ni, ba);
++
++              if (nmbm_check_bad_phys_block(ni, ba))
++                      continue;
++
++              /* Check every page.
++               * As long as at leaset one page contains valid signature,
++               * the block is treated as a valid signature block.
++               */
++              for (off = 0; off < ni->lower.erasesize;
++                   off += ni->lower.writesize) {
++                      WATCHDOG_RESET();
++
++                      ret = nmbn_read_data(ni, addr + off, &sig,
++                                           sizeof(sig));
++                      if (ret)
++                              continue;
++
++                      /* Check for header size and checksum */
++                      success = nmbm_check_header(&sig, sizeof(sig));
++                      if (!success)
++                              continue;
++
++                      /* Check for header magic */
++                      if (sig.header.magic == NMBM_MAGIC_SIGNATURE) {
++                              /* Found it */
++                              memcpy(signature, &sig, sizeof(sig));
++                              *signature_ba = ba;
++                              return true;
++                      }
++              }
++      };
++
++      return false;
++}
++
++/*
++ * is_power_of_2_u64 - Check whether a 64-bit integer is power of 2
++ * @n: number to check
++ */
++static bool is_power_of_2_u64(uint64_t n)
++{
++      return (n != 0 && ((n & (n - 1)) == 0));
++}
++
++/*
++ * nmbm_check_lower_members - Validate the members of lower NAND device
++ * @nld: Lower NAND chip structure
++ */
++static bool nmbm_check_lower_members(struct nmbm_lower_device *nld)
++{
++
++      if (!nld->size || !is_power_of_2_u64(nld->size)) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "Chip size %llu is not valid\n", nld->size);
++              return false;
++      }
++
++      if (!nld->erasesize || !is_power_of_2(nld->erasesize)) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "Block size %u is not valid\n", nld->erasesize);
++              return false;
++      }
++
++      if (!nld->writesize || !is_power_of_2(nld->writesize)) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "Page size %u is not valid\n", nld->writesize);
++              return false;
++      }
++
++      if (!nld->oobsize || !is_power_of_2(nld->oobsize)) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "Page spare size %u is not valid\n", nld->oobsize);
++              return false;
++      }
++
++      if (!nld->read_page) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR, "read_page() is required\n");
++              return false;
++      }
++
++      if (!(nld->flags & NMBM_F_READ_ONLY) && (!nld->write_page || !nld->erase_block)) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "write_page() and erase_block() are required\n");
++              return false;
++      }
++
++      /* Data sanity check */
++      if (!nld->max_ratio)
++              nld->max_ratio = 1;
++
++      if (nld->max_ratio >= NMBM_MGMT_DIV - 1) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "max ratio %u is invalid\n", nld->max_ratio);
++              return false;
++      }
++
++      if (nld->max_reserved_blocks && nld->max_reserved_blocks < NMBM_MGMT_BLOCKS_MIN) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "max reserved blocks %u is too small\n", nld->max_reserved_blocks);
++              return false;
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_calc_structure_size - Calculate the instance structure size
++ * @nld: NMBM lower device structure
++ */
++size_t nmbm_calc_structure_size(struct nmbm_lower_device *nld)
++{
++      uint32_t state_table_size, mapping_table_size, info_table_size;
++      uint32_t block_count;
++
++      block_count = nmbm_lldiv(nld->size, nld->erasesize);
++
++      /* Calculate info table size */
++      state_table_size = ((block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) /
++              NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE;
++      mapping_table_size = block_count * sizeof(int32_t);
++
++      info_table_size = NMBM_ALIGN(sizeof(struct nmbm_info_table_header),
++                                   nld->writesize);
++      info_table_size += NMBM_ALIGN(state_table_size, nld->writesize);
++      info_table_size += NMBM_ALIGN(mapping_table_size, nld->writesize);
++
++      return info_table_size + state_table_size + mapping_table_size +
++              nld->writesize + nld->oobsize + sizeof(struct nmbm_instance);
++}
++
++/*
++ * nmbm_init_structure - Initialize members of instance structure
++ * @ni: NMBM instance structure
++ */
++static void nmbm_init_structure(struct nmbm_instance *ni)
++{
++      uint32_t pages_per_block, blocks_per_chip;
++      uintptr_t ptr;
++
++      pages_per_block = ni->lower.erasesize / ni->lower.writesize;
++      blocks_per_chip = nmbm_lldiv(ni->lower.size, ni->lower.erasesize);
++
++      ni->rawpage_size = ni->lower.writesize + ni->lower.oobsize;
++      ni->rawblock_size = pages_per_block * ni->rawpage_size;
++      ni->rawchip_size = blocks_per_chip * ni->rawblock_size;
++
++      ni->writesize_mask = ni->lower.writesize - 1;
++      ni->erasesize_mask = ni->lower.erasesize - 1;
++
++      ni->writesize_shift = ffs(ni->lower.writesize) - 1;
++      ni->erasesize_shift = ffs(ni->lower.erasesize) - 1;
++
++      /* Calculate number of block this chip */
++      ni->block_count = ni->lower.size >> ni->erasesize_shift;
++
++      /* Calculate info table size */
++      ni->state_table_size = ((ni->block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) /
++              NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE;
++      ni->mapping_table_size = ni->block_count * sizeof(*ni->block_mapping);
++
++      ni->info_table_size = NMBM_ALIGN(sizeof(ni->info_table),
++                                       ni->lower.writesize);
++      ni->info_table.state_table_off = ni->info_table_size;
++
++      ni->info_table_size += NMBM_ALIGN(ni->state_table_size,
++                                        ni->lower.writesize);
++      ni->info_table.mapping_table_off = ni->info_table_size;
++
++      ni->info_table_size += NMBM_ALIGN(ni->mapping_table_size,
++                                        ni->lower.writesize);
++
++      ni->info_table_spare_blocks = nmbm_get_spare_block_count(
++              size2blk(ni, ni->info_table_size));
++
++      /* Assign memory to members */
++      ptr = (uintptr_t)ni + sizeof(*ni);
++
++      ni->info_table_cache = (void *)ptr;
++      ptr += ni->info_table_size;
++
++      ni->block_state = (void *)ptr;
++      ptr += ni->state_table_size;
++
++      ni->block_mapping = (void *)ptr;
++      ptr += ni->mapping_table_size;
++
++      ni->page_cache = (uint8_t *)ptr;
++
++      /* Initialize block state table */
++      ni->block_state_changed = 0;
++      memset(ni->block_state, 0xff, ni->state_table_size);
++
++      /* Initialize block mapping table */
++      ni->block_mapping_changed = 0;
++}
++
++/*
++ * nmbm_attach - Attach to a lower device
++ * @nld: NMBM lower device structure
++ * @ni: NMBM instance structure
++ */
++int nmbm_attach(struct nmbm_lower_device *nld, struct nmbm_instance *ni)
++{
++      bool success;
++
++      if (!nld || !ni)
++              return -EINVAL;
++
++      /* Set default log level */
++      ni->log_display_level = NMBM_DEFAULT_LOG_LEVEL;
++
++      /* Check lower members */
++      success = nmbm_check_lower_members(nld);
++      if (!success)
++              return -EINVAL;
++
++      /* Initialize NMBM instance */
++      memcpy(&ni->lower, nld, sizeof(struct nmbm_lower_device));
++      nmbm_init_structure(ni);
++
++      success = nmbm_find_signature(ni, &ni->signature, &ni->signature_ba);
++      if (!success) {
++              if (!(nld->flags & NMBM_F_CREATE)) {
++                      nlog_err(ni, "Signature not found\n");
++                      return -ENODEV;
++              }
++
++              success = nmbm_create_new(ni);
++              if (!success)
++                      return -ENODEV;
++
++              return 0;
++      }
++
++      nlog_info(ni, "Signature found at block %u [0x%08llx]\n",
++               ni->signature_ba, ba2addr(ni, ni->signature_ba));
++      nmbm_mark_block_color_signature(ni, ni->signature_ba);
++
++      if (ni->signature.header.version != NMBM_VER) {
++              nlog_err(ni, "NMBM version %u.%u is not supported\n",
++                      NMBM_VERSION_MAJOR_GET(ni->signature.header.version),
++                      NMBM_VERSION_MINOR_GET(ni->signature.header.version));
++              return -EINVAL;
++      }
++
++      if (ni->signature.nand_size != nld->size ||
++          ni->signature.block_size != nld->erasesize ||
++          ni->signature.page_size != nld->writesize ||
++          ni->signature.spare_size != nld->oobsize) {
++              nlog_err(ni, "NMBM configuration mismatch\n");
++              return -EINVAL;
++      }
++
++      success = nmbm_load_existing(ni);
++      if (!success)
++              return -ENODEV;
++
++      return 0;
++}
++
++/*
++ * nmbm_detach - Detach from a lower device, and save all tables
++ * @ni: NMBM instance structure
++ */
++int nmbm_detach(struct nmbm_instance *ni)
++{
++      if (!ni)
++              return -EINVAL;
++
++      if (!(ni->lower.flags & NMBM_F_READ_ONLY))
++              nmbm_update_info_table(ni);
++
++      nmbm_mark_block_color_normal(ni, 0, ni->block_count - 1);
++
++      return 0;
++}
++
++/*
++ * nmbm_erase_logic_block - Erase a logic block
++ * @ni: NMBM instance structure
++ * @nmbm_erase_logic_block: logic block address
++ *
++ * Logic block will be mapped to physical block before erasing.
++ * Bad block found during erasinh will be remapped to a good block if there is
++ * still at least one good spare block available.
++ */
++static int nmbm_erase_logic_block(struct nmbm_instance *ni, uint32_t block_addr)
++{
++      uint32_t pb;
++      bool success;
++
++retry:
++      /* Map logic block to physical block */
++      pb = ni->block_mapping[block_addr];
++
++      /* Whether the logic block is good (has valid mapping) */
++      if ((int32_t)pb < 0) {
++              nlog_debug(ni, "Logic block %u is a bad block\n", block_addr);
++              return -EIO;
++      }
++
++      /* Remap logic block if current physical block is a bad block */
++      if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD ||
++          nmbm_get_block_state(ni, pb) == BLOCK_ST_NEED_REMAP)
++              goto remap_logic_block;
++
++      /* Insurance to detect unexpected bad block marked by user */
++      if (nmbm_check_bad_phys_block(ni, pb)) {
++              nlog_warn(ni, "Found unexpected bad block possibly marked by user\n");
++              nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
++              goto remap_logic_block;
++      }
++
++      success = nmbm_erase_block_and_check(ni, pb);
++      if (success)
++              return 0;
++
++      /* Mark bad block */
++      nmbm_mark_phys_bad_block(ni, pb);
++      nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
++
++remap_logic_block:
++      /* Try to assign a new block */
++      success = nmbm_map_block(ni, block_addr);
++      if (!success) {
++              /* Mark logic block unusable, and update info table */
++              ni->block_mapping[block_addr] = -1;
++              if (nmbm_get_block_state(ni, pb) != BLOCK_ST_NEED_REMAP)
++                      nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
++              nmbm_update_info_table(ni);
++              return -EIO;
++      }
++
++      /* Update info table before erasing */
++      if (nmbm_get_block_state(ni, pb) != BLOCK_ST_NEED_REMAP)
++              nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
++      nmbm_update_info_table(ni);
++
++      goto retry;
++}
++
++/*
++ * nmbm_erase_block_range - Erase logic blocks
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @size: erase range
++ * @failed_addr: return failed block address if error occurs
++ */
++int nmbm_erase_block_range(struct nmbm_instance *ni, uint64_t addr,
++                         uint64_t size, uint64_t *failed_addr)
++{
++      uint32_t start_ba, end_ba;
++      int ret;
++
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      if (addr + size > ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Erase range 0xllxu is too large\n", size);
++              return -EINVAL;
++      }
++
++      if (!size) {
++              nlog_warn(ni, "No blocks to be erased\n");
++              return 0;
++      }
++
++      start_ba = addr2ba(ni, addr);
++      end_ba = addr2ba(ni, addr + size - 1);
++
++      while (start_ba <= end_ba) {
++              WATCHDOG_RESET();
++
++              ret = nmbm_erase_logic_block(ni, start_ba);
++              if (ret) {
++                      if (failed_addr)
++                              *failed_addr = ba2addr(ni, start_ba);
++                      return ret;
++              }
++
++              start_ba++;
++      }
++
++      return 0;
++}
++
++/*
++ * nmbm_read_logic_page - Read page based on logic address
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @data: buffer to store main data. optional.
++ * @oob: buffer to store oob data. optional.
++ * @mode: read mode
++ *
++ * Return 0 for success, positive value for corrected bitflip count,
++ * -EBADMSG for ecc error, other negative values for other errors
++ */
++static int nmbm_read_logic_page(struct nmbm_instance *ni, uint64_t addr,
++                              void *data, void *oob, enum nmbm_oob_mode mode)
++{
++      uint32_t lb, pb, offset;
++      uint64_t paddr;
++
++      /* Extract block address and in-block offset */
++      lb = addr2ba(ni, addr);
++      offset = addr & ni->erasesize_mask;
++
++      /* Map logic block to physical block */
++      pb = ni->block_mapping[lb];
++
++      /* Whether the logic block is good (has valid mapping) */
++      if ((int32_t)pb < 0) {
++              nlog_debug(ni, "Logic block %u is a bad block\n", lb);
++              return -EIO;
++      }
++
++      /* Fail if physical block is marked bad */
++      if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
++              return -EIO;
++
++      /* Assemble new address */
++      paddr = ba2addr(ni, pb) + offset;
++
++      return nmbm_read_phys_page(ni, paddr, data, oob, mode);
++}
++
++/*
++ * nmbm_read_single_page - Read one page based on logic address
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @data: buffer to store main data. optional.
++ * @oob: buffer to store oob data. optional.
++ * @mode: read mode
++ *
++ * Return 0 for success, positive value for corrected bitflip count,
++ * -EBADMSG for ecc error, other negative values for other errors
++ */
++int nmbm_read_single_page(struct nmbm_instance *ni, uint64_t addr, void *data,
++                        void *oob, enum nmbm_oob_mode mode)
++{
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      return nmbm_read_logic_page(ni, addr, data, oob, mode);
++}
++
++/*
++ * nmbm_read_range - Read data without oob
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @size: data size to read
++ * @data: buffer to store main data to be read
++ * @mode: read mode
++ * @retlen: return actual data size read
++ *
++ * Return 0 for success, positive value for corrected bitflip count,
++ * -EBADMSG for ecc error, other negative values for other errors
++ */
++int nmbm_read_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
++                  void *data, enum nmbm_oob_mode mode, size_t *retlen)
++{
++      uint64_t off = addr;
++      uint8_t *ptr = data;
++      size_t sizeremain = size, chunksize, leading;
++      bool has_ecc_err = false;
++      int ret, max_bitflips = 0;
++
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      if (addr + size > ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Read range 0x%llx is too large\n", size);
++              return -EINVAL;
++      }
++
++      if (!size) {
++              nlog_warn(ni, "No data to be read\n");
++              return 0;
++      }
++
++      while (sizeremain) {
++              WATCHDOG_RESET();
++
++              leading = off & ni->writesize_mask;
++              chunksize = ni->lower.writesize - leading;
++              if (chunksize > sizeremain)
++                      chunksize = sizeremain;
++
++              if (chunksize == ni->lower.writesize) {
++                      ret = nmbm_read_logic_page(ni, off - leading, ptr,
++                                                      NULL, mode);
++                      if (ret < 0 && ret != -EBADMSG)
++                              break;
++              } else {
++                      ret = nmbm_read_logic_page(ni, off - leading,
++                                                      ni->page_cache, NULL,
++                                                      mode);
++                      if (ret < 0 && ret != -EBADMSG)
++                              break;
++
++                      memcpy(ptr, ni->page_cache + leading, chunksize);
++              }
++
++              if (ret == -EBADMSG)
++                      has_ecc_err = true;
++
++              if (ret > max_bitflips)
++                      max_bitflips = ret;
++
++              off += chunksize;
++              ptr += chunksize;
++              sizeremain -= chunksize;
++      }
++
++      if (retlen)
++              *retlen = size - sizeremain;
++
++      if (ret < 0 && ret != -EBADMSG)
++              return ret;
++
++      if (has_ecc_err)
++              return -EBADMSG;
++
++      return max_bitflips;
++}
++
++/*
++ * nmbm_write_logic_page - Read page based on logic address
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @data: buffer contains main data. optional.
++ * @oob: buffer contains oob data. optional.
++ * @mode: write mode
++ */
++static int nmbm_write_logic_page(struct nmbm_instance *ni, uint64_t addr,
++                                const void *data, const void *oob,
++                                enum nmbm_oob_mode mode)
++{
++      uint32_t lb, pb, offset;
++      uint64_t paddr;
++      bool success;
++
++      /* Extract block address and in-block offset */
++      lb = addr2ba(ni, addr);
++      offset = addr & ni->erasesize_mask;
++
++      /* Map logic block to physical block */
++      pb = ni->block_mapping[lb];
++
++      /* Whether the logic block is good (has valid mapping) */
++      if ((int32_t)pb < 0) {
++              nlog_debug(ni, "Logic block %u is a bad block\n", lb);
++              return -EIO;
++      }
++
++      /* Fail if physical block is marked bad */
++      if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
++              return -EIO;
++
++      /* Assemble new address */
++      paddr = ba2addr(ni, pb) + offset;
++
++      success = nmbm_write_phys_page(ni, paddr, data, oob, mode);
++      if (success)
++              return 0;
++
++      /*
++       * Do not remap bad block here. Just mark this block in state table.
++       * Remap this block on erasing.
++       */
++      nmbm_set_block_state(ni, pb, BLOCK_ST_NEED_REMAP);
++      nmbm_update_info_table(ni);
++
++      return -EIO;
++}
++
++/*
++ * nmbm_write_single_page - Write one page based on logic address
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @data: buffer contains main data. optional.
++ * @oob: buffer contains oob data. optional.
++ * @mode: write mode
++ */
++int nmbm_write_single_page(struct nmbm_instance *ni, uint64_t addr,
++                         const void *data, const void *oob,
++                         enum nmbm_oob_mode mode)
++{
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      return nmbm_write_logic_page(ni, addr, data, oob, mode);
++}
++
++/*
++ * nmbm_write_range - Write data without oob
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @size: data size to write
++ * @data: buffer contains data to be written
++ * @mode: write mode
++ * @retlen: return actual data size written
++ */
++int nmbm_write_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
++                   const void *data, enum nmbm_oob_mode mode,
++                   size_t *retlen)
++{
++      uint64_t off = addr;
++      const uint8_t *ptr = data;
++      size_t sizeremain = size, chunksize, leading;
++      int ret;
++
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      if (addr + size > ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Write size 0x%zx is too large\n", size);
++              return -EINVAL;
++      }
++
++      if (!size) {
++              nlog_warn(ni, "No data to be written\n");
++              return 0;
++      }
++
++      while (sizeremain) {
++              WATCHDOG_RESET();
++
++              leading = off & ni->writesize_mask;
++              chunksize = ni->lower.writesize - leading;
++              if (chunksize > sizeremain)
++                      chunksize = sizeremain;
++
++              if (chunksize == ni->lower.writesize) {
++                      ret = nmbm_write_logic_page(ni, off - leading, ptr,
++                                                       NULL, mode);
++                      if (ret)
++                              break;
++              } else {
++                      memset(ni->page_cache, 0xff, leading);
++                      memcpy(ni->page_cache + leading, ptr, chunksize);
++
++                      ret = nmbm_write_logic_page(ni, off - leading,
++                                                       ni->page_cache, NULL,
++                                                       mode);
++                      if (ret)
++                              break;
++              }
++
++              off += chunksize;
++              ptr += chunksize;
++              sizeremain -= chunksize;
++      }
++
++      if (retlen)
++              *retlen = size - sizeremain;
++
++      return ret;
++}
++
++/*
++ * nmbm_check_bad_block - Check whether a logic block is usable
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ */
++int nmbm_check_bad_block(struct nmbm_instance *ni, uint64_t addr)
++{
++      uint32_t lb, pb;
++
++      if (!ni)
++              return -EINVAL;
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      lb = addr2ba(ni, addr);
++
++      /* Map logic block to physical block */
++      pb = ni->block_mapping[lb];
++
++      if ((int32_t)pb < 0)
++              return 1;
++
++      if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
++              return 1;
++
++      return 0;
++}
++
++/*
++ * nmbm_mark_bad_block - Mark a logic block unusable
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ */
++int nmbm_mark_bad_block(struct nmbm_instance *ni, uint64_t addr)
++{
++      uint32_t lb, pb;
++
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      lb = addr2ba(ni, addr);
++
++      /* Map logic block to physical block */
++      pb = ni->block_mapping[lb];
++
++      if ((int32_t)pb < 0)
++              return 0;
++
++      ni->block_mapping[lb] = -1;
++      nmbm_mark_phys_bad_block(ni, pb);
++      nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
++      nmbm_update_info_table(ni);
++
++      return 0;
++}
++
++/*
++ * nmbm_get_avail_size - Get available user data size
++ * @ni: NMBM instance structure
++ */
++uint64_t nmbm_get_avail_size(struct nmbm_instance *ni)
++{
++      if (!ni)
++              return 0;
++
++      return (uint64_t)ni->data_block_count << ni->erasesize_shift;
++}
++
++/*
++ * nmbm_get_lower_device - Get lower device structure
++ * @ni: NMBM instance structure
++ * @nld: pointer to hold the data of lower device structure
++ */
++int nmbm_get_lower_device(struct nmbm_instance *ni, struct nmbm_lower_device *nld)
++{
++      if (!ni)
++              return -EINVAL;
++
++      if (nld)
++              memcpy(nld, &ni->lower, sizeof(*nld));
++
++      return 0;
++}
++
++#include "nmbm-debug.inl"
+--- /dev/null
++++ b/drivers/mtd/nmbm/nmbm-debug.h
+@@ -0,0 +1,37 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Debug addons for NAND Mapped-block Management (NMBM)
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _NMBM_DEBUG_H_
++#define _NMBM_DEBUG_H_
++
++#include "nmbm-private.h"
++
++#define nmbm_mark_block_color_normal(ni, start_ba, end_ba)
++#define nmbm_mark_block_color_bad(ni, ba)
++#define nmbm_mark_block_color_mgmt(ni, start_ba, end_ba)
++#define nmbm_mark_block_color_signature(ni, ba)
++#define nmbm_mark_block_color_info_table(ni, start_ba, end_ba)
++#define nmbm_mark_block_color_mapped(ni, ba)
++
++uint32_t nmbm_debug_get_block_state(struct nmbm_instance *ni, uint32_t ba);
++char nmbm_debug_get_phys_block_type(struct nmbm_instance *ni, uint32_t ba);
++
++enum nmmb_block_type {
++      NMBM_BLOCK_GOOD_DATA,
++      NMBM_BLOCK_GOOD_MGMT,
++      NMBM_BLOCK_BAD,
++      NMBM_BLOCK_MAIN_INFO_TABLE,
++      NMBM_BLOCK_BACKUP_INFO_TABLE,
++      NMBM_BLOCK_REMAPPED,
++      NMBM_BLOCK_SIGNATURE,
++
++      __NMBM_BLOCK_TYPE_MAX
++};
++
++#endif /* _NMBM_DEBUG_H_ */
+--- /dev/null
++++ b/drivers/mtd/nmbm/nmbm-debug.inl
+@@ -0,0 +1,39 @@
++
++uint32_t nmbm_debug_get_block_state(struct nmbm_instance *ni, uint32_t ba)
++{
++      return nmbm_get_block_state(ni, ba);
++}
++
++char nmbm_debug_get_phys_block_type(struct nmbm_instance *ni, uint32_t ba)
++{
++      uint32_t eba, limit;
++      bool success;
++
++      if (nmbm_get_block_state(ni, ba) == BLOCK_ST_BAD)
++              return NMBM_BLOCK_BAD;
++
++      if (ba < ni->data_block_count)
++              return NMBM_BLOCK_GOOD_DATA;
++
++      if (ba == ni->signature_ba)
++              return NMBM_BLOCK_SIGNATURE;
++
++      if (ni->main_table_ba) {
++              limit = ni->backup_table_ba ? ni->backup_table_ba :
++                      ni->mapping_blocks_ba;
++
++              success = nmbm_block_walk_asc(ni, ni->main_table_ba, &eba,
++                      size2blk(ni, ni->info_table_size), limit);
++
++              if (success && ba >= ni->main_table_ba && ba < eba)
++                      return NMBM_BLOCK_MAIN_INFO_TABLE;
++      }
++
++      if (ba >= ni->backup_table_ba && ba < ni->mapping_blocks_ba)
++              return NMBM_BLOCK_BACKUP_INFO_TABLE;
++
++      if (ba > ni->mapping_blocks_top_ba && ba < ni->signature_ba)
++              return NMBM_BLOCK_REMAPPED;
++
++      return NMBM_BLOCK_GOOD_MGMT;
++}
+--- /dev/null
++++ b/drivers/mtd/nmbm/nmbm-private.h
+@@ -0,0 +1,137 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Definitions for NAND Mapped-block Management (NMBM)
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _NMBM_PRIVATE_H_
++#define _NMBM_PRIVATE_H_
++
++#include <nmbm/nmbm.h>
++
++#define NMBM_MAGIC_SIGNATURE                  0x304d4d4e      /* NMM0 */
++#define NMBM_MAGIC_INFO_TABLE                 0x314d4d4e      /* NMM1 */
++
++#define NMBM_VERSION_MAJOR_S                  0
++#define NMBM_VERSION_MAJOR_M                  0xffff
++#define NMBM_VERSION_MINOR_S                  16
++#define NMBM_VERSION_MINOR_M                  0xffff
++#define NMBM_VERSION_MAKE(major, minor)               (((major) & NMBM_VERSION_MAJOR_M) | \
++                                              (((minor) & NMBM_VERSION_MINOR_M) << \
++                                              NMBM_VERSION_MINOR_S))
++#define NMBM_VERSION_MAJOR_GET(ver)           (((ver) >> NMBM_VERSION_MAJOR_S) & \
++                                              NMBM_VERSION_MAJOR_M)
++#define NMBM_VERSION_MINOR_GET(ver)           (((ver) >> NMBM_VERSION_MINOR_S) & \
++                                              NMBM_VERSION_MINOR_M)
++
++typedef uint32_t                              nmbm_bitmap_t;
++#define NMBM_BITMAP_UNIT_SIZE                 (sizeof(nmbm_bitmap_t))
++#define NMBM_BITMAP_BITS_PER_BLOCK            2
++#define NMBM_BITMAP_BITS_PER_UNIT             (8 * sizeof(nmbm_bitmap_t))
++#define NMBM_BITMAP_BLOCKS_PER_UNIT           (NMBM_BITMAP_BITS_PER_UNIT / \
++                                               NMBM_BITMAP_BITS_PER_BLOCK)
++
++#define NMBM_SPARE_BLOCK_MULTI                        1
++#define NMBM_SPARE_BLOCK_DIV                  2
++#define NMBM_SPARE_BLOCK_MIN                  2
++
++#define NMBM_MGMT_DIV                         16
++#define NMBM_MGMT_BLOCKS_MIN                  32
++
++#define NMBM_TRY_COUNT                                3
++
++#define BLOCK_ST_BAD                          0
++#define BLOCK_ST_NEED_REMAP                   2
++#define BLOCK_ST_GOOD                         3
++#define BLOCK_ST_MASK                         3
++
++struct nmbm_header {
++      uint32_t magic;
++      uint32_t version;
++      uint32_t size;
++      uint32_t checksum;
++};
++
++struct nmbm_signature {
++      struct nmbm_header header;
++      uint64_t nand_size;
++      uint32_t block_size;
++      uint32_t page_size;
++      uint32_t spare_size;
++      uint32_t mgmt_start_pb;
++      uint8_t max_try_count;
++      uint8_t padding[3];
++};
++
++struct nmbm_info_table_header {
++      struct nmbm_header header;
++      uint32_t write_count;
++      uint32_t state_table_off;
++      uint32_t mapping_table_off;
++      uint32_t padding;
++};
++
++struct nmbm_instance {
++      struct nmbm_lower_device lower;
++
++      uint32_t rawpage_size;
++      uint32_t rawblock_size;
++      uint32_t rawchip_size;
++
++      uint32_t writesize_mask;
++      uint32_t erasesize_mask;
++      uint16_t writesize_shift;
++      uint16_t erasesize_shift;
++
++      struct nmbm_signature signature;
++
++      uint8_t *info_table_cache;
++      uint32_t info_table_size;
++      uint32_t info_table_spare_blocks;
++      struct nmbm_info_table_header info_table;
++
++      nmbm_bitmap_t *block_state;
++      uint32_t block_state_changed;
++      uint32_t state_table_size;
++
++      int32_t *block_mapping;
++      uint32_t block_mapping_changed;
++      uint32_t mapping_table_size;
++
++      uint8_t *page_cache;
++
++      int protected;
++
++      uint32_t block_count;
++      uint32_t data_block_count;
++
++      uint32_t mgmt_start_ba;
++      uint32_t main_table_ba;
++      uint32_t backup_table_ba;
++      uint32_t mapping_blocks_ba;
++      uint32_t mapping_blocks_top_ba;
++      uint32_t signature_ba;
++
++      enum nmbm_log_category log_display_level;
++};
++
++/* Log utilities */
++#define nlog_debug(ni, fmt, ...) \
++      nmbm_log(ni, NMBM_LOG_DEBUG, fmt, ##__VA_ARGS__)
++
++#define nlog_info(ni, fmt, ...) \
++      nmbm_log(ni, NMBM_LOG_INFO, fmt, ##__VA_ARGS__)
++
++#define nlog_warn(ni, fmt, ...) \
++      nmbm_log(ni, NMBM_LOG_WARN, fmt, ##__VA_ARGS__)
++
++#define nlog_err(ni, fmt, ...) \
++      nmbm_log(ni, NMBM_LOG_ERR, fmt, ##__VA_ARGS__)
++
++#define nlog_emerg(ni, fmt, ...) \
++      nmbm_log(ni, NMBM_LOG_EMERG, fmt, ##__VA_ARGS__)
++
++#endif /* _NMBM_PRIVATE_H_ */
+--- /dev/null
++++ b/include/nmbm/nmbm-os.h
+@@ -0,0 +1,66 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * OS-dependent definitions for NAND Mapped-block Management (NMBM)
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _NMBM_OS_H_
++#define _NMBM_OS_H_
++
++#include <div64.h>
++#include <stdbool.h>
++#include <watchdog.h>
++#include <u-boot/crc.h>
++#include <linux/errno.h>
++#include <linux/log2.h>
++#include <linux/types.h>
++
++static inline uint32_t nmbm_crc32(uint32_t crcval, const void *buf, size_t size)
++{
++      uint chksz;
++      const unsigned char *p = buf;
++
++      while (size) {
++              if (size > UINT_MAX)
++                      chksz = UINT_MAX;
++              else
++                      chksz = (uint)size;
++
++              crcval = crc32_no_comp(crcval, p, chksz);
++              size -= chksz;
++              p += chksz;
++      }
++
++      return crcval;
++}
++
++static inline uint32_t nmbm_lldiv(uint64_t dividend, uint32_t divisor)
++{
++#if BITS_PER_LONG == 64
++      return dividend / divisor;
++#else
++      __div64_32(&dividend, divisor);
++      return dividend;
++#endif
++}
++
++#ifdef CONFIG_NMBM_LOG_LEVEL_DEBUG
++#define NMBM_DEFAULT_LOG_LEVEL                0
++#elif defined(NMBM_LOG_LEVEL_INFO)
++#define NMBM_DEFAULT_LOG_LEVEL                1
++#elif defined(NMBM_LOG_LEVEL_WARN)
++#define NMBM_DEFAULT_LOG_LEVEL                2
++#elif defined(NMBM_LOG_LEVEL_ERR)
++#define NMBM_DEFAULT_LOG_LEVEL                3
++#elif defined(NMBM_LOG_LEVEL_EMERG)
++#define NMBM_DEFAULT_LOG_LEVEL                4
++#elif defined(NMBM_LOG_LEVEL_NONE)
++#define NMBM_DEFAULT_LOG_LEVEL                5
++#else
++#define NMBM_DEFAULT_LOG_LEVEL                1
++#endif
++
++#endif /* _NMBM_OS_H_ */
+--- /dev/null
++++ b/include/nmbm/nmbm.h
+@@ -0,0 +1,102 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Definitions for NAND Mapped-block Management (NMBM)
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _NMBM_H_
++#define _NMBM_H_
++
++#include <nmbm/nmbm-os.h>
++
++enum nmbm_log_category {
++      NMBM_LOG_DEBUG,
++      NMBM_LOG_INFO,
++      NMBM_LOG_WARN,
++      NMBM_LOG_ERR,
++      NMBM_LOG_EMERG,
++
++      __NMBM_LOG_MAX
++};
++
++enum nmbm_oob_mode {
++      NMBM_MODE_PLACE_OOB,
++      NMBM_MODE_AUTO_OOB,
++      NMBM_MODE_RAW,
++
++      __NMBM_MODE_MAX
++};
++
++struct nmbm_lower_device {
++      uint32_t max_ratio;
++      uint32_t max_reserved_blocks;
++      int flags;
++
++      uint64_t size;
++      uint32_t erasesize;
++      uint32_t writesize;
++      uint32_t oobsize;
++      uint32_t oobavail;
++
++      void *arg;
++      int (*reset_chip)(void *arg);
++
++      /*
++       * read_page:
++       *    return 0 if succeeds
++       *    return positive number for ecc error
++       *    return negative number for other errors
++       */
++      int (*read_page)(void *arg, uint64_t addr, void *buf, void *oob, enum nmbm_oob_mode mode);
++      int (*write_page)(void *arg, uint64_t addr, const void *buf, const void *oob, enum nmbm_oob_mode mode);
++      int (*erase_block)(void *arg, uint64_t addr);
++
++      int (*is_bad_block)(void *arg, uint64_t addr);
++      int (*mark_bad_block)(void *arg, uint64_t addr);
++
++      /* OS-dependent logging function */
++      void (*logprint)(void *arg, enum nmbm_log_category level, const char *fmt, va_list ap);
++};
++
++struct nmbm_instance;
++
++/* Create NMBM if management area not found, or not complete */
++#define NMBM_F_CREATE                 0x01
++
++/* Empty page is also protected by ECC, and bitflip(s) can be corrected */
++#define NMBM_F_EMPTY_PAGE_ECC_OK      0x02
++
++/* Do not write anything back to flash */
++#define NMBM_F_READ_ONLY              0x04
++
++size_t nmbm_calc_structure_size(struct nmbm_lower_device *nld);
++int nmbm_attach(struct nmbm_lower_device *nld, struct nmbm_instance *ni);
++int nmbm_detach(struct nmbm_instance *ni);
++
++enum nmbm_log_category nmbm_set_log_level(struct nmbm_instance *ni,
++                                        enum nmbm_log_category level);
++
++int nmbm_erase_block_range(struct nmbm_instance *ni, uint64_t addr,
++                         uint64_t size, uint64_t *failed_addr);
++int nmbm_read_single_page(struct nmbm_instance *ni, uint64_t addr, void *data,
++                        void *oob, enum nmbm_oob_mode mode);
++int nmbm_read_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
++                  void *data, enum nmbm_oob_mode mode, size_t *retlen);
++int nmbm_write_single_page(struct nmbm_instance *ni, uint64_t addr,
++                         const void *data, const void *oob,
++                         enum nmbm_oob_mode mode);
++int nmbm_write_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
++                   const void *data, enum nmbm_oob_mode mode,
++                   size_t *retlen);
++
++int nmbm_check_bad_block(struct nmbm_instance *ni, uint64_t addr);
++int nmbm_mark_bad_block(struct nmbm_instance *ni, uint64_t addr);
++
++uint64_t nmbm_get_avail_size(struct nmbm_instance *ni);
++
++int nmbm_get_lower_device(struct nmbm_instance *ni, struct nmbm_lower_device *nld);
++
++#endif /* _NMBM_H_ */