blob: fc6cef99afa2afe33d07d85ffc5494f089616128 [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
20#include <stdlib.h>
21#include <spi_flash.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100022
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070023#include "spi_flash_internal.h"
24
25/* S25FLxx-specific commands */
26#define CMD_S25FLXX_READ 0x03 /* Read Data Bytes */
27#define CMD_S25FLXX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
28#define CMD_S25FLXX_READID 0x90 /* Read Manufacture ID and Device ID */
29#define CMD_S25FLXX_WREN 0x06 /* Write Enable */
30#define CMD_S25FLXX_WRDI 0x04 /* Write Disable */
31#define CMD_S25FLXX_RDSR 0x05 /* Read Status Register */
32#define CMD_S25FLXX_WRSR 0x01 /* Write Status Register */
33#define CMD_S25FLXX_PP 0x02 /* Page Program */
34#define CMD_S25FLXX_SE 0xd8 /* Sector Erase */
35#define CMD_S25FLXX_BE 0xc7 /* Bulk Erase */
36#define CMD_S25FLXX_DP 0xb9 /* Deep Power-down */
37#define CMD_S25FLXX_RES 0xab /* Release from DP, and Read Signature */
38
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080039#define SPSN_MANUFACTURER_ID_S25FL116K 0x01
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070040#define SPSN_ID_S25FL008A 0x0213
41#define SPSN_ID_S25FL016A 0x0214
42#define SPSN_ID_S25FL032A 0x0215
43#define SPSN_ID_S25FL064A 0x0216
Vadim Bendeburyb1528832014-05-01 15:50:43 -070044#define SPSN_ID_S25FL128S 0x0219
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070045#define SPSN_ID_S25FL128P 0x2018
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080046#define SPSN_ID_S25FL116K 0x4015
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070047#define SPSN_EXT_ID_S25FL128P_256KB 0x0300
48#define SPSN_EXT_ID_S25FL128P_64KB 0x0301
49#define SPSN_EXT_ID_S25FL032P 0x4d00
Vadim Bendeburyb1528832014-05-01 15:50:43 -070050#define SPSN_EXT_ID_S25FLXXS_64KB 0x4d01
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070051
52struct spansion_spi_flash_params {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080053 u8 idcode0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070054 u16 idcode1;
55 u16 idcode2;
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080056 int (*identify) (const struct spansion_spi_flash_params *params,
57 u8 *idcode);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070058 u16 page_size;
59 u16 pages_per_sector;
60 u16 nr_sectors;
61 const char *name;
62};
63
64struct spansion_spi_flash {
65 struct spi_flash flash;
66 const struct spansion_spi_flash_params *params;
67};
68
69static inline struct spansion_spi_flash *to_spansion_spi_flash(struct spi_flash
70 *flash)
71{
72 return container_of(flash, struct spansion_spi_flash, flash);
73}
74
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -080075/*
76 * returns non-zero if the given idcode matches the ID of the chip. this is for
77 * chips which use 2nd, 3rd, 4th, and 5th byte.
78 */
79static int identify_2345(const struct spansion_spi_flash_params *params,
80 u8 *idcode)
81{
82 u16 jedec = idcode[1] << 8 | idcode[2];
83 u16 ext_jedec = idcode[3] << 8 | idcode[4];
84 return (params->idcode1 == jedec) && (params->idcode2 == ext_jedec);
85}
86
87/*
88 * returns non-zero if the given idcode matches the ID of the chip. this is for
89 * chips which use 1st, 2nd, and 3rd byte.
90 */
91static int identify_123(const struct spansion_spi_flash_params *params,
92 u8 *idcode)
93{
94 u16 jedec = idcode[1] << 8 | idcode[2];
95 return (params->idcode0 == idcode[0]) && (params->idcode1 == jedec);
96}
97
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070098static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {
99 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800100 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700101 .idcode1 = SPSN_ID_S25FL008A,
102 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800103 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700104 .page_size = 256,
105 .pages_per_sector = 256,
106 .nr_sectors = 16,
107 .name = "S25FL008A",
108 },
109 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800110 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700111 .idcode1 = SPSN_ID_S25FL016A,
112 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800113 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700114 .page_size = 256,
115 .pages_per_sector = 256,
116 .nr_sectors = 32,
117 .name = "S25FL016A",
118 },
119 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800120 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700121 .idcode1 = SPSN_ID_S25FL032A,
122 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800123 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700124 .page_size = 256,
125 .pages_per_sector = 256,
126 .nr_sectors = 64,
127 .name = "S25FL032A",
128 },
129 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800130 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700131 .idcode1 = SPSN_ID_S25FL064A,
132 .idcode2 = 0,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800133 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700134 .page_size = 256,
135 .pages_per_sector = 256,
136 .nr_sectors = 128,
137 .name = "S25FL064A",
138 },
139 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800140 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700141 .idcode1 = SPSN_ID_S25FL128P,
142 .idcode2 = SPSN_EXT_ID_S25FL128P_64KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800143 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700144 .page_size = 256,
145 .pages_per_sector = 256,
146 .nr_sectors = 256,
147 .name = "S25FL128P_64K",
148 },
149 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800150 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700151 .idcode1 = SPSN_ID_S25FL128P,
152 .idcode2 = SPSN_EXT_ID_S25FL128P_256KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800153 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700154 .page_size = 256,
155 .pages_per_sector = 1024,
156 .nr_sectors = 64,
157 .name = "S25FL128P_256K",
158 },
159 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800160 .idcode0 = 0,
Vadim Bendeburyb1528832014-05-01 15:50:43 -0700161 .idcode1 = SPSN_ID_S25FL128S,
162 .idcode2 = SPSN_EXT_ID_S25FLXXS_64KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800163 .identify = identify_2345,
Vadim Bendeburyb1528832014-05-01 15:50:43 -0700164 .page_size = 256,
165 .pages_per_sector = 256,
166 .nr_sectors = 512,
167 .name = "S25FL128S_256K",
168 },
169 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800170 .idcode0 = 0,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700171 .idcode1 = SPSN_ID_S25FL032A,
172 .idcode2 = SPSN_EXT_ID_S25FL032P,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800173 .identify = identify_2345,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700174 .page_size = 256,
175 .pages_per_sector = 256,
176 .nr_sectors = 64,
177 .name = "S25FL032P",
178 },
Vadim Bendebury22fa0e02014-06-23 09:16:18 -0700179 {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800180 .idcode0 = 0,
Vadim Bendebury22fa0e02014-06-23 09:16:18 -0700181 .idcode1 = SPSN_ID_S25FL128P,
182 .idcode2 = SPSN_EXT_ID_S25FLXXS_64KB,
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800183 .identify = identify_2345,
Vadim Bendebury22fa0e02014-06-23 09:16:18 -0700184 .page_size = 256,
185 .pages_per_sector = 256,
186 .nr_sectors = 256,
187 .name = "25FS128S",
188 },
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800189 {
190 .idcode0 = SPSN_MANUFACTURER_ID_S25FL116K,
191 .idcode1 = SPSN_ID_S25FL116K,
192 .idcode2 = 0,
193 .identify = identify_123,
194 .page_size = 256,
195 .pages_per_sector = 256,
196 .nr_sectors = 32,
197 .name = "S25FL116K_16M",
198 },
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700199};
200
201static int spansion_write(struct spi_flash *flash,
202 u32 offset, size_t len, const void *buf)
203{
204 struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash);
205 unsigned long page_addr;
206 unsigned long byte_addr;
207 unsigned long page_size;
208 size_t chunk_len;
209 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -0700210 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700211 u8 cmd[4];
212
213 page_size = spsn->params->page_size;
214 page_addr = offset / page_size;
215 byte_addr = offset % page_size;
216
Martin Roth3316cf22012-12-05 16:22:54 -0700217 flash->spi->rw = SPI_WRITE_FLAG;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700218
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700219 for (actual = 0; actual < len; actual += chunk_len) {
220 chunk_len = min(len - actual, page_size - byte_addr);
221
222 cmd[0] = CMD_S25FLXX_PP;
223 cmd[1] = page_addr >> 8;
224 cmd[2] = page_addr;
225 cmd[3] = byte_addr;
226
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
233 ret = spi_flash_cmd(flash->spi, CMD_S25FLXX_WREN, NULL, 0);
234 if (ret < 0) {
235 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
236 break;
237 }
238
239 ret = spi_flash_cmd_write(flash->spi, cmd, 4,
240 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
250 page_addr++;
251 byte_addr = 0;
252 }
253
Marc Jones747127d2012-12-03 22:16:29 -0700254#if CONFIG_DEBUG_SPI_FLASH
255 printk(BIOS_SPEW, "SF: SPANSION: Successfully programmed %zu bytes @ 0x%x\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700256 len, offset);
Marc Jones747127d2012-12-03 22:16:29 -0700257#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700258
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700259 return ret;
260}
261
Vadim Bendebury8e643882014-05-01 19:30:46 -0700262static struct spansion_spi_flash spsn_flash;
263
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700264struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
265{
266 const struct spansion_spi_flash_params *params;
267 struct spansion_spi_flash *spsn;
268 unsigned int i;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700269
270 for (i = 0; i < ARRAY_SIZE(spansion_spi_flash_table); i++) {
271 params = &spansion_spi_flash_table[i];
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800272 if (params->identify(params, idcode))
273 break;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700274 }
275
276 if (i == ARRAY_SIZE(spansion_spi_flash_table)) {
Daisuke Nojiri93b9cb72014-12-10 11:37:05 -0800277 printk(BIOS_WARNING,
278 "SF: Unsupported SPANSION ID %02x %02x %02x %02x %02x\n",
279 idcode[0], idcode[1], idcode[2], idcode[3], idcode[4]);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700280 return NULL;
281 }
282
Vadim Bendebury8e643882014-05-01 19:30:46 -0700283 spsn = &spsn_flash;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700284
285 spsn->params = params;
286 spsn->flash.spi = spi;
287 spsn->flash.name = params->name;
288
289 spsn->flash.write = spansion_write;
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800290 spsn->flash.erase = spi_flash_cmd_erase;
Vadim Bendebury8e643882014-05-01 19:30:46 -0700291 spsn->flash.read = spi_flash_cmd_read_slow;
Duncan Lauriefb032392015-01-15 15:28:46 -0800292 spsn->flash.status = spi_flash_cmd_status;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700293 spsn->flash.sector_size = params->page_size * params->pages_per_sector;
294 spsn->flash.size = spsn->flash.sector_size * params->nr_sectors;
Dan Ehrenberga5aac762015-01-08 10:29:19 -0800295 spsn->flash.erase_cmd = CMD_S25FLXX_SE;
Duncan Lauriefb032392015-01-15 15:28:46 -0800296 spsn->flash.status_cmd = CMD_S25FLXX_RDSR;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700297
298 return &spsn->flash;
299}