{drivers,southbridge}: Replace min() with MIN()
[coreboot.git] / src / drivers / spi / stmicro.c
blobad0a0dc538431cdd81d14e6ad404a5ee706ec4cc
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 /* M25Pxx-specific commands */
24 #define CMD_M25PXX_WREN 0x06 /* Write Enable */
25 #define CMD_M25PXX_WRDI 0x04 /* Write Disable */
26 #define CMD_M25PXX_RDSR 0x05 /* Read Status Register */
27 #define CMD_M25PXX_WRSR 0x01 /* Write Status Register */
28 #define CMD_M25PXX_READ 0x03 /* Read Data Bytes */
29 #define CMD_M25PXX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
30 #define CMD_M25PXX_PP 0x02 /* Page Program */
31 #define CMD_M25PXX_SSE 0x20 /* Subsector Erase */
32 #define CMD_M25PXX_SE 0xd8 /* Sector Erase */
33 #define CMD_M25PXX_BE 0xc7 /* Bulk Erase */
34 #define CMD_M25PXX_DP 0xb9 /* Deep Power-down */
35 #define CMD_M25PXX_RES 0xab /* Release from DP, and Read Signature */
38 * Device ID = (memory_type << 8) + memory_capacity
40 #define STM_ID_M25P10 0x2011
41 #define STM_ID_M25P20 0x2012
42 #define STM_ID_M25P40 0x2013
43 #define STM_ID_M25P80 0x2014
44 #define STM_ID_M25P16 0x2015
45 #define STM_ID_M25P32 0x2016
46 #define STM_ID_M25P64 0x2017
47 #define STM_ID_M25P128 0x2018
48 #define STM_ID_M25PX80 0x7114
49 #define STM_ID_M25PX16 0x7115
50 #define STM_ID_M25PX32 0x7116
51 #define STM_ID_M25PX64 0x7117
52 #define STM_ID_M25PE80 0x8014
53 #define STM_ID_M25PE16 0x8015
54 #define STM_ID_M25PE32 0x8016
55 #define STM_ID_M25PE64 0x8017
56 #define STM_ID_N25Q016__3E 0xba15
57 #define STM_ID_N25Q032__3E 0xba16
58 #define STM_ID_N25Q064__3E 0xba17
59 #define STM_ID_N25Q128__3E 0xba18
60 #define STM_ID_N25Q256__3E 0xba19
61 #define STM_ID_N25Q016__1E 0xbb15
62 #define STM_ID_N25Q032__1E 0xbb16
63 #define STM_ID_N25Q064__1E 0xbb17
64 #define STM_ID_N25Q128__1E 0xbb18
65 #define STM_ID_N25Q256__1E 0xbb19
67 struct stmicro_spi_flash_params {
68 u16 device_id;
69 u8 op_erase;
70 u16 page_size;
71 u16 pages_per_sector;
72 u16 nr_sectors;
73 const char *name;
76 static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {
78 .device_id = STM_ID_M25P10,
79 .op_erase = CMD_M25PXX_SE,
80 .page_size = 256,
81 .pages_per_sector = 128,
82 .nr_sectors = 4,
83 .name = "M25P10",
86 .device_id = STM_ID_M25P16,
87 .op_erase = CMD_M25PXX_SE,
88 .page_size = 256,
89 .pages_per_sector = 256,
90 .nr_sectors = 32,
91 .name = "M25P16",
94 .device_id = STM_ID_M25P20,
95 .op_erase = CMD_M25PXX_SE,
96 .page_size = 256,
97 .pages_per_sector = 256,
98 .nr_sectors = 4,
99 .name = "M25P20",
102 .device_id = STM_ID_M25P32,
103 .op_erase = CMD_M25PXX_SE,
104 .page_size = 256,
105 .pages_per_sector = 256,
106 .nr_sectors = 64,
107 .name = "M25P32",
110 .device_id = STM_ID_M25P40,
111 .op_erase = CMD_M25PXX_SE,
112 .page_size = 256,
113 .pages_per_sector = 256,
114 .nr_sectors = 8,
115 .name = "M25P40",
118 .device_id = STM_ID_M25P64,
119 .op_erase = CMD_M25PXX_SE,
120 .page_size = 256,
121 .pages_per_sector = 256,
122 .nr_sectors = 128,
123 .name = "M25P64",
126 .device_id = STM_ID_M25P80,
127 .op_erase = CMD_M25PXX_SE,
128 .page_size = 256,
129 .pages_per_sector = 256,
130 .nr_sectors = 16,
131 .name = "M25P80",
134 .device_id = STM_ID_M25P128,
135 .op_erase = CMD_M25PXX_SE,
136 .page_size = 256,
137 .pages_per_sector = 1024,
138 .nr_sectors = 64,
139 .name = "M25P128",
142 .device_id = STM_ID_M25PX80,
143 .op_erase = CMD_M25PXX_SE,
144 .page_size = 256,
145 .pages_per_sector = 256,
146 .nr_sectors = 16,
147 .name = "M25PX80",
150 .device_id = STM_ID_M25PX16,
151 .op_erase = CMD_M25PXX_SE,
152 .page_size = 256,
153 .pages_per_sector = 256,
154 .nr_sectors = 32,
155 .name = "M25PX16",
158 .device_id = STM_ID_M25PX32,
159 .op_erase = CMD_M25PXX_SE,
160 .page_size = 256,
161 .pages_per_sector = 256,
162 .nr_sectors = 64,
163 .name = "M25PX32",
166 .device_id = STM_ID_M25PX64,
167 .op_erase = CMD_M25PXX_SE,
168 .page_size = 256,
169 .pages_per_sector = 256,
170 .nr_sectors = 128,
171 .name = "M25PX64",
174 .device_id = STM_ID_M25PE80,
175 .op_erase = CMD_M25PXX_SE,
176 .page_size = 256,
177 .pages_per_sector = 256,
178 .nr_sectors = 16,
179 .name = "M25PE80",
182 .device_id = STM_ID_M25PE16,
183 .op_erase = CMD_M25PXX_SE,
184 .page_size = 256,
185 .pages_per_sector = 256,
186 .nr_sectors = 32,
187 .name = "M25PE16",
190 .device_id = STM_ID_M25PE32,
191 .op_erase = CMD_M25PXX_SE,
192 .page_size = 256,
193 .pages_per_sector = 256,
194 .nr_sectors = 64,
195 .name = "M25PE32",
198 .device_id = STM_ID_M25PE64,
199 .op_erase = CMD_M25PXX_SE,
200 .page_size = 256,
201 .pages_per_sector = 256,
202 .nr_sectors = 128,
203 .name = "M25PE64",
206 .device_id = STM_ID_N25Q016__3E,
207 .op_erase = CMD_M25PXX_SSE,
208 .page_size = 256,
209 .pages_per_sector = 16,
210 .nr_sectors = 512,
211 .name = "N25Q016..3E",
214 .device_id = STM_ID_N25Q032__3E,
215 .op_erase = CMD_M25PXX_SSE,
216 .page_size = 256,
217 .pages_per_sector = 16,
218 .nr_sectors = 1024,
219 .name = "N25Q032..3E",
222 .device_id = STM_ID_N25Q064__3E,
223 .op_erase = CMD_M25PXX_SSE,
224 .page_size = 256,
225 .pages_per_sector = 16,
226 .nr_sectors = 2048,
227 .name = "N25Q064..3E",
230 .device_id = STM_ID_N25Q128__3E,
231 .op_erase = CMD_M25PXX_SSE,
232 .page_size = 256,
233 .pages_per_sector = 16,
234 .nr_sectors = 4096,
235 .name = "N25Q128..3E",
238 .device_id = STM_ID_N25Q256__3E,
239 .op_erase = CMD_M25PXX_SSE,
240 .page_size = 256,
241 .pages_per_sector = 16,
242 .nr_sectors = 8192,
243 .name = "N25Q256..3E",
246 .device_id = STM_ID_N25Q016__1E,
247 .op_erase = CMD_M25PXX_SSE,
248 .page_size = 256,
249 .pages_per_sector = 16,
250 .nr_sectors = 512,
251 .name = "N25Q016..1E",
254 .device_id = STM_ID_N25Q032__1E,
255 .op_erase = CMD_M25PXX_SSE,
256 .page_size = 256,
257 .pages_per_sector = 16,
258 .nr_sectors = 1024,
259 .name = "N25Q032..1E",
262 .device_id = STM_ID_N25Q064__1E,
263 .op_erase = CMD_M25PXX_SSE,
264 .page_size = 256,
265 .pages_per_sector = 16,
266 .nr_sectors = 2048,
267 .name = "N25Q064..1E",
270 .device_id = STM_ID_N25Q128__1E,
271 .op_erase = CMD_M25PXX_SSE,
272 .page_size = 256,
273 .pages_per_sector = 16,
274 .nr_sectors = 4096,
275 .name = "N25Q128..1E",
278 .device_id = STM_ID_N25Q256__1E,
279 .op_erase = CMD_M25PXX_SSE,
280 .page_size = 256,
281 .pages_per_sector = 16,
282 .nr_sectors = 8192,
283 .name = "N25Q256..1E",
287 static int stmicro_write(const struct spi_flash *flash,
288 u32 offset, size_t len, const void *buf)
290 unsigned long byte_addr;
291 unsigned long page_size;
292 size_t chunk_len;
293 size_t actual;
294 int ret = 0;
295 u8 cmd[4];
297 page_size = flash->page_size;
299 for (actual = 0; actual < len; actual += chunk_len) {
300 byte_addr = offset % page_size;
301 chunk_len = MIN(len - actual, page_size - byte_addr);
302 chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);
304 cmd[0] = CMD_M25PXX_PP;
305 cmd[1] = (offset >> 16) & 0xff;
306 cmd[2] = (offset >> 8) & 0xff;
307 cmd[3] = offset & 0xff;
308 #if CONFIG(DEBUG_SPI_FLASH)
309 printk(BIOS_SPEW, "PP: %p => cmd = { 0x%02x 0x%02x%02x%02x }"
310 " chunk_len = %zu\n",
311 buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
312 #endif
314 ret = spi_flash_cmd(&flash->spi, CMD_M25PXX_WREN, NULL, 0);
315 if (ret < 0) {
316 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
317 goto out;
320 ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
321 buf + actual, chunk_len);
322 if (ret < 0) {
323 printk(BIOS_WARNING, "SF: STMicro Page Program failed\n");
324 goto out;
327 ret = spi_flash_cmd_wait_ready(flash,
328 SPI_FLASH_PROG_TIMEOUT_MS);
329 if (ret)
330 goto out;
332 offset += chunk_len;
335 #if CONFIG(DEBUG_SPI_FLASH)
336 printk(BIOS_SPEW, "SF: STMicro: Successfully programmed %zu bytes @"
337 " 0x%lx\n", len, (unsigned long)(offset - len));
338 #endif
339 ret = 0;
341 out:
342 return ret;
345 static const struct spi_flash_ops spi_flash_ops = {
346 .write = stmicro_write,
347 .erase = spi_flash_cmd_erase,
350 int spi_flash_probe_stmicro(const struct spi_slave *spi, u8 *idcode,
351 struct spi_flash *flash)
353 const struct stmicro_spi_flash_params *params;
354 unsigned int i;
356 if (idcode[0] == 0xff) {
357 i = spi_flash_cmd(spi, CMD_M25PXX_RES, idcode, 4);
358 if (i)
359 return -1;
360 if ((idcode[3] & 0xf0) == 0x10) {
361 idcode[0] = 0x20;
362 idcode[1] = 0x20;
363 idcode[2] = idcode[3] + 1;
364 } else
365 return -1;
368 for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {
369 params = &stmicro_spi_flash_table[i];
370 if (params->device_id == (idcode[1] << 8 | idcode[2])) {
371 break;
375 if (i == ARRAY_SIZE(stmicro_spi_flash_table)) {
376 printk(BIOS_WARNING, "SF: Unsupported STMicro ID %02x%02x\n",
377 idcode[1], idcode[2]);
378 return -1;
381 memcpy(&flash->spi, spi, sizeof(*spi));
382 flash->name = params->name;
383 flash->page_size = params->page_size;
384 flash->sector_size = params->page_size * params->pages_per_sector;
385 flash->size = flash->sector_size * params->nr_sectors;
386 flash->erase_cmd = params->op_erase;
388 flash->ops = &spi_flash_ops;
390 return 0;