xref: /openbmc/u-boot/cmd/mtd.c (revision d01806a8)
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