]>
Commit | Line | Data |
---|---|---|
4d5e29a6 JT |
1 | /* |
2 | * SPI flash operations | |
3 | * | |
4 | * Copyright (C) 2008 Atmel Corporation | |
5 | * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik | |
6 | * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc. | |
7 | * | |
0c88a84a | 8 | * SPDX-License-Identifier: GPL-2.0+ |
4d5e29a6 JT |
9 | */ |
10 | ||
11 | #include <common.h> | |
c6136aad | 12 | #include <errno.h> |
ff063ed4 | 13 | #include <malloc.h> |
4d5e29a6 JT |
14 | #include <spi.h> |
15 | #include <spi_flash.h> | |
16 | #include <watchdog.h> | |
17 | ||
898e76c9 | 18 | #include "sf_internal.h" |
4d5e29a6 JT |
19 | |
20 | static void spi_flash_addr(u32 addr, u8 *cmd) | |
21 | { | |
22 | /* cmd[0] is actual command */ | |
23 | cmd[1] = addr >> 16; | |
24 | cmd[2] = addr >> 8; | |
25 | cmd[3] = addr >> 0; | |
26 | } | |
27 | ||
9f4322fd | 28 | int spi_flash_cmd_read_status(struct spi_flash *flash, u8 *rs) |
4d5e29a6 | 29 | { |
4d5e29a6 | 30 | int ret; |
9f4322fd | 31 | u8 cmd; |
4d5e29a6 | 32 | |
9f4322fd JT |
33 | cmd = CMD_READ_STATUS; |
34 | ret = spi_flash_read_common(flash, &cmd, 1, rs, 1); | |
4d5e29a6 | 35 | if (ret < 0) { |
9f4322fd | 36 | debug("SF: fail to read status register\n"); |
4d5e29a6 JT |
37 | return ret; |
38 | } | |
39 | ||
40 | return 0; | |
41 | } | |
42 | ||
2ba863fa | 43 | int spi_flash_cmd_write_status(struct spi_flash *flash, u8 ws) |
06795122 | 44 | { |
06795122 JT |
45 | u8 cmd; |
46 | int ret; | |
47 | ||
9f4322fd | 48 | cmd = CMD_WRITE_STATUS; |
2ba863fa | 49 | ret = spi_flash_write_common(flash, &cmd, 1, &ws, 1); |
06795122 | 50 | if (ret < 0) { |
9f4322fd | 51 | debug("SF: fail to write status register\n"); |
06795122 JT |
52 | return ret; |
53 | } | |
54 | ||
9f4322fd | 55 | return 0; |
06795122 | 56 | } |
06795122 | 57 | |
d08a1baf | 58 | #if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) |
9f4322fd | 59 | int spi_flash_cmd_read_config(struct spi_flash *flash, u8 *rc) |
6cba6fdf | 60 | { |
6cba6fdf | 61 | int ret; |
9f4322fd | 62 | u8 cmd; |
6cba6fdf | 63 | |
9f4322fd JT |
64 | cmd = CMD_READ_CONFIG; |
65 | ret = spi_flash_read_common(flash, &cmd, 1, rc, 1); | |
6cba6fdf | 66 | if (ret < 0) { |
9f4322fd | 67 | debug("SF: fail to read config register\n"); |
6cba6fdf JT |
68 | return ret; |
69 | } | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
9f4322fd | 74 | int spi_flash_cmd_write_config(struct spi_flash *flash, u8 wc) |
d08a1baf | 75 | { |
9f4322fd | 76 | u8 data[2]; |
d08a1baf JT |
77 | u8 cmd; |
78 | int ret; | |
79 | ||
9f4322fd JT |
80 | ret = spi_flash_cmd_read_status(flash, &data[0]); |
81 | if (ret < 0) | |
d08a1baf | 82 | return ret; |
d08a1baf | 83 | |
9f4322fd JT |
84 | cmd = CMD_WRITE_STATUS; |
85 | data[1] = wc; | |
86 | ret = spi_flash_write_common(flash, &cmd, 1, &data, 2); | |
87 | if (ret) { | |
88 | debug("SF: fail to write config register\n"); | |
89 | return ret; | |
d08a1baf JT |
90 | } |
91 | ||
9f4322fd | 92 | return 0; |
d08a1baf JT |
93 | } |
94 | #endif | |
95 | ||
4d5e29a6 | 96 | #ifdef CONFIG_SPI_FLASH_BAR |
532f2f11 | 97 | static int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel) |
4d5e29a6 JT |
98 | { |
99 | u8 cmd; | |
100 | int ret; | |
101 | ||
102 | if (flash->bank_curr == bank_sel) { | |
103 | debug("SF: not require to enable bank%d\n", bank_sel); | |
104 | return 0; | |
105 | } | |
106 | ||
107 | cmd = flash->bank_write_cmd; | |
108 | ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1); | |
109 | if (ret < 0) { | |
110 | debug("SF: fail to write bank register\n"); | |
111 | return ret; | |
112 | } | |
113 | flash->bank_curr = bank_sel; | |
114 | ||
115 | return 0; | |
116 | } | |
6152dd15 JT |
117 | |
118 | static int spi_flash_bank(struct spi_flash *flash, u32 offset) | |
119 | { | |
120 | u8 bank_sel; | |
121 | int ret; | |
122 | ||
056fbc73 | 123 | bank_sel = offset / (SPI_FLASH_16MB_BOUN << flash->shift); |
6152dd15 JT |
124 | |
125 | ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); | |
126 | if (ret) { | |
127 | debug("SF: fail to set bank%d\n", bank_sel); | |
128 | return ret; | |
129 | } | |
130 | ||
ab92224f | 131 | return bank_sel; |
6152dd15 | 132 | } |
4d5e29a6 JT |
133 | #endif |
134 | ||
b902e07c | 135 | #ifdef CONFIG_SF_DUAL_FLASH |
f77f4691 JT |
136 | static void spi_flash_dual_flash(struct spi_flash *flash, u32 *addr) |
137 | { | |
138 | switch (flash->dual_flash) { | |
139 | case SF_DUAL_STACKED_FLASH: | |
140 | if (*addr >= (flash->size >> 1)) { | |
141 | *addr -= flash->size >> 1; | |
142 | flash->spi->flags |= SPI_XFER_U_PAGE; | |
143 | } else { | |
144 | flash->spi->flags &= ~SPI_XFER_U_PAGE; | |
145 | } | |
146 | break; | |
056fbc73 JT |
147 | case SF_DUAL_PARALLEL_FLASH: |
148 | *addr >>= flash->shift; | |
149 | break; | |
f77f4691 JT |
150 | default: |
151 | debug("SF: Unsupported dual_flash=%d\n", flash->dual_flash); | |
152 | break; | |
153 | } | |
154 | } | |
b902e07c | 155 | #endif |
f77f4691 | 156 | |
06bc1756 SDPP |
157 | static int spi_flash_poll_status(struct spi_slave *spi, unsigned long timeout, |
158 | u8 cmd, u8 poll_bit) | |
4d5e29a6 | 159 | { |
4d5e29a6 | 160 | unsigned long timebase; |
f77f4691 | 161 | unsigned long flags = SPI_XFER_BEGIN; |
4d5e29a6 JT |
162 | int ret; |
163 | u8 status; | |
164 | u8 check_status = 0x0; | |
4d5e29a6 | 165 | |
06bc1756 | 166 | if (cmd == CMD_FLAG_STATUS) |
4d5e29a6 | 167 | check_status = poll_bit; |
4d5e29a6 | 168 | |
b902e07c | 169 | #ifdef CONFIG_SF_DUAL_FLASH |
f77f4691 JT |
170 | if (spi->flags & SPI_XFER_U_PAGE) |
171 | flags |= SPI_XFER_U_PAGE; | |
b902e07c | 172 | #endif |
f77f4691 | 173 | ret = spi_xfer(spi, 8, &cmd, NULL, flags); |
4d5e29a6 JT |
174 | if (ret) { |
175 | debug("SF: fail to read %s status register\n", | |
176 | cmd == CMD_READ_STATUS ? "read" : "flag"); | |
177 | return ret; | |
178 | } | |
179 | ||
180 | timebase = get_timer(0); | |
181 | do { | |
182 | WATCHDOG_RESET(); | |
183 | ||
184 | ret = spi_xfer(spi, 8, NULL, &status, 0); | |
185 | if (ret) | |
186 | return -1; | |
187 | ||
188 | if ((status & poll_bit) == check_status) | |
189 | break; | |
190 | ||
191 | } while (get_timer(timebase) < timeout); | |
192 | ||
193 | spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); | |
194 | ||
195 | if ((status & poll_bit) == check_status) | |
196 | return 0; | |
197 | ||
198 | /* Timed out */ | |
199 | debug("SF: time out!\n"); | |
200 | return -1; | |
201 | } | |
202 | ||
06bc1756 SDPP |
203 | int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) |
204 | { | |
205 | struct spi_slave *spi = flash->spi; | |
206 | int ret; | |
207 | u8 poll_bit = STATUS_WIP; | |
208 | u8 cmd = CMD_READ_STATUS; | |
209 | ||
210 | ret = spi_flash_poll_status(spi, timeout, cmd, poll_bit); | |
211 | if (ret < 0) | |
212 | return ret; | |
213 | ||
214 | if (flash->poll_cmd == CMD_FLAG_STATUS) { | |
215 | poll_bit = STATUS_PEC; | |
216 | cmd = CMD_FLAG_STATUS; | |
217 | ret = spi_flash_poll_status(spi, timeout, cmd, poll_bit); | |
218 | if (ret < 0) | |
219 | return ret; | |
220 | } | |
221 | ||
222 | return 0; | |
223 | } | |
224 | ||
4d5e29a6 JT |
225 | int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, |
226 | size_t cmd_len, const void *buf, size_t buf_len) | |
227 | { | |
228 | struct spi_slave *spi = flash->spi; | |
229 | unsigned long timeout = SPI_FLASH_PROG_TIMEOUT; | |
230 | int ret; | |
231 | ||
232 | if (buf == NULL) | |
233 | timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT; | |
234 | ||
235 | ret = spi_claim_bus(flash->spi); | |
236 | if (ret) { | |
237 | debug("SF: unable to claim SPI bus\n"); | |
238 | return ret; | |
239 | } | |
240 | ||
241 | ret = spi_flash_cmd_write_enable(flash); | |
242 | if (ret < 0) { | |
243 | debug("SF: enabling write failed\n"); | |
244 | return ret; | |
245 | } | |
246 | ||
247 | ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len); | |
248 | if (ret < 0) { | |
249 | debug("SF: write cmd failed\n"); | |
250 | return ret; | |
251 | } | |
252 | ||
253 | ret = spi_flash_cmd_wait_ready(flash, timeout); | |
254 | if (ret < 0) { | |
255 | debug("SF: write %s timed out\n", | |
256 | timeout == SPI_FLASH_PROG_TIMEOUT ? | |
257 | "program" : "page erase"); | |
258 | return ret; | |
259 | } | |
260 | ||
261 | spi_release_bus(spi); | |
262 | ||
263 | return ret; | |
264 | } | |
265 | ||
a5e8199a | 266 | int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) |
4d5e29a6 | 267 | { |
f77f4691 | 268 | u32 erase_size, erase_addr; |
ff063ed4 | 269 | u8 cmd[SPI_FLASH_CMD_LEN]; |
4d5e29a6 JT |
270 | int ret = -1; |
271 | ||
f4f51a8f | 272 | erase_size = flash->erase_size; |
4d5e29a6 JT |
273 | if (offset % erase_size || len % erase_size) { |
274 | debug("SF: Erase offset/length not multiple of erase size\n"); | |
275 | return -1; | |
276 | } | |
277 | ||
f4f51a8f | 278 | cmd[0] = flash->erase_cmd; |
4d5e29a6 | 279 | while (len) { |
f77f4691 JT |
280 | erase_addr = offset; |
281 | ||
b902e07c | 282 | #ifdef CONFIG_SF_DUAL_FLASH |
f77f4691 JT |
283 | if (flash->dual_flash > SF_SINGLE_FLASH) |
284 | spi_flash_dual_flash(flash, &erase_addr); | |
b902e07c | 285 | #endif |
4d5e29a6 | 286 | #ifdef CONFIG_SPI_FLASH_BAR |
f77f4691 | 287 | ret = spi_flash_bank(flash, erase_addr); |
6152dd15 | 288 | if (ret < 0) |
4d5e29a6 | 289 | return ret; |
4d5e29a6 | 290 | #endif |
f77f4691 | 291 | spi_flash_addr(erase_addr, cmd); |
4d5e29a6 JT |
292 | |
293 | debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], | |
f77f4691 | 294 | cmd[2], cmd[3], erase_addr); |
4d5e29a6 JT |
295 | |
296 | ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); | |
297 | if (ret < 0) { | |
298 | debug("SF: erase failed\n"); | |
299 | break; | |
300 | } | |
301 | ||
302 | offset += erase_size; | |
303 | len -= erase_size; | |
304 | } | |
305 | ||
306 | return ret; | |
307 | } | |
308 | ||
a5e8199a | 309 | int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, |
4d5e29a6 JT |
310 | size_t len, const void *buf) |
311 | { | |
312 | unsigned long byte_addr, page_size; | |
f77f4691 | 313 | u32 write_addr; |
4d5e29a6 | 314 | size_t chunk_len, actual; |
ff063ed4 | 315 | u8 cmd[SPI_FLASH_CMD_LEN]; |
4d5e29a6 JT |
316 | int ret = -1; |
317 | ||
318 | page_size = flash->page_size; | |
319 | ||
3163aaa6 | 320 | cmd[0] = flash->write_cmd; |
4d5e29a6 | 321 | for (actual = 0; actual < len; actual += chunk_len) { |
f77f4691 JT |
322 | write_addr = offset; |
323 | ||
b902e07c | 324 | #ifdef CONFIG_SF_DUAL_FLASH |
f77f4691 JT |
325 | if (flash->dual_flash > SF_SINGLE_FLASH) |
326 | spi_flash_dual_flash(flash, &write_addr); | |
b902e07c | 327 | #endif |
4d5e29a6 | 328 | #ifdef CONFIG_SPI_FLASH_BAR |
f77f4691 | 329 | ret = spi_flash_bank(flash, write_addr); |
6152dd15 | 330 | if (ret < 0) |
4d5e29a6 | 331 | return ret; |
4d5e29a6 JT |
332 | #endif |
333 | byte_addr = offset % page_size; | |
b4141195 | 334 | chunk_len = min(len - actual, (size_t)(page_size - byte_addr)); |
4d5e29a6 JT |
335 | |
336 | if (flash->spi->max_write_size) | |
b4141195 MY |
337 | chunk_len = min(chunk_len, |
338 | (size_t)flash->spi->max_write_size); | |
4d5e29a6 | 339 | |
f77f4691 | 340 | spi_flash_addr(write_addr, cmd); |
4d5e29a6 | 341 | |
2ba863fa | 342 | debug("SF: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", |
4d5e29a6 JT |
343 | buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); |
344 | ||
345 | ret = spi_flash_write_common(flash, cmd, sizeof(cmd), | |
346 | buf + actual, chunk_len); | |
347 | if (ret < 0) { | |
348 | debug("SF: write failed\n"); | |
349 | break; | |
350 | } | |
351 | ||
352 | offset += chunk_len; | |
353 | } | |
354 | ||
355 | return ret; | |
356 | } | |
357 | ||
358 | int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, | |
359 | size_t cmd_len, void *data, size_t data_len) | |
360 | { | |
361 | struct spi_slave *spi = flash->spi; | |
362 | int ret; | |
363 | ||
364 | ret = spi_claim_bus(flash->spi); | |
365 | if (ret) { | |
366 | debug("SF: unable to claim SPI bus\n"); | |
367 | return ret; | |
368 | } | |
369 | ||
370 | ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); | |
371 | if (ret < 0) { | |
372 | debug("SF: read cmd failed\n"); | |
373 | return ret; | |
374 | } | |
375 | ||
376 | spi_release_bus(spi); | |
377 | ||
378 | return ret; | |
379 | } | |
380 | ||
a5e8199a | 381 | int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, |
4d5e29a6 JT |
382 | size_t len, void *data) |
383 | { | |
ab92224f | 384 | u8 *cmd, cmdsz; |
f77f4691 | 385 | u32 remain_len, read_len, read_addr; |
ab92224f | 386 | int bank_sel = 0; |
4d5e29a6 JT |
387 | int ret = -1; |
388 | ||
389 | /* Handle memory-mapped SPI */ | |
390 | if (flash->memory_map) { | |
ac5cce38 PS |
391 | ret = spi_claim_bus(flash->spi); |
392 | if (ret) { | |
393 | debug("SF: unable to claim SPI bus\n"); | |
394 | return ret; | |
395 | } | |
004f15b6 | 396 | spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP); |
4d5e29a6 | 397 | memcpy(data, flash->memory_map + offset, len); |
004f15b6 | 398 | spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP_END); |
ac5cce38 | 399 | spi_release_bus(flash->spi); |
4d5e29a6 JT |
400 | return 0; |
401 | } | |
402 | ||
ff063ed4 | 403 | cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte; |
c6136aad JT |
404 | cmd = calloc(1, cmdsz); |
405 | if (!cmd) { | |
406 | debug("SF: Failed to allocate cmd\n"); | |
407 | return -ENOMEM; | |
408 | } | |
4d5e29a6 | 409 | |
ff063ed4 | 410 | cmd[0] = flash->read_cmd; |
4d5e29a6 | 411 | while (len) { |
f77f4691 JT |
412 | read_addr = offset; |
413 | ||
b902e07c | 414 | #ifdef CONFIG_SF_DUAL_FLASH |
f77f4691 JT |
415 | if (flash->dual_flash > SF_SINGLE_FLASH) |
416 | spi_flash_dual_flash(flash, &read_addr); | |
b902e07c | 417 | #endif |
4d5e29a6 | 418 | #ifdef CONFIG_SPI_FLASH_BAR |
f77f4691 | 419 | bank_sel = spi_flash_bank(flash, read_addr); |
ab92224f | 420 | if (bank_sel < 0) |
4d5e29a6 | 421 | return ret; |
4d5e29a6 | 422 | #endif |
056fbc73 JT |
423 | remain_len = ((SPI_FLASH_16MB_BOUN << flash->shift) * |
424 | (bank_sel + 1)) - offset; | |
4d5e29a6 JT |
425 | if (len < remain_len) |
426 | read_len = len; | |
427 | else | |
428 | read_len = remain_len; | |
429 | ||
f77f4691 | 430 | spi_flash_addr(read_addr, cmd); |
4d5e29a6 | 431 | |
ff063ed4 | 432 | ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); |
4d5e29a6 JT |
433 | if (ret < 0) { |
434 | debug("SF: read failed\n"); | |
435 | break; | |
436 | } | |
437 | ||
438 | offset += read_len; | |
439 | len -= read_len; | |
440 | data += read_len; | |
441 | } | |
442 | ||
a52a178f | 443 | free(cmd); |
4d5e29a6 JT |
444 | return ret; |
445 | } | |
10ca45d0 JT |
446 | |
447 | #ifdef CONFIG_SPI_FLASH_SST | |
448 | static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) | |
449 | { | |
450 | int ret; | |
451 | u8 cmd[4] = { | |
452 | CMD_SST_BP, | |
453 | offset >> 16, | |
454 | offset >> 8, | |
455 | offset, | |
456 | }; | |
457 | ||
458 | debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", | |
459 | spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset); | |
460 | ||
461 | ret = spi_flash_cmd_write_enable(flash); | |
462 | if (ret) | |
463 | return ret; | |
464 | ||
465 | ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1); | |
466 | if (ret) | |
467 | return ret; | |
468 | ||
469 | return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); | |
470 | } | |
471 | ||
472 | int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, | |
473 | const void *buf) | |
474 | { | |
475 | size_t actual, cmd_len; | |
476 | int ret; | |
477 | u8 cmd[4]; | |
478 | ||
479 | ret = spi_claim_bus(flash->spi); | |
480 | if (ret) { | |
481 | debug("SF: Unable to claim SPI bus\n"); | |
482 | return ret; | |
483 | } | |
484 | ||
485 | /* If the data is not word aligned, write out leading single byte */ | |
486 | actual = offset % 2; | |
487 | if (actual) { | |
488 | ret = sst_byte_write(flash, offset, buf); | |
489 | if (ret) | |
490 | goto done; | |
491 | } | |
492 | offset += actual; | |
493 | ||
494 | ret = spi_flash_cmd_write_enable(flash); | |
495 | if (ret) | |
496 | goto done; | |
497 | ||
498 | cmd_len = 4; | |
499 | cmd[0] = CMD_SST_AAI_WP; | |
500 | cmd[1] = offset >> 16; | |
501 | cmd[2] = offset >> 8; | |
502 | cmd[3] = offset; | |
503 | ||
504 | for (; actual < len - 1; actual += 2) { | |
505 | debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", | |
506 | spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual, | |
507 | cmd[0], offset); | |
508 | ||
509 | ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, | |
510 | buf + actual, 2); | |
511 | if (ret) { | |
512 | debug("SF: sst word program failed\n"); | |
513 | break; | |
514 | } | |
515 | ||
516 | ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); | |
517 | if (ret) | |
518 | break; | |
519 | ||
520 | cmd_len = 1; | |
521 | offset += 2; | |
522 | } | |
523 | ||
524 | if (!ret) | |
525 | ret = spi_flash_cmd_write_disable(flash); | |
526 | ||
527 | /* If there is a single trailing byte, write it out */ | |
528 | if (!ret && actual != len) | |
529 | ret = sst_byte_write(flash, offset, buf + actual); | |
530 | ||
531 | done: | |
532 | debug("SF: sst: program %s %zu bytes @ 0x%zx\n", | |
533 | ret ? "failure" : "success", len, offset - actual); | |
534 | ||
535 | spi_release_bus(flash->spi); | |
536 | return ret; | |
537 | } | |
74c2cee4 BM |
538 | |
539 | int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len, | |
540 | const void *buf) | |
541 | { | |
542 | size_t actual; | |
543 | int ret; | |
544 | ||
545 | ret = spi_claim_bus(flash->spi); | |
546 | if (ret) { | |
547 | debug("SF: Unable to claim SPI bus\n"); | |
548 | return ret; | |
549 | } | |
550 | ||
551 | for (actual = 0; actual < len; actual++) { | |
552 | ret = sst_byte_write(flash, offset, buf + actual); | |
553 | if (ret) { | |
554 | debug("SF: sst byte program failed\n"); | |
555 | break; | |
556 | } | |
557 | offset++; | |
558 | } | |
559 | ||
560 | if (!ret) | |
561 | ret = spi_flash_cmd_write_disable(flash); | |
562 | ||
563 | debug("SF: sst: program %s %zu bytes @ 0x%zx\n", | |
564 | ret ? "failure" : "success", len, offset - actual); | |
565 | ||
566 | spi_release_bus(flash->spi); | |
567 | return ret; | |
568 | } | |
10ca45d0 | 569 | #endif |