blob: 7f57d21e147d6f6ea4cecc0ade92ea9835996a0e [file] [log] [blame]
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07001/*
2 * Copyright (C) 2009 Freescale Semiconductor, Inc.
3 *
4 * Author: Mingkai Hu (Mingkai.hu@freescale.com)
5 * Based on stmicro.c by Wolfgang Denk (wd@denx.de),
6 * TsiChung Liew (Tsi-Chung.Liew@freescale.com),
7 * and Jason McMullan (mcmullan@netapp.com)
8 *
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07009 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Paul Menzela8ae1c62013-02-20 13:21:20 +010016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070017 * GNU General Public License for more details.
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070018 */
19
Furquan Shaikhc28984d2016-11-20 21:04:00 -080020#include <console/console.h>
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070021#include <stdlib.h>
22#include <spi_flash.h>
Furquan Shaikhc28984d2016-11-20 21:04:00 -080023#include <spi-generic.h>
Furquan Shaikh810e2cd2016-12-05 20:32:24 -080024#include <string.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100025
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070026#include "spi_flash_internal.h"
27
28/* S25FLxx-specific commands */
29#define CMD_S25FLXX_READ 0x03 /* Read Data Bytes */
30#define CMD_S25FLXX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
31#define CMD_S25FLXX_READID 0x90 /* Read Manufacture ID and Device ID */
32#define CMD_S25FLXX_WREN 0x06 /* Write Enable */
33#define CMD_S25FLXX_WRDI 0x04 /* Write Disable */
34#define CMD_S25FLXX_RDSR 0x05 /* Read Status Register */
35#define CMD_S25FLXX_WRSR 0x01 /* Write Status Register */
36#define CMD_S25FLXX_PP 0x02 /* Page Program */
37#define CMD_S25FLXX_SE 0xd8 /* Sector Erase */
38#define CMD_S25FLXX_BE 0xc7 /* Bulk Erase */
39#define CMD_S25FLXX_DP 0xb9 /* Deep Power-down */
40#define CMD_S25FLXX_RES 0xab /* Release from DP, and Read Signature */
41
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080042#define SPSN_MANUFACTURER_ID_S25FL116K 0x01
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070043#define SPSN_ID_S25FL008A 0x0213
44#define SPSN_ID_S25FL016A 0x0214
45#define SPSN_ID_S25FL032A 0x0215
46#define SPSN_ID_S25FL064A 0x0216
Vadim Bendeburyb1528832014-05-01 15:50:43 -070047#define SPSN_ID_S25FL128S 0x0219
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070048#define SPSN_ID_S25FL128P 0x2018
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080049#define SPSN_ID_S25FL116K 0x4015
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070050#define SPSN_EXT_ID_S25FL128P_256KB 0x0300
51#define SPSN_EXT_ID_S25FL128P_64KB 0x0301
52#define SPSN_EXT_ID_S25FL032P 0x4d00
Vadim Bendeburyb1528832014-05-01 15:50:43 -070053#define SPSN_EXT_ID_S25FLXXS_64KB 0x4d01
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070054
55struct spansion_spi_flash_params {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080056 u8 idcode0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070057 u16 idcode1;
58 u16 idcode2;
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080059 int (*identify) (const struct spansion_spi_flash_params *params,
60 u8 *idcode);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070061 u16 page_size;
62 u16 pages_per_sector;
63 u16 nr_sectors;
64 const char *name;
65};
66
67struct spansion_spi_flash {
68 struct spi_flash flash;
69 const struct spansion_spi_flash_params *params;
70};
71
Furquan Shaikhc28984d2016-11-20 21:04:00 -080072static inline
73struct spansion_spi_flash *to_spansion_spi_flash(const struct spi_flash *flash)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070074{
75 return container_of(flash, struct spansion_spi_flash, flash);
76}
77
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080078/*
79 * returns non-zero if the given idcode matches the ID of the chip. this is for
80 * chips which use 2nd, 3rd, 4th, and 5th byte.
81 */
82static int identify_2345(const struct spansion_spi_flash_params *params,
83 u8 *idcode)
84{
85 u16 jedec = idcode[1] << 8 | idcode[2];
86 u16 ext_jedec = idcode[3] << 8 | idcode[4];
87 return (params->idcode1 == jedec) && (params->idcode2 == ext_jedec);
88}
89
90/*
91 * returns non-zero if the given idcode matches the ID of the chip. this is for
92 * chips which use 1st, 2nd, and 3rd byte.
93 */
94static int identify_123(const struct spansion_spi_flash_params *params,
95 u8 *idcode)
96{
97 u16 jedec = idcode[1] << 8 | idcode[2];
98 return (params->idcode0 == idcode[0]) && (params->idcode1 == jedec);
99}
100
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700101static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {
102 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800103 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700104 .idcode1 = SPSN_ID_S25FL008A,
105 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800106 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700107 .page_size = 256,
108 .pages_per_sector = 256,
109 .nr_sectors = 16,
110 .name = "S25FL008A",
111 },
112 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800113 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700114 .idcode1 = SPSN_ID_S25FL016A,
115 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800116 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700117 .page_size = 256,
118 .pages_per_sector = 256,
119 .nr_sectors = 32,
120 .name = "S25FL016A",
121 },
122 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800123 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700124 .idcode1 = SPSN_ID_S25FL032A,
125 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800126 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700127 .page_size = 256,
128 .pages_per_sector = 256,
129 .nr_sectors = 64,
130 .name = "S25FL032A",
131 },
132 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800133 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700134 .idcode1 = SPSN_ID_S25FL064A,
135 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800136 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700137 .page_size = 256,
138 .pages_per_sector = 256,
139 .nr_sectors = 128,
140 .name = "S25FL064A",
141 },
142 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800143 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700144 .idcode1 = SPSN_ID_S25FL128P,
145 .idcode2 = SPSN_EXT_ID_S25FL128P_64KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800146 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700147 .page_size = 256,
148 .pages_per_sector = 256,
149 .nr_sectors = 256,
150 .name = "S25FL128P_64K",
151 },
152 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800153 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700154 .idcode1 = SPSN_ID_S25FL128P,
155 .idcode2 = SPSN_EXT_ID_S25FL128P_256KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800156 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700157 .page_size = 256,
158 .pages_per_sector = 1024,
159 .nr_sectors = 64,
160 .name = "S25FL128P_256K",
161 },
162 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800163 .idcode0 = 0,
Vadim Bendeburyb1528832014-05-01 15:50:43 -0700164 .idcode1 = SPSN_ID_S25FL128S,
165 .idcode2 = SPSN_EXT_ID_S25FLXXS_64KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800166 .identify = identify_2345,
Vadim Bendeburyb1528832014-05-01 15:50:43 -0700167 .page_size = 256,
168 .pages_per_sector = 256,
169 .nr_sectors = 512,
170 .name = "S25FL128S_256K",
171 },
172 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800173 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700174 .idcode1 = SPSN_ID_S25FL032A,
175 .idcode2 = SPSN_EXT_ID_S25FL032P,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800176 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700177 .page_size = 256,
178 .pages_per_sector = 256,
179 .nr_sectors = 64,
180 .name = "S25FL032P",
181 },
Vadim Bendebury22fa0e02014-06-23 09:16:18 -0700182 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800183 .idcode0 = 0,
Vadim Bendebury22fa0e02014-06-23 09:16:18 -0700184 .idcode1 = SPSN_ID_S25FL128P,
185 .idcode2 = SPSN_EXT_ID_S25FLXXS_64KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800186 .identify = identify_2345,
Vadim Bendebury22fa0e02014-06-23 09:16:18 -0700187 .page_size = 256,
188 .pages_per_sector = 256,
189 .nr_sectors = 256,
190 .name = "25FS128S",
191 },
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800192 {
193 .idcode0 = SPSN_MANUFACTURER_ID_S25FL116K,
194 .idcode1 = SPSN_ID_S25FL116K,
195 .idcode2 = 0,
196 .identify = identify_123,
197 .page_size = 256,
198 .pages_per_sector = 256,
199 .nr_sectors = 32,
200 .name = "S25FL116K_16M",
201 },
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700202};
203
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800204static int spansion_write(const struct spi_flash *flash, u32 offset, size_t len,
205 const void *buf)
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700206{
207 struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700208 unsigned long byte_addr;
209 unsigned long page_size;
210 size_t chunk_len;
211 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -0700212 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700213 u8 cmd[4];
214
215 page_size = spsn->params->page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700216
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700217 for (actual = 0; actual < len; actual += chunk_len) {
Aaron Durbin41f66902016-12-17 13:16:07 -0600218 byte_addr = offset % page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700219 chunk_len = min(len - actual, page_size - byte_addr);
Aaron Durbin41f66902016-12-17 13:16:07 -0600220 chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700221
222 cmd[0] = CMD_S25FLXX_PP;
Aaron Durbin41f66902016-12-17 13:16:07 -0600223 cmd[1] = (offset >> 16) & 0xff;
224 cmd[2] = (offset >> 8) & 0xff;
225 cmd[3] = offset & 0xff;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700226
Stefan Reinauer5649b082012-05-23 11:03:29 -0700227#if CONFIG_DEBUG_SPI_FLASH
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700228 printk(BIOS_SPEW, "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x }"
229 " chunk_len = %zu\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700230 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
Stefan Reinauer5649b082012-05-23 11:03:29 -0700231#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700232
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800233 ret = spi_flash_cmd(&flash->spi, CMD_S25FLXX_WREN, NULL, 0);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700234 if (ret < 0) {
235 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
236 break;
237 }
238
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800239 ret = spi_flash_cmd_write(&flash->spi, cmd, 4,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700240 buf + actual, chunk_len);
241 if (ret < 0) {
242 printk(BIOS_WARNING, "SF: SPANSION Page Program failed\n");
243 break;
244 }
245
246 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
247 if (ret)
248 break;
249
Aaron Durbin41f66902016-12-17 13:16:07 -0600250 offset += chunk_len;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700251 }
252
Marc Jones747127d2012-12-03 22:16:29 -0700253#if CONFIG_DEBUG_SPI_FLASH
254 printk(BIOS_SPEW, "SF: SPANSION: Successfully programmed %zu bytes @ 0x%x\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700255 len, offset);
Marc Jones747127d2012-12-03 22:16:29 -0700256#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700257
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700258 return ret;
259}
260
Vadim Bendebury8e643882014-05-01 19:30:46 -0700261static struct spansion_spi_flash spsn_flash;
262
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700263struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
264{
265 const struct spansion_spi_flash_params *params;
266 struct spansion_spi_flash *spsn;
267 unsigned int i;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700268
269 for (i = 0; i < ARRAY_SIZE(spansion_spi_flash_table); i++) {
270 params = &spansion_spi_flash_table[i];
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800271 if (params->identify(params, idcode))
272 break;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700273 }
274
275 if (i == ARRAY_SIZE(spansion_spi_flash_table)) {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800276 printk(BIOS_WARNING,
277 "SF: Unsupported SPANSION ID %02x %02x %02x %02x %02x\n",
278 idcode[0], idcode[1], idcode[2], idcode[3], idcode[4]);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700279 return NULL;
280 }
281
Vadim Bendebury8e643882014-05-01 19:30:46 -0700282 spsn = &spsn_flash;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700283
284 spsn->params = params;
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800285 memcpy(&spsn->flash.spi, spi, sizeof(*spi));
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700286 spsn->flash.name = params->name;
287
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800288 spsn->flash.internal_write = spansion_write;
289 spsn->flash.internal_erase = spi_flash_cmd_erase;
290 spsn->flash.internal_read = spi_flash_cmd_read_slow;
291 spsn->flash.internal_status = spi_flash_cmd_status;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700292 spsn->flash.sector_size = params->page_size * params->pages_per_sector;
293 spsn->flash.size = spsn->flash.sector_size * params->nr_sectors;
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800294 spsn->flash.erase_cmd = CMD_S25FLXX_SE;
Duncan Lauriefb032392015-01-15 15:28:46 -0800295 spsn->flash.status_cmd = CMD_S25FLXX_RDSR;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700296
297 return &spsn->flash;
298}