blob: b67c07257fedec8a15b874d1e12127077746b3a9 [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
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 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800167 .device_id = STM_ID_N25Q256A,
168 .page_size = 256,
169 .pages_per_sector = 256,
170 .nr_sectors = 512,
171 .name = "N25Q256A",
172 },
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700173};
174
175static int stmicro_write(struct spi_flash *flash,
176 u32 offset, size_t len, const void *buf)
177{
178 struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700179 unsigned long byte_addr;
180 unsigned long page_size;
181 size_t chunk_len;
182 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -0700183 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700184 u8 cmd[4];
185
Kyösti Mälkki77d12802014-06-29 16:15:39 +0300186 page_size = stm->params->page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700187 byte_addr = offset % page_size;
188
Martin Roth3316cf22012-12-05 16:22:54 -0700189 flash->spi->rw = SPI_WRITE_FLAG;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700190
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700191 for (actual = 0; actual < len; actual += chunk_len) {
192 chunk_len = min(len - actual, page_size - byte_addr);
Kyösti Mälkki11104952014-06-29 16:17:33 +0300193 chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700194
195 cmd[0] = CMD_M25PXX_PP;
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300196 cmd[1] = (offset >> 16) & 0xff;
197 cmd[2] = (offset >> 8) & 0xff;
198 cmd[3] = offset & 0xff;
Stefan Reinauer5649b082012-05-23 11:03:29 -0700199#if CONFIG_DEBUG_SPI_FLASH
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700200 printk(BIOS_SPEW, "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x }"
201 " chunk_len = %zu\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700202 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
Stefan Reinauer5649b082012-05-23 11:03:29 -0700203#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700204
205 ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0);
206 if (ret < 0) {
207 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300208 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700209 }
210
Kyösti Mälkki11104952014-06-29 16:17:33 +0300211 ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd),
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700212 buf + actual, chunk_len);
213 if (ret < 0) {
214 printk(BIOS_WARNING, "SF: STMicro Page Program failed\n");
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300215 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700216 }
217
218 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
219 if (ret)
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300220 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700221
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300222 offset += chunk_len;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700223 byte_addr = 0;
224 }
225
Marc Jones747127d2012-12-03 22:16:29 -0700226#if CONFIG_DEBUG_SPI_FLASH
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300227 printk(BIOS_SPEW, "SF: STMicro: Successfully programmed %zu bytes @"
228 " 0x%lx\n", len, (unsigned long)(offset - len));
Marc Jones747127d2012-12-03 22:16:29 -0700229#endif
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300230 ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700231
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300232out:
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700233 spi_release_bus(flash->spi);
234 return ret;
235}
236
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800237static struct stmicro_spi_flash stm;
238
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700239struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
240{
241 const struct stmicro_spi_flash_params *params;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700242 unsigned int i;
243
244 if (idcode[0] == 0xff) {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800245 i = spi_flash_cmd(spi, CMD_M25PXX_RES, idcode, 4);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700246 if (i)
247 return NULL;
248 if ((idcode[3] & 0xf0) == 0x10) {
249 idcode[0] = 0x20;
250 idcode[1] = 0x20;
251 idcode[2] = idcode[3] + 1;
252 } else
253 return NULL;
254 }
255
256 for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {
257 params = &stmicro_spi_flash_table[i];
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800258 if (params->device_id == (idcode[1] << 8 | idcode[2])) {
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700259 break;
260 }
261 }
262
263 if (i == ARRAY_SIZE(stmicro_spi_flash_table)) {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800264 printk(BIOS_WARNING, "SF: Unsupported STMicro ID %02x%02x\n",
265 idcode[1], idcode[2]);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700266 return NULL;
267 }
268
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800269 stm.params = params;
270 stm.flash.spi = spi;
271 stm.flash.name = params->name;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700272
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800273 stm.flash.write = stmicro_write;
274 stm.flash.erase = spi_flash_cmd_erase;
275 stm.flash.read = spi_flash_cmd_read_fast;
276 stm.flash.sector_size = params->page_size * params->pages_per_sector;
277 stm.flash.size = stm.flash.sector_size * params->nr_sectors;
278 stm.flash.erase_cmd = params->op_erase;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700279
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800280 return &stm.flash;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700281}