{drivers,southbridge}: Replace min() with MIN()
[coreboot.git] / src / drivers / spi / eon.c
blob2cdbdbbe8d667508095d09c7c3ed3ae3a9ca67dd
1 /*
2 * This file is part of the coreboot project.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include <console/console.h>
16 #include <commonlib/helpers.h>
17 #include <spi_flash.h>
18 #include <spi-generic.h>
19 #include <string.h>
21 #include "spi_flash_internal.h"
23 /* EN25*-specific commands */
24 #define CMD_EN25_WREN 0x06 /* Write Enable */
25 #define CMD_EN25_WRDI 0x04 /* Write Disable */
26 #define CMD_EN25_RDSR 0x05 /* Read Status Register */
27 #define CMD_EN25_WRSR 0x01 /* Write Status Register */
28 #define CMD_EN25_READ 0x03 /* Read Data Bytes */
29 #define CMD_EN25_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
30 #define CMD_EN25_PP 0x02 /* Page Program */
31 #define CMD_EN25_SE 0x20 /* Sector Erase */
32 #define CMD_EN25_BE 0xd8 /* Block Erase */
33 #define CMD_EN25_DP 0xb9 /* Deep Power-down */
34 #define CMD_EN25_RES 0xab /* Release from DP, and Read Signature */
36 #define EON_ID_EN25B80 0x2014
37 #define EON_ID_EN25B16 0x2015
38 #define EON_ID_EN25B32 0x2016
39 #define EON_ID_EN25B64 0x2017
40 #define EON_ID_EN25F80 0x3114
41 #define EON_ID_EN25F16 0x3115
42 #define EON_ID_EN25F32 0x3116
43 #define EON_ID_EN25F64 0x3117
44 #define EON_ID_EN25Q80 0x3014
45 #define EON_ID_EN25Q16 0x3015 /* Same as EN25D16 */
46 #define EON_ID_EN25Q32 0x3016 /* Same as EN25Q32A and EN25Q32B */
47 #define EON_ID_EN25Q64 0x3017
48 #define EON_ID_EN25Q128 0x3018
49 #define EON_ID_EN25QH16 0x7015
50 #define EON_ID_EN25QH32 0x7016
51 #define EON_ID_EN25QH64 0x7017
52 #define EON_ID_EN25QH128 0x7018
53 #define EON_ID_EN25S80 0x3814
54 #define EON_ID_EN25S16 0x3815
55 #define EON_ID_EN25S32 0x3816
56 #define EON_ID_EN25S64 0x3817
58 struct eon_spi_flash_params {
59 u16 id;
60 u16 page_size;
61 u16 pages_per_sector;
62 u16 sectors_per_block;
63 u16 nr_sectors;
64 const char *name;
67 static const struct eon_spi_flash_params eon_spi_flash_table[] = {
69 .id = EON_ID_EN25B80,
70 .page_size = 256,
71 .pages_per_sector = 16,
72 .sectors_per_block = 16,
73 .nr_sectors = 256,
74 .name = "EN25B80",
77 .id = EON_ID_EN25B16,
78 .page_size = 256,
79 .pages_per_sector = 16,
80 .sectors_per_block = 16,
81 .nr_sectors = 512,
82 .name = "EN25B16",
85 .id = EON_ID_EN25B32,
86 .page_size = 256,
87 .pages_per_sector = 16,
88 .sectors_per_block = 16,
89 .nr_sectors = 1024,
90 .name = "EN25B32",
93 .id = EON_ID_EN25B64,
94 .page_size = 256,
95 .pages_per_sector = 16,
96 .sectors_per_block = 16,
97 .nr_sectors = 2048,
98 .name = "EN25B64",
101 .id = EON_ID_EN25F80,
102 .page_size = 256,
103 .pages_per_sector = 16,
104 .sectors_per_block = 16,
105 .nr_sectors = 256,
106 .name = "EN25F80",
109 .id = EON_ID_EN25F16,
110 .page_size = 256,
111 .pages_per_sector = 16,
112 .sectors_per_block = 16,
113 .nr_sectors = 512,
114 .name = "EN25F16",
117 .id = EON_ID_EN25F32,
118 .page_size = 256,
119 .pages_per_sector = 16,
120 .sectors_per_block = 16,
121 .nr_sectors = 1024,
122 .name = "EN25F32",
125 .id = EON_ID_EN25F64,
126 .page_size = 256,
127 .pages_per_sector = 16,
128 .sectors_per_block = 16,
129 .nr_sectors = 2048,
130 .name = "EN25F64",
133 .id = EON_ID_EN25Q80,
134 .page_size = 256,
135 .pages_per_sector = 16,
136 .sectors_per_block = 16,
137 .nr_sectors = 256,
138 .name = "EN25Q80(A)",
141 .id = EON_ID_EN25Q16,
142 .page_size = 256,
143 .pages_per_sector = 16,
144 .sectors_per_block = 16,
145 .nr_sectors = 512,
146 .name = "EN25Q16(D16)",
149 .id = EON_ID_EN25Q32,
150 .page_size = 256,
151 .pages_per_sector = 16,
152 .sectors_per_block = 16,
153 .nr_sectors = 1024,
154 .name = "EN25Q32(A/B)",
157 .id = EON_ID_EN25Q64,
158 .page_size = 256,
159 .pages_per_sector = 16,
160 .sectors_per_block = 16,
161 .nr_sectors = 2048,
162 .name = "EN25Q64",
165 .id = EON_ID_EN25Q128,
166 .page_size = 256,
167 .pages_per_sector = 16,
168 .sectors_per_block = 16,
169 .nr_sectors = 4096,
170 .name = "EN25Q128",
173 .id = EON_ID_EN25QH16,
174 .page_size = 256,
175 .pages_per_sector = 16,
176 .sectors_per_block = 16,
177 .nr_sectors = 512,
178 .name = "EN25QH16",
181 .id = EON_ID_EN25QH32,
182 .page_size = 256,
183 .pages_per_sector = 16,
184 .sectors_per_block = 16,
185 .nr_sectors = 1024,
186 .name = "EN25QH32",
189 .id = EON_ID_EN25QH64,
190 .page_size = 256,
191 .pages_per_sector = 16,
192 .sectors_per_block = 16,
193 .nr_sectors = 2048,
194 .name = "EN25QH64",
197 .id = EON_ID_EN25QH128,
198 .page_size = 256,
199 .pages_per_sector = 16,
200 .sectors_per_block = 16,
201 .nr_sectors = 4096,
202 .name = "EN25QH128",
205 .id = EON_ID_EN25S80,
206 .page_size = 256,
207 .pages_per_sector = 16,
208 .sectors_per_block = 16,
209 .nr_sectors = 256,
210 .name = "EN25S80",
213 .id = EON_ID_EN25S16,
214 .page_size = 256,
215 .pages_per_sector = 16,
216 .sectors_per_block = 16,
217 .nr_sectors = 512,
218 .name = "EN25S16",
221 .id = EON_ID_EN25S32,
222 .page_size = 256,
223 .pages_per_sector = 16,
224 .sectors_per_block = 16,
225 .nr_sectors = 1024,
226 .name = "EN25S32",
229 .id = EON_ID_EN25S64,
230 .page_size = 256,
231 .pages_per_sector = 16,
232 .sectors_per_block = 16,
233 .nr_sectors = 2048,
234 .name = "EN25S64",
238 static int eon_write(const struct spi_flash *flash,
239 u32 offset, size_t len, const void *buf)
241 unsigned long byte_addr;
242 unsigned long page_size;
243 size_t chunk_len;
244 size_t actual;
245 int ret = 0;
246 u8 cmd[4];
248 page_size = flash->page_size;
250 for (actual = 0; actual < len; actual += chunk_len) {
251 byte_addr = offset % page_size;
252 chunk_len = MIN(len - actual, page_size - byte_addr);
253 chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);
255 ret = spi_flash_cmd(&flash->spi, CMD_EN25_WREN, NULL, 0);
256 if (ret < 0) {
257 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
258 goto out;
261 cmd[0] = CMD_EN25_PP;
262 cmd[1] = (offset >> 16) & 0xff;
263 cmd[2] = (offset >> 8) & 0xff;
264 cmd[3] = offset & 0xff;
266 #if CONFIG(DEBUG_SPI_FLASH)
267 printk(BIOS_SPEW,
268 "PP: %p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n",
269 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
270 #endif
272 ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
273 buf + actual, chunk_len);
274 if (ret < 0) {
275 printk(BIOS_WARNING, "SF: EON Page Program failed\n");
276 goto out;
279 ret = spi_flash_cmd_wait_ready(flash,
280 SPI_FLASH_PROG_TIMEOUT_MS);
281 if (ret) {
282 printk(BIOS_WARNING, "SF: EON Page Program timeout\n");
283 goto out;
286 offset += chunk_len;
289 #if CONFIG(DEBUG_SPI_FLASH)
290 printk(BIOS_SPEW, "SF: EON: Successfully programmed %zu bytes @ %#x\n",
291 len, (unsigned int)(offset - len));
292 #endif
294 out:
295 return ret;
298 static const struct spi_flash_ops spi_flash_ops = {
299 .write = eon_write,
300 .erase = spi_flash_cmd_erase,
301 .status = spi_flash_cmd_status,
304 int spi_flash_probe_eon(const struct spi_slave *spi, u8 *idcode,
305 struct spi_flash *flash)
307 const struct eon_spi_flash_params *params;
308 unsigned int i;
310 for (i = 0; i < ARRAY_SIZE(eon_spi_flash_table); ++i) {
311 params = &eon_spi_flash_table[i];
312 if (params->id == ((idcode[1] << 8) | idcode[2]))
313 break;
316 if (i == ARRAY_SIZE(eon_spi_flash_table)) {
317 printk(BIOS_WARNING, "SF: Unsupported EON ID %#02x%02x\n",
318 idcode[1], idcode[2]);
319 return -1;
322 memcpy(&flash->spi, spi, sizeof(*spi));
324 flash->name = params->name;
325 flash->page_size = params->page_size;
326 flash->sector_size = params->page_size * params->pages_per_sector;
327 flash->size = flash->sector_size * params->nr_sectors;
328 flash->erase_cmd = CMD_EN25_SE;
329 flash->status_cmd = CMD_EN25_RDSR;
331 flash->ops = &spi_flash_ops;
333 return 0;