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