]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/mtd/spi/winbond.c
sf: new driver for Winbond W25X16/32/64 devices
[karo-tx-uboot.git] / 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_ID_W25X16               0x3015
28 #define WINBOND_ID_W25X32               0x3016
29 #define WINBOND_ID_W25X64               0x3017
30
31 #define WINBOND_SR_WIP          (1 << 0)        /* Write-in-Progress */
32
33 struct winbond_spi_flash_params {
34         uint16_t        id;
35         /* Log2 of page size in power-of-two mode */
36         uint8_t         l2_page_size;
37         uint16_t        pages_per_sector;
38         uint16_t        sectors_per_block;
39         uint8_t         nr_blocks;
40         const char      *name;
41 };
42
43 /* spi_flash needs to be first so upper layers can free() it */
44 struct winbond_spi_flash {
45         struct spi_flash flash;
46         const struct winbond_spi_flash_params *params;
47 };
48
49 static inline struct winbond_spi_flash *
50 to_winbond_spi_flash(struct spi_flash *flash)
51 {
52         return container_of(flash, struct winbond_spi_flash, flash);
53 }
54
55 static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
56         {
57                 .id                     = WINBOND_ID_W25X16,
58                 .l2_page_size           = 8,
59                 .pages_per_sector       = 16,
60                 .sectors_per_block      = 16,
61                 .nr_blocks              = 32,
62                 .name                   = "W25X16",
63         },
64         {
65                 .id                     = WINBOND_ID_W25X32,
66                 .l2_page_size           = 8,
67                 .pages_per_sector       = 16,
68                 .sectors_per_block      = 16,
69                 .nr_blocks              = 64,
70                 .name                   = "W25X32",
71         },
72         {
73                 .id                     = WINBOND_ID_W25X64,
74                 .l2_page_size           = 8,
75                 .pages_per_sector       = 16,
76                 .sectors_per_block      = 16,
77                 .nr_blocks              = 128,
78                 .name                   = "W25X64",
79         },
80 };
81
82 static int winbond_wait_ready(struct spi_flash *flash, unsigned long timeout)
83 {
84         struct spi_slave *spi = flash->spi;
85         unsigned long timebase;
86         int ret;
87         u8 status;
88         u8 cmd[4] = { CMD_W25_RDSR, 0xff, 0xff, 0xff };
89
90         ret = spi_xfer(spi, 32, &cmd[0], NULL, SPI_XFER_BEGIN);
91         if (ret) {
92                 debug("SF: Failed to send command %02x: %d\n", cmd, ret);
93                 return ret;
94         }
95
96         timebase = get_timer(0);
97         do {
98                 ret = spi_xfer(spi, 8, NULL, &status, 0);
99                 if (ret) {
100                         debug("SF: Failed to get status for cmd %02x: %d\n", cmd, ret);
101                         return -1;
102                 }
103
104                 if ((status & WINBOND_SR_WIP) == 0)
105                         break;
106
107         } while (get_timer(timebase) < timeout);
108
109         spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END);
110
111         if ((status & WINBOND_SR_WIP) == 0)
112                 return 0;
113
114         debug("SF: Timed out on command %02x: %d\n", cmd, ret);
115         /* Timed out */
116         return -1;
117 }
118
119 /*
120  * Assemble the address part of a command for Winbond devices in
121  * non-power-of-two page size mode.
122  */
123 static void winbond_build_address(struct winbond_spi_flash *stm, u8 *cmd, u32 offset)
124 {
125         unsigned long page_addr;
126         unsigned long byte_addr;
127         unsigned long page_size;
128         unsigned int page_shift;
129
130         /*
131          * The "extra" space per page is the power-of-two page size
132          * divided by 32.
133          */
134         page_shift = stm->params->l2_page_size;
135         page_size = (1 << page_shift);
136         page_addr = offset / page_size;
137         byte_addr = offset % page_size;
138
139         cmd[0] = page_addr >> (16 - page_shift);
140         cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8);
141         cmd[2] = byte_addr;
142 }
143
144 static int winbond_read_fast(struct spi_flash *flash,
145                 u32 offset, size_t len, void *buf)
146 {
147         struct winbond_spi_flash *stm = to_winbond_spi_flash(flash);
148         u8 cmd[5];
149
150         cmd[0] = CMD_READ_ARRAY_FAST;
151         winbond_build_address(stm, cmd + 1, offset);
152         cmd[4] = 0x00;
153
154         return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len);
155 }
156
157 static int winbond_write(struct spi_flash *flash,
158                 u32 offset, size_t len, const void *buf)
159 {
160         struct winbond_spi_flash *stm = to_winbond_spi_flash(flash);
161         unsigned long page_addr;
162         unsigned long byte_addr;
163         unsigned long page_size;
164         unsigned int page_shift;
165         size_t chunk_len;
166         size_t actual;
167         int ret;
168         u8 cmd[4];
169
170         page_shift = stm->params->l2_page_size;
171         page_size = (1 << page_shift);
172         page_addr = offset / page_size;
173         byte_addr = offset % page_size;
174
175         ret = spi_claim_bus(flash->spi);
176         if (ret) {
177                 debug("SF: Unable to claim SPI bus\n");
178                 return ret;
179         }
180
181         for (actual = 0; actual < len; actual += chunk_len) {
182                 chunk_len = min(len - actual, page_size - byte_addr);
183
184                 cmd[0] = CMD_W25_PP;
185                 cmd[1] = page_addr >> (16 - page_shift);
186                 cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8);
187                 cmd[3] = byte_addr;
188                 debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n",
189                         buf + actual,
190                         cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
191
192                 ret = spi_flash_cmd(flash->spi, CMD_W25_WREN, NULL, 0);
193                 if (ret < 0) {
194                         debug("SF: Enabling Write failed\n");
195                         goto out;
196                 }
197
198                 ret = spi_flash_cmd_write(flash->spi, cmd, 4,
199                                 buf + actual, chunk_len);
200                 if (ret < 0) {
201                         debug("SF: Winbond Page Program failed\n");
202                         goto out;
203                 }
204
205                 ret = winbond_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
206                 if (ret < 0) {
207                         debug("SF: Winbond page programming timed out\n");
208                         goto out;
209                 }
210
211                 page_addr++;
212                 byte_addr = 0;
213         }
214
215         debug("SF: Winbond: Successfully programmed %u bytes @ 0x%x\n",
216                         len, offset);
217         ret = 0;
218
219 out:
220         spi_release_bus(flash->spi);
221         return ret;
222 }
223
224 int winbond_erase(struct spi_flash *flash, u32 offset, size_t len)
225 {
226         struct winbond_spi_flash *stm = to_winbond_spi_flash(flash);
227         unsigned long sector_size;
228         unsigned int page_shift;
229         size_t actual;
230         int ret;
231         u8 cmd[4];
232
233         /*
234          * This function currently uses sector erase only.
235          * probably speed things up by using bulk erase
236          * when possible.
237          */
238
239         page_shift = stm->params->l2_page_size;
240         sector_size = (1 << page_shift) * stm->params->pages_per_sector;
241
242         if (offset % sector_size || len % sector_size) {
243                 debug("SF: Erase offset/length not multiple of sector size\n");
244                 return -1;
245         }
246
247         len /= sector_size;
248         cmd[0] = CMD_W25_SE;
249
250         ret = spi_claim_bus(flash->spi);
251         if (ret) {
252                 debug("SF: Unable to claim SPI bus\n");
253                 return ret;
254         }
255
256         for (actual = 0; actual < len; actual++) {
257                 winbond_build_address(stm, &cmd[1], offset + actual * sector_size);
258                 printf("Erase: %02x %02x %02x %02x\n",
259                                 cmd[0], cmd[1], cmd[2], cmd[3]);
260
261                 ret = spi_flash_cmd(flash->spi, CMD_W25_WREN, NULL, 0);
262                 if (ret < 0) {
263                         debug("SF: Enabling Write failed\n");
264                         goto out;
265                 }
266
267                 ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
268                 if (ret < 0) {
269                         debug("SF: Winbond sector erase failed\n");
270                         goto out;
271                 }
272
273                 ret = winbond_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
274                 if (ret < 0) {
275                         debug("SF: Winbond sector erase timed out\n");
276                         goto out;
277                 }
278         }
279
280         debug("SF: Winbond: Successfully erased %u bytes @ 0x%x\n",
281                         len * sector_size, offset);
282         ret = 0;
283
284 out:
285         spi_release_bus(flash->spi);
286         return ret;
287 }
288
289 struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
290 {
291         const struct winbond_spi_flash_params *params;
292         unsigned long page_size;
293         struct winbond_spi_flash *stm;
294         unsigned int i;
295
296         for (i = 0; i < ARRAY_SIZE(winbond_spi_flash_table); i++) {
297                 params = &winbond_spi_flash_table[i];
298                 if (params->id == ((idcode[1] << 8) | idcode[2]))
299                         break;
300         }
301
302         if (i == ARRAY_SIZE(winbond_spi_flash_table)) {
303                 debug("SF: Unsupported Winbond ID %02x%02x\n",
304                                 idcode[1], idcode[2]);
305                 return NULL;
306         }
307
308         stm = malloc(sizeof(struct winbond_spi_flash));
309         if (!stm) {
310                 debug("SF: Failed to allocate memory\n");
311                 return NULL;
312         }
313
314         stm->params = params;
315         stm->flash.spi = spi;
316         stm->flash.name = params->name;
317
318         /* Assuming power-of-two page size initially. */
319         page_size = 1 << params->l2_page_size;
320
321         stm->flash.write = winbond_write;
322         stm->flash.erase = winbond_erase;
323         stm->flash.read = winbond_read_fast;
324         stm->flash.size = page_size * params->pages_per_sector
325                                 * params->sectors_per_block
326                                 * params->nr_blocks;
327
328         debug("SF: Detected %s with page size %u, total %u bytes\n",
329                         params->name, page_size, stm->flash.size);
330
331         return &stm->flash;
332 }