blob: 1fb64467a0a12c4d6f8421824297d8c448a6b76f [file] [log] [blame]
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07001/*
Martin Rotheffaf8f2019-10-20 20:29:22 -06002 * This file is part of the coreboot project.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15/*
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070016 * Driver for SST serial flashes
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070017 */
18
Furquan Shaikhc28984d2016-11-20 21:04:00 -080019#include <console/console.h>
Elyes HAOUAS361a9352019-12-18 21:26:33 +010020#include <commonlib/helpers.h>
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070021#include <spi_flash.h>
Furquan Shaikhc28984d2016-11-20 21:04:00 -080022#include <spi-generic.h>
Furquan Shaikh810e2cd2016-12-05 20:32:24 -080023#include <string.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100024
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070025#include "spi_flash_internal.h"
26
27#define CMD_SST_WREN 0x06 /* Write Enable */
28#define CMD_SST_WRDI 0x04 /* Write Disable */
29#define CMD_SST_RDSR 0x05 /* Read Status Register */
30#define CMD_SST_WRSR 0x01 /* Write Status Register */
Zheng Baob8117b02012-11-27 18:08:53 +080031#define CMD_SST_EWSR 0x50 /* Enable Write Status Register */
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070032#define CMD_SST_READ 0x03 /* Read Data Bytes */
33#define CMD_SST_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
34#define CMD_SST_BP 0x02 /* Byte Program */
Aaron Durbin8b1cdd52019-12-27 13:48:14 -070035#define CMD_SST_PP 0x02 /* Page Program */
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070036#define CMD_SST_AAI_WP 0xAD /* Auto Address Increment Word Program */
37#define CMD_SST_SE 0x20 /* Sector Erase */
38
39#define SST_SR_WIP (1 << 0) /* Write-in-Progress */
40#define SST_SR_WEL (1 << 1) /* Write enable */
41#define SST_SR_BP0 (1 << 2) /* Block Protection 0 */
42#define SST_SR_BP1 (1 << 3) /* Block Protection 1 */
43#define SST_SR_BP2 (1 << 4) /* Block Protection 2 */
44#define SST_SR_AAI (1 << 6) /* Addressing mode */
45#define SST_SR_BPL (1 << 7) /* BP bits lock */
46
47struct sst_spi_flash_params {
48 u8 idcode1;
49 u16 nr_sectors;
50 const char *name;
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -070051 const struct spi_flash_ops *ops;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070052};
53
Furquan Shaikhc28984d2016-11-20 21:04:00 -080054static int sst_write_ai(const struct spi_flash *flash, u32 offset, size_t len,
55 const void *buf);
Alexander Couzens013accc2016-03-09 05:11:44 +010056
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -070057static const struct spi_flash_ops spi_flash_ops_write_ai = {
Aaron Durbincb01aa52020-01-11 11:44:47 -070058 .read = spi_flash_cmd_read,
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -070059 .write = sst_write_ai,
60 .erase = spi_flash_cmd_erase,
61 .status = spi_flash_cmd_status,
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -070062};
63
64static const struct spi_flash_ops spi_flash_ops_write_256 = {
Aaron Durbincb01aa52020-01-11 11:44:47 -070065 .read = spi_flash_cmd_read,
Aaron Durbind701ef72019-12-27 15:16:17 -070066 .write = spi_flash_cmd_write_page_program,
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -070067 .erase = spi_flash_cmd_erase,
68 .status = spi_flash_cmd_status,
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -070069};
70
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070071#define SST_SECTOR_SIZE (4 * 1024)
72static const struct sst_spi_flash_params sst_spi_flash_table[] = {
73 {
74 .idcode1 = 0x8d,
75 .nr_sectors = 128,
76 .name = "SST25VF040B",
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -070077 .ops = &spi_flash_ops_write_ai,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070078 },{
79 .idcode1 = 0x8e,
80 .nr_sectors = 256,
81 .name = "SST25VF080B",
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -070082 .ops = &spi_flash_ops_write_ai,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070083 },{
Mike Banon90af7202019-01-15 03:03:16 +030084 .idcode1 = 0x80,
85 .nr_sectors = 256,
86 .name = "SST25VF080",
87 .ops = &spi_flash_ops_write_ai,
88 },{
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070089 .idcode1 = 0x41,
90 .nr_sectors = 512,
91 .name = "SST25VF016B",
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -070092 .ops = &spi_flash_ops_write_ai,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070093 },{
94 .idcode1 = 0x4a,
95 .nr_sectors = 1024,
96 .name = "SST25VF032B",
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -070097 .ops = &spi_flash_ops_write_ai,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070098 },{
99 .idcode1 = 0x4b,
100 .nr_sectors = 2048,
101 .name = "SST25VF064C",
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -0700102 .ops = &spi_flash_ops_write_256,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700103 },{
104 .idcode1 = 0x01,
105 .nr_sectors = 16,
106 .name = "SST25WF512",
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -0700107 .ops = &spi_flash_ops_write_ai,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700108 },{
109 .idcode1 = 0x02,
110 .nr_sectors = 32,
111 .name = "SST25WF010",
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -0700112 .ops = &spi_flash_ops_write_ai,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700113 },{
114 .idcode1 = 0x03,
115 .nr_sectors = 64,
116 .name = "SST25WF020",
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -0700117 .ops = &spi_flash_ops_write_ai,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700118 },{
119 .idcode1 = 0x04,
120 .nr_sectors = 128,
121 .name = "SST25WF040",
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -0700122 .ops = &spi_flash_ops_write_ai,
Mike Banon90af7202019-01-15 03:03:16 +0300123 },{
124 .idcode1 = 0x05,
125 .nr_sectors = 256,
126 .name = "SST25WF080",
127 .ops = &spi_flash_ops_write_ai,
128 },{
129 .idcode1 = 0x14,
130 .nr_sectors = 256,
131 .name = "SST25WF080B",
132 .ops = &spi_flash_ops_write_ai,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700133 },
134};
135
136static int
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800137sst_enable_writing(const struct spi_flash *flash)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700138{
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800139 int ret = spi_flash_cmd(&flash->spi, CMD_SST_WREN, NULL, 0);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700140 if (ret)
141 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
142 return ret;
143}
144
145static int
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800146sst_enable_writing_status(const struct spi_flash *flash)
Zheng Baob8117b02012-11-27 18:08:53 +0800147{
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800148 int ret = spi_flash_cmd(&flash->spi, CMD_SST_EWSR, NULL, 0);
Zheng Baob8117b02012-11-27 18:08:53 +0800149 if (ret)
150 printk(BIOS_WARNING, "SF: Enabling Write Status failed\n");
151 return ret;
152}
153
154static int
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800155sst_disable_writing(const struct spi_flash *flash)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700156{
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800157 int ret = spi_flash_cmd(&flash->spi, CMD_SST_WRDI, NULL, 0);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700158 if (ret)
159 printk(BIOS_WARNING, "SF: Disabling Write failed\n");
160 return ret;
161}
162
163static int
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800164sst_byte_write(const struct spi_flash *flash, u32 offset, const void *buf)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700165{
166 int ret;
167 u8 cmd[4] = {
168 CMD_SST_BP,
169 offset >> 16,
170 offset >> 8,
171 offset,
172 };
173
Julius Wernercd49cce2019-03-05 16:53:33 -0800174#if CONFIG(DEBUG_SPI_FLASH)
Julius Werner540a9802019-12-09 13:03:29 -0800175 printk(BIOS_SPEW, "BP[%02x]: %p => cmd = { 0x%02x 0x%06x }\n",
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800176 spi_w8r8(&flash->spi, CMD_SST_RDSR), buf, cmd[0], offset);
Marc Jones747127d2012-12-03 22:16:29 -0700177#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700178
179 ret = sst_enable_writing(flash);
180 if (ret)
181 return ret;
182
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800183 ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd), buf, 1);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700184 if (ret)
185 return ret;
186
Uwe Poeche17362be2019-07-17 14:27:13 +0200187 return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT_MS);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700188}
189
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800190static int sst_write_ai(const struct spi_flash *flash, u32 offset, size_t len,
191 const void *buf)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700192{
193 size_t actual, cmd_len;
David Hendricks032c8432014-04-11 19:48:55 -0700194 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700195 u8 cmd[4];
196
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700197 /* If the data is not word aligned, write out leading single byte */
198 actual = offset % 2;
199 if (actual) {
200 ret = sst_byte_write(flash, offset, buf);
201 if (ret)
202 goto done;
203 }
204 offset += actual;
205
206 ret = sst_enable_writing(flash);
207 if (ret)
208 goto done;
209
210 cmd_len = 4;
211 cmd[0] = CMD_SST_AAI_WP;
212 cmd[1] = offset >> 16;
213 cmd[2] = offset >> 8;
214 cmd[3] = offset;
215
216 for (; actual < len - 1; actual += 2) {
Julius Wernercd49cce2019-03-05 16:53:33 -0800217#if CONFIG(DEBUG_SPI_FLASH)
Julius Werner540a9802019-12-09 13:03:29 -0800218 printk(BIOS_SPEW, "WP[%02x]: %p => cmd = { 0x%02x 0x%06x }\n",
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800219 spi_w8r8(&flash->spi, CMD_SST_RDSR), buf + actual, cmd[0],
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700220 offset);
Marc Jones747127d2012-12-03 22:16:29 -0700221#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700222
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800223 ret = spi_flash_cmd_write(&flash->spi, cmd, cmd_len,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700224 buf + actual, 2);
225 if (ret) {
226 printk(BIOS_WARNING, "SF: SST word program failed\n");
227 break;
228 }
229
Uwe Poeche17362be2019-07-17 14:27:13 +0200230 ret = spi_flash_cmd_wait_ready(flash,
231 SPI_FLASH_PROG_TIMEOUT_MS);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700232 if (ret)
233 break;
234
235 cmd_len = 1;
236 offset += 2;
237 }
238
239 if (!ret)
240 ret = sst_disable_writing(flash);
241
242 /* If there is a single trailing byte, write it out */
243 if (!ret && actual != len)
244 ret = sst_byte_write(flash, offset, buf + actual);
245
Patrick Georgi20959ba2012-05-12 23:30:36 +0200246done:
Julius Wernercd49cce2019-03-05 16:53:33 -0800247#if CONFIG(DEBUG_SPI_FLASH)
Marc Jones747127d2012-12-03 22:16:29 -0700248 printk(BIOS_SPEW, "SF: SST: program %s %zu bytes @ 0x%lx\n",
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700249 ret ? "failure" : "success", len, (unsigned long)offset - actual);
Marc Jones747127d2012-12-03 22:16:29 -0700250#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700251 return ret;
252}
253
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700254
255static int
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800256sst_unlock(const struct spi_flash *flash)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700257{
258 int ret;
259 u8 cmd, status;
260
Zheng Baob8117b02012-11-27 18:08:53 +0800261 ret = sst_enable_writing_status(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700262 if (ret)
263 return ret;
264
265 cmd = CMD_SST_WRSR;
266 status = 0;
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800267 ret = spi_flash_cmd_write(&flash->spi, &cmd, 1, &status, 1);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700268 if (ret)
269 printk(BIOS_WARNING, "SF: Unable to set status byte\n");
270
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800271 printk(BIOS_INFO, "SF: SST: status = %x\n", spi_w8r8(&flash->spi, CMD_SST_RDSR));
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700272
273 return ret;
274}
275
Furquan Shaikhbd9e32e2017-05-15 23:28:41 -0700276int spi_flash_probe_sst(const struct spi_slave *spi, u8 *idcode,
Furquan Shaikh30221b42017-05-15 14:35:15 -0700277 struct spi_flash *flash)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700278{
279 const struct sst_spi_flash_params *params;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700280 size_t i;
281
282 for (i = 0; i < ARRAY_SIZE(sst_spi_flash_table); ++i) {
283 params = &sst_spi_flash_table[i];
284 if (params->idcode1 == idcode[2])
285 break;
286 }
287
288 if (i == ARRAY_SIZE(sst_spi_flash_table)) {
289 printk(BIOS_WARNING, "SF: Unsupported SST ID %02x\n", idcode[1]);
Furquan Shaikh30221b42017-05-15 14:35:15 -0700290 return -1;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700291 }
292
Furquan Shaikhfc1a1232017-05-12 00:19:56 -0700293 memcpy(&flash->spi, spi, sizeof(*spi));
294 flash->name = params->name;
295 flash->sector_size = SST_SECTOR_SIZE;
296 flash->size = flash->sector_size * params->nr_sectors;
297 flash->erase_cmd = CMD_SST_SE;
298 flash->status_cmd = CMD_SST_RDSR;
Aaron Durbind701ef72019-12-27 15:16:17 -0700299 flash->wren_cmd = CMD_SST_WREN;
300
301 if (params->ops == &spi_flash_ops_write_256) {
302 flash->page_size = 256;
303 flash->pp_cmd = CMD_SST_PP;
304 }
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700305
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -0700306 flash->ops = params->ops;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700307
308 /* Flash powers up read-only, so clear BP# bits */
Furquan Shaikhfc1a1232017-05-12 00:19:56 -0700309 sst_unlock(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700310
Furquan Shaikh30221b42017-05-15 14:35:15 -0700311 return 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700312}