Blame view

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

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

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;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
82
83
84
  	unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
  	unsigned int mode = CONFIG_SF_DEFAULT_MODE;
  	char *endp;
fbb099183   Simon Glass   dm: Convert spi_f...
85
86
87
  #ifdef CONFIG_DM_SPI_FLASH
  	struct udevice *new, *bus_dev;
  	int ret;
96907c0fe   Vignesh R   dm: spi: Read def...
88
89
  	/* In DM mode defaults will be taken from DT */
  	speed = 0, mode = 0;
fbb099183   Simon Glass   dm: Convert spi_f...
90
  #else
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
91
  	struct spi_flash *new;
fbb099183   Simon Glass   dm: Convert spi_f...
92
  #endif
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
93

c1173bd07   Eric Nelson   sf command: allow...
94
95
96
  	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...
97
  			return -1;
c1173bd07   Eric Nelson   sf command: allow...
98
99
100
101
102
103
104
105
106
  		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...
107
108
109
110
111
  	}
  
  	if (argc >= 3) {
  		speed = simple_strtoul(argv[2], &endp, 0);
  		if (*argv[2] == 0 || *endp != 0)
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
112
  			return -1;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
113
114
  	}
  	if (argc >= 4) {
6e8d58d36   TsiChung Liew   Command for acces...
115
  		mode = simple_strtoul(argv[3], &endp, 16);
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
116
  		if (*argv[3] == 0 || *endp != 0)
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
117
  			return -1;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
118
  	}
fbb099183   Simon Glass   dm: Convert spi_f...
119
120
121
122
  #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...
123
  		device_remove(new, DM_REMOVE_NORMAL);
fbb099183   Simon Glass   dm: Convert spi_f...
124
125
126
127
128
129
130
131
132
  	}
  	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...
133
  	flash = dev_get_uclass_priv(new);
fbb099183   Simon Glass   dm: Convert spi_f...
134
  #else
8ee81b7f9   Heiko Schocher   mtd, spi: Check i...
135
136
  	if (flash)
  		spi_flash_free(flash);
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
137
  	new = spi_flash_probe(bus, cs, speed, mode);
8ee81b7f9   Heiko Schocher   mtd, spi: Check i...
138
  	flash = new;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
139
140
141
142
143
  	if (!new) {
  		printf("Failed to initialize SPI flash at %u:%u
  ", bus, cs);
  		return 1;
  	}
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
144
  	flash = new;
fbb099183   Simon Glass   dm: Convert spi_f...
145
  #endif
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
146

b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
147
  	return 0;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
148
  }
8d1af9422   Simon Glass   cmd_sf: add "upda...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  /**
   * 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...
166
  	char *ptr = (char *)buf;
a97f6efdb   Andreas Bießmann   cmd_sf.c: fix pri...
167
168
  	debug("offset=%#x, sector_size=%#x, len=%#zx
  ",
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
169
  	      offset, flash->sector_size, len);
b95f958d7   Gerlando Falauto   cmd_sf: let "sf u...
170
171
  	/* 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...
172
  		return "read";
b95f958d7   Gerlando Falauto   cmd_sf: let "sf u...
173
  	/* Compare only what is meaningful (len) */
8d1af9422   Simon Glass   cmd_sf: add "upda...
174
  	if (memcmp(cmp_buf, buf, len) == 0) {
a97f6efdb   Andreas Bießmann   cmd_sf.c: fix pri...
175
176
  		debug("Skip region %x size %zx: no change
  ",
402ba1e3a   Jagannadha Sutradharudu Teki   cmd_sf|env_sf: Fi...
177
  		      offset, len);
8d1af9422   Simon Glass   cmd_sf: add "upda...
178
179
180
  		*skipped += len;
  		return NULL;
  	}
348885062   Gerlando Falauto   cmd_sf: let "sf u...
181
182
  	/* Erase the entire sector */
  	if (spi_flash_erase(flash, offset, flash->sector_size))
8d1af9422   Simon Glass   cmd_sf: add "upda...
183
  		return "erase";
ed62756dc   Stefan Roese   cmd_sf: Fix probl...
184
  	/* If it's a partial sector, copy the data into the temp-buffer */
b95f958d7   Gerlando Falauto   cmd_sf: let "sf u...
185
  	if (len != flash->sector_size) {
ed62756dc   Stefan Roese   cmd_sf: Fix probl...
186
187
  		memcpy(cmp_buf, buf, len);
  		ptr = cmp_buf;
b95f958d7   Gerlando Falauto   cmd_sf: let "sf u...
188
  	}
ed62756dc   Stefan Roese   cmd_sf: Fix probl...
189
190
191
  	/* Write one complete sector */
  	if (spi_flash_write(flash, offset, flash->sector_size, ptr))
  		return "write";
b95f958d7   Gerlando Falauto   cmd_sf: let "sf u...
192

8d1af9422   Simon Glass   cmd_sf: add "upda...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  	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...
213
  	size_t skipped = 0;	/* statistics */
a683c288d   James Miller   spi: Add progress...
214
215
216
217
  	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...
218

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

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

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

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

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

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

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

96bbf5565   Jagannadha Sutradharudu Teki   cmd_sf: Add print...
343
  	return ret == 0 ? 0 : 1;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
344
  }
c3c016cf7   Fabio Estevam   sf: Add SPI NOR p...
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
376
  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...
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
406
  #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...
407
408
  	if (test->time_ms[stage])
  		do_div(speed, test->time_ms[stage] * 1024);
240072731   Simon Glass   spi: Add SPI flas...
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  	bps = speed * 8;
  
  	printf("%d %s: %d ticks, %d KiB/s %d.%03d Mbps
  ", stage,
  	       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 ...
435
436
  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...
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
  {
  	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...
462
463
  			print_buffer(i, vbuf + i, 1,
  				     min_t(uint, len - i, 0x40), 0);
240072731   Simon Glass   spi: Add SPI flas...
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
  			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...
488
489
  			print_buffer(i, buf + i, 1,
  				     min_t(uint, len - i, 0x40), 0);
240072731   Simon Glass   spi: Add SPI flas...
490
491
  			printf("Bad data:
  ");
b41411954   Masahiro Yamada   linux/kernel.h: s...
492
493
  			print_buffer(i, vbuf + i, 1,
  				     min_t(uint, len - i, 0x40), 0);
240072731   Simon Glass   spi: Add SPI flas...
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
  			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...
509
  	uint8_t *buf, *from;
240072731   Simon Glass   spi: Add SPI flas...
510
  	char *endp;
1e7133e99   Simon Glass   sf: Use unsigned ...
511
  	uint8_t *vbuf;
240072731   Simon Glass   spi: Add SPI flas...
512
  	int ret;
d1f22d4bd   Simon Glass   sandbox: spi: Adj...
513
514
  	if (argc < 3)
  		return -1;
240072731   Simon Glass   spi: Add SPI flas...
515
516
517
518
519
520
  	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...
521
  	vbuf = memalign(ARCH_DMA_MINALIGN, len);
240072731   Simon Glass   spi: Add SPI flas...
522
  	if (!vbuf) {
d1f22d4bd   Simon Glass   sandbox: spi: Adj...
523
524
  		printf("Cannot allocate memory (%lu bytes)
  ", len);
240072731   Simon Glass   spi: Add SPI flas...
525
526
  		return 1;
  	}
156e96f03   Ravi Babu   sf: allocate cach...
527
  	buf = memalign(ARCH_DMA_MINALIGN, len);
240072731   Simon Glass   spi: Add SPI flas...
528
529
  	if (!buf) {
  		free(vbuf);
d1f22d4bd   Simon Glass   sandbox: spi: Adj...
530
531
  		printf("Cannot allocate memory (%lu bytes)
  ", len);
240072731   Simon Glass   spi: Add SPI flas...
532
533
  		return 1;
  	}
d1f22d4bd   Simon Glass   sandbox: spi: Adj...
534
535
  	from = map_sysmem(CONFIG_SYS_TEXT_BASE, 0);
  	memcpy(buf, from, len);
240072731   Simon Glass   spi: Add SPI flas...
536
537
538
539
540
541
542
543
544
545
546
547
  	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...
548
549
  static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc,
  			char * const argv[])
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
550
551
  {
  	const char *cmd;
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
552
  	int ret;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
553
554
555
556
557
558
  
  	/* need at least two arguments */
  	if (argc < 2)
  		goto usage;
  
  	cmd = argv[1];
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
559
560
  	--argc;
  	++argv;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
561

8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
562
563
564
565
  	if (strcmp(cmd, "probe") == 0) {
  		ret = do_spi_flash_probe(argc, argv);
  		goto done;
  	}
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
566
567
568
569
570
571
572
  
  	/* 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...
573
574
  	if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 ||
  	    strcmp(cmd, "update") == 0)
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
575
576
577
  		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...
578
579
  	else if (strcmp(cmd, "protect") == 0)
  		ret = do_spi_protect(argc, argv);
240072731   Simon Glass   spi: Add SPI flas...
580
581
582
583
  #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...
584
585
586
587
588
589
  	else
  		ret = -1;
  
  done:
  	if (ret != -1)
  		return ret;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
590
591
  
  usage:
4c12eeb8b   Simon Glass   Convert cmd_usage...
592
  	return CMD_RET_USAGE;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
593
  }
240072731   Simon Glass   spi: Add SPI flas...
594
595
596
597
598
599
600
  #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...
601
602
  U_BOOT_CMD(
  	sf,	5,	1,	do_spi_flash,
2fb2604d5   Peter Tyser   Command usage cle...
603
  	"SPI flash sub-system",
c1173bd07   Eric Nelson   sf command: allow...
604
605
  	"probe [[bus:]cs] [hz] [mode]	- init flash device on given SPI bus
  "
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
606
607
  	"				  and chip select
  "
2ec1a4059   Heiko Schocher   spi, sf: Use offs...
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
  	"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...
632
633
634
635
  	"sf protect lock/unlock sector len	- protect/unprotect 'len' bytes starting
  "
  	"					  at address 'sector'
  "
240072731   Simon Glass   spi: Add SPI flas...
636
  	SF_TEST_HELP
a89c33db9   Wolfgang Denk   General help mess...
637
  );