sb/amd/{agesa,pi}/hudson: Explicitly enable LPC controller
[coreboot.git] / src / drivers / spi / amic.c
blob4943779a1a1a5613e40606723d77253baaa8c047
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 <stdlib.h>
17 #include <spi_flash.h>
18 #include <spi-generic.h>
19 #include <string.h>
21 #include "spi_flash_internal.h"
23 /* A25L-specific commands */
24 #define CMD_A25_WREN 0x06 /* Write Enable */
25 #define CMD_A25_WRDI 0x04 /* Write Disable */
26 #define CMD_A25_RDSR 0x05 /* Read Status Register */
27 #define CMD_A25_WRSR 0x01 /* Write Status Register */
28 #define CMD_A25_READ 0x03 /* Read Data Bytes */
29 #define CMD_A25_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */
30 #define CMD_A25_PP 0x02 /* Page Program */
31 #define CMD_A25_SE 0x20 /* Sector (4K) Erase */
32 #define CMD_A25_BE 0xd8 /* Block (64K) Erase */
33 #define CMD_A25_CE 0xc7 /* Chip Erase */
34 #define CMD_A25_DP 0xb9 /* Deep Power-down */
35 #define CMD_A25_RES 0xab /* Release from DP, and Read Signature */
37 struct amic_spi_flash_params {
38 uint16_t id;
39 /* Log2 of page size in power-of-two mode */
40 uint8_t l2_page_size;
41 uint16_t pages_per_sector;
42 uint16_t sectors_per_block;
43 uint16_t nr_blocks;
44 const char *name;
47 static const struct amic_spi_flash_params amic_spi_flash_table[] = {
49 .id = 0x2015,
50 .l2_page_size = 8,
51 .pages_per_sector = 16,
52 .sectors_per_block = 16,
53 .nr_blocks = 32,
54 .name = "A25L16PU",
57 .id = 0x2025,
58 .l2_page_size = 8,
59 .pages_per_sector = 16,
60 .sectors_per_block = 16,
61 .nr_blocks = 32,
62 .name = "A25L16PT",
65 .id = 0x3014,
66 .l2_page_size = 8,
67 .pages_per_sector = 16,
68 .sectors_per_block = 16,
69 .nr_blocks = 16,
70 .name = "A25L080",
73 .id = 0x3015,
74 .l2_page_size = 8,
75 .pages_per_sector = 16,
76 .sectors_per_block = 16,
77 .nr_blocks = 32,
78 .name = "A25L016",
81 .id = 0x3016,
82 .l2_page_size = 8,
83 .pages_per_sector = 16,
84 .sectors_per_block = 16,
85 .nr_blocks = 64,
86 .name = "A25L032",
89 .id = 0x4014,
90 .l2_page_size = 8,
91 .pages_per_sector = 16,
92 .sectors_per_block = 16,
93 .nr_blocks = 16,
94 .name = "A25LQ080",
97 .id = 0x4015,
98 .l2_page_size = 8,
99 .pages_per_sector = 16,
100 .sectors_per_block = 16,
101 .nr_blocks = 32,
102 .name = "A25LQ16",
105 .id = 0x4016,
106 .l2_page_size = 8,
107 .pages_per_sector = 16,
108 .sectors_per_block = 16,
109 .nr_blocks = 64,
110 .name = "A25LQ032",
113 .id = 0x4017,
114 .l2_page_size = 8,
115 .pages_per_sector = 16,
116 .sectors_per_block = 16,
117 .nr_blocks = 128,
118 .name = "A25LQ64",
122 static int amic_write(const struct spi_flash *flash, u32 offset, size_t len,
123 const void *buf)
125 unsigned long byte_addr;
126 unsigned long page_size;
127 size_t chunk_len;
128 size_t actual;
129 int ret;
130 u8 cmd[4];
132 page_size = flash->page_size;
133 byte_addr = offset % page_size;
135 for (actual = 0; actual < len; actual += chunk_len) {
136 chunk_len = min(len - actual, page_size - byte_addr);
137 chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);
139 cmd[0] = CMD_A25_PP;
140 cmd[1] = (offset >> 16) & 0xff;
141 cmd[2] = (offset >> 8) & 0xff;
142 cmd[3] = offset & 0xff;
143 #if CONFIG(DEBUG_SPI_FLASH)
144 printk(BIOS_SPEW, "PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x }"
145 " chunk_len = %zu\n", buf + actual,
146 cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
147 #endif
149 ret = spi_flash_cmd(&flash->spi, CMD_A25_WREN, NULL, 0);
150 if (ret < 0) {
151 printk(BIOS_WARNING, "SF: Enabling Write failed\n");
152 goto out;
155 ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
156 buf + actual, chunk_len);
157 if (ret < 0) {
158 printk(BIOS_WARNING, "SF: AMIC Page Program failed\n");
159 goto out;
162 ret = spi_flash_cmd_wait_ready(flash,
163 SPI_FLASH_PROG_TIMEOUT_MS);
164 if (ret)
165 goto out;
167 offset += chunk_len;
168 byte_addr = 0;
171 #if CONFIG(DEBUG_SPI_FLASH)
172 printk(BIOS_SPEW, "SF: AMIC: Successfully programmed %zu bytes @"
173 " 0x%lx\n", len, (unsigned long)(offset - len));
174 #endif
175 ret = 0;
177 out:
178 return ret;
181 static const struct spi_flash_ops spi_flash_ops = {
182 .write = amic_write,
183 .erase = spi_flash_cmd_erase,
186 int spi_flash_probe_amic(const struct spi_slave *spi, u8 *idcode,
187 struct spi_flash *flash)
189 const struct amic_spi_flash_params *params;
190 unsigned int i;
192 for (i = 0; i < ARRAY_SIZE(amic_spi_flash_table); i++) {
193 params = &amic_spi_flash_table[i];
194 if (params->id == ((idcode[1] << 8) | idcode[2]))
195 break;
198 if (i == ARRAY_SIZE(amic_spi_flash_table)) {
199 printk(BIOS_WARNING, "SF: Unsupported AMIC ID %02x%02x\n",
200 idcode[1], idcode[2]);
201 return -1;
204 memcpy(&flash->spi, spi, sizeof(*spi));
205 flash->name = params->name;
207 /* Assuming power-of-two page size initially. */
208 flash->page_size = 1 << params->l2_page_size;
209 flash->sector_size = flash->page_size * params->pages_per_sector;
210 flash->size = flash->sector_size * params->sectors_per_block *
211 params->nr_blocks;
212 flash->erase_cmd = CMD_A25_SE;
214 flash->ops = &spi_flash_ops;
216 return 0;