blob: a712156bc1ede0b6b2e9702c445ff6077359b919 [file] [log] [blame]
Christian Walterb646e282020-01-09 15:42:42 +01001/* SPDX-License-Identifier: GPL-2.0-only */
2
Patrick Rudolph35f0a8f2020-11-12 16:41:57 +01003#include <assert.h>
Christian Walterb646e282020-01-09 15:42:42 +01004#include <device/pci_ops.h>
Patrick Rudolph35f0a8f2020-11-12 16:41:57 +01005#include <delay.h>
Christian Walterb646e282020-01-09 15:42:42 +01006#include <console/console.h>
Angel Pons7c7e9a22020-12-21 18:53:44 +01007#include <crc_byte.h>
Christian Walterb646e282020-01-09 15:42:42 +01008#include <device/smbus_host.h>
Angel Pons707e03d2020-12-17 11:37:20 +01009#include <soc/intel/common/block/smbus/smbuslib.h>
Angel Pons7c7e9a22020-12-21 18:53:44 +010010#include <types.h>
11
Christian Walterb646e282020-01-09 15:42:42 +010012#include "variants/baseboard/include/eeprom.h"
13
Angel Ponsf6510222020-12-17 11:51:36 +010014#define I2C_ADDR_EEPROM 0x57
15
Christian Walterb646e282020-01-09 15:42:42 +010016/*
17 * Check Signature in EEPROM (M24C32-FMN6TP)
18 * If signature is there we assume that that the content is valid
19 */
Angel Ponsf6510222020-12-17 11:51:36 +010020int check_signature(const size_t offset, const uint64_t signature)
Christian Walterb646e282020-01-09 15:42:42 +010021{
22 u8 blob[8] = {0};
23
Angel Ponsf6510222020-12-17 11:51:36 +010024 if (!read_write_config(blob, offset, 0, ARRAY_SIZE(blob))) {
Angel Pons329ebb32020-11-24 15:03:38 +010025 /* Check signature */
Angel Ponsf9f129a2020-11-30 10:44:42 +010026 if (*(uint64_t *)blob == signature) {
Christian Walterb646e282020-01-09 15:42:42 +010027 printk(BIOS_DEBUG, "CFG EEPROM: Signature valid.\n");
28 return 1;
29 }
30 printk(BIOS_DEBUG, "CFG EEPROM: Signature invalid - skipping option write.\n");
31 return 0;
32 }
33 return 0;
34}
35
Angel Pons7c7e9a22020-12-21 18:53:44 +010036/*
37 * Read board settings from the EEPROM and verify their checksum.
38 * If checksum is valid, we assume the settings are sane as well.
39 */
40static bool get_board_settings_from_eeprom(struct eeprom_board_settings *board_cfg)
41{
Angel Pons20eb3502020-12-21 19:46:03 +010042 const size_t board_settings_offset = offsetof(struct eeprom_layout, BoardSettings);
43
44 if (read_write_config(board_cfg, board_settings_offset, 0, sizeof(*board_cfg))) {
Angel Pons7c7e9a22020-12-21 18:53:44 +010045 printk(BIOS_ERR, "CFG EEPROM: Failed to read board settings\n");
46 return false;
47 }
48
49 const uint32_t crc =
50 CRC(&board_cfg->raw_settings, sizeof(board_cfg->raw_settings), crc32_byte);
51
52 if (crc != board_cfg->signature) {
53 printk(BIOS_ERR, "CFG EEPROM: Board settings have invalid checksum\n");
54 return false;
55 }
56 return true;
57}
58
59struct eeprom_board_settings *get_board_settings(void)
60{
61 static struct eeprom_board_settings board_cfg = {0};
62
63 /* Tri-state: -1: settings are invalid, 0: uninitialized, 1: settings are valid */
64 static int checked_valid = 0;
65
66 if (checked_valid == 0) {
67 const bool success = get_board_settings_from_eeprom(&board_cfg);
68 checked_valid = success ? 1 : -1;
69 }
70 return checked_valid > 0 ? &board_cfg : NULL;
71}
72
Patrick Rudolphb67f3852021-02-15 16:35:24 +010073struct eeprom_bmc_settings *get_bmc_settings(void)
74{
75 const size_t bmc_settings_offset = offsetof(struct eeprom_layout, BMCSettings);
76 static struct eeprom_bmc_settings bmc_cfg = {0};
77
78 /* 0: uninitialized, 1: settings are valid */
79 static int valid = 0;
80
81 if (valid == 0) {
82 if (read_write_config(&bmc_cfg, bmc_settings_offset, 0, sizeof(bmc_cfg))) {
83 printk(BIOS_ERR, "CFG EEPROM: Failed to read BMC settings\n");
84 return NULL;
85 }
86 valid = 1;
87 }
88 return &bmc_cfg;
89}
90
Patrick Rudolphe75f1802021-02-15 16:41:07 +010091uint8_t get_bmc_hsi(void)
92{
93 uint8_t hsi = 0;
94 struct eeprom_bmc_settings *s = get_bmc_settings();
95 if (s)
96 hsi = s->hsi;
97 printk(BIOS_DEBUG, "CFG EEPROM: HSI 0x%x\n", hsi);
98
99 return hsi;
100}
Patrick Rudolphb67f3852021-02-15 16:35:24 +0100101
Angel Pons329ebb32020-11-24 15:03:38 +0100102/* Read data from offset and write it to offset in UPD */
Angel Ponsf6510222020-12-17 11:51:36 +0100103bool read_write_config(void *blob, size_t read_offset, size_t write_offset, size_t size)
Christian Walterb646e282020-01-09 15:42:42 +0100104{
105 int ret = 0;
106
Angel Pons13c50002020-11-24 15:39:26 +0100107 u32 smb_ctrl_reg = pci_read_config32(PCH_DEV_SMBUS, HOSTC);
Angel Pons707e03d2020-12-17 11:37:20 +0100108 pci_write_config32(PCH_DEV_SMBUS, HOSTC, smb_ctrl_reg | I2C_EN);
Christian Walterb646e282020-01-09 15:42:42 +0100109
110 printk(BIOS_SPEW, "%s\tOffset: %04zx\tSize: %02zx\n", __func__,
111 read_offset, size);
112
113 /* We can always read two bytes at a time */
114 for (size_t i = 0; i < size; i = i + 2) {
115 u8 tmp[2] = {0};
116
Angel Ponsf6510222020-12-17 11:51:36 +0100117 ret = do_smbus_process_call(SMBUS_IO_BASE, I2C_ADDR_EEPROM, 0,
Christian Walterb646e282020-01-09 15:42:42 +0100118 swab16(read_offset + i), (uint16_t *)&tmp[0]);
119 if (ret < 0)
120 break;
121
Angel Pons329ebb32020-11-24 15:03:38 +0100122 /* Write to UPD */
Christian Walterb646e282020-01-09 15:42:42 +0100123 uint8_t *writePointer = (uint8_t *)blob + write_offset + i;
Arthur Heymansaf13a6e2021-04-09 11:18:52 +0200124 writePointer[0] = tmp[0];
125 if (size - i > 1)
126 writePointer[1] = tmp[1];
Christian Walterb646e282020-01-09 15:42:42 +0100127 }
128
129 /* Restore I2C_EN bit */
Angel Pons13c50002020-11-24 15:39:26 +0100130 pci_write_config32(PCH_DEV_SMBUS, HOSTC, smb_ctrl_reg);
Christian Walterb646e282020-01-09 15:42:42 +0100131
132 return ret;
133}
Angel Pons20eb3502020-12-21 19:46:03 +0100134
135void report_eeprom_error(const size_t off)
136{
137 printk(BIOS_ERR, "MB: Failed to read from EEPROM at addr. 0x%zx\n", off);
138}
Patrick Rudolph35f0a8f2020-11-12 16:41:57 +0100139
140/*
141 * Write a single byte into the EEPROM at specified offset.
142 * Returns true on error, false on success.
143 */
144static bool write_byte_eeprom(const uint8_t data, const uint16_t write_offset)
145{
146 int ret = 0;
147
148 printk(BIOS_SPEW, "CFG EEPROM: Writing %x at %x\n", data, write_offset);
149
150 const uint32_t smb_ctrl_reg = pci_read_config32(PCH_DEV_SMBUS, HOSTC);
151 pci_write_config32(PCH_DEV_SMBUS, HOSTC, smb_ctrl_reg | I2C_EN);
152
153 /*
154 * The EEPROM expects two address bytes.
155 * Use the first byte of the block data as second address byte.
156 */
157 uint8_t buffer[2] = {
158 write_offset & 0xff,
159 data,
160 };
161
162 for (size_t retry = 3; retry > 0; retry--) {
163 /* The EEPROM NACKs request when busy writing */
164 ret = do_smbus_block_write(SMBUS_IO_BASE, I2C_ADDR_EEPROM,
165 (write_offset >> 8) & 0xff,
166 sizeof(buffer), buffer);
167 if (ret == sizeof(buffer))
168 break;
169 /* Maximum of 5 milliseconds write duration */
170 mdelay(5);
171 }
172
173 /* Restore I2C_EN bit */
174 pci_write_config32(PCH_DEV_SMBUS, HOSTC, smb_ctrl_reg);
175
176 return ret != sizeof(buffer);
177}
178
179/*
180 * Write board layout if it has changed into EEPROM.
181 * Return true on error, false on success.
182 */
183bool write_board_settings(const struct eeprom_board_layout *new_layout)
184{
185 const size_t off = offsetof(struct eeprom_layout, BoardLayout);
186 struct eeprom_board_layout old_layout = {0};
187 bool ret = false;
188 bool changed = false;
189
190 /* Read old settings */
191 if (read_write_config(&old_layout, off, 0, sizeof(old_layout))) {
192 printk(BIOS_ERR, "CFG EEPROM: Read operation failed\n");
193 return true;
194 }
195
196 assert(sizeof(old_layout) == sizeof(*new_layout));
197 const uint8_t *const old = (const uint8_t *)&old_layout;
198 const uint8_t *const new = (const uint8_t *)new_layout;
199
200 /* Compare with new settings and only write changed bytes */
201 for (size_t i = 0; i < sizeof(old_layout); i++) {
202 if (old[i] != new[i]) {
203 changed = true;
204 if (write_byte_eeprom(new[i], off + i)) {
205 printk(BIOS_ERR, "CFG EEPROM: Write operation failed\n");
206 ret = true;
207 break;
208 }
209 }
210 }
211
212 printk(BIOS_DEBUG, "CFG EEPROM: Board Layout up%s\n", changed ? "dated" : " to date");
213
214 return ret;
215}