blob: 40bce31983755b502464ace692f0047498ef65bc [file] [log] [blame]
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07001/*
2 * (C) Copyright 2000-2002
3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4 *
5 * Copyright 2008, Network Appliance Inc.
6 * Jason McMullan <mcmullan@netapp.com>
7 *
8 * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
9 * TsiChung Liew (Tsi-Chung.Liew@freescale.com)
10 *
11 * See file CREDITS for list of people who contributed to this
12 * project.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License as
16 * published by the Free Software Foundation; either version 2 of
17 * the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Paul Menzela8ae1c62013-02-20 13:21:20 +010021 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070022 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
27 * MA 02111-1307 USA
28 */
29
30#include <stdlib.h>
31#include <spi_flash.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100032
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070033#include "spi_flash_internal.h"
34
35/* M25Pxx-specific commands */
36#define CMD_M25PXX_WREN 0x06 /* Write Enable */
37#define CMD_M25PXX_WRDI 0x04 /* Write Disable */
38#define CMD_M25PXX_RDSR 0x05 /* Read Status Register */
39#define CMD_M25PXX_WRSR 0x01 /* Write Status Register */
40#define CMD_M25PXX_READ 0x03 /* Read Data Bytes */
41#define CMD_M25PXX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
42#define CMD_M25PXX_PP 0x02 /* Page Program */
43#define CMD_M25PXX_SE 0xd8 /* Sector Erase */
44#define CMD_M25PXX_BE 0xc7 /* Bulk Erase */
45#define CMD_M25PXX_DP 0xb9 /* Deep Power-down */
46#define CMD_M25PXX_RES 0xab /* Release from DP, and Read Signature */
47
48#define STM_ID_M25P10 0x11
49#define STM_ID_M25P16 0x15
50#define STM_ID_M25P20 0x12
51#define STM_ID_M25P32 0x16
52#define STM_ID_M25P40 0x13
53#define STM_ID_M25P64 0x17
54#define STM_ID_M25P80 0x14
55#define STM_ID_M25P128 0x18
56
57struct stmicro_spi_flash_params {
58 u8 idcode1;
59 u16 page_size;
60 u16 pages_per_sector;
61 u16 nr_sectors;
62 const char *name;
63};
64
65/* spi_flash needs to be first so upper layers can free() it */
66struct stmicro_spi_flash {
67 struct spi_flash flash;
68 const struct stmicro_spi_flash_params *params;
69};
70
71static inline struct stmicro_spi_flash *to_stmicro_spi_flash(struct spi_flash
72 *flash)
73{
74 return container_of(flash, struct stmicro_spi_flash, flash);
75}
76
77static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {
78 {
79 .idcode1 = STM_ID_M25P10,
80 .page_size = 256,
81 .pages_per_sector = 128,
82 .nr_sectors = 4,
83 .name = "M25P10",
84 },
85 {
86 .idcode1 = STM_ID_M25P16,
87 .page_size = 256,
88 .pages_per_sector = 256,
89 .nr_sectors = 32,
90 .name = "M25P16",
91 },
92 {
93 .idcode1 = STM_ID_M25P20,
94 .page_size = 256,
95 .pages_per_sector = 256,
96 .nr_sectors = 4,
97 .name = "M25P20",
98 },
99 {
100 .idcode1 = STM_ID_M25P32,
101 .page_size = 256,
102 .pages_per_sector = 256,
103 .nr_sectors = 64,
104 .name = "M25P32",
105 },
106 {
107 .idcode1 = STM_ID_M25P40,
108 .page_size = 256,
109 .pages_per_sector = 256,
110 .nr_sectors = 8,
111 .name = "M25P40",
112 },
113 {
114 .idcode1 = STM_ID_M25P64,
115 .page_size = 256,
116 .pages_per_sector = 256,
117 .nr_sectors = 128,
118 .name = "M25P64",
119 },
120 {
121 .idcode1 = STM_ID_M25P80,
122 .page_size = 256,
123 .pages_per_sector = 256,
124 .nr_sectors = 16,
125 .name = "M25P80",
126 },
127 {
128 .idcode1 = STM_ID_M25P128,
129 .page_size = 256,
130 .pages_per_sector = 1024,
131 .nr_sectors = 64,
132 .name = "M25P128",
133 },
134};
135
136static int stmicro_write(struct spi_flash *flash,
137 u32 offset, size_t len, const void *buf)
138{
139 struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700140 unsigned long byte_addr;
141 unsigned long page_size;
142 size_t chunk_len;
143 size_t actual;
144 int ret;
145 u8 cmd[4];
146
Kyösti Mälkki77d12802014-06-29 16:15:39 +0300147 page_size = stm->params->page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700148 byte_addr = offset % page_size;
149
Martin Roth3316cf22012-12-05 16:22:54 -0700150 flash->spi->rw = SPI_WRITE_FLAG;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700151 ret = spi_claim_bus(flash->spi);
152 if (ret) {
153 printk(BIOS_WARNING, "SF: Unable to claim SPI bus\n");
154 return ret;
155 }
156
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700157 for (actual = 0; actual < len; actual += chunk_len) {
158 chunk_len = min(len - actual, page_size - byte_addr);
Kyösti Mälkki11104952014-06-29 16:17:33 +0300159 chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700160
161 cmd[0] = CMD_M25PXX_PP;
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300162 cmd[1] = (offset >> 16) & 0xff;
163 cmd[2] = (offset >> 8) & 0xff;
164 cmd[3] = offset & 0xff;
Stefan Reinauer5649b082012-05-23 11:03:29 -0700165#if CONFIG_DEBUG_SPI_FLASH
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700166 printk(BIOS_SPEW, "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x }"
167 " chunk_len = %zu\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700168 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
Stefan Reinauer5649b082012-05-23 11:03:29 -0700169#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700170
171 ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0);
172 if (ret < 0) {
173 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300174 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700175 }
176
Kyösti Mälkki11104952014-06-29 16:17:33 +0300177 ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd),
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700178 buf + actual, chunk_len);
179 if (ret < 0) {
180 printk(BIOS_WARNING, "SF: STMicro Page Program failed\n");
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300181 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700182 }
183
184 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
185 if (ret)
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300186 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700187
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300188 offset += chunk_len;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700189 byte_addr = 0;
190 }
191
Marc Jones747127d2012-12-03 22:16:29 -0700192#if CONFIG_DEBUG_SPI_FLASH
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300193 printk(BIOS_SPEW, "SF: STMicro: Successfully programmed %zu bytes @"
194 " 0x%lx\n", len, (unsigned long)(offset - len));
Marc Jones747127d2012-12-03 22:16:29 -0700195#endif
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300196 ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700197
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300198out:
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700199 spi_release_bus(flash->spi);
200 return ret;
201}
202
203static int stmicro_erase(struct spi_flash *flash, u32 offset, size_t len)
204{
205 return spi_flash_cmd_erase(flash, CMD_M25PXX_SE, offset, len);
206}
207
208struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
209{
210 const struct stmicro_spi_flash_params *params;
211 struct stmicro_spi_flash *stm;
212 unsigned int i;
213
214 if (idcode[0] == 0xff) {
215 i = spi_flash_cmd(spi, CMD_M25PXX_RES,
216 idcode, 4);
217 if (i)
218 return NULL;
219 if ((idcode[3] & 0xf0) == 0x10) {
220 idcode[0] = 0x20;
221 idcode[1] = 0x20;
222 idcode[2] = idcode[3] + 1;
223 } else
224 return NULL;
225 }
226
227 for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {
228 params = &stmicro_spi_flash_table[i];
229 if (params->idcode1 == idcode[2]) {
230 break;
231 }
232 }
233
234 if (i == ARRAY_SIZE(stmicro_spi_flash_table)) {
235 printk(BIOS_WARNING, "SF: Unsupported STMicro ID %02x\n", idcode[1]);
236 return NULL;
237 }
238
239 stm = malloc(sizeof(struct stmicro_spi_flash));
240 if (!stm) {
241 printk(BIOS_WARNING, "SF: Failed to allocate memory\n");
242 return NULL;
243 }
244
245 stm->params = params;
246 stm->flash.spi = spi;
247 stm->flash.name = params->name;
248
249 stm->flash.write = stmicro_write;
250 stm->flash.erase = stmicro_erase;
251 stm->flash.read = spi_flash_cmd_read_fast;
252 stm->flash.sector_size = params->page_size * params->pages_per_sector;
253 stm->flash.size = stm->flash.sector_size * params->nr_sectors;
254
255 return &stm->flash;
256}