blob: 37907f62aa2f710d761a915e32826dbb3151fb84 [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 */
Scott Radcliffef5fcedf2014-10-14 15:40:59 -040043#define CMD_M25PXX_SSE 0x20 /* Subsector Erase */
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070044#define CMD_M25PXX_SE 0xd8 /* Sector Erase */
45#define CMD_M25PXX_BE 0xc7 /* Bulk Erase */
46#define CMD_M25PXX_DP 0xb9 /* Deep Power-down */
47#define CMD_M25PXX_RES 0xab /* Release from DP, and Read Signature */
48
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080049/*
50 * Device ID = (memory_type << 8) + memory_capacity
51 */
52#define STM_ID_M25P10 0x2011
53#define STM_ID_M25P16 0x2015
54#define STM_ID_M25P20 0x2012
55#define STM_ID_M25P32 0x2016
56#define STM_ID_M25P40 0x2013
57#define STM_ID_M25P64 0x2017
58#define STM_ID_M25P80 0x2014
59#define STM_ID_M25P128 0x2018
David Imhofffd1f5142015-05-03 16:57:38 +020060#define STM_ID_N25Q256 0xba19
David Imhoff61295b52015-05-03 16:02:56 +020061#define STM_ID_N25Q064 0xbb17
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080062#define STM_ID_N25Q128 0xbb18
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070063
64struct stmicro_spi_flash_params {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080065 u16 device_id;
Scott Radcliffef5fcedf2014-10-14 15:40:59 -040066 u8 op_erase;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070067 u16 page_size;
68 u16 pages_per_sector;
69 u16 nr_sectors;
70 const char *name;
71};
72
73/* spi_flash needs to be first so upper layers can free() it */
74struct stmicro_spi_flash {
75 struct spi_flash flash;
76 const struct stmicro_spi_flash_params *params;
77};
78
79static inline struct stmicro_spi_flash *to_stmicro_spi_flash(struct spi_flash
80 *flash)
81{
82 return container_of(flash, struct stmicro_spi_flash, flash);
83}
84
85static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {
86 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080087 .device_id = STM_ID_M25P10,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -040088 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070089 .page_size = 256,
90 .pages_per_sector = 128,
91 .nr_sectors = 4,
92 .name = "M25P10",
93 },
94 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080095 .device_id = STM_ID_M25P16,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -040096 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070097 .page_size = 256,
98 .pages_per_sector = 256,
99 .nr_sectors = 32,
100 .name = "M25P16",
101 },
102 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800103 .device_id = STM_ID_M25P20,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400104 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700105 .page_size = 256,
106 .pages_per_sector = 256,
107 .nr_sectors = 4,
108 .name = "M25P20",
109 },
110 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800111 .device_id = STM_ID_M25P32,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400112 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700113 .page_size = 256,
114 .pages_per_sector = 256,
115 .nr_sectors = 64,
116 .name = "M25P32",
117 },
118 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800119 .device_id = STM_ID_M25P40,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400120 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700121 .page_size = 256,
122 .pages_per_sector = 256,
123 .nr_sectors = 8,
124 .name = "M25P40",
125 },
126 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800127 .device_id = STM_ID_M25P64,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400128 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700129 .page_size = 256,
130 .pages_per_sector = 256,
131 .nr_sectors = 128,
132 .name = "M25P64",
133 },
134 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800135 .device_id = STM_ID_M25P80,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400136 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700137 .page_size = 256,
138 .pages_per_sector = 256,
139 .nr_sectors = 16,
140 .name = "M25P80",
141 },
142 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800143 .device_id = STM_ID_M25P128,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400144 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700145 .page_size = 256,
146 .pages_per_sector = 1024,
147 .nr_sectors = 64,
148 .name = "M25P128",
149 },
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800150 {
David Imhoff61295b52015-05-03 16:02:56 +0200151 .device_id = STM_ID_N25Q064,
152 .op_erase = CMD_M25PXX_SSE,
153 .page_size = 256,
154 .pages_per_sector = 16,
155 .nr_sectors = 2048,
156 .name = "N25Q064",
157 },
158 {
David Imhoff72495722015-05-03 14:06:21 +0200159 .device_id = STM_ID_N25Q128,
160 .op_erase = CMD_M25PXX_SSE,
161 .page_size = 256,
162 .pages_per_sector = 16,
163 .nr_sectors = 4096,
164 .name = "N25Q128",
165 },
166 {
David Imhofffd1f5142015-05-03 16:57:38 +0200167 .device_id = STM_ID_N25Q256,
David Imhoffa42edc302015-05-03 16:05:41 +0200168 .op_erase = CMD_M25PXX_SSE,
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800169 .page_size = 256,
David Imhoffa42edc302015-05-03 16:05:41 +0200170 .pages_per_sector = 16,
171 .nr_sectors = 8192,
David Imhofffd1f5142015-05-03 16:57:38 +0200172 .name = "N25Q256",
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800173 },
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700174};
175
176static int stmicro_write(struct spi_flash *flash,
177 u32 offset, size_t len, const void *buf)
178{
179 struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700180 unsigned long byte_addr;
181 unsigned long page_size;
182 size_t chunk_len;
183 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -0700184 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700185 u8 cmd[4];
186
Kyösti Mälkki77d12802014-06-29 16:15:39 +0300187 page_size = stm->params->page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700188 byte_addr = offset % page_size;
189
Martin Roth3316cf22012-12-05 16:22:54 -0700190 flash->spi->rw = SPI_WRITE_FLAG;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700191
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700192 for (actual = 0; actual < len; actual += chunk_len) {
193 chunk_len = min(len - actual, page_size - byte_addr);
Kyösti Mälkki11104952014-06-29 16:17:33 +0300194 chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700195
196 cmd[0] = CMD_M25PXX_PP;
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300197 cmd[1] = (offset >> 16) & 0xff;
198 cmd[2] = (offset >> 8) & 0xff;
199 cmd[3] = offset & 0xff;
Stefan Reinauer5649b082012-05-23 11:03:29 -0700200#if CONFIG_DEBUG_SPI_FLASH
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700201 printk(BIOS_SPEW, "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x }"
202 " chunk_len = %zu\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700203 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
Stefan Reinauer5649b082012-05-23 11:03:29 -0700204#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700205
206 ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0);
207 if (ret < 0) {
208 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300209 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700210 }
211
Kyösti Mälkki11104952014-06-29 16:17:33 +0300212 ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd),
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700213 buf + actual, chunk_len);
214 if (ret < 0) {
215 printk(BIOS_WARNING, "SF: STMicro Page Program failed\n");
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300216 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700217 }
218
219 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
220 if (ret)
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300221 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700222
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300223 offset += chunk_len;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700224 byte_addr = 0;
225 }
226
Marc Jones747127d2012-12-03 22:16:29 -0700227#if CONFIG_DEBUG_SPI_FLASH
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300228 printk(BIOS_SPEW, "SF: STMicro: Successfully programmed %zu bytes @"
229 " 0x%lx\n", len, (unsigned long)(offset - len));
Marc Jones747127d2012-12-03 22:16:29 -0700230#endif
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300231 ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700232
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300233out:
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700234 spi_release_bus(flash->spi);
235 return ret;
236}
237
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800238static struct stmicro_spi_flash stm;
239
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700240struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
241{
242 const struct stmicro_spi_flash_params *params;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700243 unsigned int i;
244
245 if (idcode[0] == 0xff) {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800246 i = spi_flash_cmd(spi, CMD_M25PXX_RES, idcode, 4);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700247 if (i)
248 return NULL;
249 if ((idcode[3] & 0xf0) == 0x10) {
250 idcode[0] = 0x20;
251 idcode[1] = 0x20;
252 idcode[2] = idcode[3] + 1;
253 } else
254 return NULL;
255 }
256
257 for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {
258 params = &stmicro_spi_flash_table[i];
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800259 if (params->device_id == (idcode[1] << 8 | idcode[2])) {
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700260 break;
261 }
262 }
263
264 if (i == ARRAY_SIZE(stmicro_spi_flash_table)) {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800265 printk(BIOS_WARNING, "SF: Unsupported STMicro ID %02x%02x\n",
266 idcode[1], idcode[2]);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700267 return NULL;
268 }
269
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800270 stm.params = params;
271 stm.flash.spi = spi;
272 stm.flash.name = params->name;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700273
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800274 stm.flash.write = stmicro_write;
275 stm.flash.erase = spi_flash_cmd_erase;
276 stm.flash.read = spi_flash_cmd_read_fast;
277 stm.flash.sector_size = params->page_size * params->pages_per_sector;
278 stm.flash.size = stm.flash.sector_size * params->nr_sectors;
279 stm.flash.erase_cmd = params->op_erase;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700280
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800281 return &stm.flash;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700282}