blob: 09cb80cb0e474cdaab2a5520fab87fc6ba261194 [file] [log] [blame]
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -07001/*
2 * Copyright 2009(C) Marvell International Ltd. and its affiliates
3 * Prafulla Wadaskar <prafulla@marvell.com>
4 *
5 * Based on drivers/mtd/spi/stmicro.c
6 *
7 * Copyright 2008, Network Appliance Inc.
8 * Jason McMullan <mcmullan@netapp.com>
9 *
10 * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
11 * TsiChung Liew (Tsi-Chung.Liew@freescale.com)
12 *
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070013 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of
16 * the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Paul Menzela8ae1c62013-02-20 13:21:20 +010020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070021 * GNU General Public License for more details.
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070022 */
23
24#include <stdlib.h>
25#include <spi_flash.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100026
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070027#include "spi_flash_internal.h"
28
29/* MX25xx-specific commands */
30#define CMD_MX25XX_WREN 0x06 /* Write Enable */
31#define CMD_MX25XX_WRDI 0x04 /* Write Disable */
32#define CMD_MX25XX_RDSR 0x05 /* Read Status Register */
33#define CMD_MX25XX_WRSR 0x01 /* Write Status Register */
34#define CMD_MX25XX_READ 0x03 /* Read Data Bytes */
35#define CMD_MX25XX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
36#define CMD_MX25XX_PP 0x02 /* Page Program */
37#define CMD_MX25XX_SE 0x20 /* Sector Erase */
38#define CMD_MX25XX_BE 0xD8 /* Block Erase */
39#define CMD_MX25XX_CE 0xc7 /* Chip Erase */
40#define CMD_MX25XX_DP 0xb9 /* Deep Power-down */
41#define CMD_MX25XX_RES 0xab /* Release from DP, and Read Signature */
42
43#define MACRONIX_SR_WIP (1 << 0) /* Write-in-Progress */
44
45struct macronix_spi_flash_params {
46 u16 idcode;
47 u16 page_size;
48 u16 pages_per_sector;
49 u16 sectors_per_block;
50 u16 nr_blocks;
51 const char *name;
52};
53
54struct macronix_spi_flash {
55 struct spi_flash flash;
56 const struct macronix_spi_flash_params *params;
57};
58
59static inline struct macronix_spi_flash *to_macronix_spi_flash(struct spi_flash
60 *flash)
61{
62 return container_of(flash, struct macronix_spi_flash, flash);
63}
64
65static const struct macronix_spi_flash_params macronix_spi_flash_table[] = {
66 {
67 .idcode = 0x2015,
68 .page_size = 256,
69 .pages_per_sector = 16,
70 .sectors_per_block = 16,
71 .nr_blocks = 32,
72 .name = "MX25L1605D",
73 },
74 {
75 .idcode = 0x2016,
76 .page_size = 256,
77 .pages_per_sector = 16,
78 .sectors_per_block = 16,
79 .nr_blocks = 64,
80 .name = "MX25L3205D",
81 },
82 {
Zheng Baoc269a9b2012-12-06 11:30:33 +080083 .idcode = 0x5e16,
84 .page_size = 256,
85 .pages_per_sector = 16,
86 .sectors_per_block = 16,
87 .nr_blocks = 64,
Dave Frodin01856062014-11-24 10:37:29 -070088 .name = "MX25L3235D", /* MX25L3225D/MX25L3235D/MX25L3236D/MX25L3237D */
89 },
90 {
91 .idcode = 0x2536,
92 .page_size = 256,
93 .pages_per_sector = 16,
94 .sectors_per_block = 16,
95 .nr_blocks = 64,
96 .name = "MX25L3239E",
Zheng Baoc269a9b2012-12-06 11:30:33 +080097 },
98 {
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -070099 .idcode = 0x2017,
100 .page_size = 256,
101 .pages_per_sector = 16,
102 .sectors_per_block = 16,
103 .nr_blocks = 128,
104 .name = "MX25L6405D",
105 },
106 {
107 .idcode = 0x2018,
108 .page_size = 256,
109 .pages_per_sector = 16,
110 .sectors_per_block = 16,
111 .nr_blocks = 256,
112 .name = "MX25L12805D",
113 },
114 {
115 .idcode = 0x2618,
116 .page_size = 256,
117 .pages_per_sector = 16,
118 .sectors_per_block = 16,
119 .nr_blocks = 256,
120 .name = "MX25L12855E",
121 },
Idwer Vollering06413ff2014-10-26 01:20:20 +0200122 {
123 .idcode = 0x2537,
124 .page_size = 256,
125 .pages_per_sector = 16,
126 .sectors_per_block = 16,
127 .nr_blocks = 128,
128 .name = "MX25U6435F",
129 },
130 {
Kyösti Mälkki3f382c72014-11-11 15:01:31 +0200131 .idcode = 0x2538,
132 .page_size = 256,
133 .pages_per_sector = 16,
134 .sectors_per_block = 16,
135 .nr_blocks = 256,
136 .name = "MX25U12835F",
137 },
138 {
Idwer Vollering06413ff2014-10-26 01:20:20 +0200139 .idcode = 0x9517,
140 .page_size = 256,
141 .pages_per_sector = 16,
142 .sectors_per_block = 16,
143 .nr_blocks = 128,
144 .name = "MX25L6495F",
145 },
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700146};
147
148static int macronix_write(struct spi_flash *flash,
149 u32 offset, size_t len, const void *buf)
150{
151 struct macronix_spi_flash *mcx = to_macronix_spi_flash(flash);
152 unsigned long byte_addr;
153 unsigned long page_size;
154 size_t chunk_len;
155 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -0700156 int ret = 0;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700157 u8 cmd[4];
158
Kyösti Mälkki77d12802014-06-29 16:15:39 +0300159 page_size = mcx->params->page_size;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700160 byte_addr = offset % page_size;
161
Martin Roth3316cf22012-12-05 16:22:54 -0700162 flash->spi->rw = SPI_WRITE_FLAG;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700163
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700164 for (actual = 0; actual < len; actual += chunk_len) {
165 chunk_len = min(len - actual, page_size - byte_addr);
Kyösti Mälkki11104952014-06-29 16:17:33 +0300166 chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700167
168 cmd[0] = CMD_MX25XX_PP;
169 cmd[1] = (offset >> 16) & 0xff;
170 cmd[2] = (offset >> 8) & 0xff;
171 cmd[3] = offset & 0xff;
Stefan Reinauer5649b082012-05-23 11:03:29 -0700172#if CONFIG_DEBUG_SPI_FLASH
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700173 printk(BIOS_SPEW, "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x }"
174 " chunk_len = %zu\n",
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700175 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
Stefan Reinauer5649b082012-05-23 11:03:29 -0700176#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700177
178 ret = spi_flash_cmd(flash->spi, CMD_MX25XX_WREN, NULL, 0);
179 if (ret < 0) {
180 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
181 break;
182 }
183
Kyösti Mälkki11104952014-06-29 16:17:33 +0300184 ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd),
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700185 buf + actual, chunk_len);
186 if (ret < 0) {
187 printk(BIOS_WARNING, "SF: Macronix Page Program failed\n");
188 break;
189 }
190
191 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
192 if (ret)
193 break;
194
195 offset += chunk_len;
196 byte_addr = 0;
197 }
198
Marc Jones747127d2012-12-03 22:16:29 -0700199#if CONFIG_DEBUG_SPI_FLASH
200 printk(BIOS_SPEW, "SF: Macronix: Successfully programmed %zu bytes @"
Stefan Reinauer8ea5a342012-05-14 13:52:32 -0700201 " 0x%lx\n", len, (unsigned long)(offset - len));
Marc Jones747127d2012-12-03 22:16:29 -0700202#endif
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700203
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700204 return ret;
205}
206
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800207static struct macronix_spi_flash mcx;
208
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700209struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode)
210{
211 const struct macronix_spi_flash_params *params;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700212 unsigned int i;
213 u16 id = idcode[2] | idcode[1] << 8;
214
215 for (i = 0; i < ARRAY_SIZE(macronix_spi_flash_table); i++) {
216 params = &macronix_spi_flash_table[i];
217 if (params->idcode == id)
218 break;
219 }
220
221 if (i == ARRAY_SIZE(macronix_spi_flash_table)) {
222 printk(BIOS_WARNING, "SF: Unsupported Macronix ID %04x\n", id);
223 return NULL;
224 }
225
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800226 mcx.params = params;
227 mcx.flash.spi = spi;
228 mcx.flash.name = params->name;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700229
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800230 mcx.flash.write = macronix_write;
231 mcx.flash.erase = spi_flash_cmd_erase;
232 mcx.flash.status = spi_flash_cmd_status;
Duncan Laurie23b00532012-10-10 14:21:23 -0700233#if CONFIG_SPI_FLASH_NO_FAST_READ
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800234 mcx.flash.read = spi_flash_cmd_read_slow;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700235#else
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800236 mcx.flash.read = spi_flash_cmd_read_fast;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700237#endif
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800238 mcx.flash.sector_size = params->page_size * params->pages_per_sector;
239 mcx.flash.size = mcx.flash.sector_size * params->sectors_per_block *
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700240 params->nr_blocks;
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800241 mcx.flash.erase_cmd = CMD_MX25XX_SE;
242 mcx.flash.status_cmd = CMD_MX25XX_RDSR;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700243
Vadim Bendeburye5fd1c92015-02-11 19:13:22 -0800244 return &mcx.flash;
Stefan Reinauer1c56d9b2012-05-10 11:27:32 -0700245}