blob: 3a4e6a49318054289752fb53b5b8184ec7df008a [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 *
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070011 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation; either version 2 of
14 * the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Paul Menzela8ae1c62013-02-20 13:21:20 +010018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070019 * GNU General Public License for more details.
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070020 */
21
22#include <stdlib.h>
23#include <spi_flash.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100024
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070025#include "spi_flash_internal.h"
26
27/* M25Pxx-specific commands */
28#define CMD_M25PXX_WREN 0x06 /* Write Enable */
29#define CMD_M25PXX_WRDI 0x04 /* Write Disable */
30#define CMD_M25PXX_RDSR 0x05 /* Read Status Register */
31#define CMD_M25PXX_WRSR 0x01 /* Write Status Register */
32#define CMD_M25PXX_READ 0x03 /* Read Data Bytes */
33#define CMD_M25PXX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
34#define CMD_M25PXX_PP 0x02 /* Page Program */
Scott Radcliffef5fcedf2014-10-14 15:40:59 -040035#define CMD_M25PXX_SSE 0x20 /* Subsector Erase */
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070036#define CMD_M25PXX_SE 0xd8 /* Sector Erase */
37#define CMD_M25PXX_BE 0xc7 /* Bulk Erase */
38#define CMD_M25PXX_DP 0xb9 /* Deep Power-down */
39#define CMD_M25PXX_RES 0xab /* Release from DP, and Read Signature */
40
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080041/*
42 * Device ID = (memory_type << 8) + memory_capacity
43 */
44#define STM_ID_M25P10 0x2011
45#define STM_ID_M25P16 0x2015
46#define STM_ID_M25P20 0x2012
47#define STM_ID_M25P32 0x2016
48#define STM_ID_M25P40 0x2013
49#define STM_ID_M25P64 0x2017
50#define STM_ID_M25P80 0x2014
51#define STM_ID_M25P128 0x2018
David Imhofffd1f5142015-05-03 16:57:38 +020052#define STM_ID_N25Q256 0xba19
David Imhoff61295b52015-05-03 16:02:56 +020053#define STM_ID_N25Q064 0xbb17
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080054#define STM_ID_N25Q128 0xbb18
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070055
56struct stmicro_spi_flash_params {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080057 u16 device_id;
Scott Radcliffef5fcedf2014-10-14 15:40:59 -040058 u8 op_erase;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070059 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 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080079 .device_id = STM_ID_M25P10,
Scott Radcliffef5fcedf2014-10-14 15:40:59 -040080 .op_erase = CMD_M25PXX_SE,
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070081 .page_size = 256,
82 .pages_per_sector = 128,
83 .nr_sectors = 4,
84 .name = "M25P10",
85 },
86 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080087 .device_id = STM_ID_M25P16,
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 = 256,
91 .nr_sectors = 32,
92 .name = "M25P16",
93 },
94 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -080095 .device_id = STM_ID_M25P20,
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 = 4,
100 .name = "M25P20",
101 },
102 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800103 .device_id = STM_ID_M25P32,
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 = 64,
108 .name = "M25P32",
109 },
110 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800111 .device_id = STM_ID_M25P40,
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 = 8,
116 .name = "M25P40",
117 },
118 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800119 .device_id = STM_ID_M25P64,
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 = 128,
124 .name = "M25P64",
125 },
126 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800127 .device_id = STM_ID_M25P80,
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 = 16,
132 .name = "M25P80",
133 },
134 {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800135 .device_id = STM_ID_M25P128,
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 = 1024,
139 .nr_sectors = 64,
140 .name = "M25P128",
141 },
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800142 {
David Imhoff61295b52015-05-03 16:02:56 +0200143 .device_id = STM_ID_N25Q064,
144 .op_erase = CMD_M25PXX_SSE,
145 .page_size = 256,
146 .pages_per_sector = 16,
147 .nr_sectors = 2048,
148 .name = "N25Q064",
149 },
150 {
David Imhoff72495722015-05-03 14:06:21 +0200151 .device_id = STM_ID_N25Q128,
152 .op_erase = CMD_M25PXX_SSE,
153 .page_size = 256,
154 .pages_per_sector = 16,
155 .nr_sectors = 4096,
156 .name = "N25Q128",
157 },
158 {
David Imhofffd1f5142015-05-03 16:57:38 +0200159 .device_id = STM_ID_N25Q256,
David Imhoffa42edc302015-05-03 16:05:41 +0200160 .op_erase = CMD_M25PXX_SSE,
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800161 .page_size = 256,
David Imhoffa42edc302015-05-03 16:05:41 +0200162 .pages_per_sector = 16,
163 .nr_sectors = 8192,
David Imhofffd1f5142015-05-03 16:57:38 +0200164 .name = "N25Q256",
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800165 },
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700166};
167
168static int stmicro_write(struct spi_flash *flash,
169 u32 offset, size_t len, const void *buf)
170{
171 struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700172 unsigned long byte_addr;
173 unsigned long page_size;
174 size_t chunk_len;
175 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -0700176 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700177 u8 cmd[4];
178
Kyösti Mälkki77d12802014-06-29 16:15:39 +0300179 page_size = stm->params->page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700180 byte_addr = offset % page_size;
181
Martin Roth3316cf22012-12-05 16:22:54 -0700182 flash->spi->rw = SPI_WRITE_FLAG;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700183
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700184 for (actual = 0; actual < len; actual += chunk_len) {
185 chunk_len = min(len - actual, page_size - byte_addr);
Kyösti Mälkki11104952014-06-29 16:17:33 +0300186 chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700187
188 cmd[0] = CMD_M25PXX_PP;
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300189 cmd[1] = (offset >> 16) & 0xff;
190 cmd[2] = (offset >> 8) & 0xff;
191 cmd[3] = offset & 0xff;
Stefan Reinauer5649b082012-05-23 11:03:29 -0700192#if CONFIG_DEBUG_SPI_FLASH
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700193 printk(BIOS_SPEW, "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x }"
194 " chunk_len = %zu\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700195 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
Stefan Reinauer5649b082012-05-23 11:03:29 -0700196#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700197
198 ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0);
199 if (ret < 0) {
200 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300201 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700202 }
203
Kyösti Mälkki11104952014-06-29 16:17:33 +0300204 ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd),
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700205 buf + actual, chunk_len);
206 if (ret < 0) {
207 printk(BIOS_WARNING, "SF: STMicro Page Program failed\n");
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300208 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700209 }
210
211 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
212 if (ret)
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300213 goto out;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700214
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300215 offset += chunk_len;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700216 byte_addr = 0;
217 }
218
Marc Jones747127d2012-12-03 22:16:29 -0700219#if CONFIG_DEBUG_SPI_FLASH
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300220 printk(BIOS_SPEW, "SF: STMicro: Successfully programmed %zu bytes @"
221 " 0x%lx\n", len, (unsigned long)(offset - len));
Marc Jones747127d2012-12-03 22:16:29 -0700222#endif
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300223 ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700224
Kyösti Mälkki1cf85772013-08-18 12:44:24 +0300225out:
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700226 spi_release_bus(flash->spi);
227 return ret;
228}
229
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800230static struct stmicro_spi_flash stm;
231
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700232struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
233{
234 const struct stmicro_spi_flash_params *params;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700235 unsigned int i;
236
237 if (idcode[0] == 0xff) {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800238 i = spi_flash_cmd(spi, CMD_M25PXX_RES, idcode, 4);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700239 if (i)
240 return NULL;
241 if ((idcode[3] & 0xf0) == 0x10) {
242 idcode[0] = 0x20;
243 idcode[1] = 0x20;
244 idcode[2] = idcode[3] + 1;
245 } else
246 return NULL;
247 }
248
249 for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {
250 params = &stmicro_spi_flash_table[i];
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800251 if (params->device_id == (idcode[1] << 8 | idcode[2])) {
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700252 break;
253 }
254 }
255
256 if (i == ARRAY_SIZE(stmicro_spi_flash_table)) {
Daisuke Nojirif0d038f2015-02-17 13:35:20 -0800257 printk(BIOS_WARNING, "SF: Unsupported STMicro ID %02x%02x\n",
258 idcode[1], idcode[2]);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700259 return NULL;
260 }
261
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800262 stm.params = params;
263 stm.flash.spi = spi;
264 stm.flash.name = params->name;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700265
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800266 stm.flash.write = stmicro_write;
267 stm.flash.erase = spi_flash_cmd_erase;
268 stm.flash.read = spi_flash_cmd_read_fast;
269 stm.flash.sector_size = params->page_size * params->pages_per_sector;
270 stm.flash.size = stm.flash.sector_size * params->nr_sectors;
271 stm.flash.erase_cmd = params->op_erase;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700272
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800273 return &stm.flash;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700274}