blob: 6dd703c7cebf1ce6895d0d15c4c4c659af43f967 [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 *
9 * See file CREDITS for list of people who contributed to this
10 * project.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of
15 * the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Paul Menzela8ae1c62013-02-20 13:21:20 +010019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070020 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25 * MA 02111-1307 USA
26 */
27
28#include <stdlib.h>
29#include <spi_flash.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100030
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070031#include "spi_flash_internal.h"
32
33/* S25FLxx-specific commands */
34#define CMD_S25FLXX_READ 0x03 /* Read Data Bytes */
35#define CMD_S25FLXX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
36#define CMD_S25FLXX_READID 0x90 /* Read Manufacture ID and Device ID */
37#define CMD_S25FLXX_WREN 0x06 /* Write Enable */
38#define CMD_S25FLXX_WRDI 0x04 /* Write Disable */
39#define CMD_S25FLXX_RDSR 0x05 /* Read Status Register */
40#define CMD_S25FLXX_WRSR 0x01 /* Write Status Register */
41#define CMD_S25FLXX_PP 0x02 /* Page Program */
42#define CMD_S25FLXX_SE 0xd8 /* Sector Erase */
43#define CMD_S25FLXX_BE 0xc7 /* Bulk Erase */
44#define CMD_S25FLXX_DP 0xb9 /* Deep Power-down */
45#define CMD_S25FLXX_RES 0xab /* Release from DP, and Read Signature */
46
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080047#define SPSN_MANUFACTURER_ID_S25FL116K 0x01
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070048#define SPSN_ID_S25FL008A 0x0213
49#define SPSN_ID_S25FL016A 0x0214
50#define SPSN_ID_S25FL032A 0x0215
51#define SPSN_ID_S25FL064A 0x0216
Vadim Bendeburyb1528832014-05-01 15:50:43 -070052#define SPSN_ID_S25FL128S 0x0219
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070053#define SPSN_ID_S25FL128P 0x2018
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080054#define SPSN_ID_S25FL116K 0x4015
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070055#define SPSN_EXT_ID_S25FL128P_256KB 0x0300
56#define SPSN_EXT_ID_S25FL128P_64KB 0x0301
57#define SPSN_EXT_ID_S25FL032P 0x4d00
Vadim Bendeburyb1528832014-05-01 15:50:43 -070058#define SPSN_EXT_ID_S25FLXXS_64KB 0x4d01
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070059
60struct spansion_spi_flash_params {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080061 u8 idcode0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070062 u16 idcode1;
63 u16 idcode2;
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080064 int (*identify) (const struct spansion_spi_flash_params *params,
65 u8 *idcode);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070066 u16 page_size;
67 u16 pages_per_sector;
68 u16 nr_sectors;
69 const char *name;
70};
71
72struct spansion_spi_flash {
73 struct spi_flash flash;
74 const struct spansion_spi_flash_params *params;
75};
76
77static inline struct spansion_spi_flash *to_spansion_spi_flash(struct spi_flash
78 *flash)
79{
80 return container_of(flash, struct spansion_spi_flash, flash);
81}
82
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080083/*
84 * returns non-zero if the given idcode matches the ID of the chip. this is for
85 * chips which use 2nd, 3rd, 4th, and 5th byte.
86 */
87static int identify_2345(const struct spansion_spi_flash_params *params,
88 u8 *idcode)
89{
90 u16 jedec = idcode[1] << 8 | idcode[2];
91 u16 ext_jedec = idcode[3] << 8 | idcode[4];
92 return (params->idcode1 == jedec) && (params->idcode2 == ext_jedec);
93}
94
95/*
96 * returns non-zero if the given idcode matches the ID of the chip. this is for
97 * chips which use 1st, 2nd, and 3rd byte.
98 */
99static int identify_123(const struct spansion_spi_flash_params *params,
100 u8 *idcode)
101{
102 u16 jedec = idcode[1] << 8 | idcode[2];
103 return (params->idcode0 == idcode[0]) && (params->idcode1 == jedec);
104}
105
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700106static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {
107 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800108 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700109 .idcode1 = SPSN_ID_S25FL008A,
110 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800111 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700112 .page_size = 256,
113 .pages_per_sector = 256,
114 .nr_sectors = 16,
115 .name = "S25FL008A",
116 },
117 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800118 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700119 .idcode1 = SPSN_ID_S25FL016A,
120 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800121 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700122 .page_size = 256,
123 .pages_per_sector = 256,
124 .nr_sectors = 32,
125 .name = "S25FL016A",
126 },
127 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800128 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700129 .idcode1 = SPSN_ID_S25FL032A,
130 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800131 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700132 .page_size = 256,
133 .pages_per_sector = 256,
134 .nr_sectors = 64,
135 .name = "S25FL032A",
136 },
137 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800138 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700139 .idcode1 = SPSN_ID_S25FL064A,
140 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800141 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700142 .page_size = 256,
143 .pages_per_sector = 256,
144 .nr_sectors = 128,
145 .name = "S25FL064A",
146 },
147 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800148 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700149 .idcode1 = SPSN_ID_S25FL128P,
150 .idcode2 = SPSN_EXT_ID_S25FL128P_64KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800151 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700152 .page_size = 256,
153 .pages_per_sector = 256,
154 .nr_sectors = 256,
155 .name = "S25FL128P_64K",
156 },
157 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800158 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700159 .idcode1 = SPSN_ID_S25FL128P,
160 .idcode2 = SPSN_EXT_ID_S25FL128P_256KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800161 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700162 .page_size = 256,
163 .pages_per_sector = 1024,
164 .nr_sectors = 64,
165 .name = "S25FL128P_256K",
166 },
167 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800168 .idcode0 = 0,
Vadim Bendeburyb1528832014-05-01 15:50:43 -0700169 .idcode1 = SPSN_ID_S25FL128S,
170 .idcode2 = SPSN_EXT_ID_S25FLXXS_64KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800171 .identify = identify_2345,
Vadim Bendeburyb1528832014-05-01 15:50:43 -0700172 .page_size = 256,
173 .pages_per_sector = 256,
174 .nr_sectors = 512,
175 .name = "S25FL128S_256K",
176 },
177 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800178 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700179 .idcode1 = SPSN_ID_S25FL032A,
180 .idcode2 = SPSN_EXT_ID_S25FL032P,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800181 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700182 .page_size = 256,
183 .pages_per_sector = 256,
184 .nr_sectors = 64,
185 .name = "S25FL032P",
186 },
Vadim Bendebury22fa0e02014-06-23 09:16:18 -0700187 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800188 .idcode0 = 0,
Vadim Bendebury22fa0e02014-06-23 09:16:18 -0700189 .idcode1 = SPSN_ID_S25FL128P,
190 .idcode2 = SPSN_EXT_ID_S25FLXXS_64KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800191 .identify = identify_2345,
Vadim Bendebury22fa0e02014-06-23 09:16:18 -0700192 .page_size = 256,
193 .pages_per_sector = 256,
194 .nr_sectors = 256,
195 .name = "25FS128S",
196 },
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800197 {
198 .idcode0 = SPSN_MANUFACTURER_ID_S25FL116K,
199 .idcode1 = SPSN_ID_S25FL116K,
200 .idcode2 = 0,
201 .identify = identify_123,
202 .page_size = 256,
203 .pages_per_sector = 256,
204 .nr_sectors = 32,
205 .name = "S25FL116K_16M",
206 },
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700207};
208
209static int spansion_write(struct spi_flash *flash,
210 u32 offset, size_t len, const void *buf)
211{
212 struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash);
213 unsigned long page_addr;
214 unsigned long byte_addr;
215 unsigned long page_size;
216 size_t chunk_len;
217 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -0700218 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700219 u8 cmd[4];
220
221 page_size = spsn->params->page_size;
222 page_addr = offset / page_size;
223 byte_addr = offset % page_size;
224
Martin Roth3316cf22012-12-05 16:22:54 -0700225 flash->spi->rw = SPI_WRITE_FLAG;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700226
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700227 for (actual = 0; actual < len; actual += chunk_len) {
228 chunk_len = min(len - actual, page_size - byte_addr);
229
230 cmd[0] = CMD_S25FLXX_PP;
231 cmd[1] = page_addr >> 8;
232 cmd[2] = page_addr;
233 cmd[3] = byte_addr;
234
Stefan Reinauer5649b082012-05-23 11:03:29 -0700235#if CONFIG_DEBUG_SPI_FLASH
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700236 printk(BIOS_SPEW, "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x }"
237 " chunk_len = %zu\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700238 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
Stefan Reinauer5649b082012-05-23 11:03:29 -0700239#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700240
241 ret = spi_flash_cmd(flash->spi, CMD_S25FLXX_WREN, NULL, 0);
242 if (ret < 0) {
243 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
244 break;
245 }
246
247 ret = spi_flash_cmd_write(flash->spi, cmd, 4,
248 buf + actual, chunk_len);
249 if (ret < 0) {
250 printk(BIOS_WARNING, "SF: SPANSION Page Program failed\n");
251 break;
252 }
253
254 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
255 if (ret)
256 break;
257
258 page_addr++;
259 byte_addr = 0;
260 }
261
Marc Jones747127d2012-12-03 22:16:29 -0700262#if CONFIG_DEBUG_SPI_FLASH
263 printk(BIOS_SPEW, "SF: SPANSION: Successfully programmed %zu bytes @ 0x%x\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700264 len, offset);
Marc Jones747127d2012-12-03 22:16:29 -0700265#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700266
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700267 return ret;
268}
269
Vadim Bendebury8e643882014-05-01 19:30:46 -0700270static struct spansion_spi_flash spsn_flash;
271
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700272struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
273{
274 const struct spansion_spi_flash_params *params;
275 struct spansion_spi_flash *spsn;
276 unsigned int i;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700277
278 for (i = 0; i < ARRAY_SIZE(spansion_spi_flash_table); i++) {
279 params = &spansion_spi_flash_table[i];
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800280 if (params->identify(params, idcode))
281 break;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700282 }
283
284 if (i == ARRAY_SIZE(spansion_spi_flash_table)) {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800285 printk(BIOS_WARNING,
286 "SF: Unsupported SPANSION ID %02x %02x %02x %02x %02x\n",
287 idcode[0], idcode[1], idcode[2], idcode[3], idcode[4]);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700288 return NULL;
289 }
290
Vadim Bendebury8e643882014-05-01 19:30:46 -0700291 spsn = &spsn_flash;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700292
293 spsn->params = params;
294 spsn->flash.spi = spi;
295 spsn->flash.name = params->name;
296
297 spsn->flash.write = spansion_write;
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800298 spsn->flash.erase = spi_flash_cmd_erase;
Vadim Bendebury8e643882014-05-01 19:30:46 -0700299 spsn->flash.read = spi_flash_cmd_read_slow;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700300 spsn->flash.sector_size = params->page_size * params->pages_per_sector;
301 spsn->flash.size = spsn->flash.sector_size * params->nr_sectors;
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800302 spsn->flash.erase_cmd = CMD_S25FLXX_SE;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700303
304 return &spsn->flash;
305}