aboutsummaryrefslogtreecommitdiff
path: root/src/mainboard/prodrive/hermes/eeprom.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mainboard/prodrive/hermes/eeprom.c')
-rw-r--r--src/mainboard/prodrive/hermes/eeprom.c79
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;
+}