Blame view

cmd/sf.c 15.4 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>
89054a546   Eric Lee   U-Boot v2018.03 s...
16
  #include <asm/gpio.h>
2ec1a4059   Heiko Schocher   spi, sf: Use offs...
17
18
  #include <jffs2/jffs2.h>
  #include <linux/mtd/mtd.h>
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
19
20
  
  #include <asm/io.h>
fbb099183   Simon Glass   dm: Convert spi_f...
21
  #include <dm/device-internal.h>
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
22

b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
23
  static struct spi_flash *flash;
334eb4b02   Richard Retanubun   cmd_sf: add handl...
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
56
  /*
   * 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...
57
  		*len = ROUND(len_arg, flash->sector_size);
334eb4b02   Richard Retanubun   cmd_sf: add handl...
58
59
60
61
62
  	else
  		*len = len_arg;
  
  	return 1;
  }
a683c288d   James Miller   spi: Add progress...
63
64
65
66
67
68
69
70
71
72
73
74
  /**
   * 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...
75
  		return len / (max(get_timer(start_ms) / 1024, 1UL));
a683c288d   James Miller   spi: Add progress...
76
  	else
b41411954   Masahiro Yamada   linux/kernel.h: s...
77
  		return 1024 * len / max(get_timer(start_ms), 1UL);
a683c288d   James Miller   spi: Add progress...
78
  }
54841ab50   Wolfgang Denk   Make sure that ar...
79
  static int do_spi_flash_probe(int argc, char * const argv[])
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
80
  {
c1173bd07   Eric Nelson   sf command: allow...
81
82
  	unsigned int bus = CONFIG_SF_DEFAULT_BUS;
  	unsigned int cs = CONFIG_SF_DEFAULT_CS;
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
  #ifdef CONFIG_DM_SPI_FLASH
  	struct udevice *new, *bus_dev;
  	int ret;
96907c0fe   Vignesh R   dm: spi: Read def...
89
90
  	/* In DM mode defaults will be taken from DT */
  	speed = 0, mode = 0;
fbb099183   Simon Glass   dm: Convert spi_f...
91
  #else
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
92
  	struct spi_flash *new;
fbb099183   Simon Glass   dm: Convert spi_f...
93
  #endif
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
94

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

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

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

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

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

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

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

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

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

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

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

8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
563
  	if (strcmp(cmd, "probe") == 0) {
89054a546   Eric Lee   U-Boot v2018.03 s...
564
565
566
         	gpio_request(IMX_GPIO_NR(4, 20), "SPI_LOCK_PIN");
         	/*Unlock SPI Flash*/
         	gpio_direction_output(IMX_GPIO_NR(4,20), 1);
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
567
  		ret = do_spi_flash_probe(argc, argv);
89054a546   Eric Lee   U-Boot v2018.03 s...
568
569
         	/*Lock Up SPI Flash*/
         	gpio_direction_output(IMX_GPIO_NR(4,20), 0);
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
570
571
  		goto done;
  	}
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
572
573
574
575
576
577
578
  
  	/* 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...
579
  	if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 ||
89054a546   Eric Lee   U-Boot v2018.03 s...
580
581
582
  	    strcmp(cmd, "update") == 0) {
         	/*Unlock SPI Flash*/
         	gpio_direction_output(IMX_GPIO_NR(4,20), 1);
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
583
  		ret = do_spi_flash_read_write(argc, argv);
89054a546   Eric Lee   U-Boot v2018.03 s...
584
585
586
587
588
589
         	/*Lock Up SPI Flash*/
          gpio_direction_output(IMX_GPIO_NR(4,20), 0);
  	}
  	else if (strcmp(cmd, "erase") == 0) {
         	/*Unlock SPI Flash*/
         	gpio_direction_output(IMX_GPIO_NR(4,20), 1);
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
590
  		ret = do_spi_flash_erase(argc, argv);
89054a546   Eric Lee   U-Boot v2018.03 s...
591
592
593
594
595
596
         	/*Lock Up SPI Flash*/
         	gpio_direction_output(IMX_GPIO_NR(4,20), 0);
  	}
  	else if (strcmp(cmd, "protect") == 0) {
         	/*Unlock SPI Flash*/
         	gpio_direction_output(IMX_GPIO_NR(4,20), 1);
c3c016cf7   Fabio Estevam   sf: Add SPI NOR p...
597
  		ret = do_spi_protect(argc, argv);
89054a546   Eric Lee   U-Boot v2018.03 s...
598
599
600
         	/*Lock Up SPI Flash*/
         	gpio_direction_output(IMX_GPIO_NR(4,20), 0);
  	}
240072731   Simon Glass   spi: Add SPI flas...
601
602
  #ifdef CONFIG_CMD_SF_TEST
  	else if (!strcmp(cmd, "test"))
89054a546   Eric Lee   U-Boot v2018.03 s...
603
604
         	/*Unlock SPI Flash*/
         	gpio_direction_output(IMX_GPIO_NR(4,20), 1);
240072731   Simon Glass   spi: Add SPI flas...
605
  		ret = do_spi_flash_test(argc, argv);
89054a546   Eric Lee   U-Boot v2018.03 s...
606
607
         	/*Lock Up SPI Flash*/
         	gpio_direction_output(IMX_GPIO_NR(4,20), 0);
240072731   Simon Glass   spi: Add SPI flas...
608
  #endif
8a07de03f   Mike Frysinger   cmd_sf: use cmd_u...
609
610
611
612
613
614
  	else
  		ret = -1;
  
  done:
  	if (ret != -1)
  		return ret;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
615
616
  
  usage:
4c12eeb8b   Simon Glass   Convert cmd_usage...
617
  	return CMD_RET_USAGE;
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
618
  }
240072731   Simon Glass   spi: Add SPI flas...
619
620
621
622
623
624
625
  #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...
626
627
  U_BOOT_CMD(
  	sf,	5,	1,	do_spi_flash,
2fb2604d5   Peter Tyser   Command usage cle...
628
  	"SPI flash sub-system",
c1173bd07   Eric Nelson   sf command: allow...
629
630
  	"probe [[bus:]cs] [hz] [mode]	- init flash device on given SPI bus
  "
b6368467e   Haavard Skinnemoen   SPI Flash: Add "s...
631
632
  	"				  and chip select
  "
2ec1a4059   Heiko Schocher   spi, sf: Use offs...
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
  	"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...
657
658
659
660
  	"sf protect lock/unlock sector len	- protect/unprotect 'len' bytes starting
  "
  	"					  at address 'sector'
  "
240072731   Simon Glass   spi: Add SPI flas...
661
  	SF_TEST_HELP
a89c33db9   Wolfgang Denk   General help mess...
662
  );