{drivers,southbridge}: Replace min() with MIN()
[coreboot.git] / src / drivers / spi / gigadevice.c
blob65494fa240187f4fa2366f29d77a4531f268c481
1 /*
2 * This file is part of the coreboot project.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (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 /* GD25Pxx-specific commands */
24 #define CMD_GD25_WREN 0x06 /* Write Enable */
25 #define CMD_GD25_WRDI 0x04 /* Write Disable */
26 #define CMD_GD25_RDSR 0x05 /* Read Status Register */
27 #define CMD_GD25_WRSR 0x01 /* Write Status Register */
28 #define CMD_GD25_READ 0x03 /* Read Data Bytes */
29 #define CMD_GD25_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
30 #define CMD_GD25_PP 0x02 /* Page Program */
31 #define CMD_GD25_SE 0x20 /* Sector (4K) Erase */
32 #define CMD_GD25_BE 0xd8 /* Block (64K) Erase */
33 #define CMD_GD25_CE 0xc7 /* Chip Erase */
34 #define CMD_GD25_DP 0xb9 /* Deep Power-down */
35 #define CMD_GD25_RES 0xab /* Release from DP, and Read Signature */
37 struct gigadevice_spi_flash_params {
38 uint16_t id;
39 uint8_t dual_spi : 1;
40 uint8_t _reserved_for_flags : 3;
41 uint8_t l2_page_size_shift : 4;
42 uint8_t pages_per_sector_shift : 4;
43 uint8_t sectors_per_block_shift : 4;
44 uint8_t nr_blocks_shift;
45 const char name[10];
48 static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
50 .id = 0x3114,
51 .l2_page_size_shift = 8,
52 .pages_per_sector_shift = 4,
53 .sectors_per_block_shift = 4,
54 .nr_blocks_shift = 4,
55 .name = "GD25T80",
58 .id = 0x4014,
59 .l2_page_size_shift = 8,
60 .pages_per_sector_shift = 4,
61 .sectors_per_block_shift = 4,
62 .nr_blocks_shift = 4,
63 .dual_spi = 1,
64 .name = "GD25Q80",
65 }, /* also GD25Q80B */
67 .id = 0x4015,
68 .l2_page_size_shift = 8,
69 .pages_per_sector_shift = 4,
70 .sectors_per_block_shift = 4,
71 .nr_blocks_shift = 5,
72 .dual_spi = 1,
73 .name = "GD25Q16",
74 }, /* also GD25Q16B */
76 .id = 0x4016,
77 .l2_page_size_shift = 8,
78 .pages_per_sector_shift = 4,
79 .sectors_per_block_shift = 4,
80 .nr_blocks_shift = 6,
81 .dual_spi = 1,
82 .name = "GD25Q32B",
83 }, /* also GD25Q32B */
85 .id = 0x4017,
86 .l2_page_size_shift = 8,
87 .pages_per_sector_shift = 4,
88 .sectors_per_block_shift = 4,
89 .nr_blocks_shift = 7,
90 .dual_spi = 1,
91 .name = "GD25Q64",
92 }, /* also GD25Q64B, GD25B64C */
94 .id = 0x4018,
95 .l2_page_size_shift = 8,
96 .pages_per_sector_shift = 4,
97 .sectors_per_block_shift = 4,
98 .nr_blocks_shift = 8,
99 .dual_spi = 1,
100 .name = "GD25Q128",
101 }, /* also GD25Q128B */
103 .id = 0x4214,
104 .l2_page_size_shift = 8,
105 .pages_per_sector_shift = 4,
106 .sectors_per_block_shift = 4,
107 .nr_blocks_shift = 4,
108 .dual_spi = 1,
109 .name = "GD25VQ80C",
112 .id = 0x4215,
113 .l2_page_size_shift = 8,
114 .pages_per_sector_shift = 4,
115 .sectors_per_block_shift = 4,
116 .nr_blocks_shift = 5,
117 .dual_spi = 1,
118 .name = "GD25VQ16C",
121 .id = 0x6014,
122 .l2_page_size_shift = 8,
123 .pages_per_sector_shift = 4,
124 .sectors_per_block_shift = 4,
125 .nr_blocks_shift = 4,
126 .dual_spi = 1,
127 .name = "GD25LQ80",
130 .id = 0x6015,
131 .l2_page_size_shift = 8,
132 .pages_per_sector_shift = 4,
133 .sectors_per_block_shift = 4,
134 .nr_blocks_shift = 5,
135 .dual_spi = 1,
136 .name = "GD25LQ16",
139 .id = 0x6016,
140 .l2_page_size_shift = 8,
141 .pages_per_sector_shift = 4,
142 .sectors_per_block_shift = 4,
143 .nr_blocks_shift = 6,
144 .dual_spi = 1,
145 .name = "GD25LQ32",
148 .id = 0x6017,
149 .l2_page_size_shift = 8,
150 .pages_per_sector_shift = 4,
151 .sectors_per_block_shift = 4,
152 .nr_blocks_shift = 7,
153 .dual_spi = 1,
154 .name = "GD25LQ64C",
155 }, /* also GD25LB64C */
157 .id = 0x6018,
158 .l2_page_size_shift = 8,
159 .pages_per_sector_shift = 4,
160 .sectors_per_block_shift = 4,
161 .nr_blocks_shift = 8,
162 .dual_spi = 1,
163 .name = "GD25LQ128",
167 static int gigadevice_write(const struct spi_flash *flash, u32 offset,
168 size_t len, const void *buf)
170 unsigned long byte_addr;
171 unsigned long page_size;
172 size_t chunk_len;
173 size_t actual;
174 int ret = 0;
175 u8 cmd[4];
177 page_size = flash->page_size;
179 for (actual = 0; actual < len; actual += chunk_len) {
180 byte_addr = offset % page_size;
181 chunk_len = MIN(len - actual, page_size - byte_addr);
182 chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);
184 ret = spi_flash_cmd(&flash->spi, CMD_GD25_WREN, NULL, 0);
185 if (ret < 0) {
186 printk(BIOS_WARNING,
187 "SF gigadevice.c: Enabling Write failed\n");
188 goto out;
191 cmd[0] = CMD_GD25_PP;
192 cmd[1] = (offset >> 16) & 0xff;
193 cmd[2] = (offset >> 8) & 0xff;
194 cmd[3] = offset & 0xff;
195 #if CONFIG(DEBUG_SPI_FLASH)
196 printk(BIOS_SPEW,
197 "PP gigadevice.c: %p => cmd = { 0x%02x 0x%02x%02x%02x }"
198 " chunk_len = %zu\n", buf + actual,
199 cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
200 #endif
202 ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
203 buf + actual, chunk_len);
204 if (ret < 0) {
205 printk(BIOS_WARNING,
206 "SF gigadevice.c: Page Program failed\n");
207 goto out;
210 ret = spi_flash_cmd_wait_ready(flash,
211 SPI_FLASH_PROG_TIMEOUT_MS);
212 if (ret)
213 goto out;
215 offset += chunk_len;
218 #if CONFIG(DEBUG_SPI_FLASH)
219 printk(BIOS_SPEW,
220 "SF gigadevice.c: Successfully programmed %zu bytes @ %#x\n",
221 len, (unsigned int)(offset - len));
222 #endif
224 ret = 0;
226 out:
227 return ret;
230 static const struct spi_flash_ops spi_flash_ops = {
231 .write = gigadevice_write,
232 .erase = spi_flash_cmd_erase,
233 .status = spi_flash_cmd_status,
236 int spi_flash_probe_gigadevice(const struct spi_slave *spi, u8 *idcode,
237 struct spi_flash *flash)
239 const struct gigadevice_spi_flash_params *params;
240 unsigned int i;
242 for (i = 0; i < ARRAY_SIZE(gigadevice_spi_flash_table); i++) {
243 params = &gigadevice_spi_flash_table[i];
244 if (params->id == ((idcode[1] << 8) | idcode[2]))
245 break;
248 if (i == ARRAY_SIZE(gigadevice_spi_flash_table)) {
249 printk(BIOS_WARNING,
250 "SF gigadevice.c: Unsupported ID %#02x%02x\n",
251 idcode[1], idcode[2]);
252 return -1;
255 memcpy(&flash->spi, spi, sizeof(*spi));
256 flash->name = params->name;
258 /* Assuming power-of-two page size initially. */
259 flash->page_size = 1 << params->l2_page_size_shift;
260 flash->sector_size = flash->page_size *
261 (1 << params->pages_per_sector_shift);
262 flash->size = flash->sector_size *
263 (1 << params->sectors_per_block_shift) *
264 (1 << params->nr_blocks_shift);
265 flash->erase_cmd = CMD_GD25_SE;
266 flash->status_cmd = CMD_GD25_RDSR;
268 flash->ops = &spi_flash_ops;
270 return 0;