blob: e51dfc7f29fbd15ef0ebbc3b0d8f4b64e28928d1 [file] [log] [blame]
Mike Frysingerf1cb7c82009-03-27 19:27:58 -04001/*
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
Mike Frysingerf1cb7c82009-03-27 19:27:58 -040021#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
Mike Frysingerb75d9902011-04-25 06:59:54 +000033#define SST_FEAT_WP (1 << 0) /* Supports AAI word program */
34#define SST_FEAT_MBP (1 << 1) /* Supports multibyte program */
35
Mike Frysingerf1cb7c82009-03-27 19:27:58 -040036struct sst_spi_flash_params {
37 u8 idcode1;
Mike Frysingerb75d9902011-04-25 06:59:54 +000038 u8 flags;
Mike Frysingerf1cb7c82009-03-27 19:27:58 -040039 u16 nr_sectors;
40 const char *name;
41};
42
43struct sst_spi_flash {
44 struct spi_flash flash;
45 const struct sst_spi_flash_params *params;
46};
47
48static 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)
Mike Frysingerb75d9902011-04-25 06:59:54 +000054#define SST_PAGE_SIZE 256
Mike Frysingerf1cb7c82009-03-27 19:27:58 -040055static const struct sst_spi_flash_params sst_spi_flash_table[] = {
56 {
Mike Frysingerca3e83b2009-06-19 03:27:28 -040057 .idcode1 = 0x8d,
Mike Frysingerb75d9902011-04-25 06:59:54 +000058 .flags = SST_FEAT_WP,
Mike Frysingerca3e83b2009-06-19 03:27:28 -040059 .nr_sectors = 128,
60 .name = "SST25VF040B",
61 },{
62 .idcode1 = 0x8e,
Mike Frysingerb75d9902011-04-25 06:59:54 +000063 .flags = SST_FEAT_WP,
Mike Frysingerca3e83b2009-06-19 03:27:28 -040064 .nr_sectors = 256,
65 .name = "SST25VF080B",
66 },{
67 .idcode1 = 0x41,
Mike Frysingerb75d9902011-04-25 06:59:54 +000068 .flags = SST_FEAT_WP,
Mike Frysingerca3e83b2009-06-19 03:27:28 -040069 .nr_sectors = 512,
70 .name = "SST25VF016B",
71 },{
72 .idcode1 = 0x4a,
Mike Frysingerb75d9902011-04-25 06:59:54 +000073 .flags = SST_FEAT_WP,
Mike Frysingerca3e83b2009-06-19 03:27:28 -040074 .nr_sectors = 1024,
75 .name = "SST25VF032B",
76 },{
James Kosin23ec2c22011-04-13 15:12:18 -040077 .idcode1 = 0x4b,
Mike Frysingerb75d9902011-04-25 06:59:54 +000078 .flags = SST_FEAT_MBP,
James Kosin23ec2c22011-04-13 15:12:18 -040079 .nr_sectors = 2048,
80 .name = "SST25VF064C",
81 },{
Mike Frysingerf1cb7c82009-03-27 19:27:58 -040082 .idcode1 = 0x01,
Mike Frysingerb75d9902011-04-25 06:59:54 +000083 .flags = SST_FEAT_WP,
Mike Frysingerad4897e2009-06-19 03:20:06 -040084 .nr_sectors = 16,
Mike Frysingerf1cb7c82009-03-27 19:27:58 -040085 .name = "SST25WF512",
86 },{
87 .idcode1 = 0x02,
Mike Frysingerb75d9902011-04-25 06:59:54 +000088 .flags = SST_FEAT_WP,
Mike Frysingerad4897e2009-06-19 03:20:06 -040089 .nr_sectors = 32,
Mike Frysingerf1cb7c82009-03-27 19:27:58 -040090 .name = "SST25WF010",
91 },{
92 .idcode1 = 0x03,
Mike Frysingerb75d9902011-04-25 06:59:54 +000093 .flags = SST_FEAT_WP,
Mike Frysingerad4897e2009-06-19 03:20:06 -040094 .nr_sectors = 64,
Mike Frysingerf1cb7c82009-03-27 19:27:58 -040095 .name = "SST25WF020",
96 },{
97 .idcode1 = 0x04,
Mike Frysingerb75d9902011-04-25 06:59:54 +000098 .flags = SST_FEAT_WP,
Mike Frysingerad4897e2009-06-19 03:20:06 -040099 .nr_sectors = 128,
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400100 .name = "SST25WF040",
101 },
102};
103
104static int
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400105sst_enable_writing(struct spi_flash *flash)
106{
Mike Frysinger8ec7f4c2011-04-23 23:05:55 +0000107 int ret = spi_flash_cmd_write_enable(flash);
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400108 if (ret)
109 debug("SF: Enabling Write failed\n");
110 return ret;
111}
112
113static int
114sst_disable_writing(struct spi_flash *flash)
115{
Mike Frysinger79112112011-04-25 06:59:53 +0000116 int ret = spi_flash_cmd_write_disable(flash);
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400117 if (ret)
118 debug("SF: Disabling Write failed\n");
119 return ret;
120}
121
122static int
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400123sst_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",
Mike Frysinger1302bec2012-01-28 16:26:03 -0800134 spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset);
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400135
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
Mike Frysinger37e13bc2011-01-10 02:20:12 -0500144 return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400145}
146
147static int
Mike Frysingerb75d9902011-04-25 06:59:54 +0000148sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, const void *buf)
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400149{
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",
Mike Frysinger1302bec2012-01-28 16:26:03 -0800181 spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual, cmd[0],
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400182 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
Mike Frysinger37e13bc2011-01-10 02:20:12 -0500191 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400192 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
Mike Frysingeraea4e172011-04-12 01:51:29 -0400214static int sst_erase(struct spi_flash *flash, u32 offset, size_t len)
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400215{
Richard Retanubunb0148dc2011-02-16 16:37:22 -0500216 return spi_flash_cmd_erase(flash, CMD_SST_SE, offset, len);
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400217}
218
219static int
220sst_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
Mike Frysinger1302bec2012-01-28 16:26:03 -0800229 cmd = CMD_WRITE_STATUS;
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400230 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
Mike Frysinger1302bec2012-01-28 16:26:03 -0800235 debug("SF: sst: status = %x\n", spi_w8r8(flash->spi, CMD_READ_STATUS));
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400236
237 return ret;
238}
239
240struct spi_flash *
241spi_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
Mike Frysingerb75d9902011-04-25 06:59:54 +0000268 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;
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400272 stm->flash.erase = sst_erase;
Mike Frysinger816790c2011-04-12 01:34:55 -0400273 stm->flash.read = spi_flash_cmd_read_fast;
Mike Frysingerb75d9902011-04-25 06:59:54 +0000274 stm->flash.page_size = SST_PAGE_SIZE;
Richard Retanubunb0148dc2011-02-16 16:37:22 -0500275 stm->flash.sector_size = SST_SECTOR_SIZE;
276 stm->flash.size = stm->flash.sector_size * params->nr_sectors;
Mike Frysingerf1cb7c82009-03-27 19:27:58 -0400277
278 /* Flash powers up read-only, so clear BP# bits */
279 sst_unlock(&stm->flash);
280
281 return &stm->flash;
282}