diff options
Diffstat (limited to 'src/mainboard/prodrive/hermes/eeprom.c')
-rw-r--r-- | src/mainboard/prodrive/hermes/eeprom.c | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/src/mainboard/prodrive/hermes/eeprom.c b/src/mainboard/prodrive/hermes/eeprom.c index 8db63f8fe4..58099d3aa8 100644 --- a/src/mainboard/prodrive/hermes/eeprom.c +++ b/src/mainboard/prodrive/hermes/eeprom.c @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +#include <assert.h> #include <device/pci_ops.h> +#include <delay.h> #include <console/console.h> #include <crc_byte.h> #include <device/smbus_host.h> @@ -106,3 +108,80 @@ void report_eeprom_error(const size_t off) { printk(BIOS_ERR, "MB: Failed to read from EEPROM at addr. 0x%zx\n", off); } + +/* + * Write a single byte into the EEPROM at specified offset. + * Returns true on error, false on success. + */ +static bool write_byte_eeprom(const uint8_t data, const uint16_t write_offset) +{ + int ret = 0; + + printk(BIOS_SPEW, "CFG EEPROM: Writing %x at %x\n", data, write_offset); + + const uint32_t smb_ctrl_reg = pci_read_config32(PCH_DEV_SMBUS, HOSTC); + pci_write_config32(PCH_DEV_SMBUS, HOSTC, smb_ctrl_reg | I2C_EN); + + /* + * The EEPROM expects two address bytes. + * Use the first byte of the block data as second address byte. + */ + uint8_t buffer[2] = { + write_offset & 0xff, + data, + }; + + for (size_t retry = 3; retry > 0; retry--) { + /* The EEPROM NACKs request when busy writing */ + ret = do_smbus_block_write(SMBUS_IO_BASE, I2C_ADDR_EEPROM, + (write_offset >> 8) & 0xff, + sizeof(buffer), buffer); + if (ret == sizeof(buffer)) + break; + /* Maximum of 5 milliseconds write duration */ + mdelay(5); + } + + /* Restore I2C_EN bit */ + pci_write_config32(PCH_DEV_SMBUS, HOSTC, smb_ctrl_reg); + + return ret != sizeof(buffer); +} + +/* + * Write board layout if it has changed into EEPROM. + * Return true on error, false on success. + */ +bool write_board_settings(const struct eeprom_board_layout *new_layout) +{ + const size_t off = offsetof(struct eeprom_layout, BoardLayout); + struct eeprom_board_layout old_layout = {0}; + bool ret = false; + bool changed = false; + + /* Read old settings */ + if (read_write_config(&old_layout, off, 0, sizeof(old_layout))) { + printk(BIOS_ERR, "CFG EEPROM: Read operation failed\n"); + return true; + } + + assert(sizeof(old_layout) == sizeof(*new_layout)); + const uint8_t *const old = (const uint8_t *)&old_layout; + const uint8_t *const new = (const uint8_t *)new_layout; + + /* Compare with new settings and only write changed bytes */ + for (size_t i = 0; i < sizeof(old_layout); i++) { + if (old[i] != new[i]) { + changed = true; + if (write_byte_eeprom(new[i], off + i)) { + printk(BIOS_ERR, "CFG EEPROM: Write operation failed\n"); + ret = true; + break; + } + } + } + + printk(BIOS_DEBUG, "CFG EEPROM: Board Layout up%s\n", changed ? "dated" : " to date"); + + return ret; +} |