Commit 029e728fc247bff680d202316071af64ca376aea
1 parent
27b1dc5fe2
Exists in
smarc-imx-l5.0.0_1.0.0-ga
Unlock SPI NOR in sf utility.
Showing 1 changed file with 15 additions and 3 deletions Inline Diff
common/cmd_sf.c
1 | /* | 1 | /* |
2 | * Command for accessing SPI flash. | 2 | * Command for accessing SPI flash. |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Atmel Corporation | 4 | * Copyright (C) 2008 Atmel Corporation |
5 | * | 5 | * |
6 | * SPDX-License-Identifier: GPL-2.0+ | 6 | * SPDX-License-Identifier: GPL-2.0+ |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <common.h> | 9 | #include <common.h> |
10 | #include <div64.h> | 10 | #include <div64.h> |
11 | #include <malloc.h> | 11 | #include <malloc.h> |
12 | #include <spi_flash.h> | 12 | #include <spi_flash.h> |
13 | #include <asm/gpio.h> | ||
13 | 14 | ||
14 | #include <asm/io.h> | 15 | #include <asm/io.h> |
15 | 16 | ||
16 | #ifndef CONFIG_SF_DEFAULT_SPEED | 17 | #ifndef CONFIG_SF_DEFAULT_SPEED |
17 | # define CONFIG_SF_DEFAULT_SPEED 1000000 | 18 | # define CONFIG_SF_DEFAULT_SPEED 1000000 |
18 | #endif | 19 | #endif |
19 | #ifndef CONFIG_SF_DEFAULT_MODE | 20 | #ifndef CONFIG_SF_DEFAULT_MODE |
20 | # define CONFIG_SF_DEFAULT_MODE SPI_MODE_3 | 21 | # define CONFIG_SF_DEFAULT_MODE SPI_MODE_3 |
21 | #endif | 22 | #endif |
22 | #ifndef CONFIG_SF_DEFAULT_CS | 23 | #ifndef CONFIG_SF_DEFAULT_CS |
23 | # define CONFIG_SF_DEFAULT_CS 0 | 24 | # define CONFIG_SF_DEFAULT_CS 0 |
24 | #endif | 25 | #endif |
25 | #ifndef CONFIG_SF_DEFAULT_BUS | 26 | #ifndef CONFIG_SF_DEFAULT_BUS |
26 | # define CONFIG_SF_DEFAULT_BUS 0 | 27 | # define CONFIG_SF_DEFAULT_BUS 0 |
27 | #endif | 28 | #endif |
28 | 29 | ||
29 | static struct spi_flash *flash; | 30 | static struct spi_flash *flash; |
30 | 31 | ||
31 | 32 | ||
32 | /* | 33 | /* |
33 | * This function computes the length argument for the erase command. | 34 | * This function computes the length argument for the erase command. |
34 | * The length on which the command is to operate can be given in two forms: | 35 | * The length on which the command is to operate can be given in two forms: |
35 | * 1. <cmd> offset len - operate on <'offset', 'len') | 36 | * 1. <cmd> offset len - operate on <'offset', 'len') |
36 | * 2. <cmd> offset +len - operate on <'offset', 'round_up(len)') | 37 | * 2. <cmd> offset +len - operate on <'offset', 'round_up(len)') |
37 | * If the second form is used and the length doesn't fall on the | 38 | * If the second form is used and the length doesn't fall on the |
38 | * sector boundary, than it will be adjusted to the next sector boundary. | 39 | * sector boundary, than it will be adjusted to the next sector boundary. |
39 | * If it isn't in the flash, the function will fail (return -1). | 40 | * If it isn't in the flash, the function will fail (return -1). |
40 | * Input: | 41 | * Input: |
41 | * arg: length specification (i.e. both command arguments) | 42 | * arg: length specification (i.e. both command arguments) |
42 | * Output: | 43 | * Output: |
43 | * len: computed length for operation | 44 | * len: computed length for operation |
44 | * Return: | 45 | * Return: |
45 | * 1: success | 46 | * 1: success |
46 | * -1: failure (bad format, bad address). | 47 | * -1: failure (bad format, bad address). |
47 | */ | 48 | */ |
48 | static int sf_parse_len_arg(char *arg, ulong *len) | 49 | static int sf_parse_len_arg(char *arg, ulong *len) |
49 | { | 50 | { |
50 | char *ep; | 51 | char *ep; |
51 | char round_up_len; /* indicates if the "+length" form used */ | 52 | char round_up_len; /* indicates if the "+length" form used */ |
52 | ulong len_arg; | 53 | ulong len_arg; |
53 | 54 | ||
54 | round_up_len = 0; | 55 | round_up_len = 0; |
55 | if (*arg == '+') { | 56 | if (*arg == '+') { |
56 | round_up_len = 1; | 57 | round_up_len = 1; |
57 | ++arg; | 58 | ++arg; |
58 | } | 59 | } |
59 | 60 | ||
60 | len_arg = simple_strtoul(arg, &ep, 16); | 61 | len_arg = simple_strtoul(arg, &ep, 16); |
61 | if (ep == arg || *ep != '\0') | 62 | if (ep == arg || *ep != '\0') |
62 | return -1; | 63 | return -1; |
63 | 64 | ||
64 | if (round_up_len && flash->sector_size > 0) | 65 | if (round_up_len && flash->sector_size > 0) |
65 | *len = ROUND(len_arg, flash->sector_size); | 66 | *len = ROUND(len_arg, flash->sector_size); |
66 | else | 67 | else |
67 | *len = len_arg; | 68 | *len = len_arg; |
68 | 69 | ||
69 | return 1; | 70 | return 1; |
70 | } | 71 | } |
71 | 72 | ||
72 | /** | 73 | /** |
73 | * This function takes a byte length and a delta unit of time to compute the | 74 | * This function takes a byte length and a delta unit of time to compute the |
74 | * approximate bytes per second | 75 | * approximate bytes per second |
75 | * | 76 | * |
76 | * @param len amount of bytes currently processed | 77 | * @param len amount of bytes currently processed |
77 | * @param start_ms start time of processing in ms | 78 | * @param start_ms start time of processing in ms |
78 | * @return bytes per second if OK, 0 on error | 79 | * @return bytes per second if OK, 0 on error |
79 | */ | 80 | */ |
80 | static ulong bytes_per_second(unsigned int len, ulong start_ms) | 81 | static ulong bytes_per_second(unsigned int len, ulong start_ms) |
81 | { | 82 | { |
82 | /* less accurate but avoids overflow */ | 83 | /* less accurate but avoids overflow */ |
83 | if (len >= ((unsigned int) -1) / 1024) | 84 | if (len >= ((unsigned int) -1) / 1024) |
84 | return len / (max(get_timer(start_ms) / 1024, 1)); | 85 | return len / (max(get_timer(start_ms) / 1024, 1)); |
85 | else | 86 | else |
86 | return 1024 * len / max(get_timer(start_ms), 1); | 87 | return 1024 * len / max(get_timer(start_ms), 1); |
87 | } | 88 | } |
88 | 89 | ||
89 | static int do_spi_flash_probe(int argc, char * const argv[]) | 90 | static int do_spi_flash_probe(int argc, char * const argv[]) |
90 | { | 91 | { |
91 | unsigned int bus = CONFIG_SF_DEFAULT_BUS; | 92 | unsigned int bus = CONFIG_SF_DEFAULT_BUS; |
92 | unsigned int cs = CONFIG_SF_DEFAULT_CS; | 93 | unsigned int cs = CONFIG_SF_DEFAULT_CS; |
93 | unsigned int speed = CONFIG_SF_DEFAULT_SPEED; | 94 | unsigned int speed = CONFIG_SF_DEFAULT_SPEED; |
94 | unsigned int mode = CONFIG_SF_DEFAULT_MODE; | 95 | unsigned int mode = CONFIG_SF_DEFAULT_MODE; |
95 | char *endp; | 96 | char *endp; |
96 | struct spi_flash *new; | 97 | struct spi_flash *new; |
97 | 98 | ||
98 | if (argc >= 2) { | 99 | if (argc >= 2) { |
99 | cs = simple_strtoul(argv[1], &endp, 0); | 100 | cs = simple_strtoul(argv[1], &endp, 0); |
100 | if (*argv[1] == 0 || (*endp != 0 && *endp != ':')) | 101 | if (*argv[1] == 0 || (*endp != 0 && *endp != ':')) |
101 | return -1; | 102 | return -1; |
102 | if (*endp == ':') { | 103 | if (*endp == ':') { |
103 | if (endp[1] == 0) | 104 | if (endp[1] == 0) |
104 | return -1; | 105 | return -1; |
105 | 106 | ||
106 | bus = cs; | 107 | bus = cs; |
107 | cs = simple_strtoul(endp + 1, &endp, 0); | 108 | cs = simple_strtoul(endp + 1, &endp, 0); |
108 | if (*endp != 0) | 109 | if (*endp != 0) |
109 | return -1; | 110 | return -1; |
110 | } | 111 | } |
111 | } | 112 | } |
112 | 113 | ||
113 | if (argc >= 3) { | 114 | if (argc >= 3) { |
114 | speed = simple_strtoul(argv[2], &endp, 0); | 115 | speed = simple_strtoul(argv[2], &endp, 0); |
115 | if (*argv[2] == 0 || *endp != 0) | 116 | if (*argv[2] == 0 || *endp != 0) |
116 | return -1; | 117 | return -1; |
117 | } | 118 | } |
118 | if (argc >= 4) { | 119 | if (argc >= 4) { |
119 | mode = simple_strtoul(argv[3], &endp, 16); | 120 | mode = simple_strtoul(argv[3], &endp, 16); |
120 | if (*argv[3] == 0 || *endp != 0) | 121 | if (*argv[3] == 0 || *endp != 0) |
121 | return -1; | 122 | return -1; |
122 | } | 123 | } |
123 | 124 | ||
124 | new = spi_flash_probe(bus, cs, speed, mode); | 125 | new = spi_flash_probe(bus, cs, speed, mode); |
125 | if (!new) { | 126 | if (!new) { |
126 | printf("Failed to initialize SPI flash at %u:%u\n", bus, cs); | 127 | printf("Failed to initialize SPI flash at %u:%u\n", bus, cs); |
127 | return 1; | 128 | return 1; |
128 | } | 129 | } |
129 | 130 | ||
130 | if (flash) | 131 | if (flash) |
131 | spi_flash_free(flash); | 132 | spi_flash_free(flash); |
132 | flash = new; | 133 | flash = new; |
133 | 134 | ||
134 | return 0; | 135 | return 0; |
135 | } | 136 | } |
136 | 137 | ||
137 | /** | 138 | /** |
138 | * Write a block of data to SPI flash, first checking if it is different from | 139 | * Write a block of data to SPI flash, first checking if it is different from |
139 | * what is already there. | 140 | * what is already there. |
140 | * | 141 | * |
141 | * If the data being written is the same, then *skipped is incremented by len. | 142 | * If the data being written is the same, then *skipped is incremented by len. |
142 | * | 143 | * |
143 | * @param flash flash context pointer | 144 | * @param flash flash context pointer |
144 | * @param offset flash offset to write | 145 | * @param offset flash offset to write |
145 | * @param len number of bytes to write | 146 | * @param len number of bytes to write |
146 | * @param buf buffer to write from | 147 | * @param buf buffer to write from |
147 | * @param cmp_buf read buffer to use to compare data | 148 | * @param cmp_buf read buffer to use to compare data |
148 | * @param skipped Count of skipped data (incremented by this function) | 149 | * @param skipped Count of skipped data (incremented by this function) |
149 | * @return NULL if OK, else a string containing the stage which failed | 150 | * @return NULL if OK, else a string containing the stage which failed |
150 | */ | 151 | */ |
151 | static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset, | 152 | static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset, |
152 | size_t len, const char *buf, char *cmp_buf, size_t *skipped) | 153 | size_t len, const char *buf, char *cmp_buf, size_t *skipped) |
153 | { | 154 | { |
154 | debug("offset=%#x, sector_size=%#x, len=%#zx\n", | 155 | debug("offset=%#x, sector_size=%#x, len=%#zx\n", |
155 | offset, flash->sector_size, len); | 156 | offset, flash->sector_size, len); |
156 | /* Read the entire sector so to allow for rewriting */ | 157 | /* Read the entire sector so to allow for rewriting */ |
157 | if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf)) | 158 | if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf)) |
158 | return "read"; | 159 | return "read"; |
159 | /* Compare only what is meaningful (len) */ | 160 | /* Compare only what is meaningful (len) */ |
160 | if (memcmp(cmp_buf, buf, len) == 0) { | 161 | if (memcmp(cmp_buf, buf, len) == 0) { |
161 | debug("Skip region %x size %zx: no change\n", | 162 | debug("Skip region %x size %zx: no change\n", |
162 | offset, len); | 163 | offset, len); |
163 | *skipped += len; | 164 | *skipped += len; |
164 | return NULL; | 165 | return NULL; |
165 | } | 166 | } |
166 | /* Erase the entire sector */ | 167 | /* Erase the entire sector */ |
167 | if (spi_flash_erase(flash, offset, flash->sector_size)) | 168 | if (spi_flash_erase(flash, offset, flash->sector_size)) |
168 | return "erase"; | 169 | return "erase"; |
169 | /* Write the initial part of the block from the source */ | 170 | /* Write the initial part of the block from the source */ |
170 | if (spi_flash_write(flash, offset, len, buf)) | 171 | if (spi_flash_write(flash, offset, len, buf)) |
171 | return "write"; | 172 | return "write"; |
172 | /* If it's a partial sector, rewrite the existing part */ | 173 | /* If it's a partial sector, rewrite the existing part */ |
173 | if (len != flash->sector_size) { | 174 | if (len != flash->sector_size) { |
174 | /* Rewrite the original data to the end of the sector */ | 175 | /* Rewrite the original data to the end of the sector */ |
175 | if (spi_flash_write(flash, offset + len, | 176 | if (spi_flash_write(flash, offset + len, |
176 | flash->sector_size - len, &cmp_buf[len])) | 177 | flash->sector_size - len, &cmp_buf[len])) |
177 | return "write"; | 178 | return "write"; |
178 | } | 179 | } |
179 | 180 | ||
180 | return NULL; | 181 | return NULL; |
181 | } | 182 | } |
182 | 183 | ||
183 | /** | 184 | /** |
184 | * Update an area of SPI flash by erasing and writing any blocks which need | 185 | * Update an area of SPI flash by erasing and writing any blocks which need |
185 | * to change. Existing blocks with the correct data are left unchanged. | 186 | * to change. Existing blocks with the correct data are left unchanged. |
186 | * | 187 | * |
187 | * @param flash flash context pointer | 188 | * @param flash flash context pointer |
188 | * @param offset flash offset to write | 189 | * @param offset flash offset to write |
189 | * @param len number of bytes to write | 190 | * @param len number of bytes to write |
190 | * @param buf buffer to write from | 191 | * @param buf buffer to write from |
191 | * @return 0 if ok, 1 on error | 192 | * @return 0 if ok, 1 on error |
192 | */ | 193 | */ |
193 | static int spi_flash_update(struct spi_flash *flash, u32 offset, | 194 | static int spi_flash_update(struct spi_flash *flash, u32 offset, |
194 | size_t len, const char *buf) | 195 | size_t len, const char *buf) |
195 | { | 196 | { |
196 | const char *err_oper = NULL; | 197 | const char *err_oper = NULL; |
197 | char *cmp_buf; | 198 | char *cmp_buf; |
198 | const char *end = buf + len; | 199 | const char *end = buf + len; |
199 | size_t todo; /* number of bytes to do in this pass */ | 200 | size_t todo; /* number of bytes to do in this pass */ |
200 | size_t skipped = 0; /* statistics */ | 201 | size_t skipped = 0; /* statistics */ |
201 | const ulong start_time = get_timer(0); | 202 | const ulong start_time = get_timer(0); |
202 | size_t scale = 1; | 203 | size_t scale = 1; |
203 | const char *start_buf = buf; | 204 | const char *start_buf = buf; |
204 | ulong delta; | 205 | ulong delta; |
205 | 206 | ||
206 | if (end - buf >= 200) | 207 | if (end - buf >= 200) |
207 | scale = (end - buf) / 100; | 208 | scale = (end - buf) / 100; |
208 | cmp_buf = malloc(flash->sector_size); | 209 | cmp_buf = malloc(flash->sector_size); |
209 | if (cmp_buf) { | 210 | if (cmp_buf) { |
210 | ulong last_update = get_timer(0); | 211 | ulong last_update = get_timer(0); |
211 | 212 | ||
212 | for (; buf < end && !err_oper; buf += todo, offset += todo) { | 213 | for (; buf < end && !err_oper; buf += todo, offset += todo) { |
213 | todo = min(end - buf, flash->sector_size); | 214 | todo = min(end - buf, flash->sector_size); |
214 | if (get_timer(last_update) > 100) { | 215 | if (get_timer(last_update) > 100) { |
215 | printf(" \rUpdating, %zu%% %lu B/s", | 216 | printf(" \rUpdating, %zu%% %lu B/s", |
216 | 100 - (end - buf) / scale, | 217 | 100 - (end - buf) / scale, |
217 | bytes_per_second(buf - start_buf, | 218 | bytes_per_second(buf - start_buf, |
218 | start_time)); | 219 | start_time)); |
219 | last_update = get_timer(0); | 220 | last_update = get_timer(0); |
220 | } | 221 | } |
221 | err_oper = spi_flash_update_block(flash, offset, todo, | 222 | err_oper = spi_flash_update_block(flash, offset, todo, |
222 | buf, cmp_buf, &skipped); | 223 | buf, cmp_buf, &skipped); |
223 | } | 224 | } |
224 | } else { | 225 | } else { |
225 | err_oper = "malloc"; | 226 | err_oper = "malloc"; |
226 | } | 227 | } |
227 | free(cmp_buf); | 228 | free(cmp_buf); |
228 | putc('\r'); | 229 | putc('\r'); |
229 | if (err_oper) { | 230 | if (err_oper) { |
230 | printf("SPI flash failed in %s step\n", err_oper); | 231 | printf("SPI flash failed in %s step\n", err_oper); |
231 | return 1; | 232 | return 1; |
232 | } | 233 | } |
233 | 234 | ||
234 | delta = get_timer(start_time); | 235 | delta = get_timer(start_time); |
235 | printf("%zu bytes written, %zu bytes skipped", len - skipped, | 236 | printf("%zu bytes written, %zu bytes skipped", len - skipped, |
236 | skipped); | 237 | skipped); |
237 | printf(" in %ld.%lds, speed %ld B/s\n", | 238 | printf(" in %ld.%lds, speed %ld B/s\n", |
238 | delta / 1000, delta % 1000, bytes_per_second(len, start_time)); | 239 | delta / 1000, delta % 1000, bytes_per_second(len, start_time)); |
239 | 240 | ||
240 | return 0; | 241 | return 0; |
241 | } | 242 | } |
242 | 243 | ||
243 | static int do_spi_flash_read_write(int argc, char * const argv[]) | 244 | static int do_spi_flash_read_write(int argc, char * const argv[]) |
244 | { | 245 | { |
245 | unsigned long addr; | 246 | unsigned long addr; |
246 | unsigned long offset; | 247 | unsigned long offset; |
247 | unsigned long len; | 248 | unsigned long len; |
248 | void *buf; | 249 | void *buf; |
249 | char *endp; | 250 | char *endp; |
250 | int ret = 1; | 251 | int ret = 1; |
251 | 252 | ||
252 | if (argc < 4) | 253 | if (argc < 4) |
253 | return -1; | 254 | return -1; |
254 | 255 | ||
255 | addr = simple_strtoul(argv[1], &endp, 16); | 256 | addr = simple_strtoul(argv[1], &endp, 16); |
256 | if (*argv[1] == 0 || *endp != 0) | 257 | if (*argv[1] == 0 || *endp != 0) |
257 | return -1; | 258 | return -1; |
258 | offset = simple_strtoul(argv[2], &endp, 16); | 259 | offset = simple_strtoul(argv[2], &endp, 16); |
259 | if (*argv[2] == 0 || *endp != 0) | 260 | if (*argv[2] == 0 || *endp != 0) |
260 | return -1; | 261 | return -1; |
261 | len = simple_strtoul(argv[3], &endp, 16); | 262 | len = simple_strtoul(argv[3], &endp, 16); |
262 | if (*argv[3] == 0 || *endp != 0) | 263 | if (*argv[3] == 0 || *endp != 0) |
263 | return -1; | 264 | return -1; |
264 | 265 | ||
265 | /* Consistency checking */ | 266 | /* Consistency checking */ |
266 | if (offset + len > flash->size) { | 267 | if (offset + len > flash->size) { |
267 | printf("ERROR: attempting %s past flash size (%#x)\n", | 268 | printf("ERROR: attempting %s past flash size (%#x)\n", |
268 | argv[0], flash->size); | 269 | argv[0], flash->size); |
269 | return 1; | 270 | return 1; |
270 | } | 271 | } |
271 | 272 | ||
272 | buf = map_physmem(addr, len, MAP_WRBACK); | 273 | buf = map_physmem(addr, len, MAP_WRBACK); |
273 | if (!buf) { | 274 | if (!buf) { |
274 | puts("Failed to map physical memory\n"); | 275 | puts("Failed to map physical memory\n"); |
275 | return 1; | 276 | return 1; |
276 | } | 277 | } |
277 | 278 | ||
278 | if (strcmp(argv[0], "update") == 0) { | 279 | if (strcmp(argv[0], "update") == 0) { |
279 | ret = spi_flash_update(flash, offset, len, buf); | 280 | ret = spi_flash_update(flash, offset, len, buf); |
280 | } else if (strncmp(argv[0], "read", 4) == 0 || | 281 | } else if (strncmp(argv[0], "read", 4) == 0 || |
281 | strncmp(argv[0], "write", 5) == 0) { | 282 | strncmp(argv[0], "write", 5) == 0) { |
282 | int read; | 283 | int read; |
283 | 284 | ||
284 | read = strncmp(argv[0], "read", 4) == 0; | 285 | read = strncmp(argv[0], "read", 4) == 0; |
285 | if (read) | 286 | if (read) |
286 | ret = spi_flash_read(flash, offset, len, buf); | 287 | ret = spi_flash_read(flash, offset, len, buf); |
287 | else | 288 | else |
288 | ret = spi_flash_write(flash, offset, len, buf); | 289 | ret = spi_flash_write(flash, offset, len, buf); |
289 | 290 | ||
290 | printf("SF: %zu bytes @ %#x %s: %s\n", (size_t)len, (u32)offset, | 291 | printf("SF: %zu bytes @ %#x %s: %s\n", (size_t)len, (u32)offset, |
291 | read ? "Read" : "Written", ret ? "ERROR" : "OK"); | 292 | read ? "Read" : "Written", ret ? "ERROR" : "OK"); |
292 | } | 293 | } |
293 | 294 | ||
294 | unmap_physmem(buf, len); | 295 | unmap_physmem(buf, len); |
295 | 296 | ||
296 | return ret == 0 ? 0 : 1; | 297 | return ret == 0 ? 0 : 1; |
297 | } | 298 | } |
298 | 299 | ||
299 | static int do_spi_flash_erase(int argc, char * const argv[]) | 300 | static int do_spi_flash_erase(int argc, char * const argv[]) |
300 | { | 301 | { |
301 | unsigned long offset; | 302 | unsigned long offset; |
302 | unsigned long len; | 303 | unsigned long len; |
303 | char *endp; | 304 | char *endp; |
304 | int ret; | 305 | int ret; |
305 | 306 | ||
306 | if (argc < 3) | 307 | if (argc < 3) |
307 | return -1; | 308 | return -1; |
308 | 309 | ||
309 | offset = simple_strtoul(argv[1], &endp, 16); | 310 | offset = simple_strtoul(argv[1], &endp, 16); |
310 | if (*argv[1] == 0 || *endp != 0) | 311 | if (*argv[1] == 0 || *endp != 0) |
311 | return -1; | 312 | return -1; |
312 | 313 | ||
313 | ret = sf_parse_len_arg(argv[2], &len); | 314 | ret = sf_parse_len_arg(argv[2], &len); |
314 | if (ret != 1) | 315 | if (ret != 1) |
315 | return -1; | 316 | return -1; |
316 | 317 | ||
317 | /* Consistency checking */ | 318 | /* Consistency checking */ |
318 | if (offset + len > flash->size) { | 319 | if (offset + len > flash->size) { |
319 | printf("ERROR: attempting %s past flash size (%#x)\n", | 320 | printf("ERROR: attempting %s past flash size (%#x)\n", |
320 | argv[0], flash->size); | 321 | argv[0], flash->size); |
321 | return 1; | 322 | return 1; |
322 | } | 323 | } |
323 | 324 | ||
324 | ret = spi_flash_erase(flash, offset, len); | 325 | ret = spi_flash_erase(flash, offset, len); |
325 | printf("SF: %zu bytes @ %#x Erased: %s\n", (size_t)len, (u32)offset, | 326 | printf("SF: %zu bytes @ %#x Erased: %s\n", (size_t)len, (u32)offset, |
326 | ret ? "ERROR" : "OK"); | 327 | ret ? "ERROR" : "OK"); |
327 | 328 | ||
328 | return ret == 0 ? 0 : 1; | 329 | return ret == 0 ? 0 : 1; |
329 | } | 330 | } |
330 | 331 | ||
331 | #ifdef CONFIG_CMD_SF_TEST | 332 | #ifdef CONFIG_CMD_SF_TEST |
332 | enum { | 333 | enum { |
333 | STAGE_ERASE, | 334 | STAGE_ERASE, |
334 | STAGE_CHECK, | 335 | STAGE_CHECK, |
335 | STAGE_WRITE, | 336 | STAGE_WRITE, |
336 | STAGE_READ, | 337 | STAGE_READ, |
337 | 338 | ||
338 | STAGE_COUNT, | 339 | STAGE_COUNT, |
339 | }; | 340 | }; |
340 | 341 | ||
341 | static char *stage_name[STAGE_COUNT] = { | 342 | static char *stage_name[STAGE_COUNT] = { |
342 | "erase", | 343 | "erase", |
343 | "check", | 344 | "check", |
344 | "write", | 345 | "write", |
345 | "read", | 346 | "read", |
346 | }; | 347 | }; |
347 | 348 | ||
348 | struct test_info { | 349 | struct test_info { |
349 | int stage; | 350 | int stage; |
350 | int bytes; | 351 | int bytes; |
351 | unsigned base_ms; | 352 | unsigned base_ms; |
352 | unsigned time_ms[STAGE_COUNT]; | 353 | unsigned time_ms[STAGE_COUNT]; |
353 | }; | 354 | }; |
354 | 355 | ||
355 | static void show_time(struct test_info *test, int stage) | 356 | static void show_time(struct test_info *test, int stage) |
356 | { | 357 | { |
357 | uint64_t speed; /* KiB/s */ | 358 | uint64_t speed; /* KiB/s */ |
358 | int bps; /* Bits per second */ | 359 | int bps; /* Bits per second */ |
359 | 360 | ||
360 | speed = (long long)test->bytes * 1000; | 361 | speed = (long long)test->bytes * 1000; |
361 | if (test->time_ms[stage]) | 362 | if (test->time_ms[stage]) |
362 | do_div(speed, test->time_ms[stage] * 1024); | 363 | do_div(speed, test->time_ms[stage] * 1024); |
363 | bps = speed * 8; | 364 | bps = speed * 8; |
364 | 365 | ||
365 | printf("%d %s: %d ticks, %d KiB/s %d.%03d Mbps\n", stage, | 366 | printf("%d %s: %d ticks, %d KiB/s %d.%03d Mbps\n", stage, |
366 | stage_name[stage], test->time_ms[stage], | 367 | stage_name[stage], test->time_ms[stage], |
367 | (int)speed, bps / 1000, bps % 1000); | 368 | (int)speed, bps / 1000, bps % 1000); |
368 | } | 369 | } |
369 | 370 | ||
370 | static void spi_test_next_stage(struct test_info *test) | 371 | static void spi_test_next_stage(struct test_info *test) |
371 | { | 372 | { |
372 | test->time_ms[test->stage] = get_timer(test->base_ms); | 373 | test->time_ms[test->stage] = get_timer(test->base_ms); |
373 | show_time(test, test->stage); | 374 | show_time(test, test->stage); |
374 | test->base_ms = get_timer(0); | 375 | test->base_ms = get_timer(0); |
375 | test->stage++; | 376 | test->stage++; |
376 | } | 377 | } |
377 | 378 | ||
378 | /** | 379 | /** |
379 | * Run a test on the SPI flash | 380 | * Run a test on the SPI flash |
380 | * | 381 | * |
381 | * @param flash SPI flash to use | 382 | * @param flash SPI flash to use |
382 | * @param buf Source buffer for data to write | 383 | * @param buf Source buffer for data to write |
383 | * @param len Size of data to read/write | 384 | * @param len Size of data to read/write |
384 | * @param offset Offset within flash to check | 385 | * @param offset Offset within flash to check |
385 | * @param vbuf Verification buffer | 386 | * @param vbuf Verification buffer |
386 | * @return 0 if ok, -1 on error | 387 | * @return 0 if ok, -1 on error |
387 | */ | 388 | */ |
388 | static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len, | 389 | static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len, |
389 | ulong offset, uint8_t *vbuf) | 390 | ulong offset, uint8_t *vbuf) |
390 | { | 391 | { |
391 | struct test_info test; | 392 | struct test_info test; |
392 | int i; | 393 | int i; |
393 | 394 | ||
394 | printf("SPI flash test:\n"); | 395 | printf("SPI flash test:\n"); |
395 | memset(&test, '\0', sizeof(test)); | 396 | memset(&test, '\0', sizeof(test)); |
396 | test.base_ms = get_timer(0); | 397 | test.base_ms = get_timer(0); |
397 | test.bytes = len; | 398 | test.bytes = len; |
398 | if (spi_flash_erase(flash, offset, len)) { | 399 | if (spi_flash_erase(flash, offset, len)) { |
399 | printf("Erase failed\n"); | 400 | printf("Erase failed\n"); |
400 | return -1; | 401 | return -1; |
401 | } | 402 | } |
402 | spi_test_next_stage(&test); | 403 | spi_test_next_stage(&test); |
403 | 404 | ||
404 | if (spi_flash_read(flash, offset, len, vbuf)) { | 405 | if (spi_flash_read(flash, offset, len, vbuf)) { |
405 | printf("Check read failed\n"); | 406 | printf("Check read failed\n"); |
406 | return -1; | 407 | return -1; |
407 | } | 408 | } |
408 | for (i = 0; i < len; i++) { | 409 | for (i = 0; i < len; i++) { |
409 | if (vbuf[i] != 0xff) { | 410 | if (vbuf[i] != 0xff) { |
410 | printf("Check failed at %d\n", i); | 411 | printf("Check failed at %d\n", i); |
411 | print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0); | 412 | print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0); |
412 | return -1; | 413 | return -1; |
413 | } | 414 | } |
414 | } | 415 | } |
415 | spi_test_next_stage(&test); | 416 | spi_test_next_stage(&test); |
416 | 417 | ||
417 | if (spi_flash_write(flash, offset, len, buf)) { | 418 | if (spi_flash_write(flash, offset, len, buf)) { |
418 | printf("Write failed\n"); | 419 | printf("Write failed\n"); |
419 | return -1; | 420 | return -1; |
420 | } | 421 | } |
421 | memset(vbuf, '\0', len); | 422 | memset(vbuf, '\0', len); |
422 | spi_test_next_stage(&test); | 423 | spi_test_next_stage(&test); |
423 | 424 | ||
424 | if (spi_flash_read(flash, offset, len, vbuf)) { | 425 | if (spi_flash_read(flash, offset, len, vbuf)) { |
425 | printf("Read failed\n"); | 426 | printf("Read failed\n"); |
426 | return -1; | 427 | return -1; |
427 | } | 428 | } |
428 | spi_test_next_stage(&test); | 429 | spi_test_next_stage(&test); |
429 | 430 | ||
430 | for (i = 0; i < len; i++) { | 431 | for (i = 0; i < len; i++) { |
431 | if (buf[i] != vbuf[i]) { | 432 | if (buf[i] != vbuf[i]) { |
432 | printf("Verify failed at %d, good data:\n", i); | 433 | printf("Verify failed at %d, good data:\n", i); |
433 | print_buffer(i, buf + i, 1, min(len - i, 0x40), 0); | 434 | print_buffer(i, buf + i, 1, min(len - i, 0x40), 0); |
434 | printf("Bad data:\n"); | 435 | printf("Bad data:\n"); |
435 | print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0); | 436 | print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0); |
436 | return -1; | 437 | return -1; |
437 | } | 438 | } |
438 | } | 439 | } |
439 | printf("Test passed\n"); | 440 | printf("Test passed\n"); |
440 | for (i = 0; i < STAGE_COUNT; i++) | 441 | for (i = 0; i < STAGE_COUNT; i++) |
441 | show_time(&test, i); | 442 | show_time(&test, i); |
442 | 443 | ||
443 | return 0; | 444 | return 0; |
444 | } | 445 | } |
445 | 446 | ||
446 | static int do_spi_flash_test(int argc, char * const argv[]) | 447 | static int do_spi_flash_test(int argc, char * const argv[]) |
447 | { | 448 | { |
448 | unsigned long offset; | 449 | unsigned long offset; |
449 | unsigned long len; | 450 | unsigned long len; |
450 | uint8_t *buf, *from; | 451 | uint8_t *buf, *from; |
451 | char *endp; | 452 | char *endp; |
452 | uint8_t *vbuf; | 453 | uint8_t *vbuf; |
453 | int ret; | 454 | int ret; |
454 | 455 | ||
455 | if (argc < 3) | 456 | if (argc < 3) |
456 | return -1; | 457 | return -1; |
457 | offset = simple_strtoul(argv[1], &endp, 16); | 458 | offset = simple_strtoul(argv[1], &endp, 16); |
458 | if (*argv[1] == 0 || *endp != 0) | 459 | if (*argv[1] == 0 || *endp != 0) |
459 | return -1; | 460 | return -1; |
460 | len = simple_strtoul(argv[2], &endp, 16); | 461 | len = simple_strtoul(argv[2], &endp, 16); |
461 | if (*argv[2] == 0 || *endp != 0) | 462 | if (*argv[2] == 0 || *endp != 0) |
462 | return -1; | 463 | return -1; |
463 | 464 | ||
464 | vbuf = malloc(len); | 465 | vbuf = malloc(len); |
465 | if (!vbuf) { | 466 | if (!vbuf) { |
466 | printf("Cannot allocate memory (%lu bytes)\n", len); | 467 | printf("Cannot allocate memory (%lu bytes)\n", len); |
467 | return 1; | 468 | return 1; |
468 | } | 469 | } |
469 | buf = malloc(len); | 470 | buf = malloc(len); |
470 | if (!buf) { | 471 | if (!buf) { |
471 | free(vbuf); | 472 | free(vbuf); |
472 | printf("Cannot allocate memory (%lu bytes)\n", len); | 473 | printf("Cannot allocate memory (%lu bytes)\n", len); |
473 | return 1; | 474 | return 1; |
474 | } | 475 | } |
475 | 476 | ||
476 | from = map_sysmem(CONFIG_SYS_TEXT_BASE, 0); | 477 | from = map_sysmem(CONFIG_SYS_TEXT_BASE, 0); |
477 | memcpy(buf, from, len); | 478 | memcpy(buf, from, len); |
478 | ret = spi_flash_test(flash, buf, len, offset, vbuf); | 479 | ret = spi_flash_test(flash, buf, len, offset, vbuf); |
479 | free(vbuf); | 480 | free(vbuf); |
480 | free(buf); | 481 | free(buf); |
481 | if (ret) { | 482 | if (ret) { |
482 | printf("Test failed\n"); | 483 | printf("Test failed\n"); |
483 | return 1; | 484 | return 1; |
484 | } | 485 | } |
485 | 486 | ||
486 | return 0; | 487 | return 0; |
487 | } | 488 | } |
488 | #endif /* CONFIG_CMD_SF_TEST */ | 489 | #endif /* CONFIG_CMD_SF_TEST */ |
489 | 490 | ||
490 | static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, | 491 | static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, |
491 | char * const argv[]) | 492 | char * const argv[]) |
492 | { | 493 | { |
493 | const char *cmd; | 494 | const char *cmd; |
494 | int ret; | 495 | int ret; |
495 | 496 | ||
496 | /* need at least two arguments */ | 497 | /* need at least two arguments */ |
497 | if (argc < 2) | 498 | if (argc < 2) |
498 | goto usage; | 499 | goto usage; |
499 | 500 | ||
500 | cmd = argv[1]; | 501 | cmd = argv[1]; |
501 | --argc; | 502 | --argc; |
502 | ++argv; | 503 | ++argv; |
503 | 504 | ||
504 | if (strcmp(cmd, "probe") == 0) { | 505 | if (strcmp(cmd, "probe") == 0) { |
506 | gpio_direction_output(IMX_GPIO_NR(4,20), 1); | ||
505 | ret = do_spi_flash_probe(argc, argv); | 507 | ret = do_spi_flash_probe(argc, argv); |
508 | gpio_direction_output(IMX_GPIO_NR(4,20), 0); | ||
506 | goto done; | 509 | goto done; |
507 | } | 510 | } |
508 | 511 | ||
509 | /* The remaining commands require a selected device */ | 512 | /* The remaining commands require a selected device */ |
510 | if (!flash) { | 513 | if (!flash) { |
511 | puts("No SPI flash selected. Please run `sf probe'\n"); | 514 | puts("No SPI flash selected. Please run `sf probe'\n"); |
512 | return 1; | 515 | return 1; |
513 | } | 516 | } |
514 | 517 | ||
515 | if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 || | 518 | if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 || |
516 | strcmp(cmd, "update") == 0) | 519 | strcmp(cmd, "update") == 0){ |
520 | gpio_direction_output(IMX_GPIO_NR(4,20), 1); | ||
517 | ret = do_spi_flash_read_write(argc, argv); | 521 | ret = do_spi_flash_read_write(argc, argv); |
518 | else if (strcmp(cmd, "erase") == 0) | 522 | gpio_direction_output(IMX_GPIO_NR(4,20), 0); |
523 | } | ||
524 | else if (strcmp(cmd, "erase") == 0){ | ||
525 | gpio_direction_output(IMX_GPIO_NR(4,20), 1); | ||
519 | ret = do_spi_flash_erase(argc, argv); | 526 | ret = do_spi_flash_erase(argc, argv); |
527 | gpio_direction_output(IMX_GPIO_NR(4,20), 0); | ||
528 | } | ||
520 | #ifdef CONFIG_CMD_SF_TEST | 529 | #ifdef CONFIG_CMD_SF_TEST |
521 | else if (!strcmp(cmd, "test")) | 530 | else if (!strcmp(cmd, "test")){ |
531 | gpio_direction_output(IMX_GPIO_NR(4,20), 1); | ||
522 | ret = do_spi_flash_test(argc, argv); | 532 | ret = do_spi_flash_test(argc, argv); |
533 | gpio_direction_output(IMX_GPIO_NR(4,20), 0); | ||
534 | } | ||
523 | #endif | 535 | #endif |
524 | else | 536 | else |
525 | ret = -1; | 537 | ret = -1; |
526 | 538 | ||
527 | done: | 539 | done: |
528 | if (ret != -1) | 540 | if (ret != -1) |
529 | return ret; | 541 | return ret; |
530 | 542 | ||
531 | usage: | 543 | usage: |
532 | return CMD_RET_USAGE; | 544 | return CMD_RET_USAGE; |
533 | } | 545 | } |
534 | 546 | ||
535 | #ifdef CONFIG_CMD_SF_TEST | 547 | #ifdef CONFIG_CMD_SF_TEST |
536 | #define SF_TEST_HELP "\nsf test offset len " \ | 548 | #define SF_TEST_HELP "\nsf test offset len " \ |
537 | "- run a very basic destructive test" | 549 | "- run a very basic destructive test" |
538 | #else | 550 | #else |
539 | #define SF_TEST_HELP | 551 | #define SF_TEST_HELP |
540 | #endif | 552 | #endif |
541 | 553 | ||
542 | U_BOOT_CMD( | 554 | U_BOOT_CMD( |
543 | sf, 5, 1, do_spi_flash, | 555 | sf, 5, 1, do_spi_flash, |
544 | "SPI flash sub-system", | 556 | "SPI flash sub-system", |
545 | "probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus\n" | 557 | "probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus\n" |
546 | " and chip select\n" | 558 | " and chip select\n" |
547 | "sf read addr offset len - read `len' bytes starting at\n" | 559 | "sf read addr offset len - read `len' bytes starting at\n" |
548 | " `offset' to memory at `addr'\n" | 560 | " `offset' to memory at `addr'\n" |
549 | "sf write addr offset len - write `len' bytes from memory\n" | 561 | "sf write addr offset len - write `len' bytes from memory\n" |
550 | " at `addr' to flash at `offset'\n" | 562 | " at `addr' to flash at `offset'\n" |
551 | "sf erase offset [+]len - erase `len' bytes from `offset'\n" | 563 | "sf erase offset [+]len - erase `len' bytes from `offset'\n" |
552 | " `+len' round up `len' to block size\n" | 564 | " `+len' round up `len' to block size\n" |
553 | "sf update addr offset len - erase and write `len' bytes from memory\n" | 565 | "sf update addr offset len - erase and write `len' bytes from memory\n" |
554 | " at `addr' to flash at `offset'" | 566 | " at `addr' to flash at `offset'" |
555 | SF_TEST_HELP | 567 | SF_TEST_HELP |
556 | ); | 568 | ); |
557 | 569 |