[][SW framework][common][bsp][add panic write support for nmbm]
[Description]
Add panic write support for nmbm
[Release-log]
N/A
Change-Id: I9055348d401335aac31f8aacdf0cac16c1af873c
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7654466
diff --git a/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c
index b80ea42..9dc4a10 100644
--- a/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c
+++ b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c
@@ -257,6 +257,37 @@
}
/*
+ * nmbm_panic_write_phys_page - Panic 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
+ *
+ * Write a page for at most NMBM_TRY_COUNT times.
+ */
+static bool nmbm_panic_write_phys_page(struct nmbm_instance *ni, uint64_t addr,
+ const void *data)
+{
+ 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.panic_write_page(ni->lower.arg, addr, data);
+ if (!ret)
+ return true;
+
+ nmbm_reset_chip(ni);
+ }
+
+ nlog_err(ni, "Panic 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
@@ -2730,6 +2761,56 @@
}
/*
+ * nmbm_panic_write_logic_page - Panic write page based on logic address
+ * @ni: NMBM instance structure
+ * @addr: logic linear address
+ * @data: buffer contains main data. optional.
+ */
+static int nmbm_panic_write_logic_page(struct nmbm_instance *ni, uint64_t addr,
+ const void *data)
+{
+ uint32_t lb, pb, offset;
+ uint64_t paddr;
+ bool success;
+
+ if (!ni->lower.panic_write_page)
+ return -ENOTSUPP;
+
+ /* 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_panic_write_phys_page(ni, paddr, data);
+ 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
@@ -2759,6 +2840,32 @@
}
/*
+ * nmbm_panic_write_single_page - Panic write one page based on logic address
+ * @ni: NMBM instance structure
+ * @addr: logic linear address
+ * @data: buffer contains main data. optional.
+ */
+int nmbm_panic_write_single_page(struct nmbm_instance *ni, uint64_t addr,
+ const void *data)
+{
+ 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_panic_write_logic_page(ni, addr, data);
+}
+
+/*
* nmbm_write_range - Write data without oob
* @ni: NMBM instance structure
* @addr: logic linear address
diff --git a/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c
index a3e9e18..94413a3 100644
--- a/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c
+++ b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c
@@ -141,6 +141,16 @@
return mtd_write_oob(nm->lower, addr, &ops);
}
+static int nmbm_lower_panic_write_page(void *arg, uint64_t addr,
+ const void *buf)
+{
+ struct nmbm_mtd *nm = arg;
+ size_t retlen;
+
+ return mtd_panic_write(nm->lower, addr, nm->lower->writesize, &retlen,
+ buf);
+}
+
static int nmbm_lower_erase_block(void *arg, uint64_t addr)
{
struct nmbm_mtd *nm = arg;
@@ -538,6 +548,44 @@
return ret;
}
+static int nmbm_mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
+ size_t chklen, wrlen = 0;
+ uint32_t col;
+ int ret;
+
+ col = to & nm->lower->writesize_mask;
+ to &= ~nm->lower->writesize_mask;
+
+ while (len) {
+ /* Move data */
+ chklen = nm->lower->writesize - col;
+ if (chklen > len)
+ chklen = len;
+
+ if (chklen < nm->lower->writesize)
+ memset(nm->page_cache, 0xff, nm->lower->writesize);
+ memcpy(nm->page_cache + col, buf + wrlen, chklen);
+
+ len -= chklen;
+ col = 0; /* (col + chklen) % */
+ wrlen += chklen;
+
+ ret = nmbm_panic_write_single_page(nm->ni, to, nm->page_cache);
+ if (ret)
+ break;
+
+ to += nm->lower->writesize;
+ }
+
+ if (retlen)
+ *retlen = wrlen;
+
+ return 0;
+}
+
static int nmbm_mtd_block_isbad(struct mtd_info *mtd, loff_t offs)
{
struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
@@ -634,6 +682,7 @@
nld.read_page = nmbm_lower_read_page;
nld.write_page = nmbm_lower_write_page;
+ nld.panic_write_page = nmbm_lower_panic_write_page;
nld.erase_block = nmbm_lower_erase_block;
nld.is_bad_block = nmbm_lower_is_bad_block;
nld.mark_bad_block = nmbm_lower_mark_bad_block;
@@ -697,6 +746,7 @@
mtd->_erase = nmbm_mtd_erase;
mtd->_read_oob = nmbm_mtd_read_oob;
mtd->_write_oob = nmbm_mtd_write_oob;
+ mtd->_panic_write = nmbm_mtd_panic_write;
mtd->_block_isbad = nmbm_mtd_block_isbad;
mtd->_block_markbad = nmbm_mtd_block_markbad;
mtd->_reboot = nmbm_mtd_shutdown;
diff --git a/target/linux/generic/files-5.4/include/nmbm/nmbm.h b/target/linux/generic/files-5.4/include/nmbm/nmbm.h
index c040098..0f43466 100644
--- a/target/linux/generic/files-5.4/include/nmbm/nmbm.h
+++ b/target/linux/generic/files-5.4/include/nmbm/nmbm.h
@@ -52,6 +52,7 @@
*/
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 (*panic_write_page)(void *arg, uint64_t addr, const void *buf);
int (*erase_block)(void *arg, uint64_t addr);
int (*is_bad_block)(void *arg, uint64_t addr);
@@ -88,6 +89,8 @@
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_panic_write_single_page(struct nmbm_instance *ni, uint64_t addr,
+ const void *data);
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);