blob: 887380fdeb823772303798fcf3cba2189459c9b1 [file] [log] [blame]
Patrick Georgiac959032020-05-05 22:49:26 +02001/* SPDX-License-Identifier: GPL-2.0-or-later */
Martin Rotheffaf8f2019-10-20 20:29:22 -06002
3/*
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07004 * Driver for SST serial flashes
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07005 */
6
Furquan Shaikhc28984d2016-11-20 21:04:00 -08007#include <console/console.h>
Elyes HAOUAS361a9352019-12-18 21:26:33 +01008#include <commonlib/helpers.h>
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07009#include <spi_flash.h>
Furquan Shaikhc28984d2016-11-20 21:04:00 -080010#include <spi-generic.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100011
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070012#include "spi_flash_internal.h"
13
14#define CMD_SST_WREN 0x06 /* Write Enable */
15#define CMD_SST_WRDI 0x04 /* Write Disable */
16#define CMD_SST_RDSR 0x05 /* Read Status Register */
17#define CMD_SST_WRSR 0x01 /* Write Status Register */
Zheng Baob8117b02012-11-27 18:08:53 +080018#define CMD_SST_EWSR 0x50 /* Enable Write Status Register */
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070019#define CMD_SST_READ 0x03 /* Read Data Bytes */
20#define CMD_SST_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
21#define CMD_SST_BP 0x02 /* Byte Program */
Aaron Durbin8b1cdd52019-12-27 13:48:14 -070022#define CMD_SST_PP 0x02 /* Page Program */
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070023#define CMD_SST_AAI_WP 0xAD /* Auto Address Increment Word Program */
24#define CMD_SST_SE 0x20 /* Sector Erase */
25
26#define SST_SR_WIP (1 << 0) /* Write-in-Progress */
27#define SST_SR_WEL (1 << 1) /* Write enable */
28#define SST_SR_BP0 (1 << 2) /* Block Protection 0 */
29#define SST_SR_BP1 (1 << 3) /* Block Protection 1 */
30#define SST_SR_BP2 (1 << 4) /* Block Protection 2 */
31#define SST_SR_AAI (1 << 6) /* Addressing mode */
32#define SST_SR_BPL (1 << 7) /* BP bits lock */
33
Aaron Durbina6c73c82020-01-11 23:18:51 -070034static const struct spi_flash_part_id flash_table_ai[] = {
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070035 {
Aaron Durbinfc7b9532020-01-23 11:45:30 -070036 /* SST25VF040B */
37 .id[0] = 0x8d,
Aaron Durbina6c73c82020-01-11 23:18:51 -070038 .nr_sectors_shift = 7,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070039 },{
Aaron Durbinfc7b9532020-01-23 11:45:30 -070040 /* SST25VF080B */
41 .id[0] = 0x8e,
Aaron Durbina6c73c82020-01-11 23:18:51 -070042 .nr_sectors_shift = 8,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070043 },{
Aaron Durbinfc7b9532020-01-23 11:45:30 -070044 /* SST25VF080 */
45 .id[0] = 0x80,
Aaron Durbina6c73c82020-01-11 23:18:51 -070046 .nr_sectors_shift = 8,
Mike Banon90af7202019-01-15 03:03:16 +030047 },{
Aaron Durbinfc7b9532020-01-23 11:45:30 -070048 /* SST25VF016B */
49 .id[0] = 0x41,
Aaron Durbina6c73c82020-01-11 23:18:51 -070050 .nr_sectors_shift = 9,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070051 },{
Aaron Durbinfc7b9532020-01-23 11:45:30 -070052 /* SST25VF032B */
53 .id[0] = 0x4a,
Aaron Durbina6c73c82020-01-11 23:18:51 -070054 .nr_sectors_shift = 10,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070055 },{
Aaron Durbinfc7b9532020-01-23 11:45:30 -070056 /* SST25WF512 */
57 .id[0] = 0x01,
Aaron Durbina6c73c82020-01-11 23:18:51 -070058 .nr_sectors_shift = 4,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070059 },{
Aaron Durbinfc7b9532020-01-23 11:45:30 -070060 /* SST25WF010 */
61 .id[0] = 0x02,
Aaron Durbina6c73c82020-01-11 23:18:51 -070062 .nr_sectors_shift = 5,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070063 },{
Aaron Durbinfc7b9532020-01-23 11:45:30 -070064 /* SST25WF020 */
65 .id[0] = 0x03,
Aaron Durbina6c73c82020-01-11 23:18:51 -070066 .nr_sectors_shift = 6,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070067 },{
Aaron Durbinfc7b9532020-01-23 11:45:30 -070068 /* SST25WF040 */
69 .id[0] = 0x04,
Aaron Durbina6c73c82020-01-11 23:18:51 -070070 .nr_sectors_shift = 7,
Mike Banon90af7202019-01-15 03:03:16 +030071 },{
Aaron Durbinfc7b9532020-01-23 11:45:30 -070072 /* SST25WF080 */
73 .id[0] = 0x05,
Aaron Durbina6c73c82020-01-11 23:18:51 -070074 .nr_sectors_shift = 8,
Mike Banon90af7202019-01-15 03:03:16 +030075 },{
Aaron Durbinfc7b9532020-01-23 11:45:30 -070076 /* SST25WF080B */
77 .id[0] = 0x14,
Aaron Durbina6c73c82020-01-11 23:18:51 -070078 .nr_sectors_shift = 8,
Aaron Durbina6c73c82020-01-11 23:18:51 -070079 },
80};
81
82static const struct spi_flash_part_id flash_table_pp256[] = {
83 {
Aaron Durbinfc7b9532020-01-23 11:45:30 -070084 /* SST25VF064C */
85 .id[0] = 0x4b,
Aaron Durbina6c73c82020-01-11 23:18:51 -070086 .nr_sectors_shift = 11,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070087 },
88};
89
90static int
Furquan Shaikhc28984d2016-11-20 21:04:00 -080091sst_enable_writing(const struct spi_flash *flash)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070092{
Furquan Shaikh810e2cd2016-12-05 20:32:24 -080093 int ret = spi_flash_cmd(&flash->spi, CMD_SST_WREN, NULL, 0);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070094 if (ret)
95 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
96 return ret;
97}
98
99static int
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800100sst_enable_writing_status(const struct spi_flash *flash)
Zheng Baob8117b02012-11-27 18:08:53 +0800101{
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800102 int ret = spi_flash_cmd(&flash->spi, CMD_SST_EWSR, NULL, 0);
Zheng Baob8117b02012-11-27 18:08:53 +0800103 if (ret)
104 printk(BIOS_WARNING, "SF: Enabling Write Status failed\n");
105 return ret;
106}
107
108static int
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800109sst_disable_writing(const struct spi_flash *flash)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700110{
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800111 int ret = spi_flash_cmd(&flash->spi, CMD_SST_WRDI, NULL, 0);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700112 if (ret)
113 printk(BIOS_WARNING, "SF: Disabling Write failed\n");
114 return ret;
115}
116
117static int
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800118sst_byte_write(const struct spi_flash *flash, u32 offset, const void *buf)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700119{
120 int ret;
121 u8 cmd[4] = {
122 CMD_SST_BP,
123 offset >> 16,
124 offset >> 8,
125 offset,
126 };
127
Julius Wernercd49cce2019-03-05 16:53:33 -0800128#if CONFIG(DEBUG_SPI_FLASH)
Julius Werner540a9802019-12-09 13:03:29 -0800129 printk(BIOS_SPEW, "BP[%02x]: %p => cmd = { 0x%02x 0x%06x }\n",
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800130 spi_w8r8(&flash->spi, CMD_SST_RDSR), buf, cmd[0], offset);
Marc Jones747127d2012-12-03 22:16:29 -0700131#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700132
133 ret = sst_enable_writing(flash);
134 if (ret)
135 return ret;
136
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800137 ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd), buf, 1);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700138 if (ret)
139 return ret;
140
Uwe Poeche17362be2019-07-17 14:27:13 +0200141 return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT_MS);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700142}
143
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800144static int sst_write_ai(const struct spi_flash *flash, u32 offset, size_t len,
145 const void *buf)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700146{
147 size_t actual, cmd_len;
David Hendricks032c8432014-04-11 19:48:55 -0700148 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700149 u8 cmd[4];
150
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700151 /* If the data is not word aligned, write out leading single byte */
152 actual = offset % 2;
153 if (actual) {
154 ret = sst_byte_write(flash, offset, buf);
155 if (ret)
156 goto done;
157 }
158 offset += actual;
159
160 ret = sst_enable_writing(flash);
161 if (ret)
162 goto done;
163
164 cmd_len = 4;
165 cmd[0] = CMD_SST_AAI_WP;
166 cmd[1] = offset >> 16;
167 cmd[2] = offset >> 8;
168 cmd[3] = offset;
169
170 for (; actual < len - 1; actual += 2) {
Julius Wernercd49cce2019-03-05 16:53:33 -0800171#if CONFIG(DEBUG_SPI_FLASH)
Julius Werner540a9802019-12-09 13:03:29 -0800172 printk(BIOS_SPEW, "WP[%02x]: %p => cmd = { 0x%02x 0x%06x }\n",
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800173 spi_w8r8(&flash->spi, CMD_SST_RDSR), buf + actual, cmd[0],
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700174 offset);
Marc Jones747127d2012-12-03 22:16:29 -0700175#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700176
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800177 ret = spi_flash_cmd_write(&flash->spi, cmd, cmd_len,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700178 buf + actual, 2);
179 if (ret) {
180 printk(BIOS_WARNING, "SF: SST word program failed\n");
181 break;
182 }
183
Uwe Poeche17362be2019-07-17 14:27:13 +0200184 ret = spi_flash_cmd_wait_ready(flash,
185 SPI_FLASH_PROG_TIMEOUT_MS);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700186 if (ret)
187 break;
188
189 cmd_len = 1;
190 offset += 2;
191 }
192
193 if (!ret)
194 ret = sst_disable_writing(flash);
195
196 /* If there is a single trailing byte, write it out */
197 if (!ret && actual != len)
198 ret = sst_byte_write(flash, offset, buf + actual);
199
Patrick Georgi20959ba2012-05-12 23:30:36 +0200200done:
Julius Wernercd49cce2019-03-05 16:53:33 -0800201#if CONFIG(DEBUG_SPI_FLASH)
Marc Jones747127d2012-12-03 22:16:29 -0700202 printk(BIOS_SPEW, "SF: SST: program %s %zu bytes @ 0x%lx\n",
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700203 ret ? "failure" : "success", len, (unsigned long)offset - actual);
Marc Jones747127d2012-12-03 22:16:29 -0700204#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700205 return ret;
206}
207
Aaron Durbin5abeb062020-01-12 15:12:18 -0700208/* Flash powers up read-only, so clear BP# bits */
209static int sst_unlock(const struct spi_flash *flash)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700210{
211 int ret;
212 u8 cmd, status;
213
Zheng Baob8117b02012-11-27 18:08:53 +0800214 ret = sst_enable_writing_status(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700215 if (ret)
216 return ret;
217
218 cmd = CMD_SST_WRSR;
219 status = 0;
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800220 ret = spi_flash_cmd_write(&flash->spi, &cmd, 1, &status, 1);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700221 if (ret)
222 printk(BIOS_WARNING, "SF: Unable to set status byte\n");
223
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800224 printk(BIOS_INFO, "SF: SST: status = %x\n", spi_w8r8(&flash->spi, CMD_SST_RDSR));
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700225
226 return ret;
227}
228
Aaron Durbin5abeb062020-01-12 15:12:18 -0700229static const struct spi_flash_ops_descriptor descai = {
230 .erase_cmd = CMD_SST_SE,
231 .status_cmd = CMD_SST_RDSR,
232 .wren_cmd = CMD_SST_WREN,
233 .ops = {
234 .read = spi_flash_cmd_read,
235 .write = sst_write_ai,
236 .erase = spi_flash_cmd_erase,
237 .status = spi_flash_cmd_status,
238 },
239};
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700240
Aaron Durbin5abeb062020-01-12 15:12:18 -0700241const struct spi_flash_vendor_info spi_flash_sst_ai_vi = {
242 .id = VENDOR_ID_SST,
243 .sector_size_kib_shift = 2,
Aaron Durbinfc7b9532020-01-23 11:45:30 -0700244 .match_id_mask[0] = 0xff,
Aaron Durbin5abeb062020-01-12 15:12:18 -0700245 .ids = flash_table_ai,
246 .nr_part_ids = ARRAY_SIZE(flash_table_ai),
247 .desc = &descai,
248 .after_probe = sst_unlock,
249};
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700250
Aaron Durbin5abeb062020-01-12 15:12:18 -0700251const struct spi_flash_vendor_info spi_flash_sst_vi = {
252 .id = VENDOR_ID_SST,
253 .page_size_shift = 8,
254 .sector_size_kib_shift = 2,
Aaron Durbinfc7b9532020-01-23 11:45:30 -0700255 .match_id_mask[0] = 0xff,
Aaron Durbin5abeb062020-01-12 15:12:18 -0700256 .ids = flash_table_pp256,
257 .nr_part_ids = ARRAY_SIZE(flash_table_pp256),
258 .desc = &spi_flash_pp_0x20_sector_desc,
259 .after_probe = sst_unlock,
260};