15db66b3aSMiquel Raynal // SPDX-License-Identifier: GPL-2.0+
25db66b3aSMiquel Raynal /*
35db66b3aSMiquel Raynal * mtd.c
45db66b3aSMiquel Raynal *
55db66b3aSMiquel Raynal * Generic command to handle basic operations on any memory device.
65db66b3aSMiquel Raynal *
75db66b3aSMiquel Raynal * Copyright: Bootlin, 2018
85db66b3aSMiquel Raynal * Author: Miquèl Raynal <miquel.raynal@bootlin.com>
95db66b3aSMiquel Raynal */
105db66b3aSMiquel Raynal
115db66b3aSMiquel Raynal #include <command.h>
125db66b3aSMiquel Raynal #include <common.h>
135db66b3aSMiquel Raynal #include <console.h>
145db66b3aSMiquel Raynal #include <malloc.h>
155db66b3aSMiquel Raynal #include <mapmem.h>
165db66b3aSMiquel Raynal #include <mtd.h>
175db66b3aSMiquel Raynal
189671243eSBoris Brezillon #include <linux/ctype.h>
199671243eSBoris Brezillon
get_mtd_by_name(const char * name)209671243eSBoris Brezillon static struct mtd_info *get_mtd_by_name(const char *name)
219671243eSBoris Brezillon {
229671243eSBoris Brezillon struct mtd_info *mtd;
239671243eSBoris Brezillon
249671243eSBoris Brezillon mtd_probe_devices();
259671243eSBoris Brezillon
269671243eSBoris Brezillon mtd = get_mtd_device_nm(name);
279671243eSBoris Brezillon if (IS_ERR_OR_NULL(mtd))
289671243eSBoris Brezillon printf("MTD device %s not found, ret %ld\n", name,
299671243eSBoris Brezillon PTR_ERR(mtd));
309671243eSBoris Brezillon
319671243eSBoris Brezillon return mtd;
329671243eSBoris Brezillon }
339671243eSBoris Brezillon
mtd_len_to_pages(struct mtd_info * mtd,u64 len)345db66b3aSMiquel Raynal static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len)
355db66b3aSMiquel Raynal {
365db66b3aSMiquel Raynal do_div(len, mtd->writesize);
375db66b3aSMiquel Raynal
385db66b3aSMiquel Raynal return len;
395db66b3aSMiquel Raynal }
405db66b3aSMiquel Raynal
mtd_is_aligned_with_min_io_size(struct mtd_info * mtd,u64 size)415db66b3aSMiquel Raynal static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size)
425db66b3aSMiquel Raynal {
435db66b3aSMiquel Raynal return !do_div(size, mtd->writesize);
445db66b3aSMiquel Raynal }
455db66b3aSMiquel Raynal
mtd_is_aligned_with_block_size(struct mtd_info * mtd,u64 size)465db66b3aSMiquel Raynal static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
475db66b3aSMiquel Raynal {
485db66b3aSMiquel Raynal return !do_div(size, mtd->erasesize);
495db66b3aSMiquel Raynal }
505db66b3aSMiquel Raynal
mtd_dump_buf(const u8 * buf,uint len,uint offset)515db66b3aSMiquel Raynal static void mtd_dump_buf(const u8 *buf, uint len, uint offset)
525db66b3aSMiquel Raynal {
535db66b3aSMiquel Raynal int i, j;
545db66b3aSMiquel Raynal
555db66b3aSMiquel Raynal for (i = 0; i < len; ) {
565db66b3aSMiquel Raynal printf("0x%08x:\t", offset + i);
575db66b3aSMiquel Raynal for (j = 0; j < 8; j++)
585db66b3aSMiquel Raynal printf("%02x ", buf[i + j]);
595db66b3aSMiquel Raynal printf(" ");
605db66b3aSMiquel Raynal i += 8;
615db66b3aSMiquel Raynal for (j = 0; j < 8; j++)
625db66b3aSMiquel Raynal printf("%02x ", buf[i + j]);
635db66b3aSMiquel Raynal printf("\n");
645db66b3aSMiquel Raynal i += 8;
655db66b3aSMiquel Raynal }
665db66b3aSMiquel Raynal }
675db66b3aSMiquel Raynal
mtd_dump_device_buf(struct mtd_info * mtd,u64 start_off,const u8 * buf,u64 len,bool woob)685db66b3aSMiquel Raynal static void mtd_dump_device_buf(struct mtd_info *mtd, u64 start_off,
695db66b3aSMiquel Raynal const u8 *buf, u64 len, bool woob)
705db66b3aSMiquel Raynal {
715db66b3aSMiquel Raynal bool has_pages = mtd->type == MTD_NANDFLASH ||
725db66b3aSMiquel Raynal mtd->type == MTD_MLCNANDFLASH;
735db66b3aSMiquel Raynal int npages = mtd_len_to_pages(mtd, len);
745db66b3aSMiquel Raynal uint page;
755db66b3aSMiquel Raynal
765db66b3aSMiquel Raynal if (has_pages) {
775db66b3aSMiquel Raynal for (page = 0; page < npages; page++) {
785db66b3aSMiquel Raynal u64 data_off = page * mtd->writesize;
795db66b3aSMiquel Raynal
805db66b3aSMiquel Raynal printf("\nDump %d data bytes from 0x%08llx:\n",
815db66b3aSMiquel Raynal mtd->writesize, start_off + data_off);
825db66b3aSMiquel Raynal mtd_dump_buf(&buf[data_off],
835db66b3aSMiquel Raynal mtd->writesize, start_off + data_off);
845db66b3aSMiquel Raynal
855db66b3aSMiquel Raynal if (woob) {
865db66b3aSMiquel Raynal u64 oob_off = page * mtd->oobsize;
875db66b3aSMiquel Raynal
885db66b3aSMiquel Raynal printf("Dump %d OOB bytes from page at 0x%08llx:\n",
895db66b3aSMiquel Raynal mtd->oobsize, start_off + data_off);
905db66b3aSMiquel Raynal mtd_dump_buf(&buf[len + oob_off],
915db66b3aSMiquel Raynal mtd->oobsize, 0);
925db66b3aSMiquel Raynal }
935db66b3aSMiquel Raynal }
945db66b3aSMiquel Raynal } else {
955db66b3aSMiquel Raynal printf("\nDump %lld data bytes from 0x%llx:\n",
965db66b3aSMiquel Raynal len, start_off);
975db66b3aSMiquel Raynal mtd_dump_buf(buf, len, start_off);
985db66b3aSMiquel Raynal }
995db66b3aSMiquel Raynal }
1005db66b3aSMiquel Raynal
mtd_show_parts(struct mtd_info * mtd,int level)1015db66b3aSMiquel Raynal static void mtd_show_parts(struct mtd_info *mtd, int level)
1025db66b3aSMiquel Raynal {
1035db66b3aSMiquel Raynal struct mtd_info *part;
1045db66b3aSMiquel Raynal int i;
1055db66b3aSMiquel Raynal
1065db66b3aSMiquel Raynal list_for_each_entry(part, &mtd->partitions, node) {
1075db66b3aSMiquel Raynal for (i = 0; i < level; i++)
1085db66b3aSMiquel Raynal printf("\t");
1095db66b3aSMiquel Raynal printf(" - 0x%012llx-0x%012llx : \"%s\"\n",
1105db66b3aSMiquel Raynal part->offset, part->offset + part->size, part->name);
1115db66b3aSMiquel Raynal
1125db66b3aSMiquel Raynal mtd_show_parts(part, level + 1);
1135db66b3aSMiquel Raynal }
1145db66b3aSMiquel Raynal }
1155db66b3aSMiquel Raynal
mtd_show_device(struct mtd_info * mtd)1165db66b3aSMiquel Raynal static void mtd_show_device(struct mtd_info *mtd)
1175db66b3aSMiquel Raynal {
1185db66b3aSMiquel Raynal /* Device */
1195db66b3aSMiquel Raynal printf("* %s\n", mtd->name);
1205db66b3aSMiquel Raynal #if defined(CONFIG_DM)
1215db66b3aSMiquel Raynal if (mtd->dev) {
1225db66b3aSMiquel Raynal printf(" - device: %s\n", mtd->dev->name);
1235db66b3aSMiquel Raynal printf(" - parent: %s\n", mtd->dev->parent->name);
1245db66b3aSMiquel Raynal printf(" - driver: %s\n", mtd->dev->driver->name);
1255db66b3aSMiquel Raynal }
1265db66b3aSMiquel Raynal #endif
1275db66b3aSMiquel Raynal
1285db66b3aSMiquel Raynal /* MTD device information */
1295db66b3aSMiquel Raynal printf(" - type: ");
1305db66b3aSMiquel Raynal switch (mtd->type) {
1315db66b3aSMiquel Raynal case MTD_RAM:
1325db66b3aSMiquel Raynal printf("RAM\n");
1335db66b3aSMiquel Raynal break;
1345db66b3aSMiquel Raynal case MTD_ROM:
1355db66b3aSMiquel Raynal printf("ROM\n");
1365db66b3aSMiquel Raynal break;
1375db66b3aSMiquel Raynal case MTD_NORFLASH:
1385db66b3aSMiquel Raynal printf("NOR flash\n");
1395db66b3aSMiquel Raynal break;
1405db66b3aSMiquel Raynal case MTD_NANDFLASH:
1415db66b3aSMiquel Raynal printf("NAND flash\n");
1425db66b3aSMiquel Raynal break;
1435db66b3aSMiquel Raynal case MTD_DATAFLASH:
1445db66b3aSMiquel Raynal printf("Data flash\n");
1455db66b3aSMiquel Raynal break;
1465db66b3aSMiquel Raynal case MTD_UBIVOLUME:
1475db66b3aSMiquel Raynal printf("UBI volume\n");
1485db66b3aSMiquel Raynal break;
1495db66b3aSMiquel Raynal case MTD_MLCNANDFLASH:
1505db66b3aSMiquel Raynal printf("MLC NAND flash\n");
1515db66b3aSMiquel Raynal break;
1525db66b3aSMiquel Raynal case MTD_ABSENT:
1535db66b3aSMiquel Raynal default:
1545db66b3aSMiquel Raynal printf("Unknown\n");
1555db66b3aSMiquel Raynal break;
1565db66b3aSMiquel Raynal }
1575db66b3aSMiquel Raynal
1585db66b3aSMiquel Raynal printf(" - block size: 0x%x bytes\n", mtd->erasesize);
1595db66b3aSMiquel Raynal printf(" - min I/O: 0x%x bytes\n", mtd->writesize);
1605db66b3aSMiquel Raynal
1615db66b3aSMiquel Raynal if (mtd->oobsize) {
1625db66b3aSMiquel Raynal printf(" - OOB size: %u bytes\n", mtd->oobsize);
1635db66b3aSMiquel Raynal printf(" - OOB available: %u bytes\n", mtd->oobavail);
1645db66b3aSMiquel Raynal }
1655db66b3aSMiquel Raynal
1665db66b3aSMiquel Raynal if (mtd->ecc_strength) {
1675db66b3aSMiquel Raynal printf(" - ECC strength: %u bits\n", mtd->ecc_strength);
1685db66b3aSMiquel Raynal printf(" - ECC step size: %u bytes\n", mtd->ecc_step_size);
1695db66b3aSMiquel Raynal printf(" - bitflip threshold: %u bits\n",
1705db66b3aSMiquel Raynal mtd->bitflip_threshold);
1715db66b3aSMiquel Raynal }
1725db66b3aSMiquel Raynal
1735db66b3aSMiquel Raynal printf(" - 0x%012llx-0x%012llx : \"%s\"\n",
1745db66b3aSMiquel Raynal mtd->offset, mtd->offset + mtd->size, mtd->name);
1755db66b3aSMiquel Raynal
1765db66b3aSMiquel Raynal /* MTD partitions, if any */
1775db66b3aSMiquel Raynal mtd_show_parts(mtd, 1);
1785db66b3aSMiquel Raynal }
1795db66b3aSMiquel Raynal
1805db66b3aSMiquel Raynal /* Logic taken from fs/ubifs/recovery.c:is_empty() */
mtd_oob_write_is_empty(struct mtd_oob_ops * op)1815db66b3aSMiquel Raynal static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op)
1825db66b3aSMiquel Raynal {
1835db66b3aSMiquel Raynal int i;
1845db66b3aSMiquel Raynal
1855db66b3aSMiquel Raynal for (i = 0; i < op->len; i++)
1865db66b3aSMiquel Raynal if (op->datbuf[i] != 0xff)
1875db66b3aSMiquel Raynal return false;
1885db66b3aSMiquel Raynal
1895db66b3aSMiquel Raynal for (i = 0; i < op->ooblen; i++)
1905db66b3aSMiquel Raynal if (op->oobbuf[i] != 0xff)
1915db66b3aSMiquel Raynal return false;
1925db66b3aSMiquel Raynal
1935db66b3aSMiquel Raynal return true;
1945db66b3aSMiquel Raynal }
1955db66b3aSMiquel Raynal
do_mtd_list(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])1969671243eSBoris Brezillon static int do_mtd_list(cmd_tbl_t *cmdtp, int flag, int argc,
1979671243eSBoris Brezillon char * const argv[])
1985db66b3aSMiquel Raynal {
1995db66b3aSMiquel Raynal struct mtd_info *mtd;
2005db66b3aSMiquel Raynal int dev_nb = 0;
2015db66b3aSMiquel Raynal
2025db66b3aSMiquel Raynal /* Ensure all devices (and their partitions) are probed */
2035db66b3aSMiquel Raynal mtd_probe_devices();
2045db66b3aSMiquel Raynal
2055db66b3aSMiquel Raynal printf("List of MTD devices:\n");
2065db66b3aSMiquel Raynal mtd_for_each_device(mtd) {
2075db66b3aSMiquel Raynal if (!mtd_is_partition(mtd))
2085db66b3aSMiquel Raynal mtd_show_device(mtd);
2095db66b3aSMiquel Raynal
2105db66b3aSMiquel Raynal dev_nb++;
2115db66b3aSMiquel Raynal }
2125db66b3aSMiquel Raynal
2135db66b3aSMiquel Raynal if (!dev_nb) {
2145db66b3aSMiquel Raynal printf("No MTD device found\n");
2155db66b3aSMiquel Raynal return CMD_RET_FAILURE;
2165db66b3aSMiquel Raynal }
2175db66b3aSMiquel Raynal
2185db66b3aSMiquel Raynal return CMD_RET_SUCCESS;
2195db66b3aSMiquel Raynal }
2205db66b3aSMiquel Raynal
mtd_special_write_oob(struct mtd_info * mtd,u64 off,struct mtd_oob_ops * io_op,bool write_empty_pages,bool woob)2215db66b3aSMiquel Raynal static int mtd_special_write_oob(struct mtd_info *mtd, u64 off,
2225db66b3aSMiquel Raynal struct mtd_oob_ops *io_op,
2235db66b3aSMiquel Raynal bool write_empty_pages, bool woob)
2245db66b3aSMiquel Raynal {
2255db66b3aSMiquel Raynal int ret = 0;
2265db66b3aSMiquel Raynal
2275db66b3aSMiquel Raynal /*
2285db66b3aSMiquel Raynal * By default, do not write an empty page.
2295db66b3aSMiquel Raynal * Skip it by simulating a successful write.
2305db66b3aSMiquel Raynal */
2315db66b3aSMiquel Raynal if (!write_empty_pages && mtd_oob_write_is_empty(io_op)) {
2325db66b3aSMiquel Raynal io_op->retlen = mtd->writesize;
2335db66b3aSMiquel Raynal io_op->oobretlen = woob ? mtd->oobsize : 0;
2345db66b3aSMiquel Raynal } else {
2355db66b3aSMiquel Raynal ret = mtd_write_oob(mtd, off, io_op);
2365db66b3aSMiquel Raynal }
2375db66b3aSMiquel Raynal
2385db66b3aSMiquel Raynal return ret;
2395db66b3aSMiquel Raynal }
2405db66b3aSMiquel Raynal
do_mtd_io(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])2419671243eSBoris Brezillon static int do_mtd_io(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
2425db66b3aSMiquel Raynal {
2439671243eSBoris Brezillon bool dump, read, raw, woob, write_empty_pages, has_pages = false;
2449671243eSBoris Brezillon u64 start_off, off, len, remaining, default_len;
2455db66b3aSMiquel Raynal struct mtd_oob_ops io_op = {};
2465db66b3aSMiquel Raynal uint user_addr = 0, npages;
2479671243eSBoris Brezillon const char *cmd = argv[0];
2489671243eSBoris Brezillon struct mtd_info *mtd;
2495db66b3aSMiquel Raynal u32 oob_len;
2505db66b3aSMiquel Raynal u8 *buf;
2515db66b3aSMiquel Raynal int ret;
2525db66b3aSMiquel Raynal
2539671243eSBoris Brezillon if (argc < 2)
2549671243eSBoris Brezillon return CMD_RET_USAGE;
2559671243eSBoris Brezillon
2569671243eSBoris Brezillon mtd = get_mtd_by_name(argv[1]);
2579671243eSBoris Brezillon if (IS_ERR_OR_NULL(mtd))
2589671243eSBoris Brezillon return CMD_RET_FAILURE;
2599671243eSBoris Brezillon
2609671243eSBoris Brezillon if (mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH)
2619671243eSBoris Brezillon has_pages = true;
2629671243eSBoris Brezillon
2635db66b3aSMiquel Raynal dump = !strncmp(cmd, "dump", 4);
2645db66b3aSMiquel Raynal read = dump || !strncmp(cmd, "read", 4);
2655db66b3aSMiquel Raynal raw = strstr(cmd, ".raw");
2665db66b3aSMiquel Raynal woob = strstr(cmd, ".oob");
2675db66b3aSMiquel Raynal write_empty_pages = !has_pages || strstr(cmd, ".dontskipff");
2685db66b3aSMiquel Raynal
2699671243eSBoris Brezillon argc -= 2;
2709671243eSBoris Brezillon argv += 2;
2719671243eSBoris Brezillon
2725db66b3aSMiquel Raynal if (!dump) {
2739671243eSBoris Brezillon if (!argc) {
2749671243eSBoris Brezillon ret = CMD_RET_USAGE;
2759671243eSBoris Brezillon goto out_put_mtd;
2769671243eSBoris Brezillon }
2775db66b3aSMiquel Raynal
2785db66b3aSMiquel Raynal user_addr = simple_strtoul(argv[0], NULL, 16);
2795db66b3aSMiquel Raynal argc--;
2805db66b3aSMiquel Raynal argv++;
2815db66b3aSMiquel Raynal }
2825db66b3aSMiquel Raynal
2835db66b3aSMiquel Raynal start_off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0;
2845db66b3aSMiquel Raynal if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) {
2855db66b3aSMiquel Raynal printf("Offset not aligned with a page (0x%x)\n",
2865db66b3aSMiquel Raynal mtd->writesize);
2879671243eSBoris Brezillon ret = CMD_RET_FAILURE;
2889671243eSBoris Brezillon goto out_put_mtd;
2895db66b3aSMiquel Raynal }
2905db66b3aSMiquel Raynal
2915db66b3aSMiquel Raynal default_len = dump ? mtd->writesize : mtd->size;
2929671243eSBoris Brezillon len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : default_len;
2935db66b3aSMiquel Raynal if (!mtd_is_aligned_with_min_io_size(mtd, len)) {
2945db66b3aSMiquel Raynal len = round_up(len, mtd->writesize);
2955db66b3aSMiquel Raynal printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n",
2965db66b3aSMiquel Raynal mtd->writesize, len);
2975db66b3aSMiquel Raynal }
2985db66b3aSMiquel Raynal
2995db66b3aSMiquel Raynal remaining = len;
3005db66b3aSMiquel Raynal npages = mtd_len_to_pages(mtd, len);
3015db66b3aSMiquel Raynal oob_len = woob ? npages * mtd->oobsize : 0;
3025db66b3aSMiquel Raynal
3035db66b3aSMiquel Raynal if (dump)
3045db66b3aSMiquel Raynal buf = kmalloc(len + oob_len, GFP_KERNEL);
3055db66b3aSMiquel Raynal else
3065db66b3aSMiquel Raynal buf = map_sysmem(user_addr, 0);
3075db66b3aSMiquel Raynal
3085db66b3aSMiquel Raynal if (!buf) {
3095db66b3aSMiquel Raynal printf("Could not map/allocate the user buffer\n");
3109671243eSBoris Brezillon ret = CMD_RET_FAILURE;
3119671243eSBoris Brezillon goto out_put_mtd;
3125db66b3aSMiquel Raynal }
3135db66b3aSMiquel Raynal
3145db66b3aSMiquel Raynal if (has_pages)
3155db66b3aSMiquel Raynal printf("%s %lld byte(s) (%d page(s)) at offset 0x%08llx%s%s%s\n",
3165db66b3aSMiquel Raynal read ? "Reading" : "Writing", len, npages, start_off,
3175db66b3aSMiquel Raynal raw ? " [raw]" : "", woob ? " [oob]" : "",
3185db66b3aSMiquel Raynal !read && write_empty_pages ? " [dontskipff]" : "");
3195db66b3aSMiquel Raynal else
3205db66b3aSMiquel Raynal printf("%s %lld byte(s) at offset 0x%08llx\n",
3215db66b3aSMiquel Raynal read ? "Reading" : "Writing", len, start_off);
3225db66b3aSMiquel Raynal
3235db66b3aSMiquel Raynal io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB;
3245db66b3aSMiquel Raynal io_op.len = has_pages ? mtd->writesize : len;
3255db66b3aSMiquel Raynal io_op.ooblen = woob ? mtd->oobsize : 0;
3265db66b3aSMiquel Raynal io_op.datbuf = buf;
3275db66b3aSMiquel Raynal io_op.oobbuf = woob ? &buf[len] : NULL;
3285db66b3aSMiquel Raynal
3295db66b3aSMiquel Raynal /* Search for the first good block after the given offset */
3305db66b3aSMiquel Raynal off = start_off;
3315db66b3aSMiquel Raynal while (mtd_block_isbad(mtd, off))
3325db66b3aSMiquel Raynal off += mtd->erasesize;
3335db66b3aSMiquel Raynal
3345db66b3aSMiquel Raynal /* Loop over the pages to do the actual read/write */
3355db66b3aSMiquel Raynal while (remaining) {
3365db66b3aSMiquel Raynal /* Skip the block if it is bad */
3375db66b3aSMiquel Raynal if (mtd_is_aligned_with_block_size(mtd, off) &&
3385db66b3aSMiquel Raynal mtd_block_isbad(mtd, off)) {
3395db66b3aSMiquel Raynal off += mtd->erasesize;
3405db66b3aSMiquel Raynal continue;
3415db66b3aSMiquel Raynal }
3425db66b3aSMiquel Raynal
3435db66b3aSMiquel Raynal if (read)
3445db66b3aSMiquel Raynal ret = mtd_read_oob(mtd, off, &io_op);
3455db66b3aSMiquel Raynal else
3465db66b3aSMiquel Raynal ret = mtd_special_write_oob(mtd, off, &io_op,
3479671243eSBoris Brezillon write_empty_pages, woob);
3485db66b3aSMiquel Raynal
3495db66b3aSMiquel Raynal if (ret) {
3505db66b3aSMiquel Raynal printf("Failure while %s at offset 0x%llx\n",
3515db66b3aSMiquel Raynal read ? "reading" : "writing", off);
3529671243eSBoris Brezillon break;
3535db66b3aSMiquel Raynal }
3545db66b3aSMiquel Raynal
3555db66b3aSMiquel Raynal off += io_op.retlen;
3565db66b3aSMiquel Raynal remaining -= io_op.retlen;
3575db66b3aSMiquel Raynal io_op.datbuf += io_op.retlen;
3585db66b3aSMiquel Raynal io_op.oobbuf += io_op.oobretlen;
3595db66b3aSMiquel Raynal }
3605db66b3aSMiquel Raynal
3615db66b3aSMiquel Raynal if (!ret && dump)
3625db66b3aSMiquel Raynal mtd_dump_device_buf(mtd, start_off, buf, len, woob);
3635db66b3aSMiquel Raynal
3645db66b3aSMiquel Raynal if (dump)
3655db66b3aSMiquel Raynal kfree(buf);
3665db66b3aSMiquel Raynal else
3675db66b3aSMiquel Raynal unmap_sysmem(buf);
3685db66b3aSMiquel Raynal
3695db66b3aSMiquel Raynal if (ret) {
3705db66b3aSMiquel Raynal printf("%s on %s failed with error %d\n",
3715db66b3aSMiquel Raynal read ? "Read" : "Write", mtd->name, ret);
3729671243eSBoris Brezillon ret = CMD_RET_FAILURE;
3739671243eSBoris Brezillon } else {
3749671243eSBoris Brezillon ret = CMD_RET_SUCCESS;
3755db66b3aSMiquel Raynal }
3765db66b3aSMiquel Raynal
3779671243eSBoris Brezillon out_put_mtd:
3789671243eSBoris Brezillon put_mtd_device(mtd);
3799671243eSBoris Brezillon
3809671243eSBoris Brezillon return ret;
3819671243eSBoris Brezillon }
3829671243eSBoris Brezillon
do_mtd_erase(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])3839671243eSBoris Brezillon static int do_mtd_erase(cmd_tbl_t *cmdtp, int flag, int argc,
3849671243eSBoris Brezillon char * const argv[])
3859671243eSBoris Brezillon {
3865db66b3aSMiquel Raynal struct erase_info erase_op = {};
3879671243eSBoris Brezillon struct mtd_info *mtd;
3885db66b3aSMiquel Raynal u64 off, len;
3899671243eSBoris Brezillon bool scrub;
3905db66b3aSMiquel Raynal int ret;
3915db66b3aSMiquel Raynal
3929671243eSBoris Brezillon if (argc < 2)
3939671243eSBoris Brezillon return CMD_RET_USAGE;
3949671243eSBoris Brezillon
3959671243eSBoris Brezillon mtd = get_mtd_by_name(argv[1]);
3969671243eSBoris Brezillon if (IS_ERR_OR_NULL(mtd))
3979671243eSBoris Brezillon return CMD_RET_FAILURE;
3989671243eSBoris Brezillon
3999671243eSBoris Brezillon scrub = strstr(argv[0], ".dontskipbad");
4009671243eSBoris Brezillon
4019671243eSBoris Brezillon argc -= 2;
4029671243eSBoris Brezillon argv += 2;
4039671243eSBoris Brezillon
4045db66b3aSMiquel Raynal off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0;
4055db66b3aSMiquel Raynal len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : mtd->size;
4065db66b3aSMiquel Raynal
4075db66b3aSMiquel Raynal if (!mtd_is_aligned_with_block_size(mtd, off)) {
4085db66b3aSMiquel Raynal printf("Offset not aligned with a block (0x%x)\n",
4095db66b3aSMiquel Raynal mtd->erasesize);
4109671243eSBoris Brezillon ret = CMD_RET_FAILURE;
4119671243eSBoris Brezillon goto out_put_mtd;
4125db66b3aSMiquel Raynal }
4135db66b3aSMiquel Raynal
4145db66b3aSMiquel Raynal if (!mtd_is_aligned_with_block_size(mtd, len)) {
4155db66b3aSMiquel Raynal printf("Size not a multiple of a block (0x%x)\n",
4165db66b3aSMiquel Raynal mtd->erasesize);
4179671243eSBoris Brezillon ret = CMD_RET_FAILURE;
4189671243eSBoris Brezillon goto out_put_mtd;
4195db66b3aSMiquel Raynal }
4205db66b3aSMiquel Raynal
4215db66b3aSMiquel Raynal printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n",
4225db66b3aSMiquel Raynal off, off + len - 1, mtd_div_by_eb(len, mtd));
4235db66b3aSMiquel Raynal
4245db66b3aSMiquel Raynal erase_op.mtd = mtd;
4255db66b3aSMiquel Raynal erase_op.addr = off;
4265db66b3aSMiquel Raynal erase_op.len = len;
4275db66b3aSMiquel Raynal erase_op.scrub = scrub;
4285db66b3aSMiquel Raynal
4295db66b3aSMiquel Raynal while (erase_op.len) {
4305db66b3aSMiquel Raynal ret = mtd_erase(mtd, &erase_op);
4315db66b3aSMiquel Raynal
4325db66b3aSMiquel Raynal /* Abort if its not a bad block error */
4335db66b3aSMiquel Raynal if (ret != -EIO)
4345db66b3aSMiquel Raynal break;
4355db66b3aSMiquel Raynal
4369671243eSBoris Brezillon printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr);
4375db66b3aSMiquel Raynal
4385db66b3aSMiquel Raynal /* Skip bad block and continue behind it */
4395db66b3aSMiquel Raynal erase_op.len -= erase_op.fail_addr - erase_op.addr;
4405db66b3aSMiquel Raynal erase_op.len -= mtd->erasesize;
4415db66b3aSMiquel Raynal erase_op.addr = erase_op.fail_addr + mtd->erasesize;
4425db66b3aSMiquel Raynal }
4435db66b3aSMiquel Raynal
4445db66b3aSMiquel Raynal if (ret && ret != -EIO)
4459671243eSBoris Brezillon ret = CMD_RET_FAILURE;
4469671243eSBoris Brezillon else
4479671243eSBoris Brezillon ret = CMD_RET_SUCCESS;
4489671243eSBoris Brezillon
4499671243eSBoris Brezillon out_put_mtd:
4509671243eSBoris Brezillon put_mtd_device(mtd);
4519671243eSBoris Brezillon
4529671243eSBoris Brezillon return ret;
4539671243eSBoris Brezillon }
4549671243eSBoris Brezillon
do_mtd_bad(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])4559671243eSBoris Brezillon static int do_mtd_bad(cmd_tbl_t *cmdtp, int flag, int argc,
4569671243eSBoris Brezillon char * const argv[])
4579671243eSBoris Brezillon {
4589671243eSBoris Brezillon struct mtd_info *mtd;
4595db66b3aSMiquel Raynal loff_t off;
4605db66b3aSMiquel Raynal
4619671243eSBoris Brezillon if (argc < 2)
4629671243eSBoris Brezillon return CMD_RET_USAGE;
4639671243eSBoris Brezillon
4649671243eSBoris Brezillon mtd = get_mtd_by_name(argv[1]);
4659671243eSBoris Brezillon if (IS_ERR_OR_NULL(mtd))
4669671243eSBoris Brezillon return CMD_RET_FAILURE;
4679671243eSBoris Brezillon
4685db66b3aSMiquel Raynal if (!mtd_can_have_bb(mtd)) {
4695db66b3aSMiquel Raynal printf("Only NAND-based devices can have bad blocks\n");
4709671243eSBoris Brezillon goto out_put_mtd;
4715db66b3aSMiquel Raynal }
4725db66b3aSMiquel Raynal
4735db66b3aSMiquel Raynal printf("MTD device %s bad blocks list:\n", mtd->name);
4749671243eSBoris Brezillon for (off = 0; off < mtd->size; off += mtd->erasesize) {
4755db66b3aSMiquel Raynal if (mtd_block_isbad(mtd, off))
4765db66b3aSMiquel Raynal printf("\t0x%08llx\n", off);
4775db66b3aSMiquel Raynal }
4785db66b3aSMiquel Raynal
4799671243eSBoris Brezillon out_put_mtd:
4809671243eSBoris Brezillon put_mtd_device(mtd);
4819671243eSBoris Brezillon
4825db66b3aSMiquel Raynal return CMD_RET_SUCCESS;
4835db66b3aSMiquel Raynal }
4845db66b3aSMiquel Raynal
4859671243eSBoris Brezillon #ifdef CONFIG_AUTO_COMPLETE
mtd_name_complete(int argc,char * const argv[],char last_char,int maxv,char * cmdv[])4869671243eSBoris Brezillon static int mtd_name_complete(int argc, char * const argv[], char last_char,
4879671243eSBoris Brezillon int maxv, char *cmdv[])
4889671243eSBoris Brezillon {
4899671243eSBoris Brezillon int len = 0, n_found = 0;
4909671243eSBoris Brezillon struct mtd_info *mtd;
4919671243eSBoris Brezillon
4929671243eSBoris Brezillon argc--;
4939671243eSBoris Brezillon argv++;
4949671243eSBoris Brezillon
4959671243eSBoris Brezillon if (argc > 1 ||
4969671243eSBoris Brezillon (argc == 1 && (last_char == '\0' || isblank(last_char))))
4979671243eSBoris Brezillon return 0;
4989671243eSBoris Brezillon
4999671243eSBoris Brezillon if (argc)
5009671243eSBoris Brezillon len = strlen(argv[0]);
5019671243eSBoris Brezillon
5029671243eSBoris Brezillon mtd_for_each_device(mtd) {
5039671243eSBoris Brezillon if (argc &&
5049671243eSBoris Brezillon (len > strlen(mtd->name) ||
5059671243eSBoris Brezillon strncmp(argv[0], mtd->name, len)))
5069671243eSBoris Brezillon continue;
5079671243eSBoris Brezillon
5089671243eSBoris Brezillon if (n_found >= maxv - 2) {
5099671243eSBoris Brezillon cmdv[n_found++] = "...";
5109671243eSBoris Brezillon break;
5119671243eSBoris Brezillon }
5129671243eSBoris Brezillon
5139671243eSBoris Brezillon cmdv[n_found++] = mtd->name;
5149671243eSBoris Brezillon }
5159671243eSBoris Brezillon
5169671243eSBoris Brezillon cmdv[n_found] = NULL;
5179671243eSBoris Brezillon
5189671243eSBoris Brezillon return n_found;
5199671243eSBoris Brezillon }
5209671243eSBoris Brezillon #endif /* CONFIG_AUTO_COMPLETE */
5219671243eSBoris Brezillon
5225db66b3aSMiquel Raynal #ifdef CONFIG_SYS_LONGHELP
523*a645831cSQuentin Schulz static char mtd_help_text[] =
5245db66b3aSMiquel Raynal "- generic operations on memory technology devices\n\n"
5255db66b3aSMiquel Raynal "mtd list\n"
5265db66b3aSMiquel Raynal "mtd read[.raw][.oob] <name> <addr> [<off> [<size>]]\n"
5275db66b3aSMiquel Raynal "mtd dump[.raw][.oob] <name> [<off> [<size>]]\n"
5285db66b3aSMiquel Raynal "mtd write[.raw][.oob][.dontskipff] <name> <addr> [<off> [<size>]]\n"
5295db66b3aSMiquel Raynal "mtd erase[.dontskipbad] <name> [<off> [<size>]]\n"
5305db66b3aSMiquel Raynal "\n"
5315db66b3aSMiquel Raynal "Specific functions:\n"
5325db66b3aSMiquel Raynal "mtd bad <name>\n"
5335db66b3aSMiquel Raynal "\n"
5345db66b3aSMiquel Raynal "With:\n"
5355db66b3aSMiquel Raynal "\t<name>: NAND partition/chip name\n"
5365db66b3aSMiquel Raynal "\t<addr>: user address from/to which data will be retrieved/stored\n"
5375db66b3aSMiquel Raynal "\t<off>: offset in <name> in bytes (default: start of the part)\n"
5385db66b3aSMiquel Raynal "\t\t* must be block-aligned for erase\n"
5395db66b3aSMiquel Raynal "\t\t* must be page-aligned otherwise\n"
5405db66b3aSMiquel Raynal "\t<size>: length of the operation in bytes (default: the entire device)\n"
5415db66b3aSMiquel Raynal "\t\t* must be a multiple of a block for erase\n"
5425db66b3aSMiquel Raynal "\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n"
5435db66b3aSMiquel Raynal "\n"
544*a645831cSQuentin Schulz "The .dontskipff option forces writing empty pages, don't use it if unsure.\n";
5455db66b3aSMiquel Raynal #endif
5465db66b3aSMiquel Raynal
5479671243eSBoris Brezillon U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
5489671243eSBoris Brezillon U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mtd_list),
5499671243eSBoris Brezillon U_BOOT_SUBCMD_MKENT_COMPLETE(read, 5, 0, do_mtd_io,
5509671243eSBoris Brezillon mtd_name_complete),
5519671243eSBoris Brezillon U_BOOT_SUBCMD_MKENT_COMPLETE(write, 5, 0, do_mtd_io,
5529671243eSBoris Brezillon mtd_name_complete),
5539671243eSBoris Brezillon U_BOOT_SUBCMD_MKENT_COMPLETE(dump, 4, 0, do_mtd_io,
5549671243eSBoris Brezillon mtd_name_complete),
5559671243eSBoris Brezillon U_BOOT_SUBCMD_MKENT_COMPLETE(erase, 4, 0, do_mtd_erase,
5569671243eSBoris Brezillon mtd_name_complete),
5579671243eSBoris Brezillon U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
5589671243eSBoris Brezillon mtd_name_complete));
559