xref: /rk3399_rockchip-uboot/cmd/mtd.c (revision f87151b32590263209fd0270c8c78064720ef6d5)
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