blob: 3e35120b38922fa318fa404264554384da89c45f [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
60#define STM_ID_N25Q256A 0xba19
61#define STM_ID_N25Q128 0xbb18
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070062
Scott Radcliffef5fcedf2014-10-14 15:40:59 -040063/* Some SPI flash share the same .idcode1 (idcode[2]). To handle this without
64 * (possibly) breaking existing implementations, add the new device at the top
65 * of the flash table array and set its .idcode1 = STM_ID_USE_ALT_ID. The .id
66 * is then (idcode[1] << 8 | idcode[2]).
67 */
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070068struct stmicro_spi_flash_params {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080069 u16 device_id;
Scott Radcliffef5fcedf2014-10-14 15:40:59 -040070 u8 op_erase;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070071 u16 page_size;
72 u16 pages_per_sector;
73 u16 nr_sectors;
74 const char *name;
75};
76
77/* spi_flash needs to be first so upper layers can free() it */
78struct stmicro_spi_flash {
79 struct spi_flash flash;
80 const struct stmicro_spi_flash_params *params;
81};
82
83static inline struct stmicro_spi_flash *to_stmicro_spi_flash(struct spi_flash
84 *flash)
85{
86 return container_of(flash, struct stmicro_spi_flash, flash);
87}
88
89static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {
90 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080091 .device_id = STM_ID_N25Q128,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -040092 .op_erase = CMD_M25PXX_SSE,
93 .page_size = 256,
94 .pages_per_sector = 16,
95 .nr_sectors = 4096,
96 .name = "N25Q128",
97 },
98 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080099 .device_id = STM_ID_M25P10,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400100 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700101 .page_size = 256,
102 .pages_per_sector = 128,
103 .nr_sectors = 4,
104 .name = "M25P10",
105 },
106 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800107 .device_id = STM_ID_M25P16,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400108 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700109 .page_size = 256,
110 .pages_per_sector = 256,
111 .nr_sectors = 32,
112 .name = "M25P16",
113 },
114 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800115 .device_id = STM_ID_M25P20,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400116 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700117 .page_size = 256,
118 .pages_per_sector = 256,
119 .nr_sectors = 4,
120 .name = "M25P20",
121 },
122 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800123 .device_id = STM_ID_M25P32,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400124 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700125 .page_size = 256,
126 .pages_per_sector = 256,
127 .nr_sectors = 64,
128 .name = "M25P32",
129 },
130 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800131 .device_id = STM_ID_M25P40,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400132 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700133 .page_size = 256,
134 .pages_per_sector = 256,
135 .nr_sectors = 8,
136 .name = "M25P40",
137 },
138 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800139 .device_id = STM_ID_M25P64,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400140 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700141 .page_size = 256,
142 .pages_per_sector = 256,
143 .nr_sectors = 128,
144 .name = "M25P64",
145 },
146 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800147 .device_id = STM_ID_M25P80,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400148 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700149 .page_size = 256,
150 .pages_per_sector = 256,
151 .nr_sectors = 16,
152 .name = "M25P80",
153 },
154 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800155 .device_id = STM_ID_M25P128,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -0400156 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700157 .page_size = 256,
158 .pages_per_sector = 1024,
159 .nr_sectors = 64,
160 .name = "M25P128",
161 },
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800162 {
163 .device_id = STM_ID_N25Q256A,
164 .page_size = 256,
165 .pages_per_sector = 256,
166 .nr_sectors = 512,
167 .name = "N25Q256A",
168 },
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700169};
170
171static int stmicro_write(struct spi_flash *flash,
172 u32 offset, size_t len, const void *buf)
173{
174 struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700175 unsigned long byte_addr;
176 unsigned long page_size;
177 size_t chunk_len;
178 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -0700179 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700180 u8 cmd[4];
181
Kyösti Mälkki77d12802014-06-29 16:15:39 +0300182 page_size = stm->params->page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700183 byte_addr = offset % page_size;
184
Martin Roth3316cf22012-12-05 16:22:54 -0700185 flash->spi->rw = SPI_WRITE_FLAG;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700186
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700187 for (actual = 0; actual < len; actual += chunk_len) {
188 chunk_len = min(len - actual, page_size - byte_addr);
Kyösti Mälkki11104952014-06-29 16:17:33 +0300189 chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700190
191 cmd[0] = CMD_M25PXX_PP;
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300192 cmd[1] = (offset >> 16) & 0xff;
193 cmd[2] = (offset >> 8) & 0xff;
194 cmd[3] = offset & 0xff;
Stefan Reinauer5649b082012-05-23 11:03:29 -0700195#if CONFIG_DEBUG_SPI_FLASH
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700196 printk(BIOS_SPEW, "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x }"
197 " chunk_len = %zu\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700198 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
Stefan Reinauer5649b082012-05-23 11:03:29 -0700199#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700200
201 ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0);
202 if (ret < 0) {
203 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300204 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700205 }
206
Kyösti Mälkki11104952014-06-29 16:17:33 +0300207 ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd),
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700208 buf + actual, chunk_len);
209 if (ret < 0) {
210 printk(BIOS_WARNING, "SF: STMicro Page Program failed\n");
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300211 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700212 }
213
214 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
215 if (ret)
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300216 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700217
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300218 offset += chunk_len;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700219 byte_addr = 0;
220 }
221
Marc Jones747127d2012-12-03 22:16:29 -0700222#if CONFIG_DEBUG_SPI_FLASH
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300223 printk(BIOS_SPEW, "SF: STMicro: Successfully programmed %zu bytes @"
224 " 0x%lx\n", len, (unsigned long)(offset - len));
Marc Jones747127d2012-12-03 22:16:29 -0700225#endif
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300226 ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700227
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300228out:
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700229 spi_release_bus(flash->spi);
230 return ret;
231}
232
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800233static struct stmicro_spi_flash stm;
234
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700235struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
236{
237 const struct stmicro_spi_flash_params *params;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700238 unsigned int i;
239
240 if (idcode[0] == 0xff) {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800241 i = spi_flash_cmd(spi, CMD_M25PXX_RES, idcode, 4);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700242 if (i)
243 return NULL;
244 if ((idcode[3] & 0xf0) == 0x10) {
245 idcode[0] = 0x20;
246 idcode[1] = 0x20;
247 idcode[2] = idcode[3] + 1;
248 } else
249 return NULL;
250 }
251
252 for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {
253 params = &stmicro_spi_flash_table[i];
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800254 if (params->device_id == (idcode[1] << 8 | idcode[2])) {
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700255 break;
256 }
257 }
258
259 if (i == ARRAY_SIZE(stmicro_spi_flash_table)) {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800260 printk(BIOS_WARNING, "SF: Unsupported STMicro ID %02x%02x\n",
261 idcode[1], idcode[2]);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700262 return NULL;
263 }
264
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800265 stm.params = params;
266 stm.flash.spi = spi;
267 stm.flash.name = params->name;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700268
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800269 stm.flash.write = stmicro_write;
270 stm.flash.erase = spi_flash_cmd_erase;
271 stm.flash.read = spi_flash_cmd_read_fast;
272 stm.flash.sector_size = params->page_size * params->pages_per_sector;
273 stm.flash.size = stm.flash.sector_size * params->nr_sectors;
274 stm.flash.erase_cmd = params->op_erase;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700275
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800276 return &stm.flash;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700277}