]> git.ipfire.org Git - people/ms/u-boot.git/blob - drivers/mtd/spi/sst.c
e51dfc7f29fbd15ef0ebbc3b0d8f4b64e28928d1
[people/ms/u-boot.git] / drivers / mtd / spi / sst.c
1 /*
2 * Driver for SST serial flashes
3 *
4 * (C) Copyright 2000-2002
5 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
6 * Copyright 2008, Network Appliance Inc.
7 * Jason McMullan <mcmullan@netapp.com>
8 * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
9 * TsiChung Liew (Tsi-Chung.Liew@freescale.com)
10 * Copyright (c) 2008-2009 Analog Devices Inc.
11 *
12 * Licensed under the GPL-2 or later.
13 */
14
15 #include <common.h>
16 #include <malloc.h>
17 #include <spi_flash.h>
18
19 #include "spi_flash_internal.h"
20
21 #define CMD_SST_BP 0x02 /* Byte Program */
22 #define CMD_SST_AAI_WP 0xAD /* Auto Address Increment Word Program */
23 #define CMD_SST_SE 0x20 /* Sector Erase */
24
25 #define SST_SR_WIP (1 << 0) /* Write-in-Progress */
26 #define SST_SR_WEL (1 << 1) /* Write enable */
27 #define SST_SR_BP0 (1 << 2) /* Block Protection 0 */
28 #define SST_SR_BP1 (1 << 3) /* Block Protection 1 */
29 #define SST_SR_BP2 (1 << 4) /* Block Protection 2 */
30 #define SST_SR_AAI (1 << 6) /* Addressing mode */
31 #define SST_SR_BPL (1 << 7) /* BP bits lock */
32
33 #define SST_FEAT_WP (1 << 0) /* Supports AAI word program */
34 #define SST_FEAT_MBP (1 << 1) /* Supports multibyte program */
35
36 struct sst_spi_flash_params {
37 u8 idcode1;
38 u8 flags;
39 u16 nr_sectors;
40 const char *name;
41 };
42
43 struct sst_spi_flash {
44 struct spi_flash flash;
45 const struct sst_spi_flash_params *params;
46 };
47
48 static inline struct sst_spi_flash *to_sst_spi_flash(struct spi_flash *flash)
49 {
50 return container_of(flash, struct sst_spi_flash, flash);
51 }
52
53 #define SST_SECTOR_SIZE (4 * 1024)
54 #define SST_PAGE_SIZE 256
55 static const struct sst_spi_flash_params sst_spi_flash_table[] = {
56 {
57 .idcode1 = 0x8d,
58 .flags = SST_FEAT_WP,
59 .nr_sectors = 128,
60 .name = "SST25VF040B",
61 },{
62 .idcode1 = 0x8e,
63 .flags = SST_FEAT_WP,
64 .nr_sectors = 256,
65 .name = "SST25VF080B",
66 },{
67 .idcode1 = 0x41,
68 .flags = SST_FEAT_WP,
69 .nr_sectors = 512,
70 .name = "SST25VF016B",
71 },{
72 .idcode1 = 0x4a,
73 .flags = SST_FEAT_WP,
74 .nr_sectors = 1024,
75 .name = "SST25VF032B",
76 },{
77 .idcode1 = 0x4b,
78 .flags = SST_FEAT_MBP,
79 .nr_sectors = 2048,
80 .name = "SST25VF064C",
81 },{
82 .idcode1 = 0x01,
83 .flags = SST_FEAT_WP,
84 .nr_sectors = 16,
85 .name = "SST25WF512",
86 },{
87 .idcode1 = 0x02,
88 .flags = SST_FEAT_WP,
89 .nr_sectors = 32,
90 .name = "SST25WF010",
91 },{
92 .idcode1 = 0x03,
93 .flags = SST_FEAT_WP,
94 .nr_sectors = 64,
95 .name = "SST25WF020",
96 },{
97 .idcode1 = 0x04,
98 .flags = SST_FEAT_WP,
99 .nr_sectors = 128,
100 .name = "SST25WF040",
101 },
102 };
103
104 static int
105 sst_enable_writing(struct spi_flash *flash)
106 {
107 int ret = spi_flash_cmd_write_enable(flash);
108 if (ret)
109 debug("SF: Enabling Write failed\n");
110 return ret;
111 }
112
113 static int
114 sst_disable_writing(struct spi_flash *flash)
115 {
116 int ret = spi_flash_cmd_write_disable(flash);
117 if (ret)
118 debug("SF: Disabling Write failed\n");
119 return ret;
120 }
121
122 static int
123 sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf)
124 {
125 int ret;
126 u8 cmd[4] = {
127 CMD_SST_BP,
128 offset >> 16,
129 offset >> 8,
130 offset,
131 };
132
133 debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
134 spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset);
135
136 ret = sst_enable_writing(flash);
137 if (ret)
138 return ret;
139
140 ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1);
141 if (ret)
142 return ret;
143
144 return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
145 }
146
147 static int
148 sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, const void *buf)
149 {
150 size_t actual, cmd_len;
151 int ret;
152 u8 cmd[4];
153
154 ret = spi_claim_bus(flash->spi);
155 if (ret) {
156 debug("SF: Unable to claim SPI bus\n");
157 return ret;
158 }
159
160 /* If the data is not word aligned, write out leading single byte */
161 actual = offset % 2;
162 if (actual) {
163 ret = sst_byte_write(flash, offset, buf);
164 if (ret)
165 goto done;
166 }
167 offset += actual;
168
169 ret = sst_enable_writing(flash);
170 if (ret)
171 goto done;
172
173 cmd_len = 4;
174 cmd[0] = CMD_SST_AAI_WP;
175 cmd[1] = offset >> 16;
176 cmd[2] = offset >> 8;
177 cmd[3] = offset;
178
179 for (; actual < len - 1; actual += 2) {
180 debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
181 spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual, cmd[0],
182 offset);
183
184 ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len,
185 buf + actual, 2);
186 if (ret) {
187 debug("SF: sst word program failed\n");
188 break;
189 }
190
191 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
192 if (ret)
193 break;
194
195 cmd_len = 1;
196 offset += 2;
197 }
198
199 if (!ret)
200 ret = sst_disable_writing(flash);
201
202 /* If there is a single trailing byte, write it out */
203 if (!ret && actual != len)
204 ret = sst_byte_write(flash, offset, buf + actual);
205
206 done:
207 debug("SF: sst: program %s %zu bytes @ 0x%zx\n",
208 ret ? "failure" : "success", len, offset - actual);
209
210 spi_release_bus(flash->spi);
211 return ret;
212 }
213
214 static int sst_erase(struct spi_flash *flash, u32 offset, size_t len)
215 {
216 return spi_flash_cmd_erase(flash, CMD_SST_SE, offset, len);
217 }
218
219 static int
220 sst_unlock(struct spi_flash *flash)
221 {
222 int ret;
223 u8 cmd, status;
224
225 ret = sst_enable_writing(flash);
226 if (ret)
227 return ret;
228
229 cmd = CMD_WRITE_STATUS;
230 status = 0;
231 ret = spi_flash_cmd_write(flash->spi, &cmd, 1, &status, 1);
232 if (ret)
233 debug("SF: Unable to set status byte\n");
234
235 debug("SF: sst: status = %x\n", spi_w8r8(flash->spi, CMD_READ_STATUS));
236
237 return ret;
238 }
239
240 struct spi_flash *
241 spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode)
242 {
243 const struct sst_spi_flash_params *params;
244 struct sst_spi_flash *stm;
245 size_t i;
246
247 for (i = 0; i < ARRAY_SIZE(sst_spi_flash_table); ++i) {
248 params = &sst_spi_flash_table[i];
249 if (params->idcode1 == idcode[2])
250 break;
251 }
252
253 if (i == ARRAY_SIZE(sst_spi_flash_table)) {
254 debug("SF: Unsupported SST ID %02x\n", idcode[1]);
255 return NULL;
256 }
257
258 stm = malloc(sizeof(*stm));
259 if (!stm) {
260 debug("SF: Failed to allocate memory\n");
261 return NULL;
262 }
263
264 stm->params = params;
265 stm->flash.spi = spi;
266 stm->flash.name = params->name;
267
268 if (stm->params->flags & SST_FEAT_WP)
269 stm->flash.write = sst_write_wp;
270 else
271 stm->flash.write = spi_flash_cmd_write_multi;
272 stm->flash.erase = sst_erase;
273 stm->flash.read = spi_flash_cmd_read_fast;
274 stm->flash.page_size = SST_PAGE_SIZE;
275 stm->flash.sector_size = SST_SECTOR_SIZE;
276 stm->flash.size = stm->flash.sector_size * params->nr_sectors;
277
278 /* Flash powers up read-only, so clear BP# bits */
279 sst_unlock(&stm->flash);
280
281 return &stm->flash;
282 }