Commit 0c88a84ac6ac6840e62e0100f9cb9aa8d66ae927
1 parent
469146c097
Exists in
master
and in
53 other branches
sf: Add GPL-2.0+ SPDX-License-Identifier for missing ones
Added GPL-2.0+ SPDX-License-Identifier for missed sf source files. Signed-off-by: Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com> Signed-off-by: Bo Shen <voice.shen@atmel.com>
Showing 5 changed files with 6 additions and 5 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 | * Licensed under the GPL-2 or later. | 5 | * |
6 | * SPDX-License-Identifier: GPL-2.0+ | ||
6 | */ | 7 | */ |
7 | 8 | ||
8 | #include <common.h> | 9 | #include <common.h> |
9 | #include <div64.h> | 10 | #include <div64.h> |
10 | #include <malloc.h> | 11 | #include <malloc.h> |
11 | #include <spi_flash.h> | 12 | #include <spi_flash.h> |
12 | 13 | ||
13 | #include <asm/io.h> | 14 | #include <asm/io.h> |
14 | 15 | ||
15 | #ifndef CONFIG_SF_DEFAULT_SPEED | 16 | #ifndef CONFIG_SF_DEFAULT_SPEED |
16 | # define CONFIG_SF_DEFAULT_SPEED 1000000 | 17 | # define CONFIG_SF_DEFAULT_SPEED 1000000 |
17 | #endif | 18 | #endif |
18 | #ifndef CONFIG_SF_DEFAULT_MODE | 19 | #ifndef CONFIG_SF_DEFAULT_MODE |
19 | # define CONFIG_SF_DEFAULT_MODE SPI_MODE_3 | 20 | # define CONFIG_SF_DEFAULT_MODE SPI_MODE_3 |
20 | #endif | 21 | #endif |
21 | #ifndef CONFIG_SF_DEFAULT_CS | 22 | #ifndef CONFIG_SF_DEFAULT_CS |
22 | # define CONFIG_SF_DEFAULT_CS 0 | 23 | # define CONFIG_SF_DEFAULT_CS 0 |
23 | #endif | 24 | #endif |
24 | #ifndef CONFIG_SF_DEFAULT_BUS | 25 | #ifndef CONFIG_SF_DEFAULT_BUS |
25 | # define CONFIG_SF_DEFAULT_BUS 0 | 26 | # define CONFIG_SF_DEFAULT_BUS 0 |
26 | #endif | 27 | #endif |
27 | 28 | ||
28 | static struct spi_flash *flash; | 29 | static struct spi_flash *flash; |
29 | 30 | ||
30 | 31 | ||
31 | /* | 32 | /* |
32 | * This function computes the length argument for the erase command. | 33 | * This function computes the length argument for the erase command. |
33 | * The length on which the command is to operate can be given in two forms: | 34 | * The length on which the command is to operate can be given in two forms: |
34 | * 1. <cmd> offset len - operate on <'offset', 'len') | 35 | * 1. <cmd> offset len - operate on <'offset', 'len') |
35 | * 2. <cmd> offset +len - operate on <'offset', 'round_up(len)') | 36 | * 2. <cmd> offset +len - operate on <'offset', 'round_up(len)') |
36 | * If the second form is used and the length doesn't fall on the | 37 | * If the second form is used and the length doesn't fall on the |
37 | * sector boundary, than it will be adjusted to the next sector boundary. | 38 | * sector boundary, than it will be adjusted to the next sector boundary. |
38 | * If it isn't in the flash, the function will fail (return -1). | 39 | * If it isn't in the flash, the function will fail (return -1). |
39 | * Input: | 40 | * Input: |
40 | * arg: length specification (i.e. both command arguments) | 41 | * arg: length specification (i.e. both command arguments) |
41 | * Output: | 42 | * Output: |
42 | * len: computed length for operation | 43 | * len: computed length for operation |
43 | * Return: | 44 | * Return: |
44 | * 1: success | 45 | * 1: success |
45 | * -1: failure (bad format, bad address). | 46 | * -1: failure (bad format, bad address). |
46 | */ | 47 | */ |
47 | static int sf_parse_len_arg(char *arg, ulong *len) | 48 | static int sf_parse_len_arg(char *arg, ulong *len) |
48 | { | 49 | { |
49 | char *ep; | 50 | char *ep; |
50 | char round_up_len; /* indicates if the "+length" form used */ | 51 | char round_up_len; /* indicates if the "+length" form used */ |
51 | ulong len_arg; | 52 | ulong len_arg; |
52 | 53 | ||
53 | round_up_len = 0; | 54 | round_up_len = 0; |
54 | if (*arg == '+') { | 55 | if (*arg == '+') { |
55 | round_up_len = 1; | 56 | round_up_len = 1; |
56 | ++arg; | 57 | ++arg; |
57 | } | 58 | } |
58 | 59 | ||
59 | len_arg = simple_strtoul(arg, &ep, 16); | 60 | len_arg = simple_strtoul(arg, &ep, 16); |
60 | if (ep == arg || *ep != '\0') | 61 | if (ep == arg || *ep != '\0') |
61 | return -1; | 62 | return -1; |
62 | 63 | ||
63 | if (round_up_len && flash->sector_size > 0) | 64 | if (round_up_len && flash->sector_size > 0) |
64 | *len = ROUND(len_arg, flash->sector_size); | 65 | *len = ROUND(len_arg, flash->sector_size); |
65 | else | 66 | else |
66 | *len = len_arg; | 67 | *len = len_arg; |
67 | 68 | ||
68 | return 1; | 69 | return 1; |
69 | } | 70 | } |
70 | 71 | ||
71 | /** | 72 | /** |
72 | * This function takes a byte length and a delta unit of time to compute the | 73 | * This function takes a byte length and a delta unit of time to compute the |
73 | * approximate bytes per second | 74 | * approximate bytes per second |
74 | * | 75 | * |
75 | * @param len amount of bytes currently processed | 76 | * @param len amount of bytes currently processed |
76 | * @param start_ms start time of processing in ms | 77 | * @param start_ms start time of processing in ms |
77 | * @return bytes per second if OK, 0 on error | 78 | * @return bytes per second if OK, 0 on error |
78 | */ | 79 | */ |
79 | static ulong bytes_per_second(unsigned int len, ulong start_ms) | 80 | static ulong bytes_per_second(unsigned int len, ulong start_ms) |
80 | { | 81 | { |
81 | /* less accurate but avoids overflow */ | 82 | /* less accurate but avoids overflow */ |
82 | if (len >= ((unsigned int) -1) / 1024) | 83 | if (len >= ((unsigned int) -1) / 1024) |
83 | return len / (max(get_timer(start_ms) / 1024, 1)); | 84 | return len / (max(get_timer(start_ms) / 1024, 1)); |
84 | else | 85 | else |
85 | return 1024 * len / max(get_timer(start_ms), 1); | 86 | return 1024 * len / max(get_timer(start_ms), 1); |
86 | } | 87 | } |
87 | 88 | ||
88 | static int do_spi_flash_probe(int argc, char * const argv[]) | 89 | static int do_spi_flash_probe(int argc, char * const argv[]) |
89 | { | 90 | { |
90 | unsigned int bus = CONFIG_SF_DEFAULT_BUS; | 91 | unsigned int bus = CONFIG_SF_DEFAULT_BUS; |
91 | unsigned int cs = CONFIG_SF_DEFAULT_CS; | 92 | unsigned int cs = CONFIG_SF_DEFAULT_CS; |
92 | unsigned int speed = CONFIG_SF_DEFAULT_SPEED; | 93 | unsigned int speed = CONFIG_SF_DEFAULT_SPEED; |
93 | unsigned int mode = CONFIG_SF_DEFAULT_MODE; | 94 | unsigned int mode = CONFIG_SF_DEFAULT_MODE; |
94 | char *endp; | 95 | char *endp; |
95 | struct spi_flash *new; | 96 | struct spi_flash *new; |
96 | 97 | ||
97 | if (argc >= 2) { | 98 | if (argc >= 2) { |
98 | cs = simple_strtoul(argv[1], &endp, 0); | 99 | cs = simple_strtoul(argv[1], &endp, 0); |
99 | if (*argv[1] == 0 || (*endp != 0 && *endp != ':')) | 100 | if (*argv[1] == 0 || (*endp != 0 && *endp != ':')) |
100 | return -1; | 101 | return -1; |
101 | if (*endp == ':') { | 102 | if (*endp == ':') { |
102 | if (endp[1] == 0) | 103 | if (endp[1] == 0) |
103 | return -1; | 104 | return -1; |
104 | 105 | ||
105 | bus = cs; | 106 | bus = cs; |
106 | cs = simple_strtoul(endp + 1, &endp, 0); | 107 | cs = simple_strtoul(endp + 1, &endp, 0); |
107 | if (*endp != 0) | 108 | if (*endp != 0) |
108 | return -1; | 109 | return -1; |
109 | } | 110 | } |
110 | } | 111 | } |
111 | 112 | ||
112 | if (argc >= 3) { | 113 | if (argc >= 3) { |
113 | speed = simple_strtoul(argv[2], &endp, 0); | 114 | speed = simple_strtoul(argv[2], &endp, 0); |
114 | if (*argv[2] == 0 || *endp != 0) | 115 | if (*argv[2] == 0 || *endp != 0) |
115 | return -1; | 116 | return -1; |
116 | } | 117 | } |
117 | if (argc >= 4) { | 118 | if (argc >= 4) { |
118 | mode = simple_strtoul(argv[3], &endp, 16); | 119 | mode = simple_strtoul(argv[3], &endp, 16); |
119 | if (*argv[3] == 0 || *endp != 0) | 120 | if (*argv[3] == 0 || *endp != 0) |
120 | return -1; | 121 | return -1; |
121 | } | 122 | } |
122 | 123 | ||
123 | new = spi_flash_probe(bus, cs, speed, mode); | 124 | new = spi_flash_probe(bus, cs, speed, mode); |
124 | if (!new) { | 125 | if (!new) { |
125 | printf("Failed to initialize SPI flash at %u:%u\n", bus, cs); | 126 | printf("Failed to initialize SPI flash at %u:%u\n", bus, cs); |
126 | return 1; | 127 | return 1; |
127 | } | 128 | } |
128 | 129 | ||
129 | if (flash) | 130 | if (flash) |
130 | spi_flash_free(flash); | 131 | spi_flash_free(flash); |
131 | flash = new; | 132 | flash = new; |
132 | 133 | ||
133 | return 0; | 134 | return 0; |
134 | } | 135 | } |
135 | 136 | ||
136 | /** | 137 | /** |
137 | * Write a block of data to SPI flash, first checking if it is different from | 138 | * Write a block of data to SPI flash, first checking if it is different from |
138 | * what is already there. | 139 | * what is already there. |
139 | * | 140 | * |
140 | * If the data being written is the same, then *skipped is incremented by len. | 141 | * If the data being written is the same, then *skipped is incremented by len. |
141 | * | 142 | * |
142 | * @param flash flash context pointer | 143 | * @param flash flash context pointer |
143 | * @param offset flash offset to write | 144 | * @param offset flash offset to write |
144 | * @param len number of bytes to write | 145 | * @param len number of bytes to write |
145 | * @param buf buffer to write from | 146 | * @param buf buffer to write from |
146 | * @param cmp_buf read buffer to use to compare data | 147 | * @param cmp_buf read buffer to use to compare data |
147 | * @param skipped Count of skipped data (incremented by this function) | 148 | * @param skipped Count of skipped data (incremented by this function) |
148 | * @return NULL if OK, else a string containing the stage which failed | 149 | * @return NULL if OK, else a string containing the stage which failed |
149 | */ | 150 | */ |
150 | static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset, | 151 | static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset, |
151 | size_t len, const char *buf, char *cmp_buf, size_t *skipped) | 152 | size_t len, const char *buf, char *cmp_buf, size_t *skipped) |
152 | { | 153 | { |
153 | debug("offset=%#x, sector_size=%#x, len=%#zx\n", | 154 | debug("offset=%#x, sector_size=%#x, len=%#zx\n", |
154 | offset, flash->sector_size, len); | 155 | offset, flash->sector_size, len); |
155 | /* Read the entire sector so to allow for rewriting */ | 156 | /* Read the entire sector so to allow for rewriting */ |
156 | if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf)) | 157 | if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf)) |
157 | return "read"; | 158 | return "read"; |
158 | /* Compare only what is meaningful (len) */ | 159 | /* Compare only what is meaningful (len) */ |
159 | if (memcmp(cmp_buf, buf, len) == 0) { | 160 | if (memcmp(cmp_buf, buf, len) == 0) { |
160 | debug("Skip region %x size %zx: no change\n", | 161 | debug("Skip region %x size %zx: no change\n", |
161 | offset, len); | 162 | offset, len); |
162 | *skipped += len; | 163 | *skipped += len; |
163 | return NULL; | 164 | return NULL; |
164 | } | 165 | } |
165 | /* Erase the entire sector */ | 166 | /* Erase the entire sector */ |
166 | if (spi_flash_erase(flash, offset, flash->sector_size)) | 167 | if (spi_flash_erase(flash, offset, flash->sector_size)) |
167 | return "erase"; | 168 | return "erase"; |
168 | /* Write the initial part of the block from the source */ | 169 | /* Write the initial part of the block from the source */ |
169 | if (spi_flash_write(flash, offset, len, buf)) | 170 | if (spi_flash_write(flash, offset, len, buf)) |
170 | return "write"; | 171 | return "write"; |
171 | /* If it's a partial sector, rewrite the existing part */ | 172 | /* If it's a partial sector, rewrite the existing part */ |
172 | if (len != flash->sector_size) { | 173 | if (len != flash->sector_size) { |
173 | /* Rewrite the original data to the end of the sector */ | 174 | /* Rewrite the original data to the end of the sector */ |
174 | if (spi_flash_write(flash, offset + len, | 175 | if (spi_flash_write(flash, offset + len, |
175 | flash->sector_size - len, &cmp_buf[len])) | 176 | flash->sector_size - len, &cmp_buf[len])) |
176 | return "write"; | 177 | return "write"; |
177 | } | 178 | } |
178 | 179 | ||
179 | return NULL; | 180 | return NULL; |
180 | } | 181 | } |
181 | 182 | ||
182 | /** | 183 | /** |
183 | * Update an area of SPI flash by erasing and writing any blocks which need | 184 | * Update an area of SPI flash by erasing and writing any blocks which need |
184 | * to change. Existing blocks with the correct data are left unchanged. | 185 | * to change. Existing blocks with the correct data are left unchanged. |
185 | * | 186 | * |
186 | * @param flash flash context pointer | 187 | * @param flash flash context pointer |
187 | * @param offset flash offset to write | 188 | * @param offset flash offset to write |
188 | * @param len number of bytes to write | 189 | * @param len number of bytes to write |
189 | * @param buf buffer to write from | 190 | * @param buf buffer to write from |
190 | * @return 0 if ok, 1 on error | 191 | * @return 0 if ok, 1 on error |
191 | */ | 192 | */ |
192 | static int spi_flash_update(struct spi_flash *flash, u32 offset, | 193 | static int spi_flash_update(struct spi_flash *flash, u32 offset, |
193 | size_t len, const char *buf) | 194 | size_t len, const char *buf) |
194 | { | 195 | { |
195 | const char *err_oper = NULL; | 196 | const char *err_oper = NULL; |
196 | char *cmp_buf; | 197 | char *cmp_buf; |
197 | const char *end = buf + len; | 198 | const char *end = buf + len; |
198 | size_t todo; /* number of bytes to do in this pass */ | 199 | size_t todo; /* number of bytes to do in this pass */ |
199 | size_t skipped = 0; /* statistics */ | 200 | size_t skipped = 0; /* statistics */ |
200 | const ulong start_time = get_timer(0); | 201 | const ulong start_time = get_timer(0); |
201 | size_t scale = 1; | 202 | size_t scale = 1; |
202 | const char *start_buf = buf; | 203 | const char *start_buf = buf; |
203 | ulong delta; | 204 | ulong delta; |
204 | 205 | ||
205 | if (end - buf >= 200) | 206 | if (end - buf >= 200) |
206 | scale = (end - buf) / 100; | 207 | scale = (end - buf) / 100; |
207 | cmp_buf = malloc(flash->sector_size); | 208 | cmp_buf = malloc(flash->sector_size); |
208 | if (cmp_buf) { | 209 | if (cmp_buf) { |
209 | ulong last_update = get_timer(0); | 210 | ulong last_update = get_timer(0); |
210 | 211 | ||
211 | for (; buf < end && !err_oper; buf += todo, offset += todo) { | 212 | for (; buf < end && !err_oper; buf += todo, offset += todo) { |
212 | todo = min(end - buf, flash->sector_size); | 213 | todo = min(end - buf, flash->sector_size); |
213 | if (get_timer(last_update) > 100) { | 214 | if (get_timer(last_update) > 100) { |
214 | printf(" \rUpdating, %zu%% %lu B/s", | 215 | printf(" \rUpdating, %zu%% %lu B/s", |
215 | 100 - (end - buf) / scale, | 216 | 100 - (end - buf) / scale, |
216 | bytes_per_second(buf - start_buf, | 217 | bytes_per_second(buf - start_buf, |
217 | start_time)); | 218 | start_time)); |
218 | last_update = get_timer(0); | 219 | last_update = get_timer(0); |
219 | } | 220 | } |
220 | err_oper = spi_flash_update_block(flash, offset, todo, | 221 | err_oper = spi_flash_update_block(flash, offset, todo, |
221 | buf, cmp_buf, &skipped); | 222 | buf, cmp_buf, &skipped); |
222 | } | 223 | } |
223 | } else { | 224 | } else { |
224 | err_oper = "malloc"; | 225 | err_oper = "malloc"; |
225 | } | 226 | } |
226 | free(cmp_buf); | 227 | free(cmp_buf); |
227 | putc('\r'); | 228 | putc('\r'); |
228 | if (err_oper) { | 229 | if (err_oper) { |
229 | printf("SPI flash failed in %s step\n", err_oper); | 230 | printf("SPI flash failed in %s step\n", err_oper); |
230 | return 1; | 231 | return 1; |
231 | } | 232 | } |
232 | 233 | ||
233 | delta = get_timer(start_time); | 234 | delta = get_timer(start_time); |
234 | printf("%zu bytes written, %zu bytes skipped", len - skipped, | 235 | printf("%zu bytes written, %zu bytes skipped", len - skipped, |
235 | skipped); | 236 | skipped); |
236 | printf(" in %ld.%lds, speed %ld B/s\n", | 237 | printf(" in %ld.%lds, speed %ld B/s\n", |
237 | delta / 1000, delta % 1000, bytes_per_second(len, start_time)); | 238 | delta / 1000, delta % 1000, bytes_per_second(len, start_time)); |
238 | 239 | ||
239 | return 0; | 240 | return 0; |
240 | } | 241 | } |
241 | 242 | ||
242 | static int do_spi_flash_read_write(int argc, char * const argv[]) | 243 | static int do_spi_flash_read_write(int argc, char * const argv[]) |
243 | { | 244 | { |
244 | unsigned long addr; | 245 | unsigned long addr; |
245 | unsigned long offset; | 246 | unsigned long offset; |
246 | unsigned long len; | 247 | unsigned long len; |
247 | void *buf; | 248 | void *buf; |
248 | char *endp; | 249 | char *endp; |
249 | int ret = 1; | 250 | int ret = 1; |
250 | 251 | ||
251 | if (argc < 4) | 252 | if (argc < 4) |
252 | return -1; | 253 | return -1; |
253 | 254 | ||
254 | addr = simple_strtoul(argv[1], &endp, 16); | 255 | addr = simple_strtoul(argv[1], &endp, 16); |
255 | if (*argv[1] == 0 || *endp != 0) | 256 | if (*argv[1] == 0 || *endp != 0) |
256 | return -1; | 257 | return -1; |
257 | offset = simple_strtoul(argv[2], &endp, 16); | 258 | offset = simple_strtoul(argv[2], &endp, 16); |
258 | if (*argv[2] == 0 || *endp != 0) | 259 | if (*argv[2] == 0 || *endp != 0) |
259 | return -1; | 260 | return -1; |
260 | len = simple_strtoul(argv[3], &endp, 16); | 261 | len = simple_strtoul(argv[3], &endp, 16); |
261 | if (*argv[3] == 0 || *endp != 0) | 262 | if (*argv[3] == 0 || *endp != 0) |
262 | return -1; | 263 | return -1; |
263 | 264 | ||
264 | /* Consistency checking */ | 265 | /* Consistency checking */ |
265 | if (offset + len > flash->size) { | 266 | if (offset + len > flash->size) { |
266 | printf("ERROR: attempting %s past flash size (%#x)\n", | 267 | printf("ERROR: attempting %s past flash size (%#x)\n", |
267 | argv[0], flash->size); | 268 | argv[0], flash->size); |
268 | return 1; | 269 | return 1; |
269 | } | 270 | } |
270 | 271 | ||
271 | buf = map_physmem(addr, len, MAP_WRBACK); | 272 | buf = map_physmem(addr, len, MAP_WRBACK); |
272 | if (!buf) { | 273 | if (!buf) { |
273 | puts("Failed to map physical memory\n"); | 274 | puts("Failed to map physical memory\n"); |
274 | return 1; | 275 | return 1; |
275 | } | 276 | } |
276 | 277 | ||
277 | if (strcmp(argv[0], "update") == 0) { | 278 | if (strcmp(argv[0], "update") == 0) { |
278 | ret = spi_flash_update(flash, offset, len, buf); | 279 | ret = spi_flash_update(flash, offset, len, buf); |
279 | } else if (strncmp(argv[0], "read", 4) == 0 || | 280 | } else if (strncmp(argv[0], "read", 4) == 0 || |
280 | strncmp(argv[0], "write", 5) == 0) { | 281 | strncmp(argv[0], "write", 5) == 0) { |
281 | int read; | 282 | int read; |
282 | 283 | ||
283 | read = strncmp(argv[0], "read", 4) == 0; | 284 | read = strncmp(argv[0], "read", 4) == 0; |
284 | if (read) | 285 | if (read) |
285 | ret = spi_flash_read(flash, offset, len, buf); | 286 | ret = spi_flash_read(flash, offset, len, buf); |
286 | else | 287 | else |
287 | ret = spi_flash_write(flash, offset, len, buf); | 288 | ret = spi_flash_write(flash, offset, len, buf); |
288 | 289 | ||
289 | printf("SF: %zu bytes @ %#x %s: %s\n", (size_t)len, (u32)offset, | 290 | printf("SF: %zu bytes @ %#x %s: %s\n", (size_t)len, (u32)offset, |
290 | read ? "Read" : "Written", ret ? "ERROR" : "OK"); | 291 | read ? "Read" : "Written", ret ? "ERROR" : "OK"); |
291 | } | 292 | } |
292 | 293 | ||
293 | unmap_physmem(buf, len); | 294 | unmap_physmem(buf, len); |
294 | 295 | ||
295 | return ret == 0 ? 0 : 1; | 296 | return ret == 0 ? 0 : 1; |
296 | } | 297 | } |
297 | 298 | ||
298 | static int do_spi_flash_erase(int argc, char * const argv[]) | 299 | static int do_spi_flash_erase(int argc, char * const argv[]) |
299 | { | 300 | { |
300 | unsigned long offset; | 301 | unsigned long offset; |
301 | unsigned long len; | 302 | unsigned long len; |
302 | char *endp; | 303 | char *endp; |
303 | int ret; | 304 | int ret; |
304 | 305 | ||
305 | if (argc < 3) | 306 | if (argc < 3) |
306 | return -1; | 307 | return -1; |
307 | 308 | ||
308 | offset = simple_strtoul(argv[1], &endp, 16); | 309 | offset = simple_strtoul(argv[1], &endp, 16); |
309 | if (*argv[1] == 0 || *endp != 0) | 310 | if (*argv[1] == 0 || *endp != 0) |
310 | return -1; | 311 | return -1; |
311 | 312 | ||
312 | ret = sf_parse_len_arg(argv[2], &len); | 313 | ret = sf_parse_len_arg(argv[2], &len); |
313 | if (ret != 1) | 314 | if (ret != 1) |
314 | return -1; | 315 | return -1; |
315 | 316 | ||
316 | /* Consistency checking */ | 317 | /* Consistency checking */ |
317 | if (offset + len > flash->size) { | 318 | if (offset + len > flash->size) { |
318 | printf("ERROR: attempting %s past flash size (%#x)\n", | 319 | printf("ERROR: attempting %s past flash size (%#x)\n", |
319 | argv[0], flash->size); | 320 | argv[0], flash->size); |
320 | return 1; | 321 | return 1; |
321 | } | 322 | } |
322 | 323 | ||
323 | ret = spi_flash_erase(flash, offset, len); | 324 | ret = spi_flash_erase(flash, offset, len); |
324 | printf("SF: %zu bytes @ %#x Erased: %s\n", (size_t)len, (u32)offset, | 325 | printf("SF: %zu bytes @ %#x Erased: %s\n", (size_t)len, (u32)offset, |
325 | ret ? "ERROR" : "OK"); | 326 | ret ? "ERROR" : "OK"); |
326 | 327 | ||
327 | return ret == 0 ? 0 : 1; | 328 | return ret == 0 ? 0 : 1; |
328 | } | 329 | } |
329 | 330 | ||
330 | #ifdef CONFIG_CMD_SF_TEST | 331 | #ifdef CONFIG_CMD_SF_TEST |
331 | enum { | 332 | enum { |
332 | STAGE_ERASE, | 333 | STAGE_ERASE, |
333 | STAGE_CHECK, | 334 | STAGE_CHECK, |
334 | STAGE_WRITE, | 335 | STAGE_WRITE, |
335 | STAGE_READ, | 336 | STAGE_READ, |
336 | 337 | ||
337 | STAGE_COUNT, | 338 | STAGE_COUNT, |
338 | }; | 339 | }; |
339 | 340 | ||
340 | static char *stage_name[STAGE_COUNT] = { | 341 | static char *stage_name[STAGE_COUNT] = { |
341 | "erase", | 342 | "erase", |
342 | "check", | 343 | "check", |
343 | "write", | 344 | "write", |
344 | "read", | 345 | "read", |
345 | }; | 346 | }; |
346 | 347 | ||
347 | struct test_info { | 348 | struct test_info { |
348 | int stage; | 349 | int stage; |
349 | int bytes; | 350 | int bytes; |
350 | unsigned base_ms; | 351 | unsigned base_ms; |
351 | unsigned time_ms[STAGE_COUNT]; | 352 | unsigned time_ms[STAGE_COUNT]; |
352 | }; | 353 | }; |
353 | 354 | ||
354 | static void show_time(struct test_info *test, int stage) | 355 | static void show_time(struct test_info *test, int stage) |
355 | { | 356 | { |
356 | uint64_t speed; /* KiB/s */ | 357 | uint64_t speed; /* KiB/s */ |
357 | int bps; /* Bits per second */ | 358 | int bps; /* Bits per second */ |
358 | 359 | ||
359 | speed = (long long)test->bytes * 1000; | 360 | speed = (long long)test->bytes * 1000; |
360 | do_div(speed, test->time_ms[stage] * 1024); | 361 | do_div(speed, test->time_ms[stage] * 1024); |
361 | bps = speed * 8; | 362 | bps = speed * 8; |
362 | 363 | ||
363 | printf("%d %s: %d ticks, %d KiB/s %d.%03d Mbps\n", stage, | 364 | printf("%d %s: %d ticks, %d KiB/s %d.%03d Mbps\n", stage, |
364 | stage_name[stage], test->time_ms[stage], | 365 | stage_name[stage], test->time_ms[stage], |
365 | (int)speed, bps / 1000, bps % 1000); | 366 | (int)speed, bps / 1000, bps % 1000); |
366 | } | 367 | } |
367 | 368 | ||
368 | static void spi_test_next_stage(struct test_info *test) | 369 | static void spi_test_next_stage(struct test_info *test) |
369 | { | 370 | { |
370 | test->time_ms[test->stage] = get_timer(test->base_ms); | 371 | test->time_ms[test->stage] = get_timer(test->base_ms); |
371 | show_time(test, test->stage); | 372 | show_time(test, test->stage); |
372 | test->base_ms = get_timer(0); | 373 | test->base_ms = get_timer(0); |
373 | test->stage++; | 374 | test->stage++; |
374 | } | 375 | } |
375 | 376 | ||
376 | /** | 377 | /** |
377 | * Run a test on the SPI flash | 378 | * Run a test on the SPI flash |
378 | * | 379 | * |
379 | * @param flash SPI flash to use | 380 | * @param flash SPI flash to use |
380 | * @param buf Source buffer for data to write | 381 | * @param buf Source buffer for data to write |
381 | * @param len Size of data to read/write | 382 | * @param len Size of data to read/write |
382 | * @param offset Offset within flash to check | 383 | * @param offset Offset within flash to check |
383 | * @param vbuf Verification buffer | 384 | * @param vbuf Verification buffer |
384 | * @return 0 if ok, -1 on error | 385 | * @return 0 if ok, -1 on error |
385 | */ | 386 | */ |
386 | static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len, | 387 | static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len, |
387 | ulong offset, uint8_t *vbuf) | 388 | ulong offset, uint8_t *vbuf) |
388 | { | 389 | { |
389 | struct test_info test; | 390 | struct test_info test; |
390 | int i; | 391 | int i; |
391 | 392 | ||
392 | printf("SPI flash test:\n"); | 393 | printf("SPI flash test:\n"); |
393 | memset(&test, '\0', sizeof(test)); | 394 | memset(&test, '\0', sizeof(test)); |
394 | test.base_ms = get_timer(0); | 395 | test.base_ms = get_timer(0); |
395 | test.bytes = len; | 396 | test.bytes = len; |
396 | if (spi_flash_erase(flash, offset, len)) { | 397 | if (spi_flash_erase(flash, offset, len)) { |
397 | printf("Erase failed\n"); | 398 | printf("Erase failed\n"); |
398 | return -1; | 399 | return -1; |
399 | } | 400 | } |
400 | spi_test_next_stage(&test); | 401 | spi_test_next_stage(&test); |
401 | 402 | ||
402 | if (spi_flash_read(flash, offset, len, vbuf)) { | 403 | if (spi_flash_read(flash, offset, len, vbuf)) { |
403 | printf("Check read failed\n"); | 404 | printf("Check read failed\n"); |
404 | return -1; | 405 | return -1; |
405 | } | 406 | } |
406 | for (i = 0; i < len; i++) { | 407 | for (i = 0; i < len; i++) { |
407 | if (vbuf[i] != 0xff) { | 408 | if (vbuf[i] != 0xff) { |
408 | printf("Check failed at %d\n", i); | 409 | printf("Check failed at %d\n", i); |
409 | print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0); | 410 | print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0); |
410 | return -1; | 411 | return -1; |
411 | } | 412 | } |
412 | } | 413 | } |
413 | spi_test_next_stage(&test); | 414 | spi_test_next_stage(&test); |
414 | 415 | ||
415 | if (spi_flash_write(flash, offset, len, buf)) { | 416 | if (spi_flash_write(flash, offset, len, buf)) { |
416 | printf("Write failed\n"); | 417 | printf("Write failed\n"); |
417 | return -1; | 418 | return -1; |
418 | } | 419 | } |
419 | memset(vbuf, '\0', len); | 420 | memset(vbuf, '\0', len); |
420 | spi_test_next_stage(&test); | 421 | spi_test_next_stage(&test); |
421 | 422 | ||
422 | if (spi_flash_read(flash, offset, len, vbuf)) { | 423 | if (spi_flash_read(flash, offset, len, vbuf)) { |
423 | printf("Read failed\n"); | 424 | printf("Read failed\n"); |
424 | return -1; | 425 | return -1; |
425 | } | 426 | } |
426 | spi_test_next_stage(&test); | 427 | spi_test_next_stage(&test); |
427 | 428 | ||
428 | for (i = 0; i < len; i++) { | 429 | for (i = 0; i < len; i++) { |
429 | if (buf[i] != vbuf[i]) { | 430 | if (buf[i] != vbuf[i]) { |
430 | printf("Verify failed at %d, good data:\n", i); | 431 | printf("Verify failed at %d, good data:\n", i); |
431 | print_buffer(i, buf + i, 1, min(len - i, 0x40), 0); | 432 | print_buffer(i, buf + i, 1, min(len - i, 0x40), 0); |
432 | printf("Bad data:\n"); | 433 | printf("Bad data:\n"); |
433 | print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0); | 434 | print_buffer(i, vbuf + i, 1, min(len - i, 0x40), 0); |
434 | return -1; | 435 | return -1; |
435 | } | 436 | } |
436 | } | 437 | } |
437 | printf("Test passed\n"); | 438 | printf("Test passed\n"); |
438 | for (i = 0; i < STAGE_COUNT; i++) | 439 | for (i = 0; i < STAGE_COUNT; i++) |
439 | show_time(&test, i); | 440 | show_time(&test, i); |
440 | 441 | ||
441 | return 0; | 442 | return 0; |
442 | } | 443 | } |
443 | 444 | ||
444 | static int do_spi_flash_test(int argc, char * const argv[]) | 445 | static int do_spi_flash_test(int argc, char * const argv[]) |
445 | { | 446 | { |
446 | unsigned long offset; | 447 | unsigned long offset; |
447 | unsigned long len; | 448 | unsigned long len; |
448 | uint8_t *buf = (uint8_t *)CONFIG_SYS_TEXT_BASE; | 449 | uint8_t *buf = (uint8_t *)CONFIG_SYS_TEXT_BASE; |
449 | char *endp; | 450 | char *endp; |
450 | uint8_t *vbuf; | 451 | uint8_t *vbuf; |
451 | int ret; | 452 | int ret; |
452 | 453 | ||
453 | offset = simple_strtoul(argv[1], &endp, 16); | 454 | offset = simple_strtoul(argv[1], &endp, 16); |
454 | if (*argv[1] == 0 || *endp != 0) | 455 | if (*argv[1] == 0 || *endp != 0) |
455 | return -1; | 456 | return -1; |
456 | len = simple_strtoul(argv[2], &endp, 16); | 457 | len = simple_strtoul(argv[2], &endp, 16); |
457 | if (*argv[2] == 0 || *endp != 0) | 458 | if (*argv[2] == 0 || *endp != 0) |
458 | return -1; | 459 | return -1; |
459 | 460 | ||
460 | vbuf = malloc(len); | 461 | vbuf = malloc(len); |
461 | if (!vbuf) { | 462 | if (!vbuf) { |
462 | printf("Cannot allocate memory\n"); | 463 | printf("Cannot allocate memory\n"); |
463 | return 1; | 464 | return 1; |
464 | } | 465 | } |
465 | buf = malloc(len); | 466 | buf = malloc(len); |
466 | if (!buf) { | 467 | if (!buf) { |
467 | free(vbuf); | 468 | free(vbuf); |
468 | printf("Cannot allocate memory\n"); | 469 | printf("Cannot allocate memory\n"); |
469 | return 1; | 470 | return 1; |
470 | } | 471 | } |
471 | 472 | ||
472 | memcpy(buf, (char *)CONFIG_SYS_TEXT_BASE, len); | 473 | memcpy(buf, (char *)CONFIG_SYS_TEXT_BASE, len); |
473 | ret = spi_flash_test(flash, buf, len, offset, vbuf); | 474 | ret = spi_flash_test(flash, buf, len, offset, vbuf); |
474 | free(vbuf); | 475 | free(vbuf); |
475 | free(buf); | 476 | free(buf); |
476 | if (ret) { | 477 | if (ret) { |
477 | printf("Test failed\n"); | 478 | printf("Test failed\n"); |
478 | return 1; | 479 | return 1; |
479 | } | 480 | } |
480 | 481 | ||
481 | return 0; | 482 | return 0; |
482 | } | 483 | } |
483 | #endif /* CONFIG_CMD_SF_TEST */ | 484 | #endif /* CONFIG_CMD_SF_TEST */ |
484 | 485 | ||
485 | static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, | 486 | static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, |
486 | char * const argv[]) | 487 | char * const argv[]) |
487 | { | 488 | { |
488 | const char *cmd; | 489 | const char *cmd; |
489 | int ret; | 490 | int ret; |
490 | 491 | ||
491 | /* need at least two arguments */ | 492 | /* need at least two arguments */ |
492 | if (argc < 2) | 493 | if (argc < 2) |
493 | goto usage; | 494 | goto usage; |
494 | 495 | ||
495 | cmd = argv[1]; | 496 | cmd = argv[1]; |
496 | --argc; | 497 | --argc; |
497 | ++argv; | 498 | ++argv; |
498 | 499 | ||
499 | if (strcmp(cmd, "probe") == 0) { | 500 | if (strcmp(cmd, "probe") == 0) { |
500 | ret = do_spi_flash_probe(argc, argv); | 501 | ret = do_spi_flash_probe(argc, argv); |
501 | goto done; | 502 | goto done; |
502 | } | 503 | } |
503 | 504 | ||
504 | /* The remaining commands require a selected device */ | 505 | /* The remaining commands require a selected device */ |
505 | if (!flash) { | 506 | if (!flash) { |
506 | puts("No SPI flash selected. Please run `sf probe'\n"); | 507 | puts("No SPI flash selected. Please run `sf probe'\n"); |
507 | return 1; | 508 | return 1; |
508 | } | 509 | } |
509 | 510 | ||
510 | if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 || | 511 | if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 || |
511 | strcmp(cmd, "update") == 0) | 512 | strcmp(cmd, "update") == 0) |
512 | ret = do_spi_flash_read_write(argc, argv); | 513 | ret = do_spi_flash_read_write(argc, argv); |
513 | else if (strcmp(cmd, "erase") == 0) | 514 | else if (strcmp(cmd, "erase") == 0) |
514 | ret = do_spi_flash_erase(argc, argv); | 515 | ret = do_spi_flash_erase(argc, argv); |
515 | #ifdef CONFIG_CMD_SF_TEST | 516 | #ifdef CONFIG_CMD_SF_TEST |
516 | else if (!strcmp(cmd, "test")) | 517 | else if (!strcmp(cmd, "test")) |
517 | ret = do_spi_flash_test(argc, argv); | 518 | ret = do_spi_flash_test(argc, argv); |
518 | #endif | 519 | #endif |
519 | else | 520 | else |
520 | ret = -1; | 521 | ret = -1; |
521 | 522 | ||
522 | done: | 523 | done: |
523 | if (ret != -1) | 524 | if (ret != -1) |
524 | return ret; | 525 | return ret; |
525 | 526 | ||
526 | usage: | 527 | usage: |
527 | return CMD_RET_USAGE; | 528 | return CMD_RET_USAGE; |
528 | } | 529 | } |
529 | 530 | ||
530 | #ifdef CONFIG_CMD_SF_TEST | 531 | #ifdef CONFIG_CMD_SF_TEST |
531 | #define SF_TEST_HELP "\nsf test offset len " \ | 532 | #define SF_TEST_HELP "\nsf test offset len " \ |
532 | "- run a very basic destructive test" | 533 | "- run a very basic destructive test" |
533 | #else | 534 | #else |
534 | #define SF_TEST_HELP | 535 | #define SF_TEST_HELP |
535 | #endif | 536 | #endif |
536 | 537 | ||
537 | U_BOOT_CMD( | 538 | U_BOOT_CMD( |
538 | sf, 5, 1, do_spi_flash, | 539 | sf, 5, 1, do_spi_flash, |
539 | "SPI flash sub-system", | 540 | "SPI flash sub-system", |
540 | "probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus\n" | 541 | "probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus\n" |
541 | " and chip select\n" | 542 | " and chip select\n" |
542 | "sf read addr offset len - read `len' bytes starting at\n" | 543 | "sf read addr offset len - read `len' bytes starting at\n" |
543 | " `offset' to memory at `addr'\n" | 544 | " `offset' to memory at `addr'\n" |
544 | "sf write addr offset len - write `len' bytes from memory\n" | 545 | "sf write addr offset len - write `len' bytes from memory\n" |
545 | " at `addr' to flash at `offset'\n" | 546 | " at `addr' to flash at `offset'\n" |
546 | "sf erase offset [+]len - erase `len' bytes from `offset'\n" | 547 | "sf erase offset [+]len - erase `len' bytes from `offset'\n" |
547 | " `+len' round up `len' to block size\n" | 548 | " `+len' round up `len' to block size\n" |
548 | "sf update addr offset len - erase and write `len' bytes from memory\n" | 549 | "sf update addr offset len - erase and write `len' bytes from memory\n" |
549 | " at `addr' to flash at `offset'" | 550 | " at `addr' to flash at `offset'" |
550 | SF_TEST_HELP | 551 | SF_TEST_HELP |
551 | ); | 552 | ); |
552 | 553 |
drivers/mtd/spi/sf.c
1 | /* | 1 | /* |
2 | * SPI flash interface | 2 | * SPI flash interface |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Atmel Corporation | 4 | * Copyright (C) 2008 Atmel Corporation |
5 | * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik | 5 | * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik |
6 | * | 6 | * |
7 | * Licensed under the GPL-2 or later. | 7 | * SPDX-License-Identifier: GPL-2.0+ |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <common.h> | 10 | #include <common.h> |
11 | #include <spi.h> | 11 | #include <spi.h> |
12 | 12 | ||
13 | static int spi_flash_read_write(struct spi_slave *spi, | 13 | static int spi_flash_read_write(struct spi_slave *spi, |
14 | const u8 *cmd, size_t cmd_len, | 14 | const u8 *cmd, size_t cmd_len, |
15 | const u8 *data_out, u8 *data_in, | 15 | const u8 *data_out, u8 *data_in, |
16 | size_t data_len) | 16 | size_t data_len) |
17 | { | 17 | { |
18 | unsigned long flags = SPI_XFER_BEGIN; | 18 | unsigned long flags = SPI_XFER_BEGIN; |
19 | int ret; | 19 | int ret; |
20 | 20 | ||
21 | if (data_len == 0) | 21 | if (data_len == 0) |
22 | flags |= SPI_XFER_END; | 22 | flags |= SPI_XFER_END; |
23 | 23 | ||
24 | ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); | 24 | ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); |
25 | if (ret) { | 25 | if (ret) { |
26 | debug("SF: Failed to send command (%zu bytes): %d\n", | 26 | debug("SF: Failed to send command (%zu bytes): %d\n", |
27 | cmd_len, ret); | 27 | cmd_len, ret); |
28 | } else if (data_len != 0) { | 28 | } else if (data_len != 0) { |
29 | ret = spi_xfer(spi, data_len * 8, data_out, data_in, | 29 | ret = spi_xfer(spi, data_len * 8, data_out, data_in, |
30 | SPI_XFER_END); | 30 | SPI_XFER_END); |
31 | if (ret) | 31 | if (ret) |
32 | debug("SF: Failed to transfer %zu bytes of data: %d\n", | 32 | debug("SF: Failed to transfer %zu bytes of data: %d\n", |
33 | data_len, ret); | 33 | data_len, ret); |
34 | } | 34 | } |
35 | 35 | ||
36 | return ret; | 36 | return ret; |
37 | } | 37 | } |
38 | 38 | ||
39 | int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, | 39 | int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, |
40 | size_t cmd_len, void *data, size_t data_len) | 40 | size_t cmd_len, void *data, size_t data_len) |
41 | { | 41 | { |
42 | return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len); | 42 | return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len); |
43 | } | 43 | } |
44 | 44 | ||
45 | int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len) | 45 | int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len) |
46 | { | 46 | { |
47 | return spi_flash_cmd_read(spi, &cmd, 1, response, len); | 47 | return spi_flash_cmd_read(spi, &cmd, 1, response, len); |
48 | } | 48 | } |
49 | 49 | ||
50 | int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, | 50 | int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, |
51 | const void *data, size_t data_len) | 51 | const void *data, size_t data_len) |
52 | { | 52 | { |
53 | return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len); | 53 | return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len); |
54 | } | 54 | } |
55 | 55 |
drivers/mtd/spi/sf_internal.h
1 | /* | 1 | /* |
2 | * SPI flash internal definitions | 2 | * SPI flash internal definitions |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Atmel Corporation | 4 | * Copyright (C) 2008 Atmel Corporation |
5 | * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. | 5 | * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. |
6 | * | 6 | * |
7 | * Licensed under the GPL-2 or later. | 7 | * SPDX-License-Identifier: GPL-2.0+ |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #ifndef _SF_INTERNAL_H_ | 10 | #ifndef _SF_INTERNAL_H_ |
11 | #define _SF_INTERNAL_H_ | 11 | #define _SF_INTERNAL_H_ |
12 | 12 | ||
13 | #define SPI_FLASH_16MB_BOUN 0x1000000 | 13 | #define SPI_FLASH_16MB_BOUN 0x1000000 |
14 | 14 | ||
15 | /* SECT flags */ | 15 | /* SECT flags */ |
16 | #define SECT_4K (1 << 1) | 16 | #define SECT_4K (1 << 1) |
17 | #define SECT_32K (1 << 2) | 17 | #define SECT_32K (1 << 2) |
18 | #define E_FSR (1 << 3) | 18 | #define E_FSR (1 << 3) |
19 | 19 | ||
20 | /* Erase commands */ | 20 | /* Erase commands */ |
21 | #define CMD_ERASE_4K 0x20 | 21 | #define CMD_ERASE_4K 0x20 |
22 | #define CMD_ERASE_32K 0x52 | 22 | #define CMD_ERASE_32K 0x52 |
23 | #define CMD_ERASE_CHIP 0xc7 | 23 | #define CMD_ERASE_CHIP 0xc7 |
24 | #define CMD_ERASE_64K 0xd8 | 24 | #define CMD_ERASE_64K 0xd8 |
25 | 25 | ||
26 | /* Write commands */ | 26 | /* Write commands */ |
27 | #define CMD_WRITE_STATUS 0x01 | 27 | #define CMD_WRITE_STATUS 0x01 |
28 | #define CMD_PAGE_PROGRAM 0x02 | 28 | #define CMD_PAGE_PROGRAM 0x02 |
29 | #define CMD_WRITE_DISABLE 0x04 | 29 | #define CMD_WRITE_DISABLE 0x04 |
30 | #define CMD_READ_STATUS 0x05 | 30 | #define CMD_READ_STATUS 0x05 |
31 | #define CMD_WRITE_ENABLE 0x06 | 31 | #define CMD_WRITE_ENABLE 0x06 |
32 | #define CMD_READ_CONFIG 0x35 | 32 | #define CMD_READ_CONFIG 0x35 |
33 | #define CMD_FLAG_STATUS 0x70 | 33 | #define CMD_FLAG_STATUS 0x70 |
34 | 34 | ||
35 | /* Read commands */ | 35 | /* Read commands */ |
36 | #define CMD_READ_ARRAY_SLOW 0x03 | 36 | #define CMD_READ_ARRAY_SLOW 0x03 |
37 | #define CMD_READ_ARRAY_FAST 0x0b | 37 | #define CMD_READ_ARRAY_FAST 0x0b |
38 | #define CMD_READ_ID 0x9f | 38 | #define CMD_READ_ID 0x9f |
39 | 39 | ||
40 | /* Bank addr access commands */ | 40 | /* Bank addr access commands */ |
41 | #ifdef CONFIG_SPI_FLASH_BAR | 41 | #ifdef CONFIG_SPI_FLASH_BAR |
42 | # define CMD_BANKADDR_BRWR 0x17 | 42 | # define CMD_BANKADDR_BRWR 0x17 |
43 | # define CMD_BANKADDR_BRRD 0x16 | 43 | # define CMD_BANKADDR_BRRD 0x16 |
44 | # define CMD_EXTNADDR_WREAR 0xC5 | 44 | # define CMD_EXTNADDR_WREAR 0xC5 |
45 | # define CMD_EXTNADDR_RDEAR 0xC8 | 45 | # define CMD_EXTNADDR_RDEAR 0xC8 |
46 | #endif | 46 | #endif |
47 | 47 | ||
48 | /* Common status */ | 48 | /* Common status */ |
49 | #define STATUS_WIP 0x01 | 49 | #define STATUS_WIP 0x01 |
50 | #define STATUS_PEC 0x80 | 50 | #define STATUS_PEC 0x80 |
51 | 51 | ||
52 | /* Flash timeout values */ | 52 | /* Flash timeout values */ |
53 | #define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ) | 53 | #define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ) |
54 | #define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ) | 54 | #define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ) |
55 | #define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ) | 55 | #define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ) |
56 | 56 | ||
57 | /* SST specific */ | 57 | /* SST specific */ |
58 | #ifdef CONFIG_SPI_FLASH_SST | 58 | #ifdef CONFIG_SPI_FLASH_SST |
59 | # define SST_WP 0x01 /* Supports AAI word program */ | 59 | # define SST_WP 0x01 /* Supports AAI word program */ |
60 | # define CMD_SST_BP 0x02 /* Byte Program */ | 60 | # define CMD_SST_BP 0x02 /* Byte Program */ |
61 | # define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ | 61 | # define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ |
62 | 62 | ||
63 | int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, | 63 | int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, |
64 | const void *buf); | 64 | const void *buf); |
65 | #endif | 65 | #endif |
66 | 66 | ||
67 | /* Send a single-byte command to the device and read the response */ | 67 | /* Send a single-byte command to the device and read the response */ |
68 | int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); | 68 | int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); |
69 | 69 | ||
70 | /* | 70 | /* |
71 | * Send a multi-byte command to the device and read the response. Used | 71 | * Send a multi-byte command to the device and read the response. Used |
72 | * for flash array reads, etc. | 72 | * for flash array reads, etc. |
73 | */ | 73 | */ |
74 | int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, | 74 | int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, |
75 | size_t cmd_len, void *data, size_t data_len); | 75 | size_t cmd_len, void *data, size_t data_len); |
76 | 76 | ||
77 | /* | 77 | /* |
78 | * Send a multi-byte command to the device followed by (optional) | 78 | * Send a multi-byte command to the device followed by (optional) |
79 | * data. Used for programming the flash array, etc. | 79 | * data. Used for programming the flash array, etc. |
80 | */ | 80 | */ |
81 | int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, | 81 | int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, |
82 | const void *data, size_t data_len); | 82 | const void *data, size_t data_len); |
83 | 83 | ||
84 | 84 | ||
85 | /* Flash erase(sectors) operation, support all possible erase commands */ | 85 | /* Flash erase(sectors) operation, support all possible erase commands */ |
86 | int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len); | 86 | int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len); |
87 | 87 | ||
88 | /* Program the status register */ | 88 | /* Program the status register */ |
89 | int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr); | 89 | int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr); |
90 | 90 | ||
91 | /* Set quad enbale bit */ | 91 | /* Set quad enbale bit */ |
92 | int spi_flash_set_qeb(struct spi_flash *flash); | 92 | int spi_flash_set_qeb(struct spi_flash *flash); |
93 | 93 | ||
94 | /* Enable writing on the SPI flash */ | 94 | /* Enable writing on the SPI flash */ |
95 | static inline int spi_flash_cmd_write_enable(struct spi_flash *flash) | 95 | static inline int spi_flash_cmd_write_enable(struct spi_flash *flash) |
96 | { | 96 | { |
97 | return spi_flash_cmd(flash->spi, CMD_WRITE_ENABLE, NULL, 0); | 97 | return spi_flash_cmd(flash->spi, CMD_WRITE_ENABLE, NULL, 0); |
98 | } | 98 | } |
99 | 99 | ||
100 | /* Disable writing on the SPI flash */ | 100 | /* Disable writing on the SPI flash */ |
101 | static inline int spi_flash_cmd_write_disable(struct spi_flash *flash) | 101 | static inline int spi_flash_cmd_write_disable(struct spi_flash *flash) |
102 | { | 102 | { |
103 | return spi_flash_cmd(flash->spi, CMD_WRITE_DISABLE, NULL, 0); | 103 | return spi_flash_cmd(flash->spi, CMD_WRITE_DISABLE, NULL, 0); |
104 | } | 104 | } |
105 | 105 | ||
106 | /* | 106 | /* |
107 | * Send the read status command to the device and wait for the wip | 107 | * Send the read status command to the device and wait for the wip |
108 | * (write-in-progress) bit to clear itself. | 108 | * (write-in-progress) bit to clear itself. |
109 | */ | 109 | */ |
110 | int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout); | 110 | int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout); |
111 | 111 | ||
112 | /* | 112 | /* |
113 | * Used for spi_flash write operation | 113 | * Used for spi_flash write operation |
114 | * - SPI claim | 114 | * - SPI claim |
115 | * - spi_flash_cmd_write_enable | 115 | * - spi_flash_cmd_write_enable |
116 | * - spi_flash_cmd_write | 116 | * - spi_flash_cmd_write |
117 | * - spi_flash_cmd_wait_ready | 117 | * - spi_flash_cmd_wait_ready |
118 | * - SPI release | 118 | * - SPI release |
119 | */ | 119 | */ |
120 | int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, | 120 | int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, |
121 | size_t cmd_len, const void *buf, size_t buf_len); | 121 | size_t cmd_len, const void *buf, size_t buf_len); |
122 | 122 | ||
123 | /* | 123 | /* |
124 | * Flash write operation, support all possible write commands. | 124 | * Flash write operation, support all possible write commands. |
125 | * Write the requested data out breaking it up into multiple write | 125 | * Write the requested data out breaking it up into multiple write |
126 | * commands as needed per the write size. | 126 | * commands as needed per the write size. |
127 | */ | 127 | */ |
128 | int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, | 128 | int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, |
129 | size_t len, const void *buf); | 129 | size_t len, const void *buf); |
130 | 130 | ||
131 | /* | 131 | /* |
132 | * Same as spi_flash_cmd_read() except it also claims/releases the SPI | 132 | * Same as spi_flash_cmd_read() except it also claims/releases the SPI |
133 | * bus. Used as common part of the ->read() operation. | 133 | * bus. Used as common part of the ->read() operation. |
134 | */ | 134 | */ |
135 | int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, | 135 | int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, |
136 | size_t cmd_len, void *data, size_t data_len); | 136 | size_t cmd_len, void *data, size_t data_len); |
137 | 137 | ||
138 | /* Flash read operation, support all possible read commands */ | 138 | /* Flash read operation, support all possible read commands */ |
139 | int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, | 139 | int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, |
140 | size_t len, void *data); | 140 | size_t len, void *data); |
141 | 141 | ||
142 | #endif /* _SF_INTERNAL_H_ */ | 142 | #endif /* _SF_INTERNAL_H_ */ |
143 | 143 |
drivers/mtd/spi/sf_ops.c
1 | /* | 1 | /* |
2 | * SPI flash operations | 2 | * SPI flash operations |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Atmel Corporation | 4 | * Copyright (C) 2008 Atmel Corporation |
5 | * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik | 5 | * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik |
6 | * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. | 6 | * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. |
7 | * | 7 | * |
8 | * Licensed under the GPL-2 or later. | 8 | * SPDX-License-Identifier: GPL-2.0+ |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <common.h> | 11 | #include <common.h> |
12 | #include <spi.h> | 12 | #include <spi.h> |
13 | #include <spi_flash.h> | 13 | #include <spi_flash.h> |
14 | #include <watchdog.h> | 14 | #include <watchdog.h> |
15 | 15 | ||
16 | #include "sf_internal.h" | 16 | #include "sf_internal.h" |
17 | 17 | ||
18 | static void spi_flash_addr(u32 addr, u8 *cmd) | 18 | static void spi_flash_addr(u32 addr, u8 *cmd) |
19 | { | 19 | { |
20 | /* cmd[0] is actual command */ | 20 | /* cmd[0] is actual command */ |
21 | cmd[1] = addr >> 16; | 21 | cmd[1] = addr >> 16; |
22 | cmd[2] = addr >> 8; | 22 | cmd[2] = addr >> 8; |
23 | cmd[3] = addr >> 0; | 23 | cmd[3] = addr >> 0; |
24 | } | 24 | } |
25 | 25 | ||
26 | int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr) | 26 | int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr) |
27 | { | 27 | { |
28 | u8 cmd; | 28 | u8 cmd; |
29 | int ret; | 29 | int ret; |
30 | 30 | ||
31 | cmd = CMD_WRITE_STATUS; | 31 | cmd = CMD_WRITE_STATUS; |
32 | ret = spi_flash_write_common(flash, &cmd, 1, &sr, 1); | 32 | ret = spi_flash_write_common(flash, &cmd, 1, &sr, 1); |
33 | if (ret < 0) { | 33 | if (ret < 0) { |
34 | debug("SF: fail to write status register\n"); | 34 | debug("SF: fail to write status register\n"); |
35 | return ret; | 35 | return ret; |
36 | } | 36 | } |
37 | 37 | ||
38 | return 0; | 38 | return 0; |
39 | } | 39 | } |
40 | 40 | ||
41 | #ifdef CONFIG_SPI_FLASH_BAR | 41 | #ifdef CONFIG_SPI_FLASH_BAR |
42 | static int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel) | 42 | static int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel) |
43 | { | 43 | { |
44 | u8 cmd; | 44 | u8 cmd; |
45 | int ret; | 45 | int ret; |
46 | 46 | ||
47 | if (flash->bank_curr == bank_sel) { | 47 | if (flash->bank_curr == bank_sel) { |
48 | debug("SF: not require to enable bank%d\n", bank_sel); | 48 | debug("SF: not require to enable bank%d\n", bank_sel); |
49 | return 0; | 49 | return 0; |
50 | } | 50 | } |
51 | 51 | ||
52 | cmd = flash->bank_write_cmd; | 52 | cmd = flash->bank_write_cmd; |
53 | ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1); | 53 | ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1); |
54 | if (ret < 0) { | 54 | if (ret < 0) { |
55 | debug("SF: fail to write bank register\n"); | 55 | debug("SF: fail to write bank register\n"); |
56 | return ret; | 56 | return ret; |
57 | } | 57 | } |
58 | flash->bank_curr = bank_sel; | 58 | flash->bank_curr = bank_sel; |
59 | 59 | ||
60 | return 0; | 60 | return 0; |
61 | } | 61 | } |
62 | 62 | ||
63 | static int spi_flash_bank(struct spi_flash *flash, u32 offset) | 63 | static int spi_flash_bank(struct spi_flash *flash, u32 offset) |
64 | { | 64 | { |
65 | u8 bank_sel; | 65 | u8 bank_sel; |
66 | int ret; | 66 | int ret; |
67 | 67 | ||
68 | bank_sel = offset / SPI_FLASH_16MB_BOUN; | 68 | bank_sel = offset / SPI_FLASH_16MB_BOUN; |
69 | 69 | ||
70 | ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); | 70 | ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); |
71 | if (ret) { | 71 | if (ret) { |
72 | debug("SF: fail to set bank%d\n", bank_sel); | 72 | debug("SF: fail to set bank%d\n", bank_sel); |
73 | return ret; | 73 | return ret; |
74 | } | 74 | } |
75 | 75 | ||
76 | return 0; | 76 | return 0; |
77 | } | 77 | } |
78 | #endif | 78 | #endif |
79 | 79 | ||
80 | int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) | 80 | int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) |
81 | { | 81 | { |
82 | struct spi_slave *spi = flash->spi; | 82 | struct spi_slave *spi = flash->spi; |
83 | unsigned long timebase; | 83 | unsigned long timebase; |
84 | int ret; | 84 | int ret; |
85 | u8 status; | 85 | u8 status; |
86 | u8 check_status = 0x0; | 86 | u8 check_status = 0x0; |
87 | u8 poll_bit = STATUS_WIP; | 87 | u8 poll_bit = STATUS_WIP; |
88 | u8 cmd = flash->poll_cmd; | 88 | u8 cmd = flash->poll_cmd; |
89 | 89 | ||
90 | if (cmd == CMD_FLAG_STATUS) { | 90 | if (cmd == CMD_FLAG_STATUS) { |
91 | poll_bit = STATUS_PEC; | 91 | poll_bit = STATUS_PEC; |
92 | check_status = poll_bit; | 92 | check_status = poll_bit; |
93 | } | 93 | } |
94 | 94 | ||
95 | ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); | 95 | ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); |
96 | if (ret) { | 96 | if (ret) { |
97 | debug("SF: fail to read %s status register\n", | 97 | debug("SF: fail to read %s status register\n", |
98 | cmd == CMD_READ_STATUS ? "read" : "flag"); | 98 | cmd == CMD_READ_STATUS ? "read" : "flag"); |
99 | return ret; | 99 | return ret; |
100 | } | 100 | } |
101 | 101 | ||
102 | timebase = get_timer(0); | 102 | timebase = get_timer(0); |
103 | do { | 103 | do { |
104 | WATCHDOG_RESET(); | 104 | WATCHDOG_RESET(); |
105 | 105 | ||
106 | ret = spi_xfer(spi, 8, NULL, &status, 0); | 106 | ret = spi_xfer(spi, 8, NULL, &status, 0); |
107 | if (ret) | 107 | if (ret) |
108 | return -1; | 108 | return -1; |
109 | 109 | ||
110 | if ((status & poll_bit) == check_status) | 110 | if ((status & poll_bit) == check_status) |
111 | break; | 111 | break; |
112 | 112 | ||
113 | } while (get_timer(timebase) < timeout); | 113 | } while (get_timer(timebase) < timeout); |
114 | 114 | ||
115 | spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); | 115 | spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); |
116 | 116 | ||
117 | if ((status & poll_bit) == check_status) | 117 | if ((status & poll_bit) == check_status) |
118 | return 0; | 118 | return 0; |
119 | 119 | ||
120 | /* Timed out */ | 120 | /* Timed out */ |
121 | debug("SF: time out!\n"); | 121 | debug("SF: time out!\n"); |
122 | return -1; | 122 | return -1; |
123 | } | 123 | } |
124 | 124 | ||
125 | int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, | 125 | int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, |
126 | size_t cmd_len, const void *buf, size_t buf_len) | 126 | size_t cmd_len, const void *buf, size_t buf_len) |
127 | { | 127 | { |
128 | struct spi_slave *spi = flash->spi; | 128 | struct spi_slave *spi = flash->spi; |
129 | unsigned long timeout = SPI_FLASH_PROG_TIMEOUT; | 129 | unsigned long timeout = SPI_FLASH_PROG_TIMEOUT; |
130 | int ret; | 130 | int ret; |
131 | 131 | ||
132 | if (buf == NULL) | 132 | if (buf == NULL) |
133 | timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT; | 133 | timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT; |
134 | 134 | ||
135 | ret = spi_claim_bus(flash->spi); | 135 | ret = spi_claim_bus(flash->spi); |
136 | if (ret) { | 136 | if (ret) { |
137 | debug("SF: unable to claim SPI bus\n"); | 137 | debug("SF: unable to claim SPI bus\n"); |
138 | return ret; | 138 | return ret; |
139 | } | 139 | } |
140 | 140 | ||
141 | ret = spi_flash_cmd_write_enable(flash); | 141 | ret = spi_flash_cmd_write_enable(flash); |
142 | if (ret < 0) { | 142 | if (ret < 0) { |
143 | debug("SF: enabling write failed\n"); | 143 | debug("SF: enabling write failed\n"); |
144 | return ret; | 144 | return ret; |
145 | } | 145 | } |
146 | 146 | ||
147 | ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len); | 147 | ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len); |
148 | if (ret < 0) { | 148 | if (ret < 0) { |
149 | debug("SF: write cmd failed\n"); | 149 | debug("SF: write cmd failed\n"); |
150 | return ret; | 150 | return ret; |
151 | } | 151 | } |
152 | 152 | ||
153 | ret = spi_flash_cmd_wait_ready(flash, timeout); | 153 | ret = spi_flash_cmd_wait_ready(flash, timeout); |
154 | if (ret < 0) { | 154 | if (ret < 0) { |
155 | debug("SF: write %s timed out\n", | 155 | debug("SF: write %s timed out\n", |
156 | timeout == SPI_FLASH_PROG_TIMEOUT ? | 156 | timeout == SPI_FLASH_PROG_TIMEOUT ? |
157 | "program" : "page erase"); | 157 | "program" : "page erase"); |
158 | return ret; | 158 | return ret; |
159 | } | 159 | } |
160 | 160 | ||
161 | spi_release_bus(spi); | 161 | spi_release_bus(spi); |
162 | 162 | ||
163 | return ret; | 163 | return ret; |
164 | } | 164 | } |
165 | 165 | ||
166 | int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) | 166 | int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) |
167 | { | 167 | { |
168 | u32 erase_size; | 168 | u32 erase_size; |
169 | u8 cmd[4]; | 169 | u8 cmd[4]; |
170 | int ret = -1; | 170 | int ret = -1; |
171 | 171 | ||
172 | erase_size = flash->erase_size; | 172 | erase_size = flash->erase_size; |
173 | if (offset % erase_size || len % erase_size) { | 173 | if (offset % erase_size || len % erase_size) { |
174 | debug("SF: Erase offset/length not multiple of erase size\n"); | 174 | debug("SF: Erase offset/length not multiple of erase size\n"); |
175 | return -1; | 175 | return -1; |
176 | } | 176 | } |
177 | 177 | ||
178 | cmd[0] = flash->erase_cmd; | 178 | cmd[0] = flash->erase_cmd; |
179 | while (len) { | 179 | while (len) { |
180 | #ifdef CONFIG_SPI_FLASH_BAR | 180 | #ifdef CONFIG_SPI_FLASH_BAR |
181 | ret = spi_flash_bank(flash, offset); | 181 | ret = spi_flash_bank(flash, offset); |
182 | if (ret < 0) | 182 | if (ret < 0) |
183 | return ret; | 183 | return ret; |
184 | #endif | 184 | #endif |
185 | spi_flash_addr(offset, cmd); | 185 | spi_flash_addr(offset, cmd); |
186 | 186 | ||
187 | debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], | 187 | debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], |
188 | cmd[2], cmd[3], offset); | 188 | cmd[2], cmd[3], offset); |
189 | 189 | ||
190 | ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); | 190 | ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); |
191 | if (ret < 0) { | 191 | if (ret < 0) { |
192 | debug("SF: erase failed\n"); | 192 | debug("SF: erase failed\n"); |
193 | break; | 193 | break; |
194 | } | 194 | } |
195 | 195 | ||
196 | offset += erase_size; | 196 | offset += erase_size; |
197 | len -= erase_size; | 197 | len -= erase_size; |
198 | } | 198 | } |
199 | 199 | ||
200 | return ret; | 200 | return ret; |
201 | } | 201 | } |
202 | 202 | ||
203 | int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, | 203 | int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, |
204 | size_t len, const void *buf) | 204 | size_t len, const void *buf) |
205 | { | 205 | { |
206 | unsigned long byte_addr, page_size; | 206 | unsigned long byte_addr, page_size; |
207 | size_t chunk_len, actual; | 207 | size_t chunk_len, actual; |
208 | u8 cmd[4]; | 208 | u8 cmd[4]; |
209 | int ret = -1; | 209 | int ret = -1; |
210 | 210 | ||
211 | page_size = flash->page_size; | 211 | page_size = flash->page_size; |
212 | 212 | ||
213 | cmd[0] = CMD_PAGE_PROGRAM; | 213 | cmd[0] = CMD_PAGE_PROGRAM; |
214 | for (actual = 0; actual < len; actual += chunk_len) { | 214 | for (actual = 0; actual < len; actual += chunk_len) { |
215 | #ifdef CONFIG_SPI_FLASH_BAR | 215 | #ifdef CONFIG_SPI_FLASH_BAR |
216 | ret = spi_flash_bank(flash, offset); | 216 | ret = spi_flash_bank(flash, offset); |
217 | if (ret < 0) | 217 | if (ret < 0) |
218 | return ret; | 218 | return ret; |
219 | #endif | 219 | #endif |
220 | byte_addr = offset % page_size; | 220 | byte_addr = offset % page_size; |
221 | chunk_len = min(len - actual, page_size - byte_addr); | 221 | chunk_len = min(len - actual, page_size - byte_addr); |
222 | 222 | ||
223 | if (flash->spi->max_write_size) | 223 | if (flash->spi->max_write_size) |
224 | chunk_len = min(chunk_len, flash->spi->max_write_size); | 224 | chunk_len = min(chunk_len, flash->spi->max_write_size); |
225 | 225 | ||
226 | spi_flash_addr(offset, cmd); | 226 | spi_flash_addr(offset, cmd); |
227 | 227 | ||
228 | debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", | 228 | debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", |
229 | buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); | 229 | buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); |
230 | 230 | ||
231 | ret = spi_flash_write_common(flash, cmd, sizeof(cmd), | 231 | ret = spi_flash_write_common(flash, cmd, sizeof(cmd), |
232 | buf + actual, chunk_len); | 232 | buf + actual, chunk_len); |
233 | if (ret < 0) { | 233 | if (ret < 0) { |
234 | debug("SF: write failed\n"); | 234 | debug("SF: write failed\n"); |
235 | break; | 235 | break; |
236 | } | 236 | } |
237 | 237 | ||
238 | offset += chunk_len; | 238 | offset += chunk_len; |
239 | } | 239 | } |
240 | 240 | ||
241 | return ret; | 241 | return ret; |
242 | } | 242 | } |
243 | 243 | ||
244 | int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, | 244 | int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, |
245 | size_t cmd_len, void *data, size_t data_len) | 245 | size_t cmd_len, void *data, size_t data_len) |
246 | { | 246 | { |
247 | struct spi_slave *spi = flash->spi; | 247 | struct spi_slave *spi = flash->spi; |
248 | int ret; | 248 | int ret; |
249 | 249 | ||
250 | ret = spi_claim_bus(flash->spi); | 250 | ret = spi_claim_bus(flash->spi); |
251 | if (ret) { | 251 | if (ret) { |
252 | debug("SF: unable to claim SPI bus\n"); | 252 | debug("SF: unable to claim SPI bus\n"); |
253 | return ret; | 253 | return ret; |
254 | } | 254 | } |
255 | 255 | ||
256 | ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); | 256 | ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); |
257 | if (ret < 0) { | 257 | if (ret < 0) { |
258 | debug("SF: read cmd failed\n"); | 258 | debug("SF: read cmd failed\n"); |
259 | return ret; | 259 | return ret; |
260 | } | 260 | } |
261 | 261 | ||
262 | spi_release_bus(spi); | 262 | spi_release_bus(spi); |
263 | 263 | ||
264 | return ret; | 264 | return ret; |
265 | } | 265 | } |
266 | 266 | ||
267 | int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, | 267 | int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, |
268 | size_t len, void *data) | 268 | size_t len, void *data) |
269 | { | 269 | { |
270 | u8 cmd[5], bank_sel = 0; | 270 | u8 cmd[5], bank_sel = 0; |
271 | u32 remain_len, read_len; | 271 | u32 remain_len, read_len; |
272 | int ret = -1; | 272 | int ret = -1; |
273 | 273 | ||
274 | /* Handle memory-mapped SPI */ | 274 | /* Handle memory-mapped SPI */ |
275 | if (flash->memory_map) { | 275 | if (flash->memory_map) { |
276 | spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP); | 276 | spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP); |
277 | memcpy(data, flash->memory_map + offset, len); | 277 | memcpy(data, flash->memory_map + offset, len); |
278 | spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP_END); | 278 | spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP_END); |
279 | return 0; | 279 | return 0; |
280 | } | 280 | } |
281 | 281 | ||
282 | cmd[0] = CMD_READ_ARRAY_FAST; | 282 | cmd[0] = CMD_READ_ARRAY_FAST; |
283 | cmd[4] = 0x00; | 283 | cmd[4] = 0x00; |
284 | 284 | ||
285 | while (len) { | 285 | while (len) { |
286 | #ifdef CONFIG_SPI_FLASH_BAR | 286 | #ifdef CONFIG_SPI_FLASH_BAR |
287 | bank_sel = offset / SPI_FLASH_16MB_BOUN; | 287 | bank_sel = offset / SPI_FLASH_16MB_BOUN; |
288 | 288 | ||
289 | ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); | 289 | ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); |
290 | if (ret) { | 290 | if (ret) { |
291 | debug("SF: fail to set bank%d\n", bank_sel); | 291 | debug("SF: fail to set bank%d\n", bank_sel); |
292 | return ret; | 292 | return ret; |
293 | } | 293 | } |
294 | #endif | 294 | #endif |
295 | remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1)) - offset; | 295 | remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1)) - offset; |
296 | if (len < remain_len) | 296 | if (len < remain_len) |
297 | read_len = len; | 297 | read_len = len; |
298 | else | 298 | else |
299 | read_len = remain_len; | 299 | read_len = remain_len; |
300 | 300 | ||
301 | spi_flash_addr(offset, cmd); | 301 | spi_flash_addr(offset, cmd); |
302 | 302 | ||
303 | ret = spi_flash_read_common(flash, cmd, sizeof(cmd), | 303 | ret = spi_flash_read_common(flash, cmd, sizeof(cmd), |
304 | data, read_len); | 304 | data, read_len); |
305 | if (ret < 0) { | 305 | if (ret < 0) { |
306 | debug("SF: read failed\n"); | 306 | debug("SF: read failed\n"); |
307 | break; | 307 | break; |
308 | } | 308 | } |
309 | 309 | ||
310 | offset += read_len; | 310 | offset += read_len; |
311 | len -= read_len; | 311 | len -= read_len; |
312 | data += read_len; | 312 | data += read_len; |
313 | } | 313 | } |
314 | 314 | ||
315 | return ret; | 315 | return ret; |
316 | } | 316 | } |
317 | 317 | ||
318 | #ifdef CONFIG_SPI_FLASH_SST | 318 | #ifdef CONFIG_SPI_FLASH_SST |
319 | static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) | 319 | static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) |
320 | { | 320 | { |
321 | int ret; | 321 | int ret; |
322 | u8 cmd[4] = { | 322 | u8 cmd[4] = { |
323 | CMD_SST_BP, | 323 | CMD_SST_BP, |
324 | offset >> 16, | 324 | offset >> 16, |
325 | offset >> 8, | 325 | offset >> 8, |
326 | offset, | 326 | offset, |
327 | }; | 327 | }; |
328 | 328 | ||
329 | debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", | 329 | debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", |
330 | spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset); | 330 | spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset); |
331 | 331 | ||
332 | ret = spi_flash_cmd_write_enable(flash); | 332 | ret = spi_flash_cmd_write_enable(flash); |
333 | if (ret) | 333 | if (ret) |
334 | return ret; | 334 | return ret; |
335 | 335 | ||
336 | ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1); | 336 | ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1); |
337 | if (ret) | 337 | if (ret) |
338 | return ret; | 338 | return ret; |
339 | 339 | ||
340 | return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); | 340 | return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); |
341 | } | 341 | } |
342 | 342 | ||
343 | int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, | 343 | int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, |
344 | const void *buf) | 344 | const void *buf) |
345 | { | 345 | { |
346 | size_t actual, cmd_len; | 346 | size_t actual, cmd_len; |
347 | int ret; | 347 | int ret; |
348 | u8 cmd[4]; | 348 | u8 cmd[4]; |
349 | 349 | ||
350 | ret = spi_claim_bus(flash->spi); | 350 | ret = spi_claim_bus(flash->spi); |
351 | if (ret) { | 351 | if (ret) { |
352 | debug("SF: Unable to claim SPI bus\n"); | 352 | debug("SF: Unable to claim SPI bus\n"); |
353 | return ret; | 353 | return ret; |
354 | } | 354 | } |
355 | 355 | ||
356 | /* If the data is not word aligned, write out leading single byte */ | 356 | /* If the data is not word aligned, write out leading single byte */ |
357 | actual = offset % 2; | 357 | actual = offset % 2; |
358 | if (actual) { | 358 | if (actual) { |
359 | ret = sst_byte_write(flash, offset, buf); | 359 | ret = sst_byte_write(flash, offset, buf); |
360 | if (ret) | 360 | if (ret) |
361 | goto done; | 361 | goto done; |
362 | } | 362 | } |
363 | offset += actual; | 363 | offset += actual; |
364 | 364 | ||
365 | ret = spi_flash_cmd_write_enable(flash); | 365 | ret = spi_flash_cmd_write_enable(flash); |
366 | if (ret) | 366 | if (ret) |
367 | goto done; | 367 | goto done; |
368 | 368 | ||
369 | cmd_len = 4; | 369 | cmd_len = 4; |
370 | cmd[0] = CMD_SST_AAI_WP; | 370 | cmd[0] = CMD_SST_AAI_WP; |
371 | cmd[1] = offset >> 16; | 371 | cmd[1] = offset >> 16; |
372 | cmd[2] = offset >> 8; | 372 | cmd[2] = offset >> 8; |
373 | cmd[3] = offset; | 373 | cmd[3] = offset; |
374 | 374 | ||
375 | for (; actual < len - 1; actual += 2) { | 375 | for (; actual < len - 1; actual += 2) { |
376 | debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", | 376 | debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", |
377 | spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual, | 377 | spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual, |
378 | cmd[0], offset); | 378 | cmd[0], offset); |
379 | 379 | ||
380 | ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, | 380 | ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, |
381 | buf + actual, 2); | 381 | buf + actual, 2); |
382 | if (ret) { | 382 | if (ret) { |
383 | debug("SF: sst word program failed\n"); | 383 | debug("SF: sst word program failed\n"); |
384 | break; | 384 | break; |
385 | } | 385 | } |
386 | 386 | ||
387 | ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); | 387 | ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); |
388 | if (ret) | 388 | if (ret) |
389 | break; | 389 | break; |
390 | 390 | ||
391 | cmd_len = 1; | 391 | cmd_len = 1; |
392 | offset += 2; | 392 | offset += 2; |
393 | } | 393 | } |
394 | 394 | ||
395 | if (!ret) | 395 | if (!ret) |
396 | ret = spi_flash_cmd_write_disable(flash); | 396 | ret = spi_flash_cmd_write_disable(flash); |
397 | 397 | ||
398 | /* If there is a single trailing byte, write it out */ | 398 | /* If there is a single trailing byte, write it out */ |
399 | if (!ret && actual != len) | 399 | if (!ret && actual != len) |
400 | ret = sst_byte_write(flash, offset, buf + actual); | 400 | ret = sst_byte_write(flash, offset, buf + actual); |
401 | 401 | ||
402 | done: | 402 | done: |
403 | debug("SF: sst: program %s %zu bytes @ 0x%zx\n", | 403 | debug("SF: sst: program %s %zu bytes @ 0x%zx\n", |
404 | ret ? "failure" : "success", len, offset - actual); | 404 | ret ? "failure" : "success", len, offset - actual); |
405 | 405 | ||
406 | spi_release_bus(flash->spi); | 406 | spi_release_bus(flash->spi); |
407 | return ret; | 407 | return ret; |
408 | } | 408 | } |
409 | #endif | 409 | #endif |
410 | 410 |
drivers/mtd/spi/sf_probe.c
1 | /* | 1 | /* |
2 | * SPI flash probing | 2 | * SPI flash probing |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Atmel Corporation | 4 | * Copyright (C) 2008 Atmel Corporation |
5 | * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik | 5 | * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik |
6 | * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. | 6 | * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. |
7 | * | 7 | * |
8 | * Licensed under the GPL-2 or later. | 8 | * SPDX-License-Identifier: GPL-2.0+ |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <common.h> | 11 | #include <common.h> |
12 | #include <fdtdec.h> | 12 | #include <fdtdec.h> |
13 | #include <malloc.h> | 13 | #include <malloc.h> |
14 | #include <spi.h> | 14 | #include <spi.h> |
15 | #include <spi_flash.h> | 15 | #include <spi_flash.h> |
16 | 16 | ||
17 | #include "sf_internal.h" | 17 | #include "sf_internal.h" |
18 | 18 | ||
19 | DECLARE_GLOBAL_DATA_PTR; | 19 | DECLARE_GLOBAL_DATA_PTR; |
20 | 20 | ||
21 | /** | 21 | /** |
22 | * struct spi_flash_params - SPI/QSPI flash device params structure | 22 | * struct spi_flash_params - SPI/QSPI flash device params structure |
23 | * | 23 | * |
24 | * @name: Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO]) | 24 | * @name: Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO]) |
25 | * @jedec: Device jedec ID (0x[1byte_manuf_id][2byte_dev_id]) | 25 | * @jedec: Device jedec ID (0x[1byte_manuf_id][2byte_dev_id]) |
26 | * @ext_jedec: Device ext_jedec ID | 26 | * @ext_jedec: Device ext_jedec ID |
27 | * @sector_size: Sector size of this device | 27 | * @sector_size: Sector size of this device |
28 | * @nr_sectors: No.of sectors on this device | 28 | * @nr_sectors: No.of sectors on this device |
29 | * @flags: Importent param, for flash specific behaviour | 29 | * @flags: Importent param, for flash specific behaviour |
30 | */ | 30 | */ |
31 | struct spi_flash_params { | 31 | struct spi_flash_params { |
32 | const char *name; | 32 | const char *name; |
33 | u32 jedec; | 33 | u32 jedec; |
34 | u16 ext_jedec; | 34 | u16 ext_jedec; |
35 | u32 sector_size; | 35 | u32 sector_size; |
36 | u32 nr_sectors; | 36 | u32 nr_sectors; |
37 | u16 flags; | 37 | u16 flags; |
38 | }; | 38 | }; |
39 | 39 | ||
40 | static const struct spi_flash_params spi_flash_params_table[] = { | 40 | static const struct spi_flash_params spi_flash_params_table[] = { |
41 | #ifdef CONFIG_SPI_FLASH_ATMEL /* ATMEL */ | 41 | #ifdef CONFIG_SPI_FLASH_ATMEL /* ATMEL */ |
42 | {"AT45DB011D", 0x1f2200, 0x0, 64 * 1024, 4, SECT_4K}, | 42 | {"AT45DB011D", 0x1f2200, 0x0, 64 * 1024, 4, SECT_4K}, |
43 | {"AT45DB021D", 0x1f2300, 0x0, 64 * 1024, 8, SECT_4K}, | 43 | {"AT45DB021D", 0x1f2300, 0x0, 64 * 1024, 8, SECT_4K}, |
44 | {"AT45DB041D", 0x1f2400, 0x0, 64 * 1024, 8, SECT_4K}, | 44 | {"AT45DB041D", 0x1f2400, 0x0, 64 * 1024, 8, SECT_4K}, |
45 | {"AT45DB081D", 0x1f2500, 0x0, 64 * 1024, 16, SECT_4K}, | 45 | {"AT45DB081D", 0x1f2500, 0x0, 64 * 1024, 16, SECT_4K}, |
46 | {"AT45DB161D", 0x1f2600, 0x0, 64 * 1024, 32, SECT_4K}, | 46 | {"AT45DB161D", 0x1f2600, 0x0, 64 * 1024, 32, SECT_4K}, |
47 | {"AT45DB321D", 0x1f2700, 0x0, 64 * 1024, 64, SECT_4K}, | 47 | {"AT45DB321D", 0x1f2700, 0x0, 64 * 1024, 64, SECT_4K}, |
48 | {"AT45DB641D", 0x1f2800, 0x0, 64 * 1024, 128, SECT_4K}, | 48 | {"AT45DB641D", 0x1f2800, 0x0, 64 * 1024, 128, SECT_4K}, |
49 | #endif | 49 | #endif |
50 | #ifdef CONFIG_SPI_FLASH_EON /* EON */ | 50 | #ifdef CONFIG_SPI_FLASH_EON /* EON */ |
51 | {"EN25Q32B", 0x1c3016, 0x0, 64 * 1024, 64, 0}, | 51 | {"EN25Q32B", 0x1c3016, 0x0, 64 * 1024, 64, 0}, |
52 | {"EN25Q64", 0x1c3017, 0x0, 64 * 1024, 128, SECT_4K}, | 52 | {"EN25Q64", 0x1c3017, 0x0, 64 * 1024, 128, SECT_4K}, |
53 | {"EN25Q128B", 0x1c3018, 0x0, 64 * 1024, 256, 0}, | 53 | {"EN25Q128B", 0x1c3018, 0x0, 64 * 1024, 256, 0}, |
54 | {"EN25S64", 0x1c3817, 0x0, 64 * 1024, 128, 0}, | 54 | {"EN25S64", 0x1c3817, 0x0, 64 * 1024, 128, 0}, |
55 | #endif | 55 | #endif |
56 | #ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */ | 56 | #ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */ |
57 | {"GD25Q64B", 0xc84017, 0x0, 64 * 1024, 128, SECT_4K}, | 57 | {"GD25Q64B", 0xc84017, 0x0, 64 * 1024, 128, SECT_4K}, |
58 | {"GD25LQ32", 0xc86016, 0x0, 64 * 1024, 64, SECT_4K}, | 58 | {"GD25LQ32", 0xc86016, 0x0, 64 * 1024, 64, SECT_4K}, |
59 | #endif | 59 | #endif |
60 | #ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */ | 60 | #ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */ |
61 | {"MX25L4005", 0xc22013, 0x0, 64 * 1024, 8, 0}, | 61 | {"MX25L4005", 0xc22013, 0x0, 64 * 1024, 8, 0}, |
62 | {"MX25L8005", 0xc22014, 0x0, 64 * 1024, 16, 0}, | 62 | {"MX25L8005", 0xc22014, 0x0, 64 * 1024, 16, 0}, |
63 | {"MX25L1605D", 0xc22015, 0x0, 64 * 1024, 32, 0}, | 63 | {"MX25L1605D", 0xc22015, 0x0, 64 * 1024, 32, 0}, |
64 | {"MX25L3205D", 0xc22016, 0x0, 64 * 1024, 64, 0}, | 64 | {"MX25L3205D", 0xc22016, 0x0, 64 * 1024, 64, 0}, |
65 | {"MX25L6405D", 0xc22017, 0x0, 64 * 1024, 128, 0}, | 65 | {"MX25L6405D", 0xc22017, 0x0, 64 * 1024, 128, 0}, |
66 | {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, 0}, | 66 | {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, 0}, |
67 | {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, 0}, | 67 | {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, 0}, |
68 | {"MX25L51235F", 0xc2201A, 0x0, 64 * 1024, 1024, 0}, | 68 | {"MX25L51235F", 0xc2201A, 0x0, 64 * 1024, 1024, 0}, |
69 | {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, 0}, | 69 | {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, 0}, |
70 | #endif | 70 | #endif |
71 | #ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ | 71 | #ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ |
72 | {"S25FL008A", 0x010213, 0x0, 64 * 1024, 16, 0}, | 72 | {"S25FL008A", 0x010213, 0x0, 64 * 1024, 16, 0}, |
73 | {"S25FL016A", 0x010214, 0x0, 64 * 1024, 32, 0}, | 73 | {"S25FL016A", 0x010214, 0x0, 64 * 1024, 32, 0}, |
74 | {"S25FL032A", 0x010215, 0x0, 64 * 1024, 64, 0}, | 74 | {"S25FL032A", 0x010215, 0x0, 64 * 1024, 64, 0}, |
75 | {"S25FL064A", 0x010216, 0x0, 64 * 1024, 128, 0}, | 75 | {"S25FL064A", 0x010216, 0x0, 64 * 1024, 128, 0}, |
76 | {"S25FL128P_256K", 0x012018, 0x0300, 256 * 1024, 64, 0}, | 76 | {"S25FL128P_256K", 0x012018, 0x0300, 256 * 1024, 64, 0}, |
77 | {"S25FL128P_64K", 0x012018, 0x0301, 64 * 1024, 256, 0}, | 77 | {"S25FL128P_64K", 0x012018, 0x0301, 64 * 1024, 256, 0}, |
78 | {"S25FL032P", 0x010215, 0x4d00, 64 * 1024, 64, 0}, | 78 | {"S25FL032P", 0x010215, 0x4d00, 64 * 1024, 64, 0}, |
79 | {"S25FL064P", 0x010216, 0x4d00, 64 * 1024, 128, 0}, | 79 | {"S25FL064P", 0x010216, 0x4d00, 64 * 1024, 128, 0}, |
80 | {"S25FL128S_64K", 0x012018, 0x4d01, 64 * 1024, 256, 0}, | 80 | {"S25FL128S_64K", 0x012018, 0x4d01, 64 * 1024, 256, 0}, |
81 | {"S25FL256S_256K", 0x010219, 0x4d00, 64 * 1024, 512, 0}, | 81 | {"S25FL256S_256K", 0x010219, 0x4d00, 64 * 1024, 512, 0}, |
82 | {"S25FL256S_64K", 0x010219, 0x4d01, 64 * 1024, 512, 0}, | 82 | {"S25FL256S_64K", 0x010219, 0x4d01, 64 * 1024, 512, 0}, |
83 | {"S25FL512S_256K", 0x010220, 0x4d00, 64 * 1024, 1024, 0}, | 83 | {"S25FL512S_256K", 0x010220, 0x4d00, 64 * 1024, 1024, 0}, |
84 | {"S25FL512S_64K", 0x010220, 0x4d01, 64 * 1024, 1024, 0}, | 84 | {"S25FL512S_64K", 0x010220, 0x4d01, 64 * 1024, 1024, 0}, |
85 | #endif | 85 | #endif |
86 | #ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ | 86 | #ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ |
87 | {"M25P10", 0x202011, 0x0, 32 * 1024, 4, 0}, | 87 | {"M25P10", 0x202011, 0x0, 32 * 1024, 4, 0}, |
88 | {"M25P20", 0x202012, 0x0, 64 * 1024, 4, 0}, | 88 | {"M25P20", 0x202012, 0x0, 64 * 1024, 4, 0}, |
89 | {"M25P40", 0x202013, 0x0, 64 * 1024, 8, 0}, | 89 | {"M25P40", 0x202013, 0x0, 64 * 1024, 8, 0}, |
90 | {"M25P80", 0x202014, 0x0, 64 * 1024, 16, 0}, | 90 | {"M25P80", 0x202014, 0x0, 64 * 1024, 16, 0}, |
91 | {"M25P16", 0x202015, 0x0, 64 * 1024, 32, 0}, | 91 | {"M25P16", 0x202015, 0x0, 64 * 1024, 32, 0}, |
92 | {"M25P32", 0x202016, 0x0, 64 * 1024, 64, 0}, | 92 | {"M25P32", 0x202016, 0x0, 64 * 1024, 64, 0}, |
93 | {"M25P64", 0x202017, 0x0, 64 * 1024, 128, 0}, | 93 | {"M25P64", 0x202017, 0x0, 64 * 1024, 128, 0}, |
94 | {"M25P128", 0x202018, 0x0, 256 * 1024, 64, 0}, | 94 | {"M25P128", 0x202018, 0x0, 256 * 1024, 64, 0}, |
95 | {"N25Q32", 0x20ba16, 0x0, 64 * 1024, 64, SECT_4K}, | 95 | {"N25Q32", 0x20ba16, 0x0, 64 * 1024, 64, SECT_4K}, |
96 | {"N25Q32A", 0x20bb16, 0x0, 64 * 1024, 64, SECT_4K}, | 96 | {"N25Q32A", 0x20bb16, 0x0, 64 * 1024, 64, SECT_4K}, |
97 | {"N25Q64", 0x20ba17, 0x0, 64 * 1024, 128, SECT_4K}, | 97 | {"N25Q64", 0x20ba17, 0x0, 64 * 1024, 128, SECT_4K}, |
98 | {"N25Q64A", 0x20bb17, 0x0, 64 * 1024, 128, SECT_4K}, | 98 | {"N25Q64A", 0x20bb17, 0x0, 64 * 1024, 128, SECT_4K}, |
99 | {"N25Q128", 0x20ba18, 0x0, 64 * 1024, 256, SECT_4K}, | 99 | {"N25Q128", 0x20ba18, 0x0, 64 * 1024, 256, SECT_4K}, |
100 | {"N25Q128A", 0x20bb18, 0x0, 64 * 1024, 256, SECT_4K}, | 100 | {"N25Q128A", 0x20bb18, 0x0, 64 * 1024, 256, SECT_4K}, |
101 | {"N25Q256", 0x20ba19, 0x0, 64 * 1024, 512, SECT_4K}, | 101 | {"N25Q256", 0x20ba19, 0x0, 64 * 1024, 512, SECT_4K}, |
102 | {"N25Q256A", 0x20bb19, 0x0, 64 * 1024, 512, SECT_4K}, | 102 | {"N25Q256A", 0x20bb19, 0x0, 64 * 1024, 512, SECT_4K}, |
103 | {"N25Q512", 0x20ba20, 0x0, 64 * 1024, 1024, E_FSR | SECT_4K}, | 103 | {"N25Q512", 0x20ba20, 0x0, 64 * 1024, 1024, E_FSR | SECT_4K}, |
104 | {"N25Q512A", 0x20bb20, 0x0, 64 * 1024, 1024, E_FSR | SECT_4K}, | 104 | {"N25Q512A", 0x20bb20, 0x0, 64 * 1024, 1024, E_FSR | SECT_4K}, |
105 | {"N25Q1024", 0x20ba21, 0x0, 64 * 1024, 2048, E_FSR | SECT_4K}, | 105 | {"N25Q1024", 0x20ba21, 0x0, 64 * 1024, 2048, E_FSR | SECT_4K}, |
106 | {"N25Q1024A", 0x20bb21, 0x0, 64 * 1024, 2048, E_FSR | SECT_4K}, | 106 | {"N25Q1024A", 0x20bb21, 0x0, 64 * 1024, 2048, E_FSR | SECT_4K}, |
107 | #endif | 107 | #endif |
108 | #ifdef CONFIG_SPI_FLASH_SST /* SST */ | 108 | #ifdef CONFIG_SPI_FLASH_SST /* SST */ |
109 | {"SST25VF040B", 0xbf258d, 0x0, 64 * 1024, 8, SECT_4K | SST_WP}, | 109 | {"SST25VF040B", 0xbf258d, 0x0, 64 * 1024, 8, SECT_4K | SST_WP}, |
110 | {"SST25VF080B", 0xbf258e, 0x0, 64 * 1024, 16, SECT_4K | SST_WP}, | 110 | {"SST25VF080B", 0xbf258e, 0x0, 64 * 1024, 16, SECT_4K | SST_WP}, |
111 | {"SST25VF016B", 0xbf2541, 0x0, 64 * 1024, 32, SECT_4K | SST_WP}, | 111 | {"SST25VF016B", 0xbf2541, 0x0, 64 * 1024, 32, SECT_4K | SST_WP}, |
112 | {"SST25VF032B", 0xbf254a, 0x0, 64 * 1024, 64, SECT_4K | SST_WP}, | 112 | {"SST25VF032B", 0xbf254a, 0x0, 64 * 1024, 64, SECT_4K | SST_WP}, |
113 | {"SST25VF064C", 0xbf254b, 0x0, 64 * 1024, 128, SECT_4K}, | 113 | {"SST25VF064C", 0xbf254b, 0x0, 64 * 1024, 128, SECT_4K}, |
114 | {"SST25WF512", 0xbf2501, 0x0, 64 * 1024, 1, SECT_4K | SST_WP}, | 114 | {"SST25WF512", 0xbf2501, 0x0, 64 * 1024, 1, SECT_4K | SST_WP}, |
115 | {"SST25WF010", 0xbf2502, 0x0, 64 * 1024, 2, SECT_4K | SST_WP}, | 115 | {"SST25WF010", 0xbf2502, 0x0, 64 * 1024, 2, SECT_4K | SST_WP}, |
116 | {"SST25WF020", 0xbf2503, 0x0, 64 * 1024, 4, SECT_4K | SST_WP}, | 116 | {"SST25WF020", 0xbf2503, 0x0, 64 * 1024, 4, SECT_4K | SST_WP}, |
117 | {"SST25WF040", 0xbf2504, 0x0, 64 * 1024, 8, SECT_4K | SST_WP}, | 117 | {"SST25WF040", 0xbf2504, 0x0, 64 * 1024, 8, SECT_4K | SST_WP}, |
118 | {"SST25WF080", 0xbf2505, 0x0, 64 * 1024, 16, SECT_4K | SST_WP}, | 118 | {"SST25WF080", 0xbf2505, 0x0, 64 * 1024, 16, SECT_4K | SST_WP}, |
119 | #endif | 119 | #endif |
120 | #ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */ | 120 | #ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */ |
121 | {"W25P80", 0xef2014, 0x0, 64 * 1024, 16, 0}, | 121 | {"W25P80", 0xef2014, 0x0, 64 * 1024, 16, 0}, |
122 | {"W25P16", 0xef2015, 0x0, 64 * 1024, 32, 0}, | 122 | {"W25P16", 0xef2015, 0x0, 64 * 1024, 32, 0}, |
123 | {"W25P32", 0xef2016, 0x0, 64 * 1024, 64, 0}, | 123 | {"W25P32", 0xef2016, 0x0, 64 * 1024, 64, 0}, |
124 | {"W25X40", 0xef3013, 0x0, 64 * 1024, 8, SECT_4K}, | 124 | {"W25X40", 0xef3013, 0x0, 64 * 1024, 8, SECT_4K}, |
125 | {"W25X16", 0xef3015, 0x0, 64 * 1024, 32, SECT_4K}, | 125 | {"W25X16", 0xef3015, 0x0, 64 * 1024, 32, SECT_4K}, |
126 | {"W25X32", 0xef3016, 0x0, 64 * 1024, 64, SECT_4K}, | 126 | {"W25X32", 0xef3016, 0x0, 64 * 1024, 64, SECT_4K}, |
127 | {"W25X64", 0xef3017, 0x0, 64 * 1024, 128, SECT_4K}, | 127 | {"W25X64", 0xef3017, 0x0, 64 * 1024, 128, SECT_4K}, |
128 | {"W25Q80BL", 0xef4014, 0x0, 64 * 1024, 16, SECT_4K}, | 128 | {"W25Q80BL", 0xef4014, 0x0, 64 * 1024, 16, SECT_4K}, |
129 | {"W25Q16CL", 0xef4015, 0x0, 64 * 1024, 32, SECT_4K}, | 129 | {"W25Q16CL", 0xef4015, 0x0, 64 * 1024, 32, SECT_4K}, |
130 | {"W25Q32BV", 0xef4016, 0x0, 64 * 1024, 64, SECT_4K}, | 130 | {"W25Q32BV", 0xef4016, 0x0, 64 * 1024, 64, SECT_4K}, |
131 | {"W25Q64CV", 0xef4017, 0x0, 64 * 1024, 128, SECT_4K}, | 131 | {"W25Q64CV", 0xef4017, 0x0, 64 * 1024, 128, SECT_4K}, |
132 | {"W25Q128BV", 0xef4018, 0x0, 64 * 1024, 256, SECT_4K}, | 132 | {"W25Q128BV", 0xef4018, 0x0, 64 * 1024, 256, SECT_4K}, |
133 | {"W25Q256", 0xef4019, 0x0, 64 * 1024, 512, SECT_4K}, | 133 | {"W25Q256", 0xef4019, 0x0, 64 * 1024, 512, SECT_4K}, |
134 | {"W25Q80BW", 0xef5014, 0x0, 64 * 1024, 16, SECT_4K}, | 134 | {"W25Q80BW", 0xef5014, 0x0, 64 * 1024, 16, SECT_4K}, |
135 | {"W25Q16DW", 0xef6015, 0x0, 64 * 1024, 32, SECT_4K}, | 135 | {"W25Q16DW", 0xef6015, 0x0, 64 * 1024, 32, SECT_4K}, |
136 | {"W25Q32DW", 0xef6016, 0x0, 64 * 1024, 64, SECT_4K}, | 136 | {"W25Q32DW", 0xef6016, 0x0, 64 * 1024, 64, SECT_4K}, |
137 | {"W25Q64DW", 0xef6017, 0x0, 64 * 1024, 128, SECT_4K}, | 137 | {"W25Q64DW", 0xef6017, 0x0, 64 * 1024, 128, SECT_4K}, |
138 | {"W25Q128FW", 0xef6018, 0x0, 64 * 1024, 256, SECT_4K}, | 138 | {"W25Q128FW", 0xef6018, 0x0, 64 * 1024, 256, SECT_4K}, |
139 | #endif | 139 | #endif |
140 | /* | 140 | /* |
141 | * Note: | 141 | * Note: |
142 | * Below paired flash devices has similar spi_flash params. | 142 | * Below paired flash devices has similar spi_flash params. |
143 | * (S25FL129P_64K, S25FL128S_64K) | 143 | * (S25FL129P_64K, S25FL128S_64K) |
144 | * (W25Q80BL, W25Q80BV) | 144 | * (W25Q80BL, W25Q80BV) |
145 | * (W25Q16CL, W25Q16DV) | 145 | * (W25Q16CL, W25Q16DV) |
146 | * (W25Q32BV, W25Q32FV_SPI) | 146 | * (W25Q32BV, W25Q32FV_SPI) |
147 | * (W25Q64CV, W25Q64FV_SPI) | 147 | * (W25Q64CV, W25Q64FV_SPI) |
148 | * (W25Q128BV, W25Q128FV_SPI) | 148 | * (W25Q128BV, W25Q128FV_SPI) |
149 | * (W25Q32DW, W25Q32FV_QPI) | 149 | * (W25Q32DW, W25Q32FV_QPI) |
150 | * (W25Q64DW, W25Q64FV_QPI) | 150 | * (W25Q64DW, W25Q64FV_QPI) |
151 | * (W25Q128FW, W25Q128FV_QPI) | 151 | * (W25Q128FW, W25Q128FV_QPI) |
152 | */ | 152 | */ |
153 | }; | 153 | }; |
154 | 154 | ||
155 | static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi, | 155 | static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi, |
156 | u8 *idcode) | 156 | u8 *idcode) |
157 | { | 157 | { |
158 | const struct spi_flash_params *params; | 158 | const struct spi_flash_params *params; |
159 | struct spi_flash *flash; | 159 | struct spi_flash *flash; |
160 | int i; | 160 | int i; |
161 | u16 jedec = idcode[1] << 8 | idcode[2]; | 161 | u16 jedec = idcode[1] << 8 | idcode[2]; |
162 | u16 ext_jedec = idcode[3] << 8 | idcode[4]; | 162 | u16 ext_jedec = idcode[3] << 8 | idcode[4]; |
163 | 163 | ||
164 | /* Get the flash id (jedec = manuf_id + dev_id, ext_jedec) */ | 164 | /* Get the flash id (jedec = manuf_id + dev_id, ext_jedec) */ |
165 | for (i = 0; i < ARRAY_SIZE(spi_flash_params_table); i++) { | 165 | for (i = 0; i < ARRAY_SIZE(spi_flash_params_table); i++) { |
166 | params = &spi_flash_params_table[i]; | 166 | params = &spi_flash_params_table[i]; |
167 | if ((params->jedec >> 16) == idcode[0]) { | 167 | if ((params->jedec >> 16) == idcode[0]) { |
168 | if ((params->jedec & 0xFFFF) == jedec) { | 168 | if ((params->jedec & 0xFFFF) == jedec) { |
169 | if (params->ext_jedec == 0) | 169 | if (params->ext_jedec == 0) |
170 | break; | 170 | break; |
171 | else if (params->ext_jedec == ext_jedec) | 171 | else if (params->ext_jedec == ext_jedec) |
172 | break; | 172 | break; |
173 | } | 173 | } |
174 | } | 174 | } |
175 | } | 175 | } |
176 | 176 | ||
177 | if (i == ARRAY_SIZE(spi_flash_params_table)) { | 177 | if (i == ARRAY_SIZE(spi_flash_params_table)) { |
178 | printf("SF: Unsupported flash IDs: "); | 178 | printf("SF: Unsupported flash IDs: "); |
179 | printf("manuf %02x, jedec %04x, ext_jedec %04x\n", | 179 | printf("manuf %02x, jedec %04x, ext_jedec %04x\n", |
180 | idcode[0], jedec, ext_jedec); | 180 | idcode[0], jedec, ext_jedec); |
181 | return NULL; | 181 | return NULL; |
182 | } | 182 | } |
183 | 183 | ||
184 | flash = malloc(sizeof(*flash)); | 184 | flash = malloc(sizeof(*flash)); |
185 | if (!flash) { | 185 | if (!flash) { |
186 | debug("SF: Failed to allocate spi_flash\n"); | 186 | debug("SF: Failed to allocate spi_flash\n"); |
187 | return NULL; | 187 | return NULL; |
188 | } | 188 | } |
189 | memset(flash, '\0', sizeof(*flash)); | 189 | memset(flash, '\0', sizeof(*flash)); |
190 | 190 | ||
191 | /* Assign spi data */ | 191 | /* Assign spi data */ |
192 | flash->spi = spi; | 192 | flash->spi = spi; |
193 | flash->name = params->name; | 193 | flash->name = params->name; |
194 | flash->memory_map = spi->memory_map; | 194 | flash->memory_map = spi->memory_map; |
195 | 195 | ||
196 | /* Assign spi_flash ops */ | 196 | /* Assign spi_flash ops */ |
197 | flash->write = spi_flash_cmd_write_ops; | 197 | flash->write = spi_flash_cmd_write_ops; |
198 | #ifdef CONFIG_SPI_FLASH_SST | 198 | #ifdef CONFIG_SPI_FLASH_SST |
199 | if (params->flags & SST_WP) | 199 | if (params->flags & SST_WP) |
200 | flash->write = sst_write_wp; | 200 | flash->write = sst_write_wp; |
201 | #endif | 201 | #endif |
202 | flash->erase = spi_flash_cmd_erase_ops; | 202 | flash->erase = spi_flash_cmd_erase_ops; |
203 | flash->read = spi_flash_cmd_read_ops; | 203 | flash->read = spi_flash_cmd_read_ops; |
204 | 204 | ||
205 | /* Compute the flash size */ | 205 | /* Compute the flash size */ |
206 | flash->page_size = (ext_jedec == 0x4d00) ? 512 : 256; | 206 | flash->page_size = (ext_jedec == 0x4d00) ? 512 : 256; |
207 | flash->sector_size = params->sector_size; | 207 | flash->sector_size = params->sector_size; |
208 | flash->size = flash->sector_size * params->nr_sectors; | 208 | flash->size = flash->sector_size * params->nr_sectors; |
209 | 209 | ||
210 | /* Compute erase sector and command */ | 210 | /* Compute erase sector and command */ |
211 | if (params->flags & SECT_4K) { | 211 | if (params->flags & SECT_4K) { |
212 | flash->erase_cmd = CMD_ERASE_4K; | 212 | flash->erase_cmd = CMD_ERASE_4K; |
213 | flash->erase_size = 4096; | 213 | flash->erase_size = 4096; |
214 | } else if (params->flags & SECT_32K) { | 214 | } else if (params->flags & SECT_32K) { |
215 | flash->erase_cmd = CMD_ERASE_32K; | 215 | flash->erase_cmd = CMD_ERASE_32K; |
216 | flash->erase_size = 32768; | 216 | flash->erase_size = 32768; |
217 | } else { | 217 | } else { |
218 | flash->erase_cmd = CMD_ERASE_64K; | 218 | flash->erase_cmd = CMD_ERASE_64K; |
219 | flash->erase_size = flash->sector_size; | 219 | flash->erase_size = flash->sector_size; |
220 | } | 220 | } |
221 | 221 | ||
222 | /* Poll cmd seclection */ | 222 | /* Poll cmd seclection */ |
223 | flash->poll_cmd = CMD_READ_STATUS; | 223 | flash->poll_cmd = CMD_READ_STATUS; |
224 | #ifdef CONFIG_SPI_FLASH_STMICRO | 224 | #ifdef CONFIG_SPI_FLASH_STMICRO |
225 | if (params->flags & E_FSR) | 225 | if (params->flags & E_FSR) |
226 | flash->poll_cmd = CMD_FLAG_STATUS; | 226 | flash->poll_cmd = CMD_FLAG_STATUS; |
227 | #endif | 227 | #endif |
228 | 228 | ||
229 | /* Configure the BAR - discover bank cmds and read current bank */ | 229 | /* Configure the BAR - discover bank cmds and read current bank */ |
230 | #ifdef CONFIG_SPI_FLASH_BAR | 230 | #ifdef CONFIG_SPI_FLASH_BAR |
231 | u8 curr_bank = 0; | 231 | u8 curr_bank = 0; |
232 | if (flash->size > SPI_FLASH_16MB_BOUN) { | 232 | if (flash->size > SPI_FLASH_16MB_BOUN) { |
233 | flash->bank_read_cmd = (idcode[0] == 0x01) ? | 233 | flash->bank_read_cmd = (idcode[0] == 0x01) ? |
234 | CMD_BANKADDR_BRRD : CMD_EXTNADDR_RDEAR; | 234 | CMD_BANKADDR_BRRD : CMD_EXTNADDR_RDEAR; |
235 | flash->bank_write_cmd = (idcode[0] == 0x01) ? | 235 | flash->bank_write_cmd = (idcode[0] == 0x01) ? |
236 | CMD_BANKADDR_BRWR : CMD_EXTNADDR_WREAR; | 236 | CMD_BANKADDR_BRWR : CMD_EXTNADDR_WREAR; |
237 | 237 | ||
238 | if (spi_flash_read_common(flash, &flash->bank_read_cmd, 1, | 238 | if (spi_flash_read_common(flash, &flash->bank_read_cmd, 1, |
239 | &curr_bank, 1)) { | 239 | &curr_bank, 1)) { |
240 | debug("SF: fail to read bank addr register\n"); | 240 | debug("SF: fail to read bank addr register\n"); |
241 | return NULL; | 241 | return NULL; |
242 | } | 242 | } |
243 | flash->bank_curr = curr_bank; | 243 | flash->bank_curr = curr_bank; |
244 | } else { | 244 | } else { |
245 | flash->bank_curr = curr_bank; | 245 | flash->bank_curr = curr_bank; |
246 | } | 246 | } |
247 | #endif | 247 | #endif |
248 | 248 | ||
249 | /* Flash powers up read-only, so clear BP# bits */ | 249 | /* Flash powers up read-only, so clear BP# bits */ |
250 | #if defined(CONFIG_SPI_FLASH_ATMEL) || \ | 250 | #if defined(CONFIG_SPI_FLASH_ATMEL) || \ |
251 | defined(CONFIG_SPI_FLASH_MACRONIX) || \ | 251 | defined(CONFIG_SPI_FLASH_MACRONIX) || \ |
252 | defined(CONFIG_SPI_FLASH_SST) | 252 | defined(CONFIG_SPI_FLASH_SST) |
253 | spi_flash_cmd_write_status(flash, 0); | 253 | spi_flash_cmd_write_status(flash, 0); |
254 | #endif | 254 | #endif |
255 | 255 | ||
256 | return flash; | 256 | return flash; |
257 | } | 257 | } |
258 | 258 | ||
259 | #ifdef CONFIG_OF_CONTROL | 259 | #ifdef CONFIG_OF_CONTROL |
260 | int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) | 260 | int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) |
261 | { | 261 | { |
262 | fdt_addr_t addr; | 262 | fdt_addr_t addr; |
263 | fdt_size_t size; | 263 | fdt_size_t size; |
264 | int node; | 264 | int node; |
265 | 265 | ||
266 | /* If there is no node, do nothing */ | 266 | /* If there is no node, do nothing */ |
267 | node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH); | 267 | node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH); |
268 | if (node < 0) | 268 | if (node < 0) |
269 | return 0; | 269 | return 0; |
270 | 270 | ||
271 | addr = fdtdec_get_addr_size(blob, node, "memory-map", &size); | 271 | addr = fdtdec_get_addr_size(blob, node, "memory-map", &size); |
272 | if (addr == FDT_ADDR_T_NONE) { | 272 | if (addr == FDT_ADDR_T_NONE) { |
273 | debug("%s: Cannot decode address\n", __func__); | 273 | debug("%s: Cannot decode address\n", __func__); |
274 | return 0; | 274 | return 0; |
275 | } | 275 | } |
276 | 276 | ||
277 | if (flash->size != size) { | 277 | if (flash->size != size) { |
278 | debug("%s: Memory map must cover entire device\n", __func__); | 278 | debug("%s: Memory map must cover entire device\n", __func__); |
279 | return -1; | 279 | return -1; |
280 | } | 280 | } |
281 | flash->memory_map = (void *)addr; | 281 | flash->memory_map = (void *)addr; |
282 | 282 | ||
283 | return 0; | 283 | return 0; |
284 | } | 284 | } |
285 | #endif /* CONFIG_OF_CONTROL */ | 285 | #endif /* CONFIG_OF_CONTROL */ |
286 | 286 | ||
287 | struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, | 287 | struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, |
288 | unsigned int max_hz, unsigned int spi_mode) | 288 | unsigned int max_hz, unsigned int spi_mode) |
289 | { | 289 | { |
290 | struct spi_slave *spi; | 290 | struct spi_slave *spi; |
291 | struct spi_flash *flash = NULL; | 291 | struct spi_flash *flash = NULL; |
292 | u8 idcode[5]; | 292 | u8 idcode[5]; |
293 | int ret; | 293 | int ret; |
294 | 294 | ||
295 | /* Setup spi_slave */ | 295 | /* Setup spi_slave */ |
296 | spi = spi_setup_slave(bus, cs, max_hz, spi_mode); | 296 | spi = spi_setup_slave(bus, cs, max_hz, spi_mode); |
297 | if (!spi) { | 297 | if (!spi) { |
298 | printf("SF: Failed to set up slave\n"); | 298 | printf("SF: Failed to set up slave\n"); |
299 | return NULL; | 299 | return NULL; |
300 | } | 300 | } |
301 | 301 | ||
302 | /* Claim spi bus */ | 302 | /* Claim spi bus */ |
303 | ret = spi_claim_bus(spi); | 303 | ret = spi_claim_bus(spi); |
304 | if (ret) { | 304 | if (ret) { |
305 | debug("SF: Failed to claim SPI bus: %d\n", ret); | 305 | debug("SF: Failed to claim SPI bus: %d\n", ret); |
306 | goto err_claim_bus; | 306 | goto err_claim_bus; |
307 | } | 307 | } |
308 | 308 | ||
309 | /* Read the ID codes */ | 309 | /* Read the ID codes */ |
310 | ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); | 310 | ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); |
311 | if (ret) { | 311 | if (ret) { |
312 | printf("SF: Failed to get idcodes\n"); | 312 | printf("SF: Failed to get idcodes\n"); |
313 | goto err_read_id; | 313 | goto err_read_id; |
314 | } | 314 | } |
315 | 315 | ||
316 | #ifdef DEBUG | 316 | #ifdef DEBUG |
317 | printf("SF: Got idcodes\n"); | 317 | printf("SF: Got idcodes\n"); |
318 | print_buffer(0, idcode, 1, sizeof(idcode), 0); | 318 | print_buffer(0, idcode, 1, sizeof(idcode), 0); |
319 | #endif | 319 | #endif |
320 | 320 | ||
321 | /* Validate params from spi_flash_params table */ | 321 | /* Validate params from spi_flash_params table */ |
322 | flash = spi_flash_validate_params(spi, idcode); | 322 | flash = spi_flash_validate_params(spi, idcode); |
323 | if (!flash) | 323 | if (!flash) |
324 | goto err_read_id; | 324 | goto err_read_id; |
325 | 325 | ||
326 | #ifdef CONFIG_OF_CONTROL | 326 | #ifdef CONFIG_OF_CONTROL |
327 | if (spi_flash_decode_fdt(gd->fdt_blob, flash)) { | 327 | if (spi_flash_decode_fdt(gd->fdt_blob, flash)) { |
328 | debug("SF: FDT decode error\n"); | 328 | debug("SF: FDT decode error\n"); |
329 | goto err_read_id; | 329 | goto err_read_id; |
330 | } | 330 | } |
331 | #endif | 331 | #endif |
332 | #ifndef CONFIG_SPL_BUILD | 332 | #ifndef CONFIG_SPL_BUILD |
333 | printf("SF: Detected %s with page size ", flash->name); | 333 | printf("SF: Detected %s with page size ", flash->name); |
334 | print_size(flash->page_size, ", erase size "); | 334 | print_size(flash->page_size, ", erase size "); |
335 | print_size(flash->erase_size, ", total "); | 335 | print_size(flash->erase_size, ", total "); |
336 | print_size(flash->size, ""); | 336 | print_size(flash->size, ""); |
337 | if (flash->memory_map) | 337 | if (flash->memory_map) |
338 | printf(", mapped at %p", flash->memory_map); | 338 | printf(", mapped at %p", flash->memory_map); |
339 | puts("\n"); | 339 | puts("\n"); |
340 | #endif | 340 | #endif |
341 | #ifndef CONFIG_SPI_FLASH_BAR | 341 | #ifndef CONFIG_SPI_FLASH_BAR |
342 | if (flash->size > SPI_FLASH_16MB_BOUN) { | 342 | if (flash->size > SPI_FLASH_16MB_BOUN) { |
343 | puts("SF: Warning - Only lower 16MiB accessible,"); | 343 | puts("SF: Warning - Only lower 16MiB accessible,"); |
344 | puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); | 344 | puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); |
345 | } | 345 | } |
346 | #endif | 346 | #endif |
347 | 347 | ||
348 | /* Release spi bus */ | 348 | /* Release spi bus */ |
349 | spi_release_bus(spi); | 349 | spi_release_bus(spi); |
350 | 350 | ||
351 | return flash; | 351 | return flash; |
352 | 352 | ||
353 | err_read_id: | 353 | err_read_id: |
354 | spi_release_bus(spi); | 354 | spi_release_bus(spi); |
355 | err_claim_bus: | 355 | err_claim_bus: |
356 | spi_free_slave(spi); | 356 | spi_free_slave(spi); |
357 | return NULL; | 357 | return NULL; |
358 | } | 358 | } |
359 | 359 | ||
360 | void spi_flash_free(struct spi_flash *flash) | 360 | void spi_flash_free(struct spi_flash *flash) |
361 | { | 361 | { |
362 | spi_free_slave(flash->spi); | 362 | spi_free_slave(flash->spi); |
363 | free(flash); | 363 | free(flash); |
364 | } | 364 | } |
365 | 365 |