]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/cmd_sf.c
common: fix behavior of ROUND macro when input is already rounded
[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 <spi_flash.h>
10
11 #include <asm/io.h>
12
13 #ifndef CONFIG_SF_DEFAULT_SPEED
14 # define CONFIG_SF_DEFAULT_SPEED        1000000
15 #endif
16 #ifndef CONFIG_SF_DEFAULT_MODE
17 # define CONFIG_SF_DEFAULT_MODE         SPI_MODE_3
18 #endif
19
20 static struct spi_flash *flash;
21
22
23 /*
24  * This function computes the length argument for the erase command.
25  * The length on which the command is to operate can be given in two forms:
26  * 1. <cmd> offset len  - operate on <'offset',  'len')
27  * 2. <cmd> offset +len - operate on <'offset',  'round_up(len)')
28  * If the second form is used and the length doesn't fall on the
29  * sector boundary, than it will be adjusted to the next sector boundary.
30  * If it isn't in the flash, the function will fail (return -1).
31  * Input:
32  *    arg: length specification (i.e. both command arguments)
33  * Output:
34  *    len: computed length for operation
35  * Return:
36  *    1: success
37  *   -1: failure (bad format, bad address).
38  */
39 static int sf_parse_len_arg(char *arg, ulong *len)
40 {
41         char *ep;
42         char round_up_len; /* indicates if the "+length" form used */
43         ulong len_arg;
44
45         round_up_len = 0;
46         if (*arg == '+') {
47                 round_up_len = 1;
48                 ++arg;
49         }
50
51         len_arg = simple_strtoul(arg, &ep, 16);
52         if (ep == arg || *ep != '\0')
53                 return -1;
54
55         if (round_up_len && flash->sector_size > 0)
56                 *len = ROUND(len_arg, flash->sector_size);
57         else
58                 *len = len_arg;
59
60         return 1;
61 }
62
63 static int do_spi_flash_probe(int argc, char * const argv[])
64 {
65         unsigned int bus = 0;
66         unsigned int cs;
67         unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
68         unsigned int mode = CONFIG_SF_DEFAULT_MODE;
69         char *endp;
70         struct spi_flash *new;
71
72         if (argc < 2)
73                 return -1;
74
75         cs = simple_strtoul(argv[1], &endp, 0);
76         if (*argv[1] == 0 || (*endp != 0 && *endp != ':'))
77                 return -1;
78         if (*endp == ':') {
79                 if (endp[1] == 0)
80                         return -1;
81
82                 bus = cs;
83                 cs = simple_strtoul(endp + 1, &endp, 0);
84                 if (*endp != 0)
85                         return -1;
86         }
87
88         if (argc >= 3) {
89                 speed = simple_strtoul(argv[2], &endp, 0);
90                 if (*argv[2] == 0 || *endp != 0)
91                         return -1;
92         }
93         if (argc >= 4) {
94                 mode = simple_strtoul(argv[3], &endp, 16);
95                 if (*argv[3] == 0 || *endp != 0)
96                         return -1;
97         }
98
99         new = spi_flash_probe(bus, cs, speed, mode);
100         if (!new) {
101                 printf("Failed to initialize SPI flash at %u:%u\n", bus, cs);
102                 return 1;
103         }
104
105         if (flash)
106                 spi_flash_free(flash);
107         flash = new;
108
109         return 0;
110 }
111
112 static int do_spi_flash_read_write(int argc, char * const argv[])
113 {
114         unsigned long addr;
115         unsigned long offset;
116         unsigned long len;
117         void *buf;
118         char *endp;
119         int ret;
120
121         if (argc < 4)
122                 return -1;
123
124         addr = simple_strtoul(argv[1], &endp, 16);
125         if (*argv[1] == 0 || *endp != 0)
126                 return -1;
127         offset = simple_strtoul(argv[2], &endp, 16);
128         if (*argv[2] == 0 || *endp != 0)
129                 return -1;
130         len = simple_strtoul(argv[3], &endp, 16);
131         if (*argv[3] == 0 || *endp != 0)
132                 return -1;
133
134         buf = map_physmem(addr, len, MAP_WRBACK);
135         if (!buf) {
136                 puts("Failed to map physical memory\n");
137                 return 1;
138         }
139
140         if (strcmp(argv[0], "read") == 0)
141                 ret = spi_flash_read(flash, offset, len, buf);
142         else
143                 ret = spi_flash_write(flash, offset, len, buf);
144
145         unmap_physmem(buf, len);
146
147         if (ret) {
148                 printf("SPI flash %s failed\n", argv[0]);
149                 return 1;
150         }
151
152         return 0;
153 }
154
155 static int do_spi_flash_erase(int argc, char * const argv[])
156 {
157         unsigned long offset;
158         unsigned long len;
159         char *endp;
160         int ret;
161
162         if (argc < 3)
163                 return -1;
164
165         offset = simple_strtoul(argv[1], &endp, 16);
166         if (*argv[1] == 0 || *endp != 0)
167                 return -1;
168
169         ret = sf_parse_len_arg(argv[2], &len);
170         if (ret != 1)
171                 return -1;
172
173         ret = spi_flash_erase(flash, offset, len);
174         if (ret) {
175                 printf("SPI flash %s failed\n", argv[0]);
176                 return 1;
177         }
178
179         return 0;
180 }
181
182 static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
183 {
184         const char *cmd;
185         int ret;
186
187         /* need at least two arguments */
188         if (argc < 2)
189                 goto usage;
190
191         cmd = argv[1];
192         --argc;
193         ++argv;
194
195         if (strcmp(cmd, "probe") == 0) {
196                 ret = do_spi_flash_probe(argc, argv);
197                 goto done;
198         }
199
200         /* The remaining commands require a selected device */
201         if (!flash) {
202                 puts("No SPI flash selected. Please run `sf probe'\n");
203                 return 1;
204         }
205
206         if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0)
207                 ret = do_spi_flash_read_write(argc, argv);
208         else if (strcmp(cmd, "erase") == 0)
209                 ret = do_spi_flash_erase(argc, argv);
210         else
211                 ret = -1;
212
213 done:
214         if (ret != -1)
215                 return ret;
216
217 usage:
218         return cmd_usage(cmdtp);
219 }
220
221 U_BOOT_CMD(
222         sf,     5,      1,      do_spi_flash,
223         "SPI flash sub-system",
224         "probe [bus:]cs [hz] [mode]     - init flash device on given SPI bus\n"
225         "                                 and chip select\n"
226         "sf read addr offset len        - read `len' bytes starting at\n"
227         "                                 `offset' to memory at `addr'\n"
228         "sf write addr offset len       - write `len' bytes from memory\n"
229         "                                 at `addr' to flash at `offset'\n"
230         "sf erase offset [+]len         - erase `len' bytes from `offset'\n"
231         "                                 `+len' round up `len' to block size"
232 );