1f87151b3SMiquel Raynal // SPDX-License-Identifier: GPL-2.0+ 2f87151b3SMiquel Raynal /* 3f87151b3SMiquel Raynal * mtd.c 4f87151b3SMiquel Raynal * 5f87151b3SMiquel Raynal * Generic command to handle basic operations on any memory device. 6f87151b3SMiquel Raynal * 7f87151b3SMiquel Raynal * Copyright: Bootlin, 2018 8f87151b3SMiquel Raynal * Author: Miquèl Raynal <miquel.raynal@bootlin.com> 9f87151b3SMiquel Raynal */ 10f87151b3SMiquel Raynal 11f87151b3SMiquel Raynal #include <command.h> 12f87151b3SMiquel Raynal #include <common.h> 13f87151b3SMiquel Raynal #include <console.h> 14f87151b3SMiquel Raynal #include <malloc.h> 15f87151b3SMiquel Raynal #include <mapmem.h> 16f87151b3SMiquel Raynal #include <mtd.h> 17f87151b3SMiquel Raynal 18*b3d8fba6SBoris Brezillon #include <linux/ctype.h> 19*b3d8fba6SBoris Brezillon 20*b3d8fba6SBoris Brezillon static struct mtd_info *get_mtd_by_name(const char *name) 21*b3d8fba6SBoris Brezillon { 22*b3d8fba6SBoris Brezillon struct mtd_info *mtd; 23*b3d8fba6SBoris Brezillon 24*b3d8fba6SBoris Brezillon mtd_probe_devices(); 25*b3d8fba6SBoris Brezillon 26*b3d8fba6SBoris Brezillon mtd = get_mtd_device_nm(name); 27*b3d8fba6SBoris Brezillon if (IS_ERR_OR_NULL(mtd)) 28*b3d8fba6SBoris Brezillon printf("MTD device %s not found, ret %ld\n", name, 29*b3d8fba6SBoris Brezillon PTR_ERR(mtd)); 30*b3d8fba6SBoris Brezillon 31*b3d8fba6SBoris Brezillon return mtd; 32*b3d8fba6SBoris Brezillon } 33*b3d8fba6SBoris Brezillon 34f87151b3SMiquel Raynal static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len) 35f87151b3SMiquel Raynal { 36f87151b3SMiquel Raynal do_div(len, mtd->writesize); 37f87151b3SMiquel Raynal 38f87151b3SMiquel Raynal return len; 39f87151b3SMiquel Raynal } 40f87151b3SMiquel Raynal 41f87151b3SMiquel Raynal static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size) 42f87151b3SMiquel Raynal { 43f87151b3SMiquel Raynal return !do_div(size, mtd->writesize); 44f87151b3SMiquel Raynal } 45f87151b3SMiquel Raynal 46f87151b3SMiquel Raynal static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size) 47f87151b3SMiquel Raynal { 48f87151b3SMiquel Raynal return !do_div(size, mtd->erasesize); 49f87151b3SMiquel Raynal } 50f87151b3SMiquel Raynal 51f87151b3SMiquel Raynal static void mtd_dump_buf(const u8 *buf, uint len, uint offset) 52f87151b3SMiquel Raynal { 53f87151b3SMiquel Raynal int i, j; 54f87151b3SMiquel Raynal 55f87151b3SMiquel Raynal for (i = 0; i < len; ) { 56f87151b3SMiquel Raynal printf("0x%08x:\t", offset + i); 57f87151b3SMiquel Raynal for (j = 0; j < 8; j++) 58f87151b3SMiquel Raynal printf("%02x ", buf[i + j]); 59f87151b3SMiquel Raynal printf(" "); 60f87151b3SMiquel Raynal i += 8; 61f87151b3SMiquel Raynal for (j = 0; j < 8; j++) 62f87151b3SMiquel Raynal printf("%02x ", buf[i + j]); 63f87151b3SMiquel Raynal printf("\n"); 64f87151b3SMiquel Raynal i += 8; 65f87151b3SMiquel Raynal } 66f87151b3SMiquel Raynal } 67f87151b3SMiquel Raynal 68f87151b3SMiquel Raynal static void mtd_dump_device_buf(struct mtd_info *mtd, u64 start_off, 69f87151b3SMiquel Raynal const u8 *buf, u64 len, bool woob) 70f87151b3SMiquel Raynal { 71f87151b3SMiquel Raynal bool has_pages = mtd->type == MTD_NANDFLASH || 72f87151b3SMiquel Raynal mtd->type == MTD_MLCNANDFLASH; 73f87151b3SMiquel Raynal int npages = mtd_len_to_pages(mtd, len); 74f87151b3SMiquel Raynal uint page; 75f87151b3SMiquel Raynal 76f87151b3SMiquel Raynal if (has_pages) { 77f87151b3SMiquel Raynal for (page = 0; page < npages; page++) { 78f87151b3SMiquel Raynal u64 data_off = page * mtd->writesize; 79f87151b3SMiquel Raynal 80f87151b3SMiquel Raynal printf("\nDump %d data bytes from 0x%08llx:\n", 81f87151b3SMiquel Raynal mtd->writesize, start_off + data_off); 82f87151b3SMiquel Raynal mtd_dump_buf(&buf[data_off], 83f87151b3SMiquel Raynal mtd->writesize, start_off + data_off); 84f87151b3SMiquel Raynal 85f87151b3SMiquel Raynal if (woob) { 86f87151b3SMiquel Raynal u64 oob_off = page * mtd->oobsize; 87f87151b3SMiquel Raynal 88f87151b3SMiquel Raynal printf("Dump %d OOB bytes from page at 0x%08llx:\n", 89f87151b3SMiquel Raynal mtd->oobsize, start_off + data_off); 90f87151b3SMiquel Raynal mtd_dump_buf(&buf[len + oob_off], 91f87151b3SMiquel Raynal mtd->oobsize, 0); 92f87151b3SMiquel Raynal } 93f87151b3SMiquel Raynal } 94f87151b3SMiquel Raynal } else { 95f87151b3SMiquel Raynal printf("\nDump %lld data bytes from 0x%llx:\n", 96f87151b3SMiquel Raynal len, start_off); 97f87151b3SMiquel Raynal mtd_dump_buf(buf, len, start_off); 98f87151b3SMiquel Raynal } 99f87151b3SMiquel Raynal } 100f87151b3SMiquel Raynal 101f87151b3SMiquel Raynal static void mtd_show_parts(struct mtd_info *mtd, int level) 102f87151b3SMiquel Raynal { 103f87151b3SMiquel Raynal struct mtd_info *part; 104f87151b3SMiquel Raynal int i; 105f87151b3SMiquel Raynal 106f87151b3SMiquel Raynal list_for_each_entry(part, &mtd->partitions, node) { 107f87151b3SMiquel Raynal for (i = 0; i < level; i++) 108f87151b3SMiquel Raynal printf("\t"); 109f87151b3SMiquel Raynal printf(" - 0x%012llx-0x%012llx : \"%s\"\n", 110f87151b3SMiquel Raynal part->offset, part->offset + part->size, part->name); 111f87151b3SMiquel Raynal 112f87151b3SMiquel Raynal mtd_show_parts(part, level + 1); 113f87151b3SMiquel Raynal } 114f87151b3SMiquel Raynal } 115f87151b3SMiquel Raynal 116f87151b3SMiquel Raynal static void mtd_show_device(struct mtd_info *mtd) 117f87151b3SMiquel Raynal { 118f87151b3SMiquel Raynal /* Device */ 119f87151b3SMiquel Raynal printf("* %s\n", mtd->name); 120f87151b3SMiquel Raynal #if defined(CONFIG_DM) 121f87151b3SMiquel Raynal if (mtd->dev) { 122f87151b3SMiquel Raynal printf(" - device: %s\n", mtd->dev->name); 123f87151b3SMiquel Raynal printf(" - parent: %s\n", mtd->dev->parent->name); 124f87151b3SMiquel Raynal printf(" - driver: %s\n", mtd->dev->driver->name); 125f87151b3SMiquel Raynal } 126f87151b3SMiquel Raynal #endif 127f87151b3SMiquel Raynal 128f87151b3SMiquel Raynal /* MTD device information */ 129f87151b3SMiquel Raynal printf(" - type: "); 130f87151b3SMiquel Raynal switch (mtd->type) { 131f87151b3SMiquel Raynal case MTD_RAM: 132f87151b3SMiquel Raynal printf("RAM\n"); 133f87151b3SMiquel Raynal break; 134f87151b3SMiquel Raynal case MTD_ROM: 135f87151b3SMiquel Raynal printf("ROM\n"); 136f87151b3SMiquel Raynal break; 137f87151b3SMiquel Raynal case MTD_NORFLASH: 138f87151b3SMiquel Raynal printf("NOR flash\n"); 139f87151b3SMiquel Raynal break; 140f87151b3SMiquel Raynal case MTD_NANDFLASH: 141f87151b3SMiquel Raynal printf("NAND flash\n"); 142f87151b3SMiquel Raynal break; 143f87151b3SMiquel Raynal case MTD_DATAFLASH: 144f87151b3SMiquel Raynal printf("Data flash\n"); 145f87151b3SMiquel Raynal break; 146f87151b3SMiquel Raynal case MTD_UBIVOLUME: 147f87151b3SMiquel Raynal printf("UBI volume\n"); 148f87151b3SMiquel Raynal break; 149f87151b3SMiquel Raynal case MTD_MLCNANDFLASH: 150f87151b3SMiquel Raynal printf("MLC NAND flash\n"); 151f87151b3SMiquel Raynal break; 152f87151b3SMiquel Raynal case MTD_ABSENT: 153f87151b3SMiquel Raynal default: 154f87151b3SMiquel Raynal printf("Unknown\n"); 155f87151b3SMiquel Raynal break; 156f87151b3SMiquel Raynal } 157f87151b3SMiquel Raynal 158f87151b3SMiquel Raynal printf(" - block size: 0x%x bytes\n", mtd->erasesize); 159f87151b3SMiquel Raynal printf(" - min I/O: 0x%x bytes\n", mtd->writesize); 160f87151b3SMiquel Raynal 161f87151b3SMiquel Raynal if (mtd->oobsize) { 162f87151b3SMiquel Raynal printf(" - OOB size: %u bytes\n", mtd->oobsize); 163f87151b3SMiquel Raynal printf(" - OOB available: %u bytes\n", mtd->oobavail); 164f87151b3SMiquel Raynal } 165f87151b3SMiquel Raynal 166f87151b3SMiquel Raynal if (mtd->ecc_strength) { 167f87151b3SMiquel Raynal printf(" - ECC strength: %u bits\n", mtd->ecc_strength); 168f87151b3SMiquel Raynal printf(" - ECC step size: %u bytes\n", mtd->ecc_step_size); 169f87151b3SMiquel Raynal printf(" - bitflip threshold: %u bits\n", 170f87151b3SMiquel Raynal mtd->bitflip_threshold); 171f87151b3SMiquel Raynal } 172f87151b3SMiquel Raynal 173f87151b3SMiquel Raynal printf(" - 0x%012llx-0x%012llx : \"%s\"\n", 174f87151b3SMiquel Raynal mtd->offset, mtd->offset + mtd->size, mtd->name); 175f87151b3SMiquel Raynal 176f87151b3SMiquel Raynal /* MTD partitions, if any */ 177f87151b3SMiquel Raynal mtd_show_parts(mtd, 1); 178f87151b3SMiquel Raynal } 179f87151b3SMiquel Raynal 180f87151b3SMiquel Raynal /* Logic taken from fs/ubifs/recovery.c:is_empty() */ 181f87151b3SMiquel Raynal static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op) 182f87151b3SMiquel Raynal { 183f87151b3SMiquel Raynal int i; 184f87151b3SMiquel Raynal 185f87151b3SMiquel Raynal for (i = 0; i < op->len; i++) 186f87151b3SMiquel Raynal if (op->datbuf[i] != 0xff) 187f87151b3SMiquel Raynal return false; 188f87151b3SMiquel Raynal 189f87151b3SMiquel Raynal for (i = 0; i < op->ooblen; i++) 190f87151b3SMiquel Raynal if (op->oobbuf[i] != 0xff) 191f87151b3SMiquel Raynal return false; 192f87151b3SMiquel Raynal 193f87151b3SMiquel Raynal return true; 194f87151b3SMiquel Raynal } 195f87151b3SMiquel Raynal 196*b3d8fba6SBoris Brezillon static int do_mtd_list(cmd_tbl_t *cmdtp, int flag, int argc, 197*b3d8fba6SBoris Brezillon char * const argv[]) 198f87151b3SMiquel Raynal { 199f87151b3SMiquel Raynal struct mtd_info *mtd; 200f87151b3SMiquel Raynal int dev_nb = 0; 201f87151b3SMiquel Raynal 202f87151b3SMiquel Raynal /* Ensure all devices (and their partitions) are probed */ 203f87151b3SMiquel Raynal mtd_probe_devices(); 204f87151b3SMiquel Raynal 205f87151b3SMiquel Raynal printf("List of MTD devices:\n"); 206f87151b3SMiquel Raynal mtd_for_each_device(mtd) { 207f87151b3SMiquel Raynal if (!mtd_is_partition(mtd)) 208f87151b3SMiquel Raynal mtd_show_device(mtd); 209f87151b3SMiquel Raynal 210f87151b3SMiquel Raynal dev_nb++; 211f87151b3SMiquel Raynal } 212f87151b3SMiquel Raynal 213f87151b3SMiquel Raynal if (!dev_nb) { 214f87151b3SMiquel Raynal printf("No MTD device found\n"); 215f87151b3SMiquel Raynal return CMD_RET_FAILURE; 216f87151b3SMiquel Raynal } 217f87151b3SMiquel Raynal 218f87151b3SMiquel Raynal return CMD_RET_SUCCESS; 219f87151b3SMiquel Raynal } 220f87151b3SMiquel Raynal 221f87151b3SMiquel Raynal static int mtd_special_write_oob(struct mtd_info *mtd, u64 off, 222f87151b3SMiquel Raynal struct mtd_oob_ops *io_op, 223f87151b3SMiquel Raynal bool write_empty_pages, bool woob) 224f87151b3SMiquel Raynal { 225f87151b3SMiquel Raynal int ret = 0; 226f87151b3SMiquel Raynal 227f87151b3SMiquel Raynal /* 228f87151b3SMiquel Raynal * By default, do not write an empty page. 229f87151b3SMiquel Raynal * Skip it by simulating a successful write. 230f87151b3SMiquel Raynal */ 231f87151b3SMiquel Raynal if (!write_empty_pages && mtd_oob_write_is_empty(io_op)) { 232f87151b3SMiquel Raynal io_op->retlen = mtd->writesize; 233f87151b3SMiquel Raynal io_op->oobretlen = woob ? mtd->oobsize : 0; 234f87151b3SMiquel Raynal } else { 235f87151b3SMiquel Raynal ret = mtd_write_oob(mtd, off, io_op); 236f87151b3SMiquel Raynal } 237f87151b3SMiquel Raynal 238f87151b3SMiquel Raynal return ret; 239f87151b3SMiquel Raynal } 240f87151b3SMiquel Raynal 241*b3d8fba6SBoris Brezillon static int do_mtd_io(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 242f87151b3SMiquel Raynal { 243*b3d8fba6SBoris Brezillon bool dump, read, raw, woob, write_empty_pages, has_pages = false; 244*b3d8fba6SBoris Brezillon u64 start_off, off, len, remaining, default_len; 245f87151b3SMiquel Raynal struct mtd_oob_ops io_op = {}; 246f87151b3SMiquel Raynal uint user_addr = 0, npages; 247*b3d8fba6SBoris Brezillon const char *cmd = argv[0]; 248*b3d8fba6SBoris Brezillon struct mtd_info *mtd; 249f87151b3SMiquel Raynal u32 oob_len; 250f87151b3SMiquel Raynal u8 *buf; 251f87151b3SMiquel Raynal int ret; 252f87151b3SMiquel Raynal 253*b3d8fba6SBoris Brezillon if (argc < 2) 254*b3d8fba6SBoris Brezillon return CMD_RET_USAGE; 255*b3d8fba6SBoris Brezillon 256*b3d8fba6SBoris Brezillon mtd = get_mtd_by_name(argv[1]); 257*b3d8fba6SBoris Brezillon if (IS_ERR_OR_NULL(mtd)) 258*b3d8fba6SBoris Brezillon return CMD_RET_FAILURE; 259*b3d8fba6SBoris Brezillon 260*b3d8fba6SBoris Brezillon if (mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH) 261*b3d8fba6SBoris Brezillon has_pages = true; 262*b3d8fba6SBoris Brezillon 263f87151b3SMiquel Raynal dump = !strncmp(cmd, "dump", 4); 264f87151b3SMiquel Raynal read = dump || !strncmp(cmd, "read", 4); 265f87151b3SMiquel Raynal raw = strstr(cmd, ".raw"); 266f87151b3SMiquel Raynal woob = strstr(cmd, ".oob"); 267f87151b3SMiquel Raynal write_empty_pages = !has_pages || strstr(cmd, ".dontskipff"); 268f87151b3SMiquel Raynal 269*b3d8fba6SBoris Brezillon argc -= 2; 270*b3d8fba6SBoris Brezillon argv += 2; 271*b3d8fba6SBoris Brezillon 272f87151b3SMiquel Raynal if (!dump) { 273*b3d8fba6SBoris Brezillon if (!argc) { 274*b3d8fba6SBoris Brezillon ret = CMD_RET_USAGE; 275*b3d8fba6SBoris Brezillon goto out_put_mtd; 276*b3d8fba6SBoris Brezillon } 277f87151b3SMiquel Raynal 278f87151b3SMiquel Raynal user_addr = simple_strtoul(argv[0], NULL, 16); 279f87151b3SMiquel Raynal argc--; 280f87151b3SMiquel Raynal argv++; 281f87151b3SMiquel Raynal } 282f87151b3SMiquel Raynal 283f87151b3SMiquel Raynal start_off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0; 284f87151b3SMiquel Raynal if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) { 285f87151b3SMiquel Raynal printf("Offset not aligned with a page (0x%x)\n", 286f87151b3SMiquel Raynal mtd->writesize); 287*b3d8fba6SBoris Brezillon ret = CMD_RET_FAILURE; 288*b3d8fba6SBoris Brezillon goto out_put_mtd; 289f87151b3SMiquel Raynal } 290f87151b3SMiquel Raynal 291f87151b3SMiquel Raynal default_len = dump ? mtd->writesize : mtd->size; 292*b3d8fba6SBoris Brezillon len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : default_len; 293f87151b3SMiquel Raynal if (!mtd_is_aligned_with_min_io_size(mtd, len)) { 294f87151b3SMiquel Raynal len = round_up(len, mtd->writesize); 295f87151b3SMiquel Raynal printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n", 296f87151b3SMiquel Raynal mtd->writesize, len); 297f87151b3SMiquel Raynal } 298f87151b3SMiquel Raynal 299f87151b3SMiquel Raynal remaining = len; 300f87151b3SMiquel Raynal npages = mtd_len_to_pages(mtd, len); 301f87151b3SMiquel Raynal oob_len = woob ? npages * mtd->oobsize : 0; 302f87151b3SMiquel Raynal 303f87151b3SMiquel Raynal if (dump) 304f87151b3SMiquel Raynal buf = kmalloc(len + oob_len, GFP_KERNEL); 305f87151b3SMiquel Raynal else 306f87151b3SMiquel Raynal buf = map_sysmem(user_addr, 0); 307f87151b3SMiquel Raynal 308f87151b3SMiquel Raynal if (!buf) { 309f87151b3SMiquel Raynal printf("Could not map/allocate the user buffer\n"); 310*b3d8fba6SBoris Brezillon ret = CMD_RET_FAILURE; 311*b3d8fba6SBoris Brezillon goto out_put_mtd; 312f87151b3SMiquel Raynal } 313f87151b3SMiquel Raynal 314f87151b3SMiquel Raynal if (has_pages) 315f87151b3SMiquel Raynal printf("%s %lld byte(s) (%d page(s)) at offset 0x%08llx%s%s%s\n", 316f87151b3SMiquel Raynal read ? "Reading" : "Writing", len, npages, start_off, 317f87151b3SMiquel Raynal raw ? " [raw]" : "", woob ? " [oob]" : "", 318f87151b3SMiquel Raynal !read && write_empty_pages ? " [dontskipff]" : ""); 319f87151b3SMiquel Raynal else 320f87151b3SMiquel Raynal printf("%s %lld byte(s) at offset 0x%08llx\n", 321f87151b3SMiquel Raynal read ? "Reading" : "Writing", len, start_off); 322f87151b3SMiquel Raynal 323f87151b3SMiquel Raynal io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB; 324f87151b3SMiquel Raynal io_op.len = has_pages ? mtd->writesize : len; 325f87151b3SMiquel Raynal io_op.ooblen = woob ? mtd->oobsize : 0; 326f87151b3SMiquel Raynal io_op.datbuf = buf; 327f87151b3SMiquel Raynal io_op.oobbuf = woob ? &buf[len] : NULL; 328f87151b3SMiquel Raynal 329f87151b3SMiquel Raynal /* Search for the first good block after the given offset */ 330f87151b3SMiquel Raynal off = start_off; 331f87151b3SMiquel Raynal while (mtd_block_isbad(mtd, off)) 332f87151b3SMiquel Raynal off += mtd->erasesize; 333f87151b3SMiquel Raynal 334f87151b3SMiquel Raynal /* Loop over the pages to do the actual read/write */ 335f87151b3SMiquel Raynal while (remaining) { 336f87151b3SMiquel Raynal /* Skip the block if it is bad */ 337f87151b3SMiquel Raynal if (mtd_is_aligned_with_block_size(mtd, off) && 338f87151b3SMiquel Raynal mtd_block_isbad(mtd, off)) { 339f87151b3SMiquel Raynal off += mtd->erasesize; 340f87151b3SMiquel Raynal continue; 341f87151b3SMiquel Raynal } 342f87151b3SMiquel Raynal 343f87151b3SMiquel Raynal if (read) 344f87151b3SMiquel Raynal ret = mtd_read_oob(mtd, off, &io_op); 345f87151b3SMiquel Raynal else 346f87151b3SMiquel Raynal ret = mtd_special_write_oob(mtd, off, &io_op, 347*b3d8fba6SBoris Brezillon write_empty_pages, woob); 348f87151b3SMiquel Raynal 349f87151b3SMiquel Raynal if (ret) { 350f87151b3SMiquel Raynal printf("Failure while %s at offset 0x%llx\n", 351f87151b3SMiquel Raynal read ? "reading" : "writing", off); 352*b3d8fba6SBoris Brezillon break; 353f87151b3SMiquel Raynal } 354f87151b3SMiquel Raynal 355f87151b3SMiquel Raynal off += io_op.retlen; 356f87151b3SMiquel Raynal remaining -= io_op.retlen; 357f87151b3SMiquel Raynal io_op.datbuf += io_op.retlen; 358f87151b3SMiquel Raynal io_op.oobbuf += io_op.oobretlen; 359f87151b3SMiquel Raynal } 360f87151b3SMiquel Raynal 361f87151b3SMiquel Raynal if (!ret && dump) 362f87151b3SMiquel Raynal mtd_dump_device_buf(mtd, start_off, buf, len, woob); 363f87151b3SMiquel Raynal 364f87151b3SMiquel Raynal if (dump) 365f87151b3SMiquel Raynal kfree(buf); 366f87151b3SMiquel Raynal else 367f87151b3SMiquel Raynal unmap_sysmem(buf); 368f87151b3SMiquel Raynal 369f87151b3SMiquel Raynal if (ret) { 370f87151b3SMiquel Raynal printf("%s on %s failed with error %d\n", 371f87151b3SMiquel Raynal read ? "Read" : "Write", mtd->name, ret); 372*b3d8fba6SBoris Brezillon ret = CMD_RET_FAILURE; 373*b3d8fba6SBoris Brezillon } else { 374*b3d8fba6SBoris Brezillon ret = CMD_RET_SUCCESS; 375f87151b3SMiquel Raynal } 376f87151b3SMiquel Raynal 377*b3d8fba6SBoris Brezillon out_put_mtd: 378*b3d8fba6SBoris Brezillon put_mtd_device(mtd); 379*b3d8fba6SBoris Brezillon 380*b3d8fba6SBoris Brezillon return ret; 381*b3d8fba6SBoris Brezillon } 382*b3d8fba6SBoris Brezillon 383*b3d8fba6SBoris Brezillon static int do_mtd_erase(cmd_tbl_t *cmdtp, int flag, int argc, 384*b3d8fba6SBoris Brezillon char * const argv[]) 385*b3d8fba6SBoris Brezillon { 386f87151b3SMiquel Raynal struct erase_info erase_op = {}; 387*b3d8fba6SBoris Brezillon struct mtd_info *mtd; 388f87151b3SMiquel Raynal u64 off, len; 389*b3d8fba6SBoris Brezillon bool scrub; 390f87151b3SMiquel Raynal int ret; 391f87151b3SMiquel Raynal 392*b3d8fba6SBoris Brezillon if (argc < 2) 393*b3d8fba6SBoris Brezillon return CMD_RET_USAGE; 394*b3d8fba6SBoris Brezillon 395*b3d8fba6SBoris Brezillon mtd = get_mtd_by_name(argv[1]); 396*b3d8fba6SBoris Brezillon if (IS_ERR_OR_NULL(mtd)) 397*b3d8fba6SBoris Brezillon return CMD_RET_FAILURE; 398*b3d8fba6SBoris Brezillon 399*b3d8fba6SBoris Brezillon scrub = strstr(argv[0], ".dontskipbad"); 400*b3d8fba6SBoris Brezillon 401*b3d8fba6SBoris Brezillon argc -= 2; 402*b3d8fba6SBoris Brezillon argv += 2; 403*b3d8fba6SBoris Brezillon 404f87151b3SMiquel Raynal off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0; 405f87151b3SMiquel Raynal len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : mtd->size; 406f87151b3SMiquel Raynal 407f87151b3SMiquel Raynal if (!mtd_is_aligned_with_block_size(mtd, off)) { 408f87151b3SMiquel Raynal printf("Offset not aligned with a block (0x%x)\n", 409f87151b3SMiquel Raynal mtd->erasesize); 410*b3d8fba6SBoris Brezillon ret = CMD_RET_FAILURE; 411*b3d8fba6SBoris Brezillon goto out_put_mtd; 412f87151b3SMiquel Raynal } 413f87151b3SMiquel Raynal 414f87151b3SMiquel Raynal if (!mtd_is_aligned_with_block_size(mtd, len)) { 415f87151b3SMiquel Raynal printf("Size not a multiple of a block (0x%x)\n", 416f87151b3SMiquel Raynal mtd->erasesize); 417*b3d8fba6SBoris Brezillon ret = CMD_RET_FAILURE; 418*b3d8fba6SBoris Brezillon goto out_put_mtd; 419f87151b3SMiquel Raynal } 420f87151b3SMiquel Raynal 421f87151b3SMiquel Raynal printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n", 422f87151b3SMiquel Raynal off, off + len - 1, mtd_div_by_eb(len, mtd)); 423f87151b3SMiquel Raynal 424f87151b3SMiquel Raynal erase_op.mtd = mtd; 425f87151b3SMiquel Raynal erase_op.addr = off; 426f87151b3SMiquel Raynal erase_op.len = len; 427f87151b3SMiquel Raynal erase_op.scrub = scrub; 428f87151b3SMiquel Raynal 429f87151b3SMiquel Raynal while (erase_op.len) { 430f87151b3SMiquel Raynal ret = mtd_erase(mtd, &erase_op); 431f87151b3SMiquel Raynal 432f87151b3SMiquel Raynal /* Abort if its not a bad block error */ 433f87151b3SMiquel Raynal if (ret != -EIO) 434f87151b3SMiquel Raynal break; 435f87151b3SMiquel Raynal 436*b3d8fba6SBoris Brezillon printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr); 437f87151b3SMiquel Raynal 438f87151b3SMiquel Raynal /* Skip bad block and continue behind it */ 439f87151b3SMiquel Raynal erase_op.len -= erase_op.fail_addr - erase_op.addr; 440f87151b3SMiquel Raynal erase_op.len -= mtd->erasesize; 441f87151b3SMiquel Raynal erase_op.addr = erase_op.fail_addr + mtd->erasesize; 442f87151b3SMiquel Raynal } 443f87151b3SMiquel Raynal 444f87151b3SMiquel Raynal if (ret && ret != -EIO) 445*b3d8fba6SBoris Brezillon ret = CMD_RET_FAILURE; 446*b3d8fba6SBoris Brezillon else 447*b3d8fba6SBoris Brezillon ret = CMD_RET_SUCCESS; 448*b3d8fba6SBoris Brezillon 449*b3d8fba6SBoris Brezillon out_put_mtd: 450*b3d8fba6SBoris Brezillon put_mtd_device(mtd); 451*b3d8fba6SBoris Brezillon 452*b3d8fba6SBoris Brezillon return ret; 453*b3d8fba6SBoris Brezillon } 454*b3d8fba6SBoris Brezillon 455*b3d8fba6SBoris Brezillon static int do_mtd_bad(cmd_tbl_t *cmdtp, int flag, int argc, 456*b3d8fba6SBoris Brezillon char * const argv[]) 457*b3d8fba6SBoris Brezillon { 458*b3d8fba6SBoris Brezillon struct mtd_info *mtd; 459f87151b3SMiquel Raynal loff_t off; 460f87151b3SMiquel Raynal 461*b3d8fba6SBoris Brezillon if (argc < 2) 462*b3d8fba6SBoris Brezillon return CMD_RET_USAGE; 463*b3d8fba6SBoris Brezillon 464*b3d8fba6SBoris Brezillon mtd = get_mtd_by_name(argv[1]); 465*b3d8fba6SBoris Brezillon if (IS_ERR_OR_NULL(mtd)) 466*b3d8fba6SBoris Brezillon return CMD_RET_FAILURE; 467*b3d8fba6SBoris Brezillon 468f87151b3SMiquel Raynal if (!mtd_can_have_bb(mtd)) { 469f87151b3SMiquel Raynal printf("Only NAND-based devices can have bad blocks\n"); 470*b3d8fba6SBoris Brezillon goto out_put_mtd; 471f87151b3SMiquel Raynal } 472f87151b3SMiquel Raynal 473f87151b3SMiquel Raynal printf("MTD device %s bad blocks list:\n", mtd->name); 474*b3d8fba6SBoris Brezillon for (off = 0; off < mtd->size; off += mtd->erasesize) { 475f87151b3SMiquel Raynal if (mtd_block_isbad(mtd, off)) 476f87151b3SMiquel Raynal printf("\t0x%08llx\n", off); 477f87151b3SMiquel Raynal } 478f87151b3SMiquel Raynal 479*b3d8fba6SBoris Brezillon out_put_mtd: 480*b3d8fba6SBoris Brezillon put_mtd_device(mtd); 481*b3d8fba6SBoris Brezillon 482f87151b3SMiquel Raynal return CMD_RET_SUCCESS; 483f87151b3SMiquel Raynal } 484f87151b3SMiquel Raynal 485*b3d8fba6SBoris Brezillon #ifdef CONFIG_AUTO_COMPLETE 486*b3d8fba6SBoris Brezillon static int mtd_name_complete(int argc, char * const argv[], char last_char, 487*b3d8fba6SBoris Brezillon int maxv, char *cmdv[]) 488*b3d8fba6SBoris Brezillon { 489*b3d8fba6SBoris Brezillon int len = 0, n_found = 0; 490*b3d8fba6SBoris Brezillon struct mtd_info *mtd; 491*b3d8fba6SBoris Brezillon 492*b3d8fba6SBoris Brezillon argc--; 493*b3d8fba6SBoris Brezillon argv++; 494*b3d8fba6SBoris Brezillon 495*b3d8fba6SBoris Brezillon if (argc > 1 || 496*b3d8fba6SBoris Brezillon (argc == 1 && (last_char == '\0' || isblank(last_char)))) 497*b3d8fba6SBoris Brezillon return 0; 498*b3d8fba6SBoris Brezillon 499*b3d8fba6SBoris Brezillon if (argc) 500*b3d8fba6SBoris Brezillon len = strlen(argv[0]); 501*b3d8fba6SBoris Brezillon 502*b3d8fba6SBoris Brezillon mtd_for_each_device(mtd) { 503*b3d8fba6SBoris Brezillon if (argc && 504*b3d8fba6SBoris Brezillon (len > strlen(mtd->name) || 505*b3d8fba6SBoris Brezillon strncmp(argv[0], mtd->name, len))) 506*b3d8fba6SBoris Brezillon continue; 507*b3d8fba6SBoris Brezillon 508*b3d8fba6SBoris Brezillon if (n_found >= maxv - 2) { 509*b3d8fba6SBoris Brezillon cmdv[n_found++] = "..."; 510*b3d8fba6SBoris Brezillon break; 511*b3d8fba6SBoris Brezillon } 512*b3d8fba6SBoris Brezillon 513*b3d8fba6SBoris Brezillon cmdv[n_found++] = mtd->name; 514*b3d8fba6SBoris Brezillon } 515*b3d8fba6SBoris Brezillon 516*b3d8fba6SBoris Brezillon cmdv[n_found] = NULL; 517*b3d8fba6SBoris Brezillon 518*b3d8fba6SBoris Brezillon return n_found; 519*b3d8fba6SBoris Brezillon } 520*b3d8fba6SBoris Brezillon #endif /* CONFIG_AUTO_COMPLETE */ 521*b3d8fba6SBoris Brezillon 522f87151b3SMiquel Raynal static char mtd_help_text[] = 523f87151b3SMiquel Raynal #ifdef CONFIG_SYS_LONGHELP 524f87151b3SMiquel Raynal "- generic operations on memory technology devices\n\n" 525f87151b3SMiquel Raynal "mtd list\n" 526f87151b3SMiquel Raynal "mtd read[.raw][.oob] <name> <addr> [<off> [<size>]]\n" 527f87151b3SMiquel Raynal "mtd dump[.raw][.oob] <name> [<off> [<size>]]\n" 528f87151b3SMiquel Raynal "mtd write[.raw][.oob][.dontskipff] <name> <addr> [<off> [<size>]]\n" 529f87151b3SMiquel Raynal "mtd erase[.dontskipbad] <name> [<off> [<size>]]\n" 530f87151b3SMiquel Raynal "\n" 531f87151b3SMiquel Raynal "Specific functions:\n" 532f87151b3SMiquel Raynal "mtd bad <name>\n" 533f87151b3SMiquel Raynal "\n" 534f87151b3SMiquel Raynal "With:\n" 535f87151b3SMiquel Raynal "\t<name>: NAND partition/chip name\n" 536f87151b3SMiquel Raynal "\t<addr>: user address from/to which data will be retrieved/stored\n" 537f87151b3SMiquel Raynal "\t<off>: offset in <name> in bytes (default: start of the part)\n" 538f87151b3SMiquel Raynal "\t\t* must be block-aligned for erase\n" 539f87151b3SMiquel Raynal "\t\t* must be page-aligned otherwise\n" 540f87151b3SMiquel Raynal "\t<size>: length of the operation in bytes (default: the entire device)\n" 541f87151b3SMiquel Raynal "\t\t* must be a multiple of a block for erase\n" 542f87151b3SMiquel Raynal "\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n" 543f87151b3SMiquel Raynal "\n" 544f87151b3SMiquel Raynal "The .dontskipff option forces writing empty pages, don't use it if unsure.\n" 545f87151b3SMiquel Raynal #endif 546f87151b3SMiquel Raynal ""; 547f87151b3SMiquel Raynal 548*b3d8fba6SBoris Brezillon U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text, 549*b3d8fba6SBoris Brezillon U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mtd_list), 550*b3d8fba6SBoris Brezillon U_BOOT_SUBCMD_MKENT_COMPLETE(read, 5, 0, do_mtd_io, 551*b3d8fba6SBoris Brezillon mtd_name_complete), 552*b3d8fba6SBoris Brezillon U_BOOT_SUBCMD_MKENT_COMPLETE(write, 5, 0, do_mtd_io, 553*b3d8fba6SBoris Brezillon mtd_name_complete), 554*b3d8fba6SBoris Brezillon U_BOOT_SUBCMD_MKENT_COMPLETE(dump, 4, 0, do_mtd_io, 555*b3d8fba6SBoris Brezillon mtd_name_complete), 556*b3d8fba6SBoris Brezillon U_BOOT_SUBCMD_MKENT_COMPLETE(erase, 4, 0, do_mtd_erase, 557*b3d8fba6SBoris Brezillon mtd_name_complete), 558*b3d8fba6SBoris Brezillon U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad, 559*b3d8fba6SBoris Brezillon mtd_name_complete)); 560