blob: ff1ce2f8e6e3624db2d491695e1c6d34baef1b98 [file] [log] [blame]
Martin Rothbceaf7f2012-09-07 15:02:35 -06001/*
2 * Copyright (c) 2012, Google Inc.
3 *
4 * Based on drivers/spi/winbond.c
5 *
6 * Copyright 2008, Network Appliance Inc.
7 * Jason McMullan <mcmullan@netapp.com>
8 *
Martin Rothbceaf7f2012-09-07 15:02:35 -06009 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
Martin Rothbceaf7f2012-09-07 15:02:35 -060018 */
19
Furquan Shaikhc28984d2016-11-20 21:04:00 -080020#include <console/console.h>
Martin Rothbceaf7f2012-09-07 15:02:35 -060021#include <stdlib.h>
22#include <spi_flash.h>
Furquan Shaikhc28984d2016-11-20 21:04:00 -080023#include <spi-generic.h>
Furquan Shaikh810e2cd2016-12-05 20:32:24 -080024#include <string.h>
Edward O'Callaghanc4561e22014-06-26 15:02:40 +100025
Martin Rothbceaf7f2012-09-07 15:02:35 -060026#include "spi_flash_internal.h"
27
28/* GD25Pxx-specific commands */
29#define CMD_GD25_WREN 0x06 /* Write Enable */
30#define CMD_GD25_WRDI 0x04 /* Write Disable */
31#define CMD_GD25_RDSR 0x05 /* Read Status Register */
32#define CMD_GD25_WRSR 0x01 /* Write Status Register */
33#define CMD_GD25_READ 0x03 /* Read Data Bytes */
34#define CMD_GD25_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
35#define CMD_GD25_PP 0x02 /* Page Program */
36#define CMD_GD25_SE 0x20 /* Sector (4K) Erase */
37#define CMD_GD25_BE 0xd8 /* Block (64K) Erase */
38#define CMD_GD25_CE 0xc7 /* Chip Erase */
39#define CMD_GD25_DP 0xb9 /* Deep Power-down */
40#define CMD_GD25_RES 0xab /* Release from DP, and Read Signature */
41
42struct gigadevice_spi_flash_params {
43 uint16_t id;
44 /* Log2 of page size in power-of-two mode */
45 uint8_t l2_page_size;
46 uint16_t pages_per_sector;
47 uint16_t sectors_per_block;
48 uint16_t nr_blocks;
49 const char *name;
50};
51
Martin Rothbceaf7f2012-09-07 15:02:35 -060052static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
53 {
Mike Banon2db6e682019-01-12 19:01:10 +030054 .id = 0x3114,
55 .l2_page_size = 8,
56 .pages_per_sector = 16,
57 .sectors_per_block = 16,
58 .nr_blocks = 16,
59 .name = "GD25T80",
60 },
61 {
Martin Rothbceaf7f2012-09-07 15:02:35 -060062 .id = 0x4014,
63 .l2_page_size = 8,
64 .pages_per_sector = 16,
65 .sectors_per_block = 16,
66 .nr_blocks = 16,
Mike Banon2db6e682019-01-12 19:01:10 +030067 .name = "GD25Q80(B)",
Martin Rothbceaf7f2012-09-07 15:02:35 -060068 },
69 {
70 .id = 0x4015,
71 .l2_page_size = 8,
72 .pages_per_sector = 16,
73 .sectors_per_block = 16,
74 .nr_blocks = 32,
75 .name = "GD25Q16(B)",
76 },
77 {
78 .id = 0x4016,
79 .l2_page_size = 8,
80 .pages_per_sector = 16,
81 .sectors_per_block = 16,
82 .nr_blocks = 64,
83 .name = "GD25Q32(B)",
84 },
85 {
Mike Banon2db6e682019-01-12 19:01:10 +030086 .id = 0x4017,
87 .l2_page_size = 8,
88 .pages_per_sector = 16,
89 .sectors_per_block = 16,
90 .nr_blocks = 128,
91 .name = "GD25Q64(B)/GD25B64C",
92 },
93 {
94 .id = 0x4018,
95 .l2_page_size = 8,
96 .pages_per_sector = 16,
97 .sectors_per_block = 16,
98 .nr_blocks = 256,
99 .name = "GD25Q128(B)",
100 },
101 {
102 .id = 0x4214,
103 .l2_page_size = 8,
104 .pages_per_sector = 16,
105 .sectors_per_block = 16,
106 .nr_blocks = 16,
107 .name = "GD25VQ80C",
108 },
109 {
110 .id = 0x4215,
111 .l2_page_size = 8,
112 .pages_per_sector = 16,
113 .sectors_per_block = 16,
114 .nr_blocks = 32,
115 .name = "GD25VQ16C",
116 },
117 {
118 .id = 0x6014,
119 .l2_page_size = 8,
120 .pages_per_sector = 16,
121 .sectors_per_block = 16,
122 .nr_blocks = 16,
123 .name = "GD25LQ80",
124 },
125 {
126 .id = 0x6015,
127 .l2_page_size = 8,
128 .pages_per_sector = 16,
129 .sectors_per_block = 16,
130 .nr_blocks = 32,
131 .name = "GD25LQ16",
132 },
133 {
Martin Rothbceaf7f2012-09-07 15:02:35 -0600134 .id = 0x6016,
135 .l2_page_size = 8,
136 .pages_per_sector = 16,
137 .sectors_per_block = 16,
138 .nr_blocks = 64,
139 .name = "GD25LQ32",
140 },
141 {
Marc Jones9bab54b2014-04-03 16:37:06 -0600142 .id = 0x6017,
143 .l2_page_size = 8,
144 .pages_per_sector = 16,
145 .sectors_per_block = 16,
146 .nr_blocks = 128,
147 .name = "GD25LQ64C/GD25LB64C",
Martin Rothbceaf7f2012-09-07 15:02:35 -0600148 },
149 {
Mike Banon2db6e682019-01-12 19:01:10 +0300150 .id = 0x6018,
Martin Rothbceaf7f2012-09-07 15:02:35 -0600151 .l2_page_size = 8,
152 .pages_per_sector = 16,
153 .sectors_per_block = 16,
154 .nr_blocks = 256,
Mike Banon2db6e682019-01-12 19:01:10 +0300155 .name = "GD25LQ128",
Martin Rothbceaf7f2012-09-07 15:02:35 -0600156 },
157};
158
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800159static int gigadevice_write(const struct spi_flash *flash, u32 offset,
160 size_t len, const void *buf)
Martin Rothbceaf7f2012-09-07 15:02:35 -0600161{
Martin Rothbceaf7f2012-09-07 15:02:35 -0600162 unsigned long byte_addr;
163 unsigned long page_size;
164 size_t chunk_len;
165 size_t actual;
David Hendricks032c8432014-04-11 19:48:55 -0700166 int ret = 0;
Martin Rothbceaf7f2012-09-07 15:02:35 -0600167 u8 cmd[4];
168
Furquan Shaikhfc1a1232017-05-12 00:19:56 -0700169 page_size = flash->page_size;
Martin Rothbceaf7f2012-09-07 15:02:35 -0600170
Martin Rothbceaf7f2012-09-07 15:02:35 -0600171 for (actual = 0; actual < len; actual += chunk_len) {
Aaron Durbin41f66902016-12-17 13:16:07 -0600172 byte_addr = offset % page_size;
Martin Rothbceaf7f2012-09-07 15:02:35 -0600173 chunk_len = min(len - actual, page_size - byte_addr);
Furquan Shaikhde705fa2017-04-19 19:27:28 -0700174 chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);
Martin Rothbceaf7f2012-09-07 15:02:35 -0600175
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800176 ret = spi_flash_cmd(&flash->spi, CMD_GD25_WREN, NULL, 0);
Martin Rothbceaf7f2012-09-07 15:02:35 -0600177 if (ret < 0) {
178 printk(BIOS_WARNING,
179 "SF gigadevice.c: Enabling Write failed\n");
180 goto out;
181 }
182
183 cmd[0] = CMD_GD25_PP;
184 cmd[1] = (offset >> 16) & 0xff;
185 cmd[2] = (offset >> 8) & 0xff;
186 cmd[3] = offset & 0xff;
Martin Rothe7d0a372017-06-24 14:02:00 -0600187#if IS_ENABLED(CONFIG_DEBUG_SPI_FLASH)
Martin Rothbceaf7f2012-09-07 15:02:35 -0600188 printk(BIOS_SPEW,
Duncan Laurie23b00532012-10-10 14:21:23 -0700189 "PP gigadevice.c: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x }"
Martin Rothbceaf7f2012-09-07 15:02:35 -0600190 " chunk_len = %zu\n", buf + actual,
191 cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
192#endif
193
Furquan Shaikh810e2cd2016-12-05 20:32:24 -0800194 ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
Martin Rothbceaf7f2012-09-07 15:02:35 -0600195 buf + actual, chunk_len);
196 if (ret < 0) {
197 printk(BIOS_WARNING,
198 "SF gigadevice.c: Page Program failed\n");
199 goto out;
200 }
201
202 ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
203 if (ret)
204 goto out;
205
206 offset += chunk_len;
Martin Rothbceaf7f2012-09-07 15:02:35 -0600207 }
208
Martin Rothe7d0a372017-06-24 14:02:00 -0600209#if IS_ENABLED(CONFIG_DEBUG_SPI_FLASH)
Marc Jones747127d2012-12-03 22:16:29 -0700210 printk(BIOS_SPEW,
Martin Rothbceaf7f2012-09-07 15:02:35 -0600211 "SF gigadevice.c: Successfully programmed %zu bytes @ %#x\n",
212 len, (unsigned int)(offset - len));
Marc Jones747127d2012-12-03 22:16:29 -0700213#endif
214
Martin Rothbceaf7f2012-09-07 15:02:35 -0600215 ret = 0;
216
217out:
Martin Rothbceaf7f2012-09-07 15:02:35 -0600218 return ret;
219}
220
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -0700221static const struct spi_flash_ops spi_flash_ops = {
222 .write = gigadevice_write,
223 .erase = spi_flash_cmd_erase,
224 .status = spi_flash_cmd_status,
225#if IS_ENABLED(CONFIG_SPI_FLASH_NO_FAST_READ)
226 .read = spi_flash_cmd_read_slow,
227#else
228 .read = spi_flash_cmd_read_fast,
229#endif
230};
231
Furquan Shaikhbd9e32e2017-05-15 23:28:41 -0700232int spi_flash_probe_gigadevice(const struct spi_slave *spi, u8 *idcode,
Furquan Shaikh30221b42017-05-15 14:35:15 -0700233 struct spi_flash *flash)
Martin Rothbceaf7f2012-09-07 15:02:35 -0600234{
235 const struct gigadevice_spi_flash_params *params;
Martin Rothbceaf7f2012-09-07 15:02:35 -0600236 unsigned int i;
237
238 for (i = 0; i < ARRAY_SIZE(gigadevice_spi_flash_table); i++) {
239 params = &gigadevice_spi_flash_table[i];
240 if (params->id == ((idcode[1] << 8) | idcode[2]))
241 break;
242 }
243
244 if (i == ARRAY_SIZE(gigadevice_spi_flash_table)) {
245 printk(BIOS_WARNING,
246 "SF gigadevice.c: Unsupported ID %#02x%02x\n",
247 idcode[1], idcode[2]);
Furquan Shaikh30221b42017-05-15 14:35:15 -0700248 return -1;
Martin Rothbceaf7f2012-09-07 15:02:35 -0600249 }
250
Furquan Shaikh30221b42017-05-15 14:35:15 -0700251 memcpy(&flash->spi, spi, sizeof(*spi));
252 flash->name = params->name;
Martin Rothbceaf7f2012-09-07 15:02:35 -0600253
254 /* Assuming power-of-two page size initially. */
Furquan Shaikh30221b42017-05-15 14:35:15 -0700255 flash->page_size = 1 << params->l2_page_size;
256 flash->sector_size = flash->page_size * params->pages_per_sector;
257 flash->size = flash->sector_size * params->sectors_per_block *
Furquan Shaikhfc1a1232017-05-12 00:19:56 -0700258 params->nr_blocks;
Furquan Shaikh30221b42017-05-15 14:35:15 -0700259 flash->erase_cmd = CMD_GD25_SE;
260 flash->status_cmd = CMD_GD25_RDSR;
Martin Rothbceaf7f2012-09-07 15:02:35 -0600261
Furquan Shaikhe2fc5e22017-05-17 17:26:01 -0700262 flash->ops = &spi_flash_ops;
Martin Rothbceaf7f2012-09-07 15:02:35 -0600263
Furquan Shaikh30221b42017-05-15 14:35:15 -0700264 return 0;
Martin Rothbceaf7f2012-09-07 15:02:35 -0600265}