Blame view

cmd/sf.c 14.5 KB
83d290c56   Tom Rini   SPDX: Convert all...
1
  // SPDX-License-Identifier: GPL-2.0+
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
2
3
4
5
6
  /*
   * Command for accessing SPI flash.
   *
   * Copyright (C) 2008 Atmel Corporation
   */
4166ee58d   Mike Frysinger   sf: add GPL-2 lic...
7

b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
8
  #include <common.h>
381c6e2c9   Mingkai Hu   cmd_sf: include h...
9
  #include <div64.h>
fbb099183   Simon Glass   dm: Convert spi_f...
10
  #include <dm.h>
8d1af9422   Simon Glass   cmd_sf: add "upda...
11
  #include <malloc.h>
0eb25b619   Joe Hershberger   common: Make sure...
12
  #include <mapmem.h>
ff0960f9a   Simon Glass   sf: Tidy up publi...
13
  #include <spi.h>
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
14
  #include <spi_flash.h>
2ec1a4059   Heiko Schocher   spi, sf: Use offs...
15
16
  #include <jffs2/jffs2.h>
  #include <linux/mtd/mtd.h>
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
17
18
  
  #include <asm/io.h>
fbb099183   Simon Glass   dm: Convert spi_f...
19
  #include <dm/device-internal.h>
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
20

eb446ef62   Miquel Raynal   cmd: nand/sf: iso...
21
  #include "legacy-mtd-utils.h"
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
22
  static struct spi_flash *flash;
334eb4b02   Richard Retanubun   cmd_sf: add handl...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  /*
   * This function computes the length argument for the erase command.
   * The length on which the command is to operate can be given in two forms:
   * 1. <cmd> offset len  - operate on <'offset',  'len')
   * 2. <cmd> offset +len - operate on <'offset',  'round_up(len)')
   * If the second form is used and the length doesn't fall on the
   * sector boundary, than it will be adjusted to the next sector boundary.
   * If it isn't in the flash, the function will fail (return -1).
   * Input:
   *    arg: length specification (i.e. both command arguments)
   * Output:
   *    len: computed length for operation
   * Return:
   *    1: success
   *   -1: failure (bad format, bad address).
   */
  static int sf_parse_len_arg(char *arg, ulong *len)
  {
  	char *ep;
  	char round_up_len; /* indicates if the "+length" form used */
  	ulong len_arg;
  
  	round_up_len = 0;
  	if (*arg == '+') {
  		round_up_len = 1;
  		++arg;
  	}
  
  	len_arg = simple_strtoul(arg, &ep, 16);
  	if (ep == arg || *ep != '\0')
  		return -1;
  
  	if (round_up_len && flash->sector_size > 0)
155cfb5ef   Anton Staaf   common: fix behav...
56
  		*len = ROUND(len_arg, flash->sector_size);
334eb4b02   Richard Retanubun   cmd_sf: add handl...
57
58
59
60
61
  	else
  		*len = len_arg;
  
  	return 1;
  }
a683c288d   James Miller   spi: Add progress...
62
63
64
65
66
67
68
69
70
71
72
73
  /**
   * This function takes a byte length and a delta unit of time to compute the
   * approximate bytes per second
   *
   * @param len		amount of bytes currently processed
   * @param start_ms	start time of processing in ms
   * @return bytes per second if OK, 0 on error
   */
  static ulong bytes_per_second(unsigned int len, ulong start_ms)
  {
  	/* less accurate but avoids overflow */
  	if (len >= ((unsigned int) -1) / 1024)
b41411954   Masahiro Yamada   linux/kernel.h: s...
74
  		return len / (max(get_timer(start_ms) / 1024, 1UL));
a683c288d   James Miller   spi: Add progress...
75
  	else
b41411954   Masahiro Yamada   linux/kernel.h: s...
76
  		return 1024 * len / max(get_timer(start_ms), 1UL);
a683c288d   James Miller   spi: Add progress...
77
  }
54841ab50   Wolfgang Denk   Make sure that ar...
78
  static int do_spi_flash_probe(int argc, char * const argv[])
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
79
  {
c1173bd07   Eric Nelson   sf command: allow...
80
81
  	unsigned int bus = CONFIG_SF_DEFAULT_BUS;
  	unsigned int cs = CONFIG_SF_DEFAULT_CS;
b0cc1b846   Patrick Delaunay   dm: spi: Read def...
82
  	/* In DM mode, defaults speed and mode will be taken from DT */
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
83
84
85
  	unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
  	unsigned int mode = CONFIG_SF_DEFAULT_MODE;
  	char *endp;
fbb099183   Simon Glass   dm: Convert spi_f...
86
87
88
89
  #ifdef CONFIG_DM_SPI_FLASH
  	struct udevice *new, *bus_dev;
  	int ret;
  #else
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
90
  	struct spi_flash *new;
fbb099183   Simon Glass   dm: Convert spi_f...
91
  #endif
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
92

c1173bd07   Eric Nelson   sf command: allow...
93
94
95
  	if (argc >= 2) {
  		cs = simple_strtoul(argv[1], &endp, 0);
  		if (*argv[1] == 0 || (*endp != 0 && *endp != ':'))
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
96
  			return -1;
c1173bd07   Eric Nelson   sf command: allow...
97
98
99
100
101
102
103
104
105
  		if (*endp == ':') {
  			if (endp[1] == 0)
  				return -1;
  
  			bus = cs;
  			cs = simple_strtoul(endp + 1, &endp, 0);
  			if (*endp != 0)
  				return -1;
  		}
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
106
107
108
109
110
  	}
  
  	if (argc >= 3) {
  		speed = simple_strtoul(argv[2], &endp, 0);
  		if (*argv[2] == 0 || *endp != 0)
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
111
  			return -1;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
112
113
  	}
  	if (argc >= 4) {
6e8d58d36   TsiChung Liew   Command for acces...
114
  		mode = simple_strtoul(argv[3], &endp, 16);
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
115
  		if (*argv[3] == 0 || *endp != 0)
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
116
  			return -1;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
117
  	}
fbb099183   Simon Glass   dm: Convert spi_f...
118
119
120
121
  #ifdef CONFIG_DM_SPI_FLASH
  	/* Remove the old device, otherwise probe will just be a nop */
  	ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new);
  	if (!ret) {
706865afe   Stefan Roese   dm: core: Add fla...
122
  		device_remove(new, DM_REMOVE_NORMAL);
fbb099183   Simon Glass   dm: Convert spi_f...
123
124
125
126
127
128
129
130
131
  	}
  	flash = NULL;
  	ret = spi_flash_probe_bus_cs(bus, cs, speed, mode, &new);
  	if (ret) {
  		printf("Failed to initialize SPI flash at %u:%u (error %d)
  ",
  		       bus, cs, ret);
  		return 1;
  	}
e564f054a   Simon Glass   dm: core: Add dev...
132
  	flash = dev_get_uclass_priv(new);
fbb099183   Simon Glass   dm: Convert spi_f...
133
  #else
8ee81b7f9   Heiko Schocher   mtd, spi: Check i...
134
135
  	if (flash)
  		spi_flash_free(flash);
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
136
  	new = spi_flash_probe(bus, cs, speed, mode);
8ee81b7f9   Heiko Schocher   mtd, spi: Check i...
137
  	flash = new;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
138
139
140
141
142
  	if (!new) {
  		printf("Failed to initialize SPI flash at %u:%u
  ", bus, cs);
  		return 1;
  	}
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
143
  	flash = new;
fbb099183   Simon Glass   dm: Convert spi_f...
144
  #endif
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
145

b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
146
  	return 0;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
147
  }
8d1af9422   Simon Glass   cmd_sf: add "upda...
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
  /**
   * Write a block of data to SPI flash, first checking if it is different from
   * what is already there.
   *
   * If the data being written is the same, then *skipped is incremented by len.
   *
   * @param flash		flash context pointer
   * @param offset	flash offset to write
   * @param len		number of bytes to write
   * @param buf		buffer to write from
   * @param cmp_buf	read buffer to use to compare data
   * @param skipped	Count of skipped data (incremented by this function)
   * @return NULL if OK, else a string containing the stage which failed
   */
  static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset,
  		size_t len, const char *buf, char *cmp_buf, size_t *skipped)
  {
ed62756dc   Stefan Roese   cmd_sf: Fix probl...
165
  	char *ptr = (char *)buf;
a97f6efdb   Andreas Bießmann   cmd_sf.c: fix pri...
166
167
  	debug("offset=%#x, sector_size=%#x, len=%#zx
  ",
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
168
  	      offset, flash->sector_size, len);
b95f958d7   Gerlando Falauto   cmd_sf: let "sf u...
169
170
  	/* Read the entire sector so to allow for rewriting */
  	if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf))
8d1af9422   Simon Glass   cmd_sf: add "upda...
171
  		return "read";
b95f958d7   Gerlando Falauto   cmd_sf: let "sf u...
172
  	/* Compare only what is meaningful (len) */
8d1af9422   Simon Glass   cmd_sf: add "upda...
173
  	if (memcmp(cmp_buf, buf, len) == 0) {
a97f6efdb   Andreas Bießmann   cmd_sf.c: fix pri...
174
175
  		debug("Skip region %x size %zx: no change
  ",
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
176
  		      offset, len);
8d1af9422   Simon Glass   cmd_sf: add "upda...
177
178
179
  		*skipped += len;
  		return NULL;
  	}
348885062   Gerlando Falauto   cmd_sf: let "sf u...
180
181
  	/* Erase the entire sector */
  	if (spi_flash_erase(flash, offset, flash->sector_size))
8d1af9422   Simon Glass   cmd_sf: add "upda...
182
  		return "erase";
ed62756dc   Stefan Roese   cmd_sf: Fix probl...
183
  	/* If it's a partial sector, copy the data into the temp-buffer */
b95f958d7   Gerlando Falauto   cmd_sf: let "sf u...
184
  	if (len != flash->sector_size) {
ed62756dc   Stefan Roese   cmd_sf: Fix probl...
185
186
  		memcpy(cmp_buf, buf, len);
  		ptr = cmp_buf;
b95f958d7   Gerlando Falauto   cmd_sf: let "sf u...
187
  	}
ed62756dc   Stefan Roese   cmd_sf: Fix probl...
188
189
190
  	/* Write one complete sector */
  	if (spi_flash_write(flash, offset, flash->sector_size, ptr))
  		return "write";
b95f958d7   Gerlando Falauto   cmd_sf: let "sf u...
191

8d1af9422   Simon Glass   cmd_sf: add "upda...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
  	return NULL;
  }
  
  /**
   * Update an area of SPI flash by erasing and writing any blocks which need
   * to change. Existing blocks with the correct data are left unchanged.
   *
   * @param flash		flash context pointer
   * @param offset	flash offset to write
   * @param len		number of bytes to write
   * @param buf		buffer to write from
   * @return 0 if ok, 1 on error
   */
  static int spi_flash_update(struct spi_flash *flash, u32 offset,
  		size_t len, const char *buf)
  {
  	const char *err_oper = NULL;
  	char *cmp_buf;
  	const char *end = buf + len;
  	size_t todo;		/* number of bytes to do in this pass */
8e8a4bc22   Kumar Gala   cmd_sf: Fix compi...
212
  	size_t skipped = 0;	/* statistics */
a683c288d   James Miller   spi: Add progress...
213
214
215
216
  	const ulong start_time = get_timer(0);
  	size_t scale = 1;
  	const char *start_buf = buf;
  	ulong delta;
8d1af9422   Simon Glass   cmd_sf: add "upda...
217

a683c288d   James Miller   spi: Add progress...
218
219
  	if (end - buf >= 200)
  		scale = (end - buf) / 100;
156e96f03   Ravi Babu   sf: allocate cach...
220
  	cmp_buf = memalign(ARCH_DMA_MINALIGN, flash->sector_size);
8d1af9422   Simon Glass   cmd_sf: add "upda...
221
  	if (cmp_buf) {
a683c288d   James Miller   spi: Add progress...
222
  		ulong last_update = get_timer(0);
8e8a4bc22   Kumar Gala   cmd_sf: Fix compi...
223
  		for (; buf < end && !err_oper; buf += todo, offset += todo) {
b41411954   Masahiro Yamada   linux/kernel.h: s...
224
  			todo = min_t(size_t, end - buf, flash->sector_size);
a683c288d   James Miller   spi: Add progress...
225
226
  			if (get_timer(last_update) > 100) {
  				printf("   \rUpdating, %zu%% %lu B/s",
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
227
  				       100 - (end - buf) / scale,
a683c288d   James Miller   spi: Add progress...
228
229
230
231
  					bytes_per_second(buf - start_buf,
  							 start_time));
  				last_update = get_timer(0);
  			}
8d1af9422   Simon Glass   cmd_sf: add "upda...
232
233
234
235
236
237
238
  			err_oper = spi_flash_update_block(flash, offset, todo,
  					buf, cmp_buf, &skipped);
  		}
  	} else {
  		err_oper = "malloc";
  	}
  	free(cmp_buf);
a683c288d   James Miller   spi: Add progress...
239
  	putc('\r');
8d1af9422   Simon Glass   cmd_sf: add "upda...
240
241
242
243
244
  	if (err_oper) {
  		printf("SPI flash failed in %s step
  ", err_oper);
  		return 1;
  	}
a683c288d   James Miller   spi: Add progress...
245
246
247
  
  	delta = get_timer(start_time);
  	printf("%zu bytes written, %zu bytes skipped", len - skipped,
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
248
  	       skipped);
a683c288d   James Miller   spi: Add progress...
249
250
  	printf(" in %ld.%lds, speed %ld B/s
  ",
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
251
  	       delta / 1000, delta % 1000, bytes_per_second(len, start_time));
8e8a4bc22   Kumar Gala   cmd_sf: Fix compi...
252

8d1af9422   Simon Glass   cmd_sf: add "upda...
253
254
  	return 0;
  }
54841ab50   Wolfgang Denk   Make sure that ar...
255
  static int do_spi_flash_read_write(int argc, char * const argv[])
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
256
257
  {
  	unsigned long addr;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
258
259
  	void *buf;
  	char *endp;
60b6614ac   Jagannadha Sutradharudu Teki   cmd_sf: Add print...
260
  	int ret = 1;
2ec1a4059   Heiko Schocher   spi, sf: Use offs...
261
262
  	int dev = 0;
  	loff_t offset, len, maxsize;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
263

2ec1a4059   Heiko Schocher   spi, sf: Use offs...
264
  	if (argc < 3)
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
265
  		return -1;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
266
267
268
  
  	addr = simple_strtoul(argv[1], &endp, 16);
  	if (*argv[1] == 0 || *endp != 0)
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
269
  		return -1;
2ec1a4059   Heiko Schocher   spi, sf: Use offs...
270
271
272
  
  	if (mtd_arg_off_size(argc - 2, &argv[2], &dev, &offset, &len,
  			     &maxsize, MTD_DEV_TYPE_NOR, flash->size))
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
273
  		return -1;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
274

864939949   Gerlando Falauto   cmd_sf: add size ...
275
276
277
278
  	/* Consistency checking */
  	if (offset + len > flash->size) {
  		printf("ERROR: attempting %s past flash size (%#x)
  ",
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
279
  		       argv[0], flash->size);
864939949   Gerlando Falauto   cmd_sf: add size ...
280
281
  		return 1;
  	}
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
282
  	buf = map_physmem(addr, len, MAP_WRBACK);
10f087f73   Liam Beguin   cmd: sf: fix map_...
283
  	if (!buf && addr) {
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
284
285
286
287
  		puts("Failed to map physical memory
  ");
  		return 1;
  	}
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
288
  	if (strcmp(argv[0], "update") == 0) {
8d1af9422   Simon Glass   cmd_sf: add "upda...
289
  		ret = spi_flash_update(flash, offset, len, buf);
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
290
  	} else if (strncmp(argv[0], "read", 4) == 0 ||
60b6614ac   Jagannadha Sutradharudu Teki   cmd_sf: Add print...
291
292
293
294
295
296
297
298
  			strncmp(argv[0], "write", 5) == 0) {
  		int read;
  
  		read = strncmp(argv[0], "read", 4) == 0;
  		if (read)
  			ret = spi_flash_read(flash, offset, len, buf);
  		else
  			ret = spi_flash_write(flash, offset, len, buf);
a7d0711a2   Simon Glass   spi: sf: Print th...
299
300
301
302
303
304
305
306
  		printf("SF: %zu bytes @ %#x %s: ", (size_t)len, (u32)offset,
  		       read ? "Read" : "Written");
  		if (ret)
  			printf("ERROR %d
  ", ret);
  		else
  			printf("OK
  ");
60b6614ac   Jagannadha Sutradharudu Teki   cmd_sf: Add print...
307
  	}
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
308
309
  
  	unmap_physmem(buf, len);
60b6614ac   Jagannadha Sutradharudu Teki   cmd_sf: Add print...
310
  	return ret == 0 ? 0 : 1;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
311
  }
54841ab50   Wolfgang Denk   Make sure that ar...
312
  static int do_spi_flash_erase(int argc, char * const argv[])
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
313
  {
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
314
  	int ret;
2ec1a4059   Heiko Schocher   spi, sf: Use offs...
315
316
317
  	int dev = 0;
  	loff_t offset, len, maxsize;
  	ulong size;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
318
319
  
  	if (argc < 3)
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
320
  		return -1;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
321

2ec1a4059   Heiko Schocher   spi, sf: Use offs...
322
323
  	if (mtd_arg_off(argv[1], &dev, &offset, &len, &maxsize,
  			MTD_DEV_TYPE_NOR, flash->size))
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
324
  		return -1;
334eb4b02   Richard Retanubun   cmd_sf: add handl...
325

2ec1a4059   Heiko Schocher   spi, sf: Use offs...
326
  	ret = sf_parse_len_arg(argv[2], &size);
334eb4b02   Richard Retanubun   cmd_sf: add handl...
327
  	if (ret != 1)
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
328
  		return -1;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
329

864939949   Gerlando Falauto   cmd_sf: add size ...
330
  	/* Consistency checking */
2ec1a4059   Heiko Schocher   spi, sf: Use offs...
331
  	if (offset + size > flash->size) {
864939949   Gerlando Falauto   cmd_sf: add size ...
332
333
  		printf("ERROR: attempting %s past flash size (%#x)
  ",
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
334
  		       argv[0], flash->size);
864939949   Gerlando Falauto   cmd_sf: add size ...
335
336
  		return 1;
  	}
2ec1a4059   Heiko Schocher   spi, sf: Use offs...
337
338
339
  	ret = spi_flash_erase(flash, offset, size);
  	printf("SF: %zu bytes @ %#x Erased: %s
  ", (size_t)size, (u32)offset,
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
340
  	       ret ? "ERROR" : "OK");
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
341

96bbf5565   Jagannadha Sutradharudu Teki   cmd_sf: Add print...
342
  	return ret == 0 ? 0 : 1;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
343
  }
c3c016cf7   Fabio Estevam   sf: Add SPI NOR p...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  static int do_spi_protect(int argc, char * const argv[])
  {
  	int ret = 0;
  	loff_t start, len;
  	bool prot = false;
  
  	if (argc != 4)
  		return -1;
  
  	if (!str2off(argv[2], &start)) {
  		puts("start sector is not a valid number
  ");
  		return 1;
  	}
  
  	if (!str2off(argv[3], &len)) {
  		puts("len is not a valid number
  ");
  		return 1;
  	}
  
  	if (strcmp(argv[1], "lock") == 0)
  		prot = true;
  	else if (strcmp(argv[1], "unlock") == 0)
  		prot = false;
  	else
  		return -1;  /* Unknown parameter */
  
  	ret = spi_flash_protect(flash, start, len, prot);
  
  	return ret == 0 ? 0 : 1;
  }
240072731   Simon Glass   spi: Add SPI flas...
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
  #ifdef CONFIG_CMD_SF_TEST
  enum {
  	STAGE_ERASE,
  	STAGE_CHECK,
  	STAGE_WRITE,
  	STAGE_READ,
  
  	STAGE_COUNT,
  };
  
  static char *stage_name[STAGE_COUNT] = {
  	"erase",
  	"check",
  	"write",
  	"read",
  };
  
  struct test_info {
  	int stage;
  	int bytes;
  	unsigned base_ms;
  	unsigned time_ms[STAGE_COUNT];
  };
  
  static void show_time(struct test_info *test, int stage)
  {
  	uint64_t speed;	/* KiB/s */
  	int bps;	/* Bits per second */
  
  	speed = (long long)test->bytes * 1000;
d1f22d4bd   Simon Glass   sandbox: spi: Adj...
406
407
  	if (test->time_ms[stage])
  		do_div(speed, test->time_ms[stage] * 1024);
240072731   Simon Glass   spi: Add SPI flas...
408
  	bps = speed * 8;
c6b2ea379   Heinrich Schuchardt   cmd: sf: use corr...
409
410
  	printf("%d %s: %u ticks, %d KiB/s %d.%03d Mbps
  ", stage,
240072731   Simon Glass   spi: Add SPI flas...
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
  	       stage_name[stage], test->time_ms[stage],
  	       (int)speed, bps / 1000, bps % 1000);
  }
  
  static void spi_test_next_stage(struct test_info *test)
  {
  	test->time_ms[test->stage] = get_timer(test->base_ms);
  	show_time(test, test->stage);
  	test->base_ms = get_timer(0);
  	test->stage++;
  }
  
  /**
   * Run a test on the SPI flash
   *
   * @param flash		SPI flash to use
   * @param buf		Source buffer for data to write
   * @param len		Size of data to read/write
   * @param offset	Offset within flash to check
   * @param vbuf		Verification buffer
   * @return 0 if ok, -1 on error
   */
1e7133e99   Simon Glass   sf: Use unsigned ...
433
434
  static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len,
  			   ulong offset, uint8_t *vbuf)
240072731   Simon Glass   spi: Add SPI flas...
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
  {
  	struct test_info test;
  	int i;
  
  	printf("SPI flash test:
  ");
  	memset(&test, '\0', sizeof(test));
  	test.base_ms = get_timer(0);
  	test.bytes = len;
  	if (spi_flash_erase(flash, offset, len)) {
  		printf("Erase failed
  ");
  		return -1;
  	}
  	spi_test_next_stage(&test);
  
  	if (spi_flash_read(flash, offset, len, vbuf)) {
  		printf("Check read failed
  ");
  		return -1;
  	}
  	for (i = 0; i < len; i++) {
  		if (vbuf[i] != 0xff) {
  			printf("Check failed at %d
  ", i);
b41411954   Masahiro Yamada   linux/kernel.h: s...
460
461
  			print_buffer(i, vbuf + i, 1,
  				     min_t(uint, len - i, 0x40), 0);
240072731   Simon Glass   spi: Add SPI flas...
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
  			return -1;
  		}
  	}
  	spi_test_next_stage(&test);
  
  	if (spi_flash_write(flash, offset, len, buf)) {
  		printf("Write failed
  ");
  		return -1;
  	}
  	memset(vbuf, '\0', len);
  	spi_test_next_stage(&test);
  
  	if (spi_flash_read(flash, offset, len, vbuf)) {
  		printf("Read failed
  ");
  		return -1;
  	}
  	spi_test_next_stage(&test);
  
  	for (i = 0; i < len; i++) {
  		if (buf[i] != vbuf[i]) {
  			printf("Verify failed at %d, good data:
  ", i);
b41411954   Masahiro Yamada   linux/kernel.h: s...
486
487
  			print_buffer(i, buf + i, 1,
  				     min_t(uint, len - i, 0x40), 0);
240072731   Simon Glass   spi: Add SPI flas...
488
489
  			printf("Bad data:
  ");
b41411954   Masahiro Yamada   linux/kernel.h: s...
490
491
  			print_buffer(i, vbuf + i, 1,
  				     min_t(uint, len - i, 0x40), 0);
240072731   Simon Glass   spi: Add SPI flas...
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
  			return -1;
  		}
  	}
  	printf("Test passed
  ");
  	for (i = 0; i < STAGE_COUNT; i++)
  		show_time(&test, i);
  
  	return 0;
  }
  
  static int do_spi_flash_test(int argc, char * const argv[])
  {
  	unsigned long offset;
  	unsigned long len;
d1f22d4bd   Simon Glass   sandbox: spi: Adj...
507
  	uint8_t *buf, *from;
240072731   Simon Glass   spi: Add SPI flas...
508
  	char *endp;
1e7133e99   Simon Glass   sf: Use unsigned ...
509
  	uint8_t *vbuf;
240072731   Simon Glass   spi: Add SPI flas...
510
  	int ret;
d1f22d4bd   Simon Glass   sandbox: spi: Adj...
511
512
  	if (argc < 3)
  		return -1;
240072731   Simon Glass   spi: Add SPI flas...
513
514
515
516
517
518
  	offset = simple_strtoul(argv[1], &endp, 16);
  	if (*argv[1] == 0 || *endp != 0)
  		return -1;
  	len = simple_strtoul(argv[2], &endp, 16);
  	if (*argv[2] == 0 || *endp != 0)
  		return -1;
156e96f03   Ravi Babu   sf: allocate cach...
519
  	vbuf = memalign(ARCH_DMA_MINALIGN, len);
240072731   Simon Glass   spi: Add SPI flas...
520
  	if (!vbuf) {
d1f22d4bd   Simon Glass   sandbox: spi: Adj...
521
522
  		printf("Cannot allocate memory (%lu bytes)
  ", len);
240072731   Simon Glass   spi: Add SPI flas...
523
524
  		return 1;
  	}
156e96f03   Ravi Babu   sf: allocate cach...
525
  	buf = memalign(ARCH_DMA_MINALIGN, len);
240072731   Simon Glass   spi: Add SPI flas...
526
527
  	if (!buf) {
  		free(vbuf);
d1f22d4bd   Simon Glass   sandbox: spi: Adj...
528
529
  		printf("Cannot allocate memory (%lu bytes)
  ", len);
240072731   Simon Glass   spi: Add SPI flas...
530
531
  		return 1;
  	}
d1f22d4bd   Simon Glass   sandbox: spi: Adj...
532
533
  	from = map_sysmem(CONFIG_SYS_TEXT_BASE, 0);
  	memcpy(buf, from, len);
240072731   Simon Glass   spi: Add SPI flas...
534
535
536
537
538
539
540
541
542
543
544
545
  	ret = spi_flash_test(flash, buf, len, offset, vbuf);
  	free(vbuf);
  	free(buf);
  	if (ret) {
  		printf("Test failed
  ");
  		return 1;
  	}
  
  	return 0;
  }
  #endif /* CONFIG_CMD_SF_TEST */
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
546
547
  static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc,
  			char * const argv[])
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
548
549
  {
  	const char *cmd;
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
550
  	int ret;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
551
552
553
554
555
556
  
  	/* need at least two arguments */
  	if (argc < 2)
  		goto usage;
  
  	cmd = argv[1];
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
557
558
  	--argc;
  	++argv;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
559

8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
560
561
562
563
  	if (strcmp(cmd, "probe") == 0) {
  		ret = do_spi_flash_probe(argc, argv);
  		goto done;
  	}
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
564
565
566
567
568
569
570
  
  	/* The remaining commands require a selected device */
  	if (!flash) {
  		puts("No SPI flash selected. Please run `sf probe'
  ");
  		return 1;
  	}
8d1af9422   Simon Glass   cmd_sf: add "upda...
571
572
  	if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 ||
  	    strcmp(cmd, "update") == 0)
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
573
574
575
  		ret = do_spi_flash_read_write(argc, argv);
  	else if (strcmp(cmd, "erase") == 0)
  		ret = do_spi_flash_erase(argc, argv);
c3c016cf7   Fabio Estevam   sf: Add SPI NOR p...
576
577
  	else if (strcmp(cmd, "protect") == 0)
  		ret = do_spi_protect(argc, argv);
240072731   Simon Glass   spi: Add SPI flas...
578
579
580
581
  #ifdef CONFIG_CMD_SF_TEST
  	else if (!strcmp(cmd, "test"))
  		ret = do_spi_flash_test(argc, argv);
  #endif
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
582
583
584
585
586
587
  	else
  		ret = -1;
  
  done:
  	if (ret != -1)
  		return ret;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
588
589
  
  usage:
4c12eeb8b   Simon Glass   Convert cmd_usage...
590
  	return CMD_RET_USAGE;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
591
  }
240072731   Simon Glass   spi: Add SPI flas...
592
593
594
595
596
597
598
  #ifdef CONFIG_CMD_SF_TEST
  #define SF_TEST_HELP "
  sf test offset len		" \
  		"- run a very basic destructive test"
  #else
  #define SF_TEST_HELP
  #endif
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
599
600
  U_BOOT_CMD(
  	sf,	5,	1,	do_spi_flash,
2fb2604d5   Peter Tyser   Command usage cle...
601
  	"SPI flash sub-system",
c1173bd07   Eric Nelson   sf command: allow...
602
603
  	"probe [[bus:]cs] [hz] [mode]	- init flash device on given SPI bus
  "
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
604
605
  	"				  and chip select
  "
2ec1a4059   Heiko Schocher   spi, sf: Use offs...
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
  	"sf read addr offset|partition len	- read `len' bytes starting at
  "
  	"				          `offset' or from start of mtd
  "
  	"					  `partition'to memory at `addr'
  "
  	"sf write addr offset|partition len	- write `len' bytes from memory
  "
  	"				          at `addr' to flash at `offset'
  "
  	"					  or to start of mtd `partition'
  "
  	"sf erase offset|partition [+]len	- erase `len' bytes from `offset'
  "
  	"					  or from start of mtd `partition'
  "
  	"					 `+len' round up `len' to block size
  "
  	"sf update addr offset|partition len	- erase and write `len' bytes from memory
  "
  	"					  at `addr' to flash at `offset'
  "
  	"					  or to start of mtd `partition'
  "
c3c016cf7   Fabio Estevam   sf: Add SPI NOR p...
630
631
632
633
  	"sf protect lock/unlock sector len	- protect/unprotect 'len' bytes starting
  "
  	"					  at address 'sector'
  "
240072731   Simon Glass   spi: Add SPI flas...
634
  	SF_TEST_HELP
a89c33db9   Wolfgang Denk   General help mess...
635
  );