summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2024-12-17 13:16:37 -0600
committerTom Rini <trini@konsulko.com>2024-12-17 13:17:41 -0600
commit4561977c10be371ae57d355fade3b4cded092df7 (patch)
treed08390da14d6f330bafe623eed8d83aaf528560d
parenta4ee6396fe779b351c8ed5121f2c74d5f66bdaa5 (diff)
parent1fac577207196e12932c2c733b58e3b14013b1ce (diff)
Merge branch 'u-boot-nand-20241212' of https://source.denx.de/u-boot/custodians/u-boot-nand-flash into next
CI: https://source.denx.de/u-boot/custodians/u-boot-nand-flash/-/pipelines/23837 Small addition to uboot-nand. Nothing relevant for now. Anyway a nice new command coming from Miquel Raynal and small changes.
-rw-r--r--cmd/Kconfig5
-rw-r--r--cmd/mtd.c2
-rw-r--r--cmd/nand.c103
-rw-r--r--drivers/mtd/mtdcore.c22
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c10
5 files changed, 132 insertions, 10 deletions
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 4936a70f3ef..93efeaec6f4 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1497,6 +1497,11 @@ config CMD_NAND_TORTURE
help
NAND torture support.
+config CMD_NAND_WATCH
+ bool "nand watch"
+ help
+ NAND watch bitflip support.
+
endif # CMD_NAND
config CMD_NVME
diff --git a/cmd/mtd.c b/cmd/mtd.c
index f178d7bea61..c25997cfb24 100644
--- a/cmd/mtd.c
+++ b/cmd/mtd.c
@@ -122,13 +122,11 @@ static void mtd_show_device(struct mtd_info *mtd)
{
/* Device */
printf("* %s\n", mtd->name);
-#if defined(CONFIG_DM)
if (mtd->dev) {
printf(" - device: %s\n", mtd->dev->name);
printf(" - parent: %s\n", mtd->dev->parent->name);
printf(" - driver: %s\n", mtd->dev->driver->name);
}
-#endif
if (IS_ENABLED(CONFIG_OF_CONTROL) && mtd->dev) {
char buf[256];
int res;
diff --git a/cmd/nand.c b/cmd/nand.c
index 5a328e0acdd..2f785deeb7f 100644
--- a/cmd/nand.c
+++ b/cmd/nand.c
@@ -231,6 +231,54 @@ free_dat:
return ret;
}
+#ifdef CONFIG_CMD_NAND_WATCH
+static int nand_watch_bf(struct mtd_info *mtd, ulong off, ulong size, bool quiet)
+{
+ unsigned int max_bf = 0, pages_wbf = 0;
+ unsigned int first_page, pages, i;
+ struct mtd_oob_ops ops = {};
+ u_char *buf;
+ int ret;
+
+ buf = memalign(ARCH_DMA_MINALIGN, mtd->writesize);
+ if (!buf) {
+ puts("No memory for page buffer\n");
+ return 1;
+ }
+
+ first_page = off / mtd->writesize;
+ pages = size / mtd->writesize;
+
+ ops.datbuf = buf;
+ ops.len = mtd->writesize;
+ for (i = first_page; i < first_page + pages; i++) {
+ ulong addr = mtd->writesize * i;
+ ret = mtd_read_oob_bf(mtd, addr, &ops);
+ if (ret < 0) {
+ if (quiet)
+ continue;
+
+ printf("Page %7d (0x%08lx) -> error %d\n",
+ i, addr, ret);
+ } else if (ret) {
+ max_bf = max(max_bf, (unsigned int)ret);
+ pages_wbf++;
+ if (quiet)
+ continue;
+ printf("Page %7d (0x%08lx) -> up to %2d bf/chunk\n",
+ i, addr, ret);
+ }
+ }
+
+ printf("Maximum number of bitflips: %u\n", max_bf);
+ printf("Pages with bitflips: %u/%u\n", pages_wbf, pages);
+
+ free(buf);
+
+ return 0;
+}
+#endif
+
/* ------------------------------------------------------------------------- */
static int set_dev(int dev)
@@ -781,6 +829,55 @@ static int do_nand(struct cmd_tbl *cmdtp, int flag, int argc,
return ret == 0 ? 0 : 1;
}
+#ifdef CONFIG_CMD_NAND_WATCH
+ if (strncmp(cmd, "watch", 5) == 0) {
+ int args = 2;
+
+ if (cmd[5]) {
+ if (!strncmp(&cmd[5], ".part", 5)) {
+ args = 1;
+ } else if (!strncmp(&cmd[5], ".chip", 5)) {
+ args = 0;
+ } else {
+ goto usage;
+ }
+ }
+
+ if (cmd[10])
+ if (!strncmp(&cmd[10], ".quiet", 6))
+ quiet = true;
+
+ if (argc != 2 + args)
+ goto usage;
+
+ ret = mtd_arg_off_size(argc - 2, argv + 2, &dev, &off, &size,
+ &maxsize, MTD_DEV_TYPE_NAND, mtd->size);
+ if (ret)
+ return ret;
+
+ /* size is unspecified */
+ if (argc < 4)
+ adjust_size_for_badblocks(&size, off, dev);
+
+ if ((off & (mtd->writesize - 1)) ||
+ (size & (mtd->writesize - 1))) {
+ printf("Attempt to read non page-aligned data\n");
+ return -EINVAL;
+ }
+
+ ret = set_dev(dev);
+ if (ret)
+ return ret;
+
+ mtd = get_nand_dev_by_index(dev);
+
+ printf("\nNAND watch for bitflips in area 0x%llx-0x%llx:\n",
+ off, off + size);
+
+ return nand_watch_bf(mtd, off, size, quiet);
+ }
+#endif
+
#ifdef CONFIG_CMD_NAND_TORTURE
if (strcmp(cmd, "torture") == 0) {
loff_t endoff;
@@ -946,6 +1043,12 @@ U_BOOT_LONGHELP(nand,
"nand erase.chip [clean] - erase entire chip'\n"
"nand bad - show bad blocks\n"
"nand dump[.oob] off - dump page\n"
+#ifdef CONFIG_CMD_NAND_WATCH
+ "nand watch <off> <size> - check an area for bitflips\n"
+ "nand watch.part <part> - check a partition for bitflips\n"
+ "nand watch.chip - check the whole device for bitflips\n"
+ "\t\t.quiet - Query only the summary, not the details\n"
+#endif
#ifdef CONFIG_CMD_NAND_TORTURE
"nand torture off - torture one block at offset\n"
"nand torture off [size] - torture blocks from off to off+size\n"
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 5bd64bd6ad4..3bfa5aebbc6 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -1124,6 +1124,28 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
}
EXPORT_SYMBOL_GPL(mtd_read_oob);
+/* This is a bare copy of mtd_read_oob returning the actual number of bitflips */
+int mtd_read_oob_bf(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
+{
+ int ret_code;
+ ops->retlen = ops->oobretlen = 0;
+ if (!mtd->_read_oob)
+ return -EOPNOTSUPP;
+ /*
+ * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
+ * similar to mtd->_read(), returning a non-negative integer
+ * representing max bitflips. In other cases, mtd->_read_oob() may
+ * return -EUCLEAN. In all cases, perform similar logic to mtd_read().
+ */
+ ret_code = mtd->_read_oob(mtd, from, ops);
+ if (unlikely(ret_code < 0))
+ return ret_code;
+ if (mtd->ecc_strength == 0)
+ return 0; /* device lacks ecc */
+ return ret_code;
+}
+EXPORT_SYMBOL_GPL(mtd_read_oob_bf);
+
int mtd_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
index 25f187a2eec..56fbd64ef68 100644
--- a/drivers/mtd/nand/raw/atmel/nand-controller.c
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -568,12 +568,9 @@ static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf,
struct mtd_info *mtd = nand_to_mtd(chip);
struct atmel_nand *nand = to_atmel_nand(chip);
struct atmel_hsmc_nand_controller *nc;
- int ret = -EIO;
nc = to_hsmc_nand_controller(nand->controller);
-
- if (ret)
- memcpy_toio(nc->sram.virt, buf, mtd->writesize);
+ memcpy_toio(nc->sram.virt, buf, mtd->writesize);
if (oob_required)
memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi,
@@ -586,12 +583,9 @@ static void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf,
struct mtd_info *mtd = nand_to_mtd(chip);
struct atmel_nand *nand = to_atmel_nand(chip);
struct atmel_hsmc_nand_controller *nc;
- int ret = -EIO;
nc = to_hsmc_nand_controller(nand->controller);
-
- if (ret)
- memcpy_fromio(buf, nc->sram.virt, mtd->writesize);
+ memcpy_fromio(buf, nc->sram.virt, mtd->writesize);
if (oob_required)
memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize,