]> git.sur5r.net Git - u-boot/blob - drivers/mtd/spi/spansion.c
sf: atmel: implement power-of-two write/erase funcs
[u-boot] / drivers / mtd / spi / spansion.c
1 /*
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
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * 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 <common.h>
29 #include <malloc.h>
30 #include <spi_flash.h>
31
32 #include "spi_flash_internal.h"
33
34 /* S25FLxx-specific commands */
35 #define CMD_S25FLXX_READ        0x03    /* Read Data Bytes */
36 #define CMD_S25FLXX_FAST_READ   0x0b    /* Read Data Bytes at Higher Speed */
37 #define CMD_S25FLXX_READID      0x90    /* Read Manufacture ID and Device ID */
38 #define CMD_S25FLXX_WREN        0x06    /* Write Enable */
39 #define CMD_S25FLXX_WRDI        0x04    /* Write Disable */
40 #define CMD_S25FLXX_RDSR        0x05    /* Read Status Register */
41 #define CMD_S25FLXX_WRSR        0x01    /* Write Status Register */
42 #define CMD_S25FLXX_PP          0x02    /* Page Program */
43 #define CMD_S25FLXX_SE          0xd8    /* Sector Erase */
44 #define CMD_S25FLXX_BE          0xc7    /* Bulk Erase */
45 #define CMD_S25FLXX_DP          0xb9    /* Deep Power-down */
46 #define CMD_S25FLXX_RES         0xab    /* Release from DP, and Read Signature */
47
48 #define SPSN_ID_S25FL008A       0x0213
49 #define SPSN_ID_S25FL016A       0x0214
50 #define SPSN_ID_S25FL032A       0x0215
51 #define SPSN_ID_S25FL064A       0x0216
52 #define SPSN_ID_S25FL128P       0x2018
53 #define SPSN_EXT_ID_S25FL128P_256KB     0x0300
54 #define SPSN_EXT_ID_S25FL128P_64KB      0x0301
55
56 #define SPANSION_SR_WIP         (1 << 0)        /* Write-in-Progress */
57
58 struct spansion_spi_flash_params {
59         u16 idcode1;
60         u16 idcode2;
61         u16 page_size;
62         u16 pages_per_sector;
63         u16 nr_sectors;
64         const char *name;
65 };
66
67 struct spansion_spi_flash {
68         struct spi_flash flash;
69         const struct spansion_spi_flash_params *params;
70 };
71
72 static inline struct spansion_spi_flash *to_spansion_spi_flash(struct spi_flash
73                                                              *flash)
74 {
75         return container_of(flash, struct spansion_spi_flash, flash);
76 }
77
78 static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {
79         {
80                 .idcode1 = SPSN_ID_S25FL008A,
81                 .idcode2 = 0,
82                 .page_size = 256,
83                 .pages_per_sector = 256,
84                 .nr_sectors = 16,
85                 .name = "S25FL008A",
86         },
87         {
88                 .idcode1 = SPSN_ID_S25FL016A,
89                 .idcode2 = 0,
90                 .page_size = 256,
91                 .pages_per_sector = 256,
92                 .nr_sectors = 32,
93                 .name = "S25FL016A",
94         },
95         {
96                 .idcode1 = SPSN_ID_S25FL032A,
97                 .idcode2 = 0,
98                 .page_size = 256,
99                 .pages_per_sector = 256,
100                 .nr_sectors = 64,
101                 .name = "S25FL032A",
102         },
103         {
104                 .idcode1 = SPSN_ID_S25FL064A,
105                 .idcode2 = 0,
106                 .page_size = 256,
107                 .pages_per_sector = 256,
108                 .nr_sectors = 128,
109                 .name = "S25FL064A",
110         },
111         {
112                 .idcode1 = SPSN_ID_S25FL128P,
113                 .idcode2 = SPSN_EXT_ID_S25FL128P_64KB,
114                 .page_size = 256,
115                 .pages_per_sector = 256,
116                 .nr_sectors = 256,
117                 .name = "S25FL128P_64K",
118         },
119         {
120                 .idcode1 = SPSN_ID_S25FL128P,
121                 .idcode2 = SPSN_EXT_ID_S25FL128P_256KB,
122                 .page_size = 256,
123                 .pages_per_sector = 1024,
124                 .nr_sectors = 64,
125                 .name = "S25FL128P_256K",
126         },
127 };
128
129 static int spansion_wait_ready(struct spi_flash *flash, unsigned long timeout)
130 {
131         struct spi_slave *spi = flash->spi;
132         unsigned long timebase;
133         int ret;
134         u8 status;
135
136         timebase = get_timer(0);
137         do {
138                 ret = spi_flash_cmd(spi, CMD_S25FLXX_RDSR, &status, sizeof(status));
139                 if (ret)
140                         return -1;
141
142                 if ((status & SPANSION_SR_WIP) == 0)
143                         break;
144
145         } while (get_timer(timebase) < timeout);
146
147
148         if ((status & SPANSION_SR_WIP) == 0)
149                 return 0;
150
151         /* Timed out */
152         return -1;
153 }
154
155 static int spansion_read_fast(struct spi_flash *flash,
156                              u32 offset, size_t len, void *buf)
157 {
158         struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash);
159         unsigned long page_addr;
160         unsigned long page_size;
161         u8 cmd[5];
162
163         page_size = spsn->params->page_size;
164         page_addr = offset / page_size;
165
166         cmd[0] = CMD_READ_ARRAY_FAST;
167         cmd[1] = page_addr >> 8;
168         cmd[2] = page_addr;
169         cmd[3] = offset % page_size;
170         cmd[4] = 0x00;
171
172         debug
173                 ("READ: 0x%x => cmd = { 0x%02x 0x%02x%02x%02x%02x } len = 0x%x\n",
174                  offset, cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], len);
175
176         return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len);
177 }
178
179 static int spansion_write(struct spi_flash *flash,
180                          u32 offset, size_t len, const void *buf)
181 {
182         struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash);
183         unsigned long page_addr;
184         unsigned long byte_addr;
185         unsigned long page_size;
186         size_t chunk_len;
187         size_t actual;
188         int ret;
189         u8 cmd[4];
190
191         page_size = spsn->params->page_size;
192         page_addr = offset / page_size;
193         byte_addr = offset % page_size;
194
195         ret = spi_claim_bus(flash->spi);
196         if (ret) {
197                 debug("SF: Unable to claim SPI bus\n");
198                 return ret;
199         }
200
201         ret = 0;
202         for (actual = 0; actual < len; actual += chunk_len) {
203                 chunk_len = min(len - actual, page_size - byte_addr);
204
205                 cmd[0] = CMD_S25FLXX_PP;
206                 cmd[1] = page_addr >> 8;
207                 cmd[2] = page_addr;
208                 cmd[3] = byte_addr;
209
210                 debug
211                     ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n",
212                      buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
213
214                 ret = spi_flash_cmd(flash->spi, CMD_S25FLXX_WREN, NULL, 0);
215                 if (ret < 0) {
216                         debug("SF: Enabling Write failed\n");
217                         break;
218                 }
219
220                 ret = spi_flash_cmd_write(flash->spi, cmd, 4,
221                                           buf + actual, chunk_len);
222                 if (ret < 0) {
223                         debug("SF: SPANSION Page Program failed\n");
224                         break;
225                 }
226
227                 ret = spansion_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
228                 if (ret < 0) {
229                         debug("SF: SPANSION page programming timed out\n");
230                         break;
231                 }
232
233                 page_addr++;
234                 byte_addr = 0;
235         }
236
237         debug("SF: SPANSION: Successfully programmed %u bytes @ 0x%x\n",
238               len, offset);
239
240         spi_release_bus(flash->spi);
241         return ret;
242 }
243
244 int spansion_erase(struct spi_flash *flash, u32 offset, size_t len)
245 {
246         struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash);
247         unsigned long sector_size;
248         size_t actual;
249         int ret;
250         u8 cmd[4];
251
252         /*
253          * This function currently uses sector erase only.
254          * probably speed things up by using bulk erase
255          * when possible.
256          */
257
258         sector_size = spsn->params->page_size * spsn->params->pages_per_sector;
259
260         if (offset % sector_size || len % sector_size) {
261                 debug("SF: Erase offset/length not multiple of sector size\n");
262                 return -1;
263         }
264
265         len /= sector_size;
266         cmd[0] = CMD_S25FLXX_SE;
267         cmd[2] = 0x00;
268         cmd[3] = 0x00;
269
270         ret = spi_claim_bus(flash->spi);
271         if (ret) {
272                 debug("SF: Unable to claim SPI bus\n");
273                 return ret;
274         }
275
276         ret = 0;
277         for (actual = 0; actual < len; actual++) {
278                 cmd[1] = (offset / sector_size) + actual;
279
280                 ret = spi_flash_cmd(flash->spi, CMD_S25FLXX_WREN, NULL, 0);
281                 if (ret < 0) {
282                         debug("SF: Enabling Write failed\n");
283                         break;
284                 }
285
286                 ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
287                 if (ret < 0) {
288                         debug("SF: SPANSION page erase failed\n");
289                         break;
290                 }
291
292                 /* Up to 2 seconds */
293                 ret = spansion_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
294                 if (ret < 0) {
295                         debug("SF: SPANSION page erase timed out\n");
296                         break;
297                 }
298         }
299
300         debug("SF: SPANSION: Successfully erased %u bytes @ 0x%x\n",
301               len * sector_size, offset);
302
303         spi_release_bus(flash->spi);
304         return ret;
305 }
306
307 struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
308 {
309         const struct spansion_spi_flash_params *params;
310         struct spansion_spi_flash *spsn;
311         unsigned int i;
312         unsigned short jedec, ext_jedec;
313
314         jedec = idcode[1] << 8 | idcode[2];
315         ext_jedec = idcode[3] << 8 | idcode[4];
316
317         for (i = 0; i < ARRAY_SIZE(spansion_spi_flash_table); i++) {
318                 params = &spansion_spi_flash_table[i];
319                 if (params->idcode1 == jedec) {
320                         if (params->idcode2 == ext_jedec)
321                                 break;
322                 }
323         }
324
325         if (i == ARRAY_SIZE(spansion_spi_flash_table)) {
326                 debug("SF: Unsupported SPANSION ID %04x %04x\n", jedec, ext_jedec);
327                 return NULL;
328         }
329
330         spsn = malloc(sizeof(struct spansion_spi_flash));
331         if (!spsn) {
332                 debug("SF: Failed to allocate memory\n");
333                 return NULL;
334         }
335
336         spsn->params = params;
337         spsn->flash.spi = spi;
338         spsn->flash.name = params->name;
339
340         spsn->flash.write = spansion_write;
341         spsn->flash.erase = spansion_erase;
342         spsn->flash.read = spansion_read_fast;
343         spsn->flash.size = params->page_size * params->pages_per_sector
344             * params->nr_sectors;
345
346         debug("SF: Detected %s with page size %u, total %u bytes\n",
347               params->name, params->page_size, spsn->flash.size);
348
349         return &spsn->flash;
350 }