blob: 5182684478d516a78521c4e9ca23a3e91dbabe24 [file] [log] [blame]
Patrick Georgiac959032020-05-05 22:49:26 +02001/* SPDX-License-Identifier: GPL-2.0-or-later */
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07002
Furquan Shaikhc28984d2016-11-20 21:04:00 -08003#include <assert.h>
Elyes HAOUAS0edf6a52019-10-26 18:41:47 +02004#include <boot/coreboot_tables.h>
Elyes HAOUASf7a7e652021-02-01 19:39:07 +01005#include <commonlib/region.h>
Elyes HAOUAS20eaef02019-03-29 17:45:28 +01006#include <console/console.h>
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07007#include <string.h>
Zheng Bao600784e2013-02-07 17:30:23 +08008#include <spi-generic.h>
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07009#include <spi_flash.h>
Elyes HAOUASbf0970e2019-03-21 11:10:03 +010010#include <timer.h>
Elyes HAOUASede8dd02019-06-23 06:57:53 +020011#include <types.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100012
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070013#include "spi_flash_internal.h"
14
Fred Reitberger52be7f62023-03-21 15:37:06 -040015#if CONFIG(SPI_FLASH_FORCE_4_BYTE_ADDR_MODE)
16#define ADDR_MOD 1
17#else
18#define ADDR_MOD 0
19#endif
20
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070021static void spi_flash_addr(u32 addr, u8 *cmd)
22{
23 /* cmd[0] is actual command */
Fred Reitberger52be7f62023-03-21 15:37:06 -040024 if (CONFIG(SPI_FLASH_FORCE_4_BYTE_ADDR_MODE)) {
25 cmd[1] = addr >> 24;
26 cmd[2] = addr >> 16;
27 cmd[3] = addr >> 8;
28 cmd[4] = addr >> 0;
29 } else {
30 cmd[1] = addr >> 16;
31 cmd[2] = addr >> 8;
32 cmd[3] = addr >> 0;
33 }
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070034}
35
Julius Wernerdf506222021-07-13 15:57:29 -070036static int do_spi_flash_cmd(const struct spi_slave *spi, const u8 *dout,
Furquan Shaikh0dba0252016-11-30 04:34:22 -080037 size_t bytes_out, void *din, size_t bytes_in)
David Hendricksf101bbe2014-03-21 19:13:34 -070038{
Julius Werner1b7f99b2019-06-05 16:34:01 -070039 int ret;
Furquan Shaikhc2973d12016-11-29 22:07:42 -080040 /*
41 * SPI flash requires command-response kind of behavior. Thus, two
42 * separate SPI vectors are required -- first to transmit dout and other
43 * to receive in din. If some specialized SPI flash controllers
44 * (e.g. x86) can perform both command and response together, it should
45 * be handled at SPI flash controller driver level.
46 */
47 struct spi_op vectors[] = {
48 [0] = { .dout = dout, .bytesout = bytes_out,
49 .din = NULL, .bytesin = 0, },
50 [1] = { .dout = NULL, .bytesout = 0,
51 .din = din, .bytesin = bytes_in },
52 };
Kyösti Mälkki85b2b272017-01-10 06:25:57 +020053 size_t count = ARRAY_SIZE(vectors);
54 if (!bytes_in)
55 count = 1;
David Hendricksf101bbe2014-03-21 19:13:34 -070056
Julius Werner1b7f99b2019-06-05 16:34:01 -070057 ret = spi_claim_bus(spi);
58 if (ret)
David Hendricks032c8432014-04-11 19:48:55 -070059 return ret;
60
Julius Werner1b7f99b2019-06-05 16:34:01 -070061 ret = spi_xfer_vector(spi, vectors, count);
David Hendricksf101bbe2014-03-21 19:13:34 -070062
David Hendricks032c8432014-04-11 19:48:55 -070063 spi_release_bus(spi);
David Hendricksf101bbe2014-03-21 19:13:34 -070064 return ret;
65}
66
Julius Wernerdf506222021-07-13 15:57:29 -070067static int do_dual_output_cmd(const struct spi_slave *spi, const u8 *dout,
68 size_t bytes_out, void *din, size_t bytes_in)
Julius Werner99e45ce2019-06-06 17:03:44 -070069{
70 int ret;
71
72 /*
73 * spi_xfer_vector() will automatically fall back to .xfer() if
74 * .xfer_vector() is unimplemented. So using vector API here is more
75 * flexible, even though a controller that implements .xfer_vector()
76 * and (the non-vector based) .xfer_dual() but not .xfer() would be
77 * pretty odd.
78 */
79 struct spi_op vector = { .dout = dout, .bytesout = bytes_out,
80 .din = NULL, .bytesin = 0 };
81
82 ret = spi_claim_bus(spi);
83 if (ret)
84 return ret;
85
86 ret = spi_xfer_vector(spi, &vector, 1);
87
88 if (!ret)
89 ret = spi->ctrlr->xfer_dual(spi, NULL, 0, din, bytes_in);
90
91 spi_release_bus(spi);
92 return ret;
93}
94
Julius Wernerdf506222021-07-13 15:57:29 -070095static int do_dual_io_cmd(const struct spi_slave *spi, const u8 *dout,
96 size_t bytes_out, void *din, size_t bytes_in)
97{
98 int ret;
99
100 /* Only the very first byte (opcode) is transferred in "single" mode. */
101 struct spi_op vector = { .dout = dout, .bytesout = 1,
102 .din = NULL, .bytesin = 0 };
103
104 ret = spi_claim_bus(spi);
105 if (ret)
106 return ret;
107
108 ret = spi_xfer_vector(spi, &vector, 1);
109
110 if (!ret)
111 ret = spi->ctrlr->xfer_dual(spi, &dout[1], bytes_out - 1, NULL, 0);
112
113 if (!ret)
114 ret = spi->ctrlr->xfer_dual(spi, NULL, 0, din, bytes_in);
115
116 spi_release_bus(spi);
117 return ret;
118}
119
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800120int spi_flash_cmd(const struct spi_slave *spi, u8 cmd, void *response, size_t len)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700121{
David Hendricksf101bbe2014-03-21 19:13:34 -0700122 int ret = do_spi_flash_cmd(spi, &cmd, sizeof(cmd), response, len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700123 if (ret)
124 printk(BIOS_WARNING, "SF: Failed to send command %02x: %d\n", cmd, ret);
125
126 return ret;
127}
128
Angel Ponsdf40ca92021-04-16 13:57:07 +0000129/* TODO: This code is quite possibly broken and overflowing stacks. Fix ASAP! */
130#pragma GCC diagnostic push
131#if defined(__GNUC__) && !defined(__clang__)
132#pragma GCC diagnostic ignored "-Wstack-usage="
133#endif
134#pragma GCC diagnostic ignored "-Wvla"
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800135int spi_flash_cmd_write(const struct spi_slave *spi, const u8 *cmd,
136 size_t cmd_len, const void *data, size_t data_len)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700137{
Patrick Georgi24151962018-08-24 22:45:50 +0000138 int ret;
Angel Ponsdf40ca92021-04-16 13:57:07 +0000139 u8 buff[cmd_len + data_len];
Patrick Georgi24151962018-08-24 22:45:50 +0000140 memcpy(buff, cmd, cmd_len);
141 memcpy(buff + cmd_len, data, data_len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700142
Patrick Georgi24151962018-08-24 22:45:50 +0000143 ret = do_spi_flash_cmd(spi, buff, cmd_len + data_len, NULL, 0);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700144 if (ret) {
145 printk(BIOS_WARNING, "SF: Failed to send write command (%zu bytes): %d\n",
146 data_len, ret);
147 }
148
149 return ret;
150}
Angel Ponsdf40ca92021-04-16 13:57:07 +0000151#pragma GCC diagnostic pop
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700152
Aaron Durbin3a649ee2018-01-25 17:30:45 -0700153/* Perform the read operation honoring spi controller fifo size, reissuing
154 * the read command until the full request completed. */
Aaron Durbincb01aa52020-01-11 11:44:47 -0700155int spi_flash_cmd_read(const struct spi_flash *flash, u32 offset,
Julius Werner1b7f99b2019-06-05 16:34:01 -0700156 size_t len, void *buf)
Aaron Durbin3a649ee2018-01-25 17:30:45 -0700157{
Fred Reitberger52be7f62023-03-21 15:37:06 -0400158 u8 cmd[5 + ADDR_MOD];
Julius Werner1b7f99b2019-06-05 16:34:01 -0700159 int ret, cmd_len;
Julius Wernerdf506222021-07-13 15:57:29 -0700160 int (*do_cmd)(const struct spi_slave *spi, const u8 *din,
Julius Werner1b7f99b2019-06-05 16:34:01 -0700161 size_t in_bytes, void *out, size_t out_bytes);
162
163 if (CONFIG(SPI_FLASH_NO_FAST_READ)) {
Fred Reitberger52be7f62023-03-21 15:37:06 -0400164 cmd_len = 4 + ADDR_MOD;
Julius Werner1b7f99b2019-06-05 16:34:01 -0700165 cmd[0] = CMD_READ_ARRAY_SLOW;
166 do_cmd = do_spi_flash_cmd;
Julius Wernerdf506222021-07-13 15:57:29 -0700167 } else if (flash->flags.dual_io && flash->spi.ctrlr->xfer_dual) {
Fred Reitberger52be7f62023-03-21 15:37:06 -0400168 cmd_len = 5 + ADDR_MOD;
Julius Wernerdf506222021-07-13 15:57:29 -0700169 cmd[0] = CMD_READ_FAST_DUAL_IO;
Fred Reitberger52be7f62023-03-21 15:37:06 -0400170 cmd[4 + ADDR_MOD] = 0;
Julius Wernerdf506222021-07-13 15:57:29 -0700171 do_cmd = do_dual_io_cmd;
172 } else if (flash->flags.dual_output && flash->spi.ctrlr->xfer_dual) {
Fred Reitberger52be7f62023-03-21 15:37:06 -0400173 cmd_len = 5 + ADDR_MOD;
Julius Werner99e45ce2019-06-06 17:03:44 -0700174 cmd[0] = CMD_READ_FAST_DUAL_OUTPUT;
Fred Reitberger52be7f62023-03-21 15:37:06 -0400175 cmd[4 + ADDR_MOD] = 0;
Julius Wernerdf506222021-07-13 15:57:29 -0700176 do_cmd = do_dual_output_cmd;
Julius Werner1b7f99b2019-06-05 16:34:01 -0700177 } else {
Fred Reitberger52be7f62023-03-21 15:37:06 -0400178 cmd_len = 5 + ADDR_MOD;
Julius Werner1b7f99b2019-06-05 16:34:01 -0700179 cmd[0] = CMD_READ_ARRAY_FAST;
Fred Reitberger52be7f62023-03-21 15:37:06 -0400180 cmd[4 + ADDR_MOD] = 0;
Julius Werner1b7f99b2019-06-05 16:34:01 -0700181 do_cmd = do_spi_flash_cmd;
182 }
183
Aaron Durbin3a649ee2018-01-25 17:30:45 -0700184 uint8_t *data = buf;
Aaron Durbin3a649ee2018-01-25 17:30:45 -0700185 while (len) {
Julius Werner1b7f99b2019-06-05 16:34:01 -0700186 size_t xfer_len = spi_crop_chunk(&flash->spi, cmd_len, len);
187 spi_flash_addr(offset, cmd);
188 ret = do_cmd(&flash->spi, cmd, cmd_len, data, xfer_len);
189 if (ret) {
190 printk(BIOS_WARNING,
191 "SF: Failed to send read command %#.2x(%#x, %#zx): %d\n",
192 cmd[0], offset, xfer_len, ret);
Aaron Durbin3a649ee2018-01-25 17:30:45 -0700193 return ret;
Julius Werner1b7f99b2019-06-05 16:34:01 -0700194 }
Aaron Durbin3a649ee2018-01-25 17:30:45 -0700195 offset += xfer_len;
196 data += xfer_len;
197 len -= xfer_len;
198 }
199
200 return 0;
201}
202
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800203int spi_flash_cmd_poll_bit(const struct spi_flash *flash, unsigned long timeout,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700204 u8 cmd, u8 poll_bit)
205{
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800206 const struct spi_slave *spi = &flash->spi;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700207 int ret;
Raul E Rangelb90e2512022-04-29 14:06:09 -0600208 int attempt = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700209 u8 status;
Raul E Rangelb10cbd02022-04-28 16:17:01 -0600210 struct stopwatch sw;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700211
Raul E Rangelb10cbd02022-04-28 16:17:01 -0600212 stopwatch_init_msecs_expire(&sw, timeout);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700213 do {
Raul E Rangelb90e2512022-04-29 14:06:09 -0600214 attempt++;
215
Julius Werner1b7f99b2019-06-05 16:34:01 -0700216 ret = do_spi_flash_cmd(spi, &cmd, 1, &status, 1);
Raul E Rangelb90e2512022-04-29 14:06:09 -0600217 if (ret) {
218 printk(BIOS_WARNING,
219 "SF: SPI command failed on attempt %d with rc %d\n", attempt,
220 ret);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700221 return -1;
Raul E Rangelb90e2512022-04-29 14:06:09 -0600222 }
223
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700224 if ((status & poll_bit) == 0)
Dave Frodinc50c0ab2014-06-11 12:53:47 -0600225 return 0;
Raul E Rangelb10cbd02022-04-28 16:17:01 -0600226 } while (!stopwatch_expired(&sw));
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700227
Rob Barnesd522f382022-09-12 06:31:47 -0600228 printk(BIOS_WARNING, "SF: timeout at %lld msec after %d attempts\n",
Raul E Rangelb90e2512022-04-29 14:06:09 -0600229 stopwatch_duration_msecs(&sw), attempt);
Raul E Rangelb10cbd02022-04-28 16:17:01 -0600230
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700231 return -1;
232}
233
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800234int spi_flash_cmd_wait_ready(const struct spi_flash *flash,
235 unsigned long timeout)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700236{
237 return spi_flash_cmd_poll_bit(flash, timeout,
238 CMD_READ_STATUS, STATUS_WIP);
239}
240
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800241int spi_flash_cmd_erase(const struct spi_flash *flash, u32 offset, size_t len)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700242{
243 u32 start, end, erase_size;
Richard Spiegel22ba2052019-02-18 15:31:33 -0700244 int ret = -1;
Fred Reitberger52be7f62023-03-21 15:37:06 -0400245 u8 cmd[4 + ADDR_MOD];
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700246
247 erase_size = flash->sector_size;
248 if (offset % erase_size || len % erase_size) {
249 printk(BIOS_WARNING, "SF: Erase offset/length not multiple of erase size\n");
250 return -1;
251 }
Richard Spiegeld87a9b82018-08-07 16:41:02 -0700252 if (len == 0) {
253 printk(BIOS_WARNING, "SF: Erase length cannot be 0\n");
254 return -1;
255 }
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700256
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800257 cmd[0] = flash->erase_cmd;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700258 start = offset;
259 end = start + len;
260
261 while (offset < end) {
262 spi_flash_addr(offset, cmd);
263 offset += erase_size;
264
Fred Reitberger3b5d9ee2023-03-23 14:35:39 -0400265 if (CONFIG(DEBUG_SPI_FLASH)) {
266 if (ADDR_MOD)
267 printk(BIOS_SPEW, "SF: erase %2x %2x %2x %2x %2x (%x)\n",
268 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], offset);
269 else
270 printk(BIOS_SPEW, "SF: erase %2x %2x %2x %2x (%x)\n",
271 cmd[0], cmd[1], cmd[2], cmd[3], offset);
272 }
273
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800274 ret = spi_flash_cmd(&flash->spi, CMD_WRITE_ENABLE, NULL, 0);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700275 if (ret)
276 goto out;
277
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800278 ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd), NULL, 0);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700279 if (ret)
280 goto out;
281
Uwe Poeche17362be2019-07-17 14:27:13 +0200282 ret = spi_flash_cmd_wait_ready(flash,
283 SPI_FLASH_PAGE_ERASE_TIMEOUT_MS);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700284 if (ret)
285 goto out;
286 }
287
288 printk(BIOS_DEBUG, "SF: Successfully erased %zu bytes @ %#x\n", len, start);
289
Patrick Georgi20959ba2012-05-12 23:30:36 +0200290out:
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700291 return ret;
292}
293
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800294int spi_flash_cmd_status(const struct spi_flash *flash, u8 *reg)
Duncan Lauriefb032392015-01-15 15:28:46 -0800295{
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800296 return spi_flash_cmd(&flash->spi, flash->status_cmd, reg, sizeof(*reg));
Duncan Lauriefb032392015-01-15 15:28:46 -0800297}
298
Aaron Durbind701ef72019-12-27 15:16:17 -0700299int spi_flash_cmd_write_page_program(const struct spi_flash *flash, u32 offset,
300 size_t len, const void *buf)
301{
302 unsigned long byte_addr;
303 unsigned long page_size;
304 size_t chunk_len;
305 size_t actual;
306 int ret = 0;
Fred Reitberger52be7f62023-03-21 15:37:06 -0400307 u8 cmd[4 + ADDR_MOD];
Aaron Durbind701ef72019-12-27 15:16:17 -0700308
309 page_size = flash->page_size;
310 cmd[0] = flash->pp_cmd;
311
312 for (actual = 0; actual < len; actual += chunk_len) {
313 byte_addr = offset % page_size;
314 chunk_len = MIN(len - actual, page_size - byte_addr);
315 chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);
316
317 spi_flash_addr(offset, cmd);
318 if (CONFIG(DEBUG_SPI_FLASH)) {
Fred Reitberger52be7f62023-03-21 15:37:06 -0400319 if (ADDR_MOD)
320 printk(BIOS_SPEW,
321 "PP: %p => cmd = { 0x%02x 0x%02x%02x%02x%02x } chunk_len = %zu\n",
322 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], cmd[4],
323 chunk_len);
324 else
325 printk(BIOS_SPEW,
326 "PP: %p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n",
327 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3],
328 chunk_len);
Aaron Durbind701ef72019-12-27 15:16:17 -0700329 }
330
331 ret = spi_flash_cmd(&flash->spi, flash->wren_cmd, NULL, 0);
332 if (ret < 0) {
333 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
334 goto out;
335 }
336
337 ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
338 buf + actual, chunk_len);
339 if (ret < 0) {
340 printk(BIOS_WARNING, "SF: Page Program failed\n");
341 goto out;
342 }
343
344 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT_MS);
345 if (ret)
346 goto out;
347
348 offset += chunk_len;
349 }
350
351 if (CONFIG(DEBUG_SPI_FLASH))
352 printk(BIOS_SPEW, "SF: : Successfully programmed %zu bytes @ 0x%lx\n",
353 len, (unsigned long)(offset - len));
354 ret = 0;
355
356out:
357 return ret;
358}
359
Aaron Durbin5abeb062020-01-12 15:12:18 -0700360static const struct spi_flash_vendor_info *spi_flash_vendors[] = {
361#if CONFIG(SPI_FLASH_ADESTO)
362 &spi_flash_adesto_vi,
363#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800364#if CONFIG(SPI_FLASH_AMIC)
Aaron Durbin5abeb062020-01-12 15:12:18 -0700365 &spi_flash_amic_vi,
Idwer Vollering73a10182014-02-16 00:32:13 +0000366#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800367#if CONFIG(SPI_FLASH_ATMEL)
Aaron Durbin5abeb062020-01-12 15:12:18 -0700368 &spi_flash_atmel_vi,
Kyösti Mälkki96d92762014-11-11 15:04:38 +0200369#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800370#if CONFIG(SPI_FLASH_EON)
Aaron Durbin5abeb062020-01-12 15:12:18 -0700371 &spi_flash_eon_vi,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700372#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800373#if CONFIG(SPI_FLASH_GIGADEVICE)
Aaron Durbin5abeb062020-01-12 15:12:18 -0700374 &spi_flash_gigadevice_vi,
Martin Rothbceaf7f2012-09-07 15:02:35 -0600375#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800376#if CONFIG(SPI_FLASH_MACRONIX)
Aaron Durbin5abeb062020-01-12 15:12:18 -0700377 &spi_flash_macronix_vi,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700378#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800379#if CONFIG(SPI_FLASH_SPANSION)
Aaron Durbin5abeb062020-01-12 15:12:18 -0700380 &spi_flash_spansion_ext1_vi,
381 &spi_flash_spansion_ext2_vi,
382 &spi_flash_spansion_vi,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700383#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800384#if CONFIG(SPI_FLASH_SST)
Aaron Durbin5abeb062020-01-12 15:12:18 -0700385 &spi_flash_sst_ai_vi,
386 &spi_flash_sst_vi,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700387#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800388#if CONFIG(SPI_FLASH_STMICRO)
Aaron Durbin5abeb062020-01-12 15:12:18 -0700389 &spi_flash_stmicro1_vi,
390 &spi_flash_stmicro2_vi,
391 &spi_flash_stmicro3_vi,
392 &spi_flash_stmicro4_vi,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700393#endif
Julius Wernercd49cce2019-03-05 16:53:33 -0800394#if CONFIG(SPI_FLASH_WINBOND)
Aaron Durbin5abeb062020-01-12 15:12:18 -0700395 &spi_flash_winbond_vi,
Chris Douglassb34739b2014-02-14 13:51:26 -0500396#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700397};
Aaron Durbinae43f322020-01-11 09:59:32 -0700398#define IDCODE_LEN 5
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700399
Aaron Durbin5abeb062020-01-12 15:12:18 -0700400static int fill_spi_flash(const struct spi_slave *spi, struct spi_flash *flash,
401 const struct spi_flash_vendor_info *vi,
402 const struct spi_flash_part_id *part)
403{
404 memcpy(&flash->spi, spi, sizeof(*spi));
405 flash->vendor = vi->id;
Aaron Durbinfc7b9532020-01-23 11:45:30 -0700406 flash->model = part->id[0];
Aaron Durbin5abeb062020-01-12 15:12:18 -0700407
408 flash->page_size = 1U << vi->page_size_shift;
409 flash->sector_size = (1U << vi->sector_size_kib_shift) * KiB;
410 flash->size = flash->sector_size * (1U << part->nr_sectors_shift);
411 flash->erase_cmd = vi->desc->erase_cmd;
412 flash->status_cmd = vi->desc->status_cmd;
413 flash->pp_cmd = vi->desc->pp_cmd;
414 flash->wren_cmd = vi->desc->wren_cmd;
415
Julius Wernerdf506222021-07-13 15:57:29 -0700416 flash->flags.dual_output = part->fast_read_dual_output_support;
417 flash->flags.dual_io = part->fast_read_dual_io_support;
Aaron Durbin5abeb062020-01-12 15:12:18 -0700418
419 flash->ops = &vi->desc->ops;
420 flash->prot_ops = vi->prot_ops;
421 flash->part = part;
422
423 if (vi->after_probe)
424 return vi->after_probe(flash);
425
426 return 0;
427}
428
429static const struct spi_flash_part_id *find_part(const struct spi_flash_vendor_info *vi,
Aaron Durbinfc7b9532020-01-23 11:45:30 -0700430 uint16_t id[2])
Aaron Durbin5abeb062020-01-12 15:12:18 -0700431{
432 size_t i;
Aaron Durbinfc7b9532020-01-23 11:45:30 -0700433 const uint16_t lid[2] = {
434 [0] = id[0] & vi->match_id_mask[0],
435 [1] = id[1] & vi->match_id_mask[1],
436 };
437
Aaron Durbin5abeb062020-01-12 15:12:18 -0700438 for (i = 0; i < vi->nr_part_ids; i++) {
439 const struct spi_flash_part_id *part = &vi->ids[i];
440
Aaron Durbinfc7b9532020-01-23 11:45:30 -0700441 if (part->id[0] == lid[0] && part->id[1] == lid[1])
Aaron Durbin5abeb062020-01-12 15:12:18 -0700442 return part;
443 }
444
445 return NULL;
446}
447
448static int find_match(const struct spi_slave *spi, struct spi_flash *flash,
Aaron Durbinfc7b9532020-01-23 11:45:30 -0700449 uint8_t manuf_id, uint16_t id[2])
Aaron Durbin5abeb062020-01-12 15:12:18 -0700450{
451 int i;
452
453 for (i = 0; i < (int)ARRAY_SIZE(spi_flash_vendors); i++) {
454 const struct spi_flash_vendor_info *vi;
455 const struct spi_flash_part_id *part;
456
457 vi = spi_flash_vendors[i];
458
459 if (manuf_id != vi->id)
460 continue;
461
Aaron Durbinfc7b9532020-01-23 11:45:30 -0700462 part = find_part(vi, id);
Aaron Durbin5abeb062020-01-12 15:12:18 -0700463
464 if (part == NULL)
465 continue;
466
467 return fill_spi_flash(spi, flash, vi, part);
468 }
469
Fred Reitberger5f5c7212023-06-01 15:59:43 -0400470 printk(BIOS_WARNING, "SF: no match for ID %04x %04x\n", id[0], id[1]);
Aaron Durbin5abeb062020-01-12 15:12:18 -0700471 return -1;
472}
473
Furquan Shaikha1491572017-05-17 19:14:06 -0700474int spi_flash_generic_probe(const struct spi_slave *spi,
Furquan Shaikhbd9e32e2017-05-15 23:28:41 -0700475 struct spi_flash *flash)
Furquan Shaikhd2fb6ae2016-11-17 20:38:07 -0800476{
Aaron Durbinae43f322020-01-11 09:59:32 -0700477 int ret, i;
478 u8 idcode[IDCODE_LEN];
479 u8 manuf_id;
Aaron Durbinfc7b9532020-01-23 11:45:30 -0700480 u16 id[2];
Vladimir Serbinenkoe23bd0e2014-01-18 17:45:32 +0100481
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700482 /* Read the ID codes */
483 ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode));
484 if (ret)
Furquan Shaikh30221b42017-05-15 14:35:15 -0700485 return -1;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700486
Julius Wernercd49cce2019-03-05 16:53:33 -0800487 if (CONFIG(DEBUG_SPI_FLASH)) {
Furquan Shaikhd2fb6ae2016-11-17 20:38:07 -0800488 printk(BIOS_SPEW, "SF: Got idcode: ");
489 for (i = 0; i < sizeof(idcode); i++)
490 printk(BIOS_SPEW, "%02x ", idcode[i]);
491 printk(BIOS_SPEW, "\n");
492 }
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700493
Aaron Durbinae43f322020-01-11 09:59:32 -0700494 manuf_id = idcode[0];
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700495
Aaron Durbinae43f322020-01-11 09:59:32 -0700496 printk(BIOS_INFO, "Manufacturer: %02x\n", manuf_id);
Furquan Shaikhd2fb6ae2016-11-17 20:38:07 -0800497
Aaron Durbin1c3086a2020-01-03 15:58:11 -0700498 /* If no result from RDID command and STMicro parts are enabled attempt
499 to wake the part from deep sleep and obtain alternative id info. */
Aaron Durbinae43f322020-01-11 09:59:32 -0700500 if (CONFIG(SPI_FLASH_STMICRO) && manuf_id == 0xff) {
Aaron Durbin1c3086a2020-01-03 15:58:11 -0700501 if (stmicro_release_deep_sleep_identify(spi, idcode))
502 return -1;
Aaron Durbinae43f322020-01-11 09:59:32 -0700503 manuf_id = idcode[0];
Aaron Durbin1c3086a2020-01-03 15:58:11 -0700504 }
505
Aaron Durbinfc7b9532020-01-23 11:45:30 -0700506 id[0] = (idcode[1] << 8) | idcode[2];
507 id[1] = (idcode[3] << 8) | idcode[4];
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700508
Aaron Durbin5abeb062020-01-12 15:12:18 -0700509 return find_match(spi, flash, manuf_id, id);
Furquan Shaikhd2fb6ae2016-11-17 20:38:07 -0800510}
511
Furquan Shaikh30221b42017-05-15 14:35:15 -0700512int spi_flash_probe(unsigned int bus, unsigned int cs, struct spi_flash *flash)
Furquan Shaikhd2fb6ae2016-11-17 20:38:07 -0800513{
Furquan Shaikh36b81af2016-12-01 01:02:44 -0800514 struct spi_slave spi;
Furquan Shaikha1491572017-05-17 19:14:06 -0700515 int ret = -1;
Furquan Shaikhd2fb6ae2016-11-17 20:38:07 -0800516
Furquan Shaikh36b81af2016-12-01 01:02:44 -0800517 if (spi_setup_slave(bus, cs, &spi)) {
Furquan Shaikhd2fb6ae2016-11-17 20:38:07 -0800518 printk(BIOS_WARNING, "SF: Failed to set up slave\n");
Furquan Shaikh30221b42017-05-15 14:35:15 -0700519 return -1;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700520 }
521
Furquan Shaikha1491572017-05-17 19:14:06 -0700522 /* Try special programmer probe if any. */
523 if (spi.ctrlr->flash_probe)
524 ret = spi.ctrlr->flash_probe(&spi, flash);
Furquan Shaikhd2fb6ae2016-11-17 20:38:07 -0800525
526 /* If flash is not found, try generic spi flash probe. */
Furquan Shaikha1491572017-05-17 19:14:06 -0700527 if (ret)
528 ret = spi_flash_generic_probe(&spi, flash);
Furquan Shaikhd2fb6ae2016-11-17 20:38:07 -0800529
530 /* Give up -- nothing more to try if flash is not found. */
Furquan Shaikha1491572017-05-17 19:14:06 -0700531 if (ret) {
532 printk(BIOS_WARNING, "SF: Unsupported manufacturer!\n");
533 return -1;
534 }
Furquan Shaikhd2fb6ae2016-11-17 20:38:07 -0800535
Julius Werner99e45ce2019-06-06 17:03:44 -0700536 const char *mode_string = "";
Julius Wernerdf506222021-07-13 15:57:29 -0700537 if (flash->flags.dual_io && spi.ctrlr->xfer_dual)
538 mode_string = " (Dual I/O mode)";
539 else if (flash->flags.dual_output && spi.ctrlr->xfer_dual)
540 mode_string = " (Dual Output mode)";
Julius Werner99e45ce2019-06-06 17:03:44 -0700541 printk(BIOS_INFO,
Aaron Durbinfc7b9532020-01-23 11:45:30 -0700542 "SF: Detected %02x %04x with sector size 0x%x, total 0x%x%s\n",
543 flash->vendor, flash->model, flash->sector_size, flash->size, mode_string);
Arthur Heymans950332b2018-02-11 19:41:05 +0100544 if (bus == CONFIG_BOOT_DEVICE_SPI_FLASH_BUS
545 && flash->size != CONFIG_ROM_SIZE) {
546 printk(BIOS_ERR, "SF size 0x%x does not correspond to"
547 " CONFIG_ROM_SIZE 0x%x!!\n", flash->size,
548 CONFIG_ROM_SIZE);
549 }
T Michael Turneyd43e6882022-01-20 11:55:40 -0800550
551 if (CONFIG(SPI_FLASH_EXIT_4_BYTE_ADDR_MODE) && ENV_INITIAL_STAGE)
552 spi_flash_cmd(&flash->spi, CMD_EXIT_4BYTE_ADDR_MODE, NULL, 0);
553
Furquan Shaikh30221b42017-05-15 14:35:15 -0700554 return 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700555}
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800556
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800557int spi_flash_read(const struct spi_flash *flash, u32 offset, size_t len,
558 void *buf)
559{
Aaron Durbincb01aa52020-01-11 11:44:47 -0700560 return flash->ops->read(flash, offset, len, buf);
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800561}
562
563int spi_flash_write(const struct spi_flash *flash, u32 offset, size_t len,
564 const void *buf)
565{
566 int ret;
567
568 if (spi_flash_volatile_group_begin(flash))
569 return -1;
570
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -0700571 ret = flash->ops->write(flash, offset, len, buf);
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800572
573 if (spi_flash_volatile_group_end(flash))
574 return -1;
575
576 return ret;
577}
578
579int spi_flash_erase(const struct spi_flash *flash, u32 offset, size_t len)
580{
581 int ret;
582
583 if (spi_flash_volatile_group_begin(flash))
584 return -1;
585
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -0700586 ret = flash->ops->erase(flash, offset, len);
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800587
588 if (spi_flash_volatile_group_end(flash))
589 return -1;
590
591 return ret;
592}
593
594int spi_flash_status(const struct spi_flash *flash, u8 *reg)
595{
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -0700596 if (flash->ops->status)
597 return flash->ops->status(flash, reg);
598
599 return -1;
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800600}
601
Patrick Rudolph0f8bf022018-03-09 14:20:25 +0100602int spi_flash_is_write_protected(const struct spi_flash *flash,
603 const struct region *region)
604{
605 struct region flash_region = { 0 };
606
607 if (!flash || !region)
608 return -1;
609
610 flash_region.size = flash->size;
611
612 if (!region_is_subregion(&flash_region, region))
613 return -1;
614
Aaron Durbinf584f192020-01-11 14:03:27 -0700615 if (!flash->prot_ops) {
Patrick Rudolph0f8bf022018-03-09 14:20:25 +0100616 printk(BIOS_WARNING, "SPI: Write-protection gathering not "
617 "implemented for this vendor.\n");
Patrick Rudolphb66907a2018-11-07 15:23:40 +0100618 return -1;
Patrick Rudolph0f8bf022018-03-09 14:20:25 +0100619 }
620
Aaron Durbinf584f192020-01-11 14:03:27 -0700621 return flash->prot_ops->get_write(flash, region);
Patrick Rudolph0f8bf022018-03-09 14:20:25 +0100622}
623
Patrick Rudolphe63a5f12018-03-12 11:34:53 +0100624int spi_flash_set_write_protected(const struct spi_flash *flash,
625 const struct region *region,
Patrick Rudolphe63a5f12018-03-12 11:34:53 +0100626 const enum spi_flash_status_reg_lockdown mode)
627{
628 struct region flash_region = { 0 };
629 int ret;
630
631 if (!flash)
632 return -1;
633
634 flash_region.size = flash->size;
635
636 if (!region_is_subregion(&flash_region, region))
637 return -1;
638
Aaron Durbinf584f192020-01-11 14:03:27 -0700639 if (!flash->prot_ops) {
Patrick Rudolphe63a5f12018-03-12 11:34:53 +0100640 printk(BIOS_WARNING, "SPI: Setting write-protection is not "
641 "implemented for this vendor.\n");
Patrick Rudolphb66907a2018-11-07 15:23:40 +0100642 return -1;
Patrick Rudolphe63a5f12018-03-12 11:34:53 +0100643 }
644
Daniel Gröber0d0b2f42020-05-26 22:12:14 +0200645 ret = flash->prot_ops->set_write(flash, region, mode);
Patrick Rudolphe63a5f12018-03-12 11:34:53 +0100646
647 if (ret == 0 && mode != SPI_WRITE_PROTECTION_PRESERVE) {
648 printk(BIOS_INFO, "SPI: SREG lock-down was set to ");
649 switch (mode) {
650 case SPI_WRITE_PROTECTION_NONE:
651 printk(BIOS_INFO, "NEVER\n");
652 break;
653 case SPI_WRITE_PROTECTION_PIN:
654 printk(BIOS_INFO, "WP\n");
655 break;
656 case SPI_WRITE_PROTECTION_REBOOT:
657 printk(BIOS_INFO, "REBOOT\n");
658 break;
659 case SPI_WRITE_PROTECTION_PERMANENT:
660 printk(BIOS_INFO, "PERMANENT\n");
661 break;
662 default:
663 printk(BIOS_INFO, "UNKNOWN\n");
664 break;
665 }
666 }
667
668 return ret;
669}
670
Arthur Heymansc2a9c422019-11-20 21:33:31 +0100671static uint32_t volatile_group_count;
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800672
673int spi_flash_volatile_group_begin(const struct spi_flash *flash)
674{
675 uint32_t count;
676 int ret = 0;
677
Julius Wernercd49cce2019-03-05 16:53:33 -0800678 if (!CONFIG(SPI_FLASH_HAS_VOLATILE_GROUP))
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800679 return ret;
680
Arthur Heymansc2a9c422019-11-20 21:33:31 +0100681 count = volatile_group_count;
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800682 if (count == 0)
683 ret = chipset_volatile_group_begin(flash);
684
685 count++;
Arthur Heymansc2a9c422019-11-20 21:33:31 +0100686 volatile_group_count = count;
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800687 return ret;
688}
689
690int spi_flash_volatile_group_end(const struct spi_flash *flash)
691{
692 uint32_t count;
693 int ret = 0;
694
Julius Wernercd49cce2019-03-05 16:53:33 -0800695 if (!CONFIG(SPI_FLASH_HAS_VOLATILE_GROUP))
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800696 return ret;
697
Arthur Heymansc2a9c422019-11-20 21:33:31 +0100698 count = volatile_group_count;
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800699 assert(count == 0);
700 count--;
Arthur Heymansc2a9c422019-11-20 21:33:31 +0100701 volatile_group_count = count;
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800702
703 if (count == 0)
704 ret = chipset_volatile_group_end(flash);
705
706 return ret;
707}
708
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800709void lb_spi_flash(struct lb_header *header)
710{
711 struct lb_spi_flash *flash;
Furquan Shaikhf422fd22017-05-07 13:05:39 -0700712 const struct spi_flash *spi_flash_dev;
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800713
Julius Wernercd49cce2019-03-05 16:53:33 -0800714 if (!CONFIG(BOOT_DEVICE_SPI_FLASH))
Aaron Durbinc0a823c2016-08-11 14:51:38 -0500715 return;
716
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800717 flash = (struct lb_spi_flash *)lb_new_record(header);
718
719 flash->tag = LB_TAG_SPI_FLASH;
720 flash->size = sizeof(*flash);
721
Furquan Shaikhf422fd22017-05-07 13:05:39 -0700722 spi_flash_dev = boot_device_spi_flash();
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800723
724 if (spi_flash_dev) {
725 flash->flash_size = spi_flash_dev->size;
726 flash->sector_size = spi_flash_dev->sector_size;
727 flash->erase_cmd = spi_flash_dev->erase_cmd;
728 } else {
729 flash->flash_size = CONFIG_ROM_SIZE;
730 /* Default 64k erase command should work on most flash.
731 * Uniform 4k erase only works on certain devices. */
732 flash->sector_size = 64 * KiB;
733 flash->erase_cmd = CMD_BLOCK_ERASE;
734 }
Furquan Shaikh493937e2020-11-25 17:15:09 -0800735
736 if (!CONFIG(BOOT_DEVICE_MEMORY_MAPPED)) {
737 flash->mmap_count = 0;
738 } else {
739 struct flash_mmap_window *table = (struct flash_mmap_window *)(flash + 1);
740 flash->mmap_count = spi_flash_get_mmap_windows(table);
741 flash->size += flash->mmap_count * sizeof(*table);
742 }
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800743}
Aaron Durbin10d65b02017-12-14 14:34:47 -0700744
Aaron Durbin10d65b02017-12-14 14:34:47 -0700745int spi_flash_ctrlr_protect_region(const struct spi_flash *flash,
Rizwan Qureshif9f50932018-12-31 15:19:16 +0530746 const struct region *region,
747 const enum ctrlr_prot_type type)
Aaron Durbin10d65b02017-12-14 14:34:47 -0700748{
749 const struct spi_ctrlr *ctrlr;
750 struct region flash_region = { 0 };
751
752 if (!flash)
753 return -1;
754
755 flash_region.size = flash->size;
756
757 if (!region_is_subregion(&flash_region, region))
758 return -1;
759
760 ctrlr = flash->spi.ctrlr;
761
762 if (!ctrlr)
763 return -1;
764
765 if (ctrlr->flash_protect)
Rizwan Qureshif9f50932018-12-31 15:19:16 +0530766 return ctrlr->flash_protect(flash, region, type);
Aaron Durbin10d65b02017-12-14 14:34:47 -0700767
768 return -1;
769}
Aaron Durbin851dde82018-04-19 21:15:25 -0600770
771int spi_flash_vector_helper(const struct spi_slave *slave,
772 struct spi_op vectors[], size_t count,
773 int (*func)(const struct spi_slave *slave, const void *dout,
774 size_t bytesout, void *din, size_t bytesin))
775{
776 int ret;
777 void *din;
778 size_t bytes_in;
779
780 if (count < 1 || count > 2)
781 return -1;
782
783 /* SPI flash commands always have a command first... */
784 if (!vectors[0].dout || !vectors[0].bytesout)
785 return -1;
786 /* And not read any data during the command. */
787 if (vectors[0].din || vectors[0].bytesin)
788 return -1;
789
790 if (count == 2) {
791 /* If response bytes requested ensure the buffer is valid. */
792 if (vectors[1].bytesin && !vectors[1].din)
793 return -1;
794 /* No sends can accompany a receive. */
795 if (vectors[1].dout || vectors[1].bytesout)
796 return -1;
797 din = vectors[1].din;
798 bytes_in = vectors[1].bytesin;
799 } else {
800 din = NULL;
801 bytes_in = 0;
802 }
803
804 ret = func(slave, vectors[0].dout, vectors[0].bytesout, din, bytes_in);
805
806 if (ret) {
807 vectors[0].status = SPI_OP_FAILURE;
808 if (count == 2)
809 vectors[1].status = SPI_OP_FAILURE;
810 } else {
811 vectors[0].status = SPI_OP_SUCCESS;
812 if (count == 2)
813 vectors[1].status = SPI_OP_SUCCESS;
814 }
815
816 return ret;
817}
Aaron Durbin5abeb062020-01-12 15:12:18 -0700818
819const struct spi_flash_ops_descriptor spi_flash_pp_0x20_sector_desc = {
820 .erase_cmd = 0x20, /* Sector Erase */
821 .status_cmd = 0x05, /* Read Status */
822 .pp_cmd = 0x02, /* Page Program */
823 .wren_cmd = 0x06, /* Write Enable */
824 .ops = {
825 .read = spi_flash_cmd_read,
826 .write = spi_flash_cmd_write_page_program,
827 .erase = spi_flash_cmd_erase,
828 .status = spi_flash_cmd_status,
829 },
830};
831
832const struct spi_flash_ops_descriptor spi_flash_pp_0xd8_sector_desc = {
833 .erase_cmd = 0xd8, /* Sector Erase */
834 .status_cmd = 0x05, /* Read Status */
835 .pp_cmd = 0x02, /* Page Program */
836 .wren_cmd = 0x06, /* Write Enable */
837 .ops = {
838 .read = spi_flash_cmd_read,
839 .write = spi_flash_cmd_write_page_program,
840 .erase = spi_flash_cmd_erase,
841 .status = spi_flash_cmd_status,
842 },
843};