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