]> git.sur5r.net Git - u-boot/blob - drivers/mtd/spi/winbond.c
sf: winbond: add support for W25Q16/32/128 parts
[u-boot] / drivers / mtd / spi / winbond.c
1 /*
2  * Copyright 2008, Network Appliance Inc.
3  * Author: Jason McMullan <mcmullan <at> netapp.com>
4  * Licensed under the GPL-2 or later.
5  */
6
7 #include <common.h>
8 #include <malloc.h>
9 #include <spi_flash.h>
10
11 #include "spi_flash_internal.h"
12
13 /* M25Pxx-specific commands */
14 #define CMD_W25_WREN            0x06    /* Write Enable */
15 #define CMD_W25_WRDI            0x04    /* Write Disable */
16 #define CMD_W25_RDSR            0x05    /* Read Status Register */
17 #define CMD_W25_WRSR            0x01    /* Write Status Register */
18 #define CMD_W25_READ            0x03    /* Read Data Bytes */
19 #define CMD_W25_FAST_READ       0x0b    /* Read Data Bytes at Higher Speed */
20 #define CMD_W25_PP              0x02    /* Page Program */
21 #define CMD_W25_SE              0x20    /* Sector (4K) Erase */
22 #define CMD_W25_BE              0xd8    /* Block (64K) Erase */
23 #define CMD_W25_CE              0xc7    /* Chip Erase */
24 #define CMD_W25_DP              0xb9    /* Deep Power-down */
25 #define CMD_W25_RES             0xab    /* Release from DP, and Read Signature */
26
27 #define WINBOND_SR_WIP          (1 << 0)        /* Write-in-Progress */
28
29 struct winbond_spi_flash_params {
30         uint16_t        id;
31         /* Log2 of page size in power-of-two mode */
32         uint8_t         l2_page_size;
33         uint16_t        pages_per_sector;
34         uint16_t        sectors_per_block;
35         uint16_t        nr_blocks;
36         const char      *name;
37 };
38
39 /* spi_flash needs to be first so upper layers can free() it */
40 struct winbond_spi_flash {
41         struct spi_flash flash;
42         const struct winbond_spi_flash_params *params;
43 };
44
45 static inline struct winbond_spi_flash *
46 to_winbond_spi_flash(struct spi_flash *flash)
47 {
48         return container_of(flash, struct winbond_spi_flash, flash);
49 }
50
51 static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
52         {
53                 .id                     = 0x3015,
54                 .l2_page_size           = 8,
55                 .pages_per_sector       = 16,
56                 .sectors_per_block      = 16,
57                 .nr_blocks              = 32,
58                 .name                   = "W25X16",
59         },
60         {
61                 .id                     = 0x3016,
62                 .l2_page_size           = 8,
63                 .pages_per_sector       = 16,
64                 .sectors_per_block      = 16,
65                 .nr_blocks              = 64,
66                 .name                   = "W25X32",
67         },
68         {
69                 .id                     = 0x3017,
70                 .l2_page_size           = 8,
71                 .pages_per_sector       = 16,
72                 .sectors_per_block      = 16,
73                 .nr_blocks              = 128,
74                 .name                   = "W25X64",
75         },
76         {
77                 .id                     = 0x4015,
78                 .l2_page_size           = 8,
79                 .pages_per_sector       = 16,
80                 .sectors_per_block      = 16,
81                 .nr_blocks              = 32,
82                 .name                   = "W25Q16",
83         },
84         {
85                 .id                     = 0x4016,
86                 .l2_page_size           = 8,
87                 .pages_per_sector       = 16,
88                 .sectors_per_block      = 16,
89                 .nr_blocks              = 64,
90                 .name                   = "W25Q32",
91         },
92         {
93                 .id                     = 0x4017,
94                 .l2_page_size           = 8,
95                 .pages_per_sector       = 16,
96                 .sectors_per_block      = 16,
97                 .nr_blocks              = 128,
98                 .name                   = "W25Q64",
99         },
100         {
101                 .id                     = 0x4018,
102                 .l2_page_size           = 8,
103                 .pages_per_sector       = 16,
104                 .sectors_per_block      = 16,
105                 .nr_blocks              = 256,
106                 .name                   = "W25Q128",
107         },
108 };
109
110 static int winbond_wait_ready(struct spi_flash *flash, unsigned long timeout)
111 {
112         struct spi_slave *spi = flash->spi;
113         unsigned long timebase;
114         int ret;
115         u8 status;
116         u8 cmd[4] = { CMD_W25_RDSR, 0xff, 0xff, 0xff };
117
118         ret = spi_xfer(spi, 32, &cmd[0], NULL, SPI_XFER_BEGIN);
119         if (ret) {
120                 debug("SF: Failed to send command %02x: %d\n", cmd, ret);
121                 return ret;
122         }
123
124         timebase = get_timer(0);
125         do {
126                 ret = spi_xfer(spi, 8, NULL, &status, 0);
127                 if (ret) {
128                         debug("SF: Failed to get status for cmd %02x: %d\n", cmd, ret);
129                         return -1;
130                 }
131
132                 if ((status & WINBOND_SR_WIP) == 0)
133                         break;
134
135         } while (get_timer(timebase) < timeout);
136
137         spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END);
138
139         if ((status & WINBOND_SR_WIP) == 0)
140                 return 0;
141
142         debug("SF: Timed out on command %02x: %d\n", cmd, ret);
143         /* Timed out */
144         return -1;
145 }
146
147 /*
148  * Assemble the address part of a command for Winbond devices in
149  * non-power-of-two page size mode.
150  */
151 static void winbond_build_address(struct winbond_spi_flash *stm, u8 *cmd, u32 offset)
152 {
153         unsigned long page_addr;
154         unsigned long byte_addr;
155         unsigned long page_size;
156         unsigned int page_shift;
157
158         /*
159          * The "extra" space per page is the power-of-two page size
160          * divided by 32.
161          */
162         page_shift = stm->params->l2_page_size;
163         page_size = (1 << page_shift);
164         page_addr = offset / page_size;
165         byte_addr = offset % page_size;
166
167         cmd[0] = page_addr >> (16 - page_shift);
168         cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8);
169         cmd[2] = byte_addr;
170 }
171
172 static int winbond_read_fast(struct spi_flash *flash,
173                 u32 offset, size_t len, void *buf)
174 {
175         struct winbond_spi_flash *stm = to_winbond_spi_flash(flash);
176         u8 cmd[5];
177
178         cmd[0] = CMD_READ_ARRAY_FAST;
179         winbond_build_address(stm, cmd + 1, offset);
180         cmd[4] = 0x00;
181
182         return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len);
183 }
184
185 static int winbond_write(struct spi_flash *flash,
186                 u32 offset, size_t len, const void *buf)
187 {
188         struct winbond_spi_flash *stm = to_winbond_spi_flash(flash);
189         unsigned long page_addr;
190         unsigned long byte_addr;
191         unsigned long page_size;
192         unsigned int page_shift;
193         size_t chunk_len;
194         size_t actual;
195         int ret;
196         u8 cmd[4];
197
198         page_shift = stm->params->l2_page_size;
199         page_size = (1 << page_shift);
200         page_addr = offset / page_size;
201         byte_addr = offset % page_size;
202
203         ret = spi_claim_bus(flash->spi);
204         if (ret) {
205                 debug("SF: Unable to claim SPI bus\n");
206                 return ret;
207         }
208
209         for (actual = 0; actual < len; actual += chunk_len) {
210                 chunk_len = min(len - actual, page_size - byte_addr);
211
212                 cmd[0] = CMD_W25_PP;
213                 cmd[1] = page_addr >> (16 - page_shift);
214                 cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8);
215                 cmd[3] = byte_addr;
216                 debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n",
217                         buf + actual,
218                         cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
219
220                 ret = spi_flash_cmd(flash->spi, CMD_W25_WREN, NULL, 0);
221                 if (ret < 0) {
222                         debug("SF: Enabling Write failed\n");
223                         goto out;
224                 }
225
226                 ret = spi_flash_cmd_write(flash->spi, cmd, 4,
227                                 buf + actual, chunk_len);
228                 if (ret < 0) {
229                         debug("SF: Winbond Page Program failed\n");
230                         goto out;
231                 }
232
233                 ret = winbond_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
234                 if (ret < 0) {
235                         debug("SF: Winbond page programming timed out\n");
236                         goto out;
237                 }
238
239                 page_addr++;
240                 byte_addr = 0;
241         }
242
243         debug("SF: Winbond: Successfully programmed %u bytes @ 0x%x\n",
244                         len, offset);
245         ret = 0;
246
247 out:
248         spi_release_bus(flash->spi);
249         return ret;
250 }
251
252 int winbond_erase(struct spi_flash *flash, u32 offset, size_t len)
253 {
254         struct winbond_spi_flash *stm = to_winbond_spi_flash(flash);
255         unsigned long sector_size;
256         unsigned int page_shift;
257         size_t actual;
258         int ret;
259         u8 cmd[4];
260
261         /*
262          * This function currently uses sector erase only.
263          * probably speed things up by using bulk erase
264          * when possible.
265          */
266
267         page_shift = stm->params->l2_page_size;
268         sector_size = (1 << page_shift) * stm->params->pages_per_sector;
269
270         if (offset % sector_size || len % sector_size) {
271                 debug("SF: Erase offset/length not multiple of sector size\n");
272                 return -1;
273         }
274
275         len /= sector_size;
276         cmd[0] = CMD_W25_SE;
277
278         ret = spi_claim_bus(flash->spi);
279         if (ret) {
280                 debug("SF: Unable to claim SPI bus\n");
281                 return ret;
282         }
283
284         for (actual = 0; actual < len; actual++) {
285                 winbond_build_address(stm, &cmd[1], offset + actual * sector_size);
286                 printf("Erase: %02x %02x %02x %02x\n",
287                                 cmd[0], cmd[1], cmd[2], cmd[3]);
288
289                 ret = spi_flash_cmd(flash->spi, CMD_W25_WREN, NULL, 0);
290                 if (ret < 0) {
291                         debug("SF: Enabling Write failed\n");
292                         goto out;
293                 }
294
295                 ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
296                 if (ret < 0) {
297                         debug("SF: Winbond sector erase failed\n");
298                         goto out;
299                 }
300
301                 ret = winbond_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
302                 if (ret < 0) {
303                         debug("SF: Winbond sector erase timed out\n");
304                         goto out;
305                 }
306         }
307
308         debug("SF: Winbond: Successfully erased %u bytes @ 0x%x\n",
309                         len * sector_size, offset);
310         ret = 0;
311
312 out:
313         spi_release_bus(flash->spi);
314         return ret;
315 }
316
317 struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
318 {
319         const struct winbond_spi_flash_params *params;
320         unsigned page_size;
321         struct winbond_spi_flash *stm;
322         unsigned int i;
323
324         for (i = 0; i < ARRAY_SIZE(winbond_spi_flash_table); i++) {
325                 params = &winbond_spi_flash_table[i];
326                 if (params->id == ((idcode[1] << 8) | idcode[2]))
327                         break;
328         }
329
330         if (i == ARRAY_SIZE(winbond_spi_flash_table)) {
331                 debug("SF: Unsupported Winbond ID %02x%02x\n",
332                                 idcode[1], idcode[2]);
333                 return NULL;
334         }
335
336         stm = malloc(sizeof(struct winbond_spi_flash));
337         if (!stm) {
338                 debug("SF: Failed to allocate memory\n");
339                 return NULL;
340         }
341
342         stm->params = params;
343         stm->flash.spi = spi;
344         stm->flash.name = params->name;
345
346         /* Assuming power-of-two page size initially. */
347         page_size = 1 << params->l2_page_size;
348
349         stm->flash.write = winbond_write;
350         stm->flash.erase = winbond_erase;
351         stm->flash.read = winbond_read_fast;
352         stm->flash.size = page_size * params->pages_per_sector
353                                 * params->sectors_per_block
354                                 * params->nr_blocks;
355
356         printf("SF: Detected %s with page size %u, total ",
357                params->name, page_size);
358         print_size(stm->flash.size, "\n");
359
360         return &stm->flash;
361 }