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.
This commit is contained in:
Tom Rini
2024-12-17 13:16:37 -06:00
5 changed files with 132 additions and 10 deletions

View File

@@ -1497,6 +1497,11 @@ config CMD_NAND_TORTURE
help help
NAND torture support. NAND torture support.
config CMD_NAND_WATCH
bool "nand watch"
help
NAND watch bitflip support.
endif # CMD_NAND endif # CMD_NAND
config CMD_NVME config CMD_NVME

View File

@@ -122,13 +122,11 @@ static void mtd_show_device(struct mtd_info *mtd)
{ {
/* Device */ /* Device */
printf("* %s\n", mtd->name); printf("* %s\n", mtd->name);
#if defined(CONFIG_DM)
if (mtd->dev) { if (mtd->dev) {
printf(" - device: %s\n", mtd->dev->name); printf(" - device: %s\n", mtd->dev->name);
printf(" - parent: %s\n", mtd->dev->parent->name); printf(" - parent: %s\n", mtd->dev->parent->name);
printf(" - driver: %s\n", mtd->dev->driver->name); printf(" - driver: %s\n", mtd->dev->driver->name);
} }
#endif
if (IS_ENABLED(CONFIG_OF_CONTROL) && mtd->dev) { if (IS_ENABLED(CONFIG_OF_CONTROL) && mtd->dev) {
char buf[256]; char buf[256];
int res; int res;

View File

@@ -231,6 +231,54 @@ free_dat:
return ret; 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) 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; 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 #ifdef CONFIG_CMD_NAND_TORTURE
if (strcmp(cmd, "torture") == 0) { if (strcmp(cmd, "torture") == 0) {
loff_t endoff; loff_t endoff;
@@ -946,6 +1043,12 @@ U_BOOT_LONGHELP(nand,
"nand erase.chip [clean] - erase entire chip'\n" "nand erase.chip [clean] - erase entire chip'\n"
"nand bad - show bad blocks\n" "nand bad - show bad blocks\n"
"nand dump[.oob] off - dump page\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 #ifdef CONFIG_CMD_NAND_TORTURE
"nand torture off - torture one block at offset\n" "nand torture off - torture one block at offset\n"
"nand torture off [size] - torture blocks from off to off+size\n" "nand torture off [size] - torture blocks from off to off+size\n"

View File

@@ -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); 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, int mtd_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops) struct mtd_oob_ops *ops)
{ {

View File

@@ -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 mtd_info *mtd = nand_to_mtd(chip);
struct atmel_nand *nand = to_atmel_nand(chip); struct atmel_nand *nand = to_atmel_nand(chip);
struct atmel_hsmc_nand_controller *nc; struct atmel_hsmc_nand_controller *nc;
int ret = -EIO;
nc = to_hsmc_nand_controller(nand->controller); nc = to_hsmc_nand_controller(nand->controller);
memcpy_toio(nc->sram.virt, buf, mtd->writesize);
if (ret)
memcpy_toio(nc->sram.virt, buf, mtd->writesize);
if (oob_required) if (oob_required)
memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi, 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 mtd_info *mtd = nand_to_mtd(chip);
struct atmel_nand *nand = to_atmel_nand(chip); struct atmel_nand *nand = to_atmel_nand(chip);
struct atmel_hsmc_nand_controller *nc; struct atmel_hsmc_nand_controller *nc;
int ret = -EIO;
nc = to_hsmc_nand_controller(nand->controller); nc = to_hsmc_nand_controller(nand->controller);
memcpy_fromio(buf, nc->sram.virt, mtd->writesize);
if (ret)
memcpy_fromio(buf, nc->sram.virt, mtd->writesize);
if (oob_required) if (oob_required)
memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize, memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize,