]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/cmd_sf.c
spi: Add progress percentage and write speed to `sf update`
[karo-tx-uboot.git] / common / cmd_sf.c
1 /*
2  * Command for accessing SPI flash.
3  *
4  * Copyright (C) 2008 Atmel Corporation
5  * Licensed under the GPL-2 or later.
6  */
7
8 #include <common.h>
9 #include <malloc.h>
10 #include <spi_flash.h>
11
12 #include <asm/io.h>
13
14 #ifndef CONFIG_SF_DEFAULT_SPEED
15 # define CONFIG_SF_DEFAULT_SPEED        1000000
16 #endif
17 #ifndef CONFIG_SF_DEFAULT_MODE
18 # define CONFIG_SF_DEFAULT_MODE         SPI_MODE_3
19 #endif
20 #ifndef CONFIG_SF_DEFAULT_CS
21 # define CONFIG_SF_DEFAULT_CS           0
22 #endif
23 #ifndef CONFIG_SF_DEFAULT_BUS
24 # define CONFIG_SF_DEFAULT_BUS          0
25 #endif
26
27 static struct spi_flash *flash;
28
29
30 /*
31  * This function computes the length argument for the erase command.
32  * The length on which the command is to operate can be given in two forms:
33  * 1. <cmd> offset len  - operate on <'offset',  'len')
34  * 2. <cmd> offset +len - operate on <'offset',  'round_up(len)')
35  * If the second form is used and the length doesn't fall on the
36  * sector boundary, than it will be adjusted to the next sector boundary.
37  * If it isn't in the flash, the function will fail (return -1).
38  * Input:
39  *    arg: length specification (i.e. both command arguments)
40  * Output:
41  *    len: computed length for operation
42  * Return:
43  *    1: success
44  *   -1: failure (bad format, bad address).
45  */
46 static int sf_parse_len_arg(char *arg, ulong *len)
47 {
48         char *ep;
49         char round_up_len; /* indicates if the "+length" form used */
50         ulong len_arg;
51
52         round_up_len = 0;
53         if (*arg == '+') {
54                 round_up_len = 1;
55                 ++arg;
56         }
57
58         len_arg = simple_strtoul(arg, &ep, 16);
59         if (ep == arg || *ep != '\0')
60                 return -1;
61
62         if (round_up_len && flash->sector_size > 0)
63                 *len = ROUND(len_arg, flash->sector_size);
64         else
65                 *len = len_arg;
66
67         return 1;
68 }
69
70 /**
71  * This function takes a byte length and a delta unit of time to compute the
72  * approximate bytes per second
73  *
74  * @param len           amount of bytes currently processed
75  * @param start_ms      start time of processing in ms
76  * @return bytes per second if OK, 0 on error
77  */
78 static ulong bytes_per_second(unsigned int len, ulong start_ms)
79 {
80         /* less accurate but avoids overflow */
81         if (len >= ((unsigned int) -1) / 1024)
82                 return len / (max(get_timer(start_ms) / 1024, 1));
83         else
84                 return 1024 * len / max(get_timer(start_ms), 1);
85 }
86
87 static int do_spi_flash_probe(int argc, char * const argv[])
88 {
89         unsigned int bus = CONFIG_SF_DEFAULT_BUS;
90         unsigned int cs = CONFIG_SF_DEFAULT_CS;
91         unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
92         unsigned int mode = CONFIG_SF_DEFAULT_MODE;
93         char *endp;
94         struct spi_flash *new;
95
96         if (argc >= 2) {
97                 cs = simple_strtoul(argv[1], &endp, 0);
98                 if (*argv[1] == 0 || (*endp != 0 && *endp != ':'))
99                         return -1;
100                 if (*endp == ':') {
101                         if (endp[1] == 0)
102                                 return -1;
103
104                         bus = cs;
105                         cs = simple_strtoul(endp + 1, &endp, 0);
106                         if (*endp != 0)
107                                 return -1;
108                 }
109         }
110
111         if (argc >= 3) {
112                 speed = simple_strtoul(argv[2], &endp, 0);
113                 if (*argv[2] == 0 || *endp != 0)
114                         return -1;
115         }
116         if (argc >= 4) {
117                 mode = simple_strtoul(argv[3], &endp, 16);
118                 if (*argv[3] == 0 || *endp != 0)
119                         return -1;
120         }
121
122         new = spi_flash_probe(bus, cs, speed, mode);
123         if (!new) {
124                 printf("Failed to initialize SPI flash at %u:%u\n", bus, cs);
125                 return 1;
126         }
127
128         if (flash)
129                 spi_flash_free(flash);
130         flash = new;
131
132         return 0;
133 }
134
135 /**
136  * Write a block of data to SPI flash, first checking if it is different from
137  * what is already there.
138  *
139  * If the data being written is the same, then *skipped is incremented by len.
140  *
141  * @param flash         flash context pointer
142  * @param offset        flash offset to write
143  * @param len           number of bytes to write
144  * @param buf           buffer to write from
145  * @param cmp_buf       read buffer to use to compare data
146  * @param skipped       Count of skipped data (incremented by this function)
147  * @return NULL if OK, else a string containing the stage which failed
148  */
149 static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset,
150                 size_t len, const char *buf, char *cmp_buf, size_t *skipped)
151 {
152         debug("offset=%#x, sector_size=%#x, len=%#zx\n",
153                 offset, flash->sector_size, len);
154         if (spi_flash_read(flash, offset, len, cmp_buf))
155                 return "read";
156         if (memcmp(cmp_buf, buf, len) == 0) {
157                 debug("Skip region %x size %zx: no change\n",
158                         offset, len);
159                 *skipped += len;
160                 return NULL;
161         }
162         if (spi_flash_erase(flash, offset, len))
163                 return "erase";
164         if (spi_flash_write(flash, offset, len, buf))
165                 return "write";
166         return NULL;
167 }
168
169 /**
170  * Update an area of SPI flash by erasing and writing any blocks which need
171  * to change. Existing blocks with the correct data are left unchanged.
172  *
173  * @param flash         flash context pointer
174  * @param offset        flash offset to write
175  * @param len           number of bytes to write
176  * @param buf           buffer to write from
177  * @return 0 if ok, 1 on error
178  */
179 static int spi_flash_update(struct spi_flash *flash, u32 offset,
180                 size_t len, const char *buf)
181 {
182         const char *err_oper = NULL;
183         char *cmp_buf;
184         const char *end = buf + len;
185         size_t todo;            /* number of bytes to do in this pass */
186         size_t skipped = 0;     /* statistics */
187         const ulong start_time = get_timer(0);
188         size_t scale = 1;
189         const char *start_buf = buf;
190         ulong delta;
191
192         if (end - buf >= 200)
193                 scale = (end - buf) / 100;
194         cmp_buf = malloc(flash->sector_size);
195         if (cmp_buf) {
196                 ulong last_update = get_timer(0);
197
198                 for (; buf < end && !err_oper; buf += todo, offset += todo) {
199                         todo = min(end - buf, flash->sector_size);
200                         if (get_timer(last_update) > 100) {
201                                 printf("   \rUpdating, %zu%% %lu B/s",
202                                         100 - (end - buf) / scale,
203                                         bytes_per_second(buf - start_buf,
204                                                          start_time));
205                                 last_update = get_timer(0);
206                         }
207                         err_oper = spi_flash_update_block(flash, offset, todo,
208                                         buf, cmp_buf, &skipped);
209                 }
210         } else {
211                 err_oper = "malloc";
212         }
213         free(cmp_buf);
214         putc('\r');
215         if (err_oper) {
216                 printf("SPI flash failed in %s step\n", err_oper);
217                 return 1;
218         }
219
220         delta = get_timer(start_time);
221         printf("%zu bytes written, %zu bytes skipped", len - skipped,
222                 skipped);
223         printf(" in %ld.%lds, speed %ld B/s\n",
224                 delta / 1000, delta % 1000, bytes_per_second(len, start_time));
225
226         return 0;
227 }
228
229 static int do_spi_flash_read_write(int argc, char * const argv[])
230 {
231         unsigned long addr;
232         unsigned long offset;
233         unsigned long len;
234         void *buf;
235         char *endp;
236         int ret;
237
238         if (argc < 4)
239                 return -1;
240
241         addr = simple_strtoul(argv[1], &endp, 16);
242         if (*argv[1] == 0 || *endp != 0)
243                 return -1;
244         offset = simple_strtoul(argv[2], &endp, 16);
245         if (*argv[2] == 0 || *endp != 0)
246                 return -1;
247         len = simple_strtoul(argv[3], &endp, 16);
248         if (*argv[3] == 0 || *endp != 0)
249                 return -1;
250
251         /* Consistency checking */
252         if (offset + len > flash->size) {
253                 printf("ERROR: attempting %s past flash size (%#x)\n",
254                         argv[0], flash->size);
255                 return 1;
256         }
257
258         buf = map_physmem(addr, len, MAP_WRBACK);
259         if (!buf) {
260                 puts("Failed to map physical memory\n");
261                 return 1;
262         }
263
264         if (strcmp(argv[0], "update") == 0)
265                 ret = spi_flash_update(flash, offset, len, buf);
266         else if (strcmp(argv[0], "read") == 0)
267                 ret = spi_flash_read(flash, offset, len, buf);
268         else
269                 ret = spi_flash_write(flash, offset, len, buf);
270
271         unmap_physmem(buf, len);
272
273         if (ret) {
274                 printf("SPI flash %s failed\n", argv[0]);
275                 return 1;
276         }
277
278         return 0;
279 }
280
281 static int do_spi_flash_erase(int argc, char * const argv[])
282 {
283         unsigned long offset;
284         unsigned long len;
285         char *endp;
286         int ret;
287
288         if (argc < 3)
289                 return -1;
290
291         offset = simple_strtoul(argv[1], &endp, 16);
292         if (*argv[1] == 0 || *endp != 0)
293                 return -1;
294
295         ret = sf_parse_len_arg(argv[2], &len);
296         if (ret != 1)
297                 return -1;
298
299         /* Consistency checking */
300         if (offset + len > flash->size) {
301                 printf("ERROR: attempting %s past flash size (%#x)\n",
302                         argv[0], flash->size);
303                 return 1;
304         }
305
306         ret = spi_flash_erase(flash, offset, len);
307         if (ret) {
308                 printf("SPI flash %s failed\n", argv[0]);
309                 return 1;
310         }
311
312         return 0;
313 }
314
315 static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
316 {
317         const char *cmd;
318         int ret;
319
320         /* need at least two arguments */
321         if (argc < 2)
322                 goto usage;
323
324         cmd = argv[1];
325         --argc;
326         ++argv;
327
328         if (strcmp(cmd, "probe") == 0) {
329                 ret = do_spi_flash_probe(argc, argv);
330                 goto done;
331         }
332
333         /* The remaining commands require a selected device */
334         if (!flash) {
335                 puts("No SPI flash selected. Please run `sf probe'\n");
336                 return 1;
337         }
338
339         if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 ||
340             strcmp(cmd, "update") == 0)
341                 ret = do_spi_flash_read_write(argc, argv);
342         else if (strcmp(cmd, "erase") == 0)
343                 ret = do_spi_flash_erase(argc, argv);
344         else
345                 ret = -1;
346
347 done:
348         if (ret != -1)
349                 return ret;
350
351 usage:
352         return CMD_RET_USAGE;
353 }
354
355 U_BOOT_CMD(
356         sf,     5,      1,      do_spi_flash,
357         "SPI flash sub-system",
358         "probe [[bus:]cs] [hz] [mode]   - init flash device on given SPI bus\n"
359         "                                 and chip select\n"
360         "sf read addr offset len        - read `len' bytes starting at\n"
361         "                                 `offset' to memory at `addr'\n"
362         "sf write addr offset len       - write `len' bytes from memory\n"
363         "                                 at `addr' to flash at `offset'\n"
364         "sf erase offset [+]len         - erase `len' bytes from `offset'\n"
365         "                                 `+len' round up `len' to block size\n"
366         "sf update addr offset len      - erase and write `len' bytes from memory\n"
367         "                                 at `addr' to flash at `offset'"
368 );