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