summaryrefslogtreecommitdiff
path: root/src/mainboard/prodrive/hermes/eeprom.c
diff options
context:
space:
mode:
authorPatrick Rudolph <patrick.rudolph@9elements.com>2020-11-12 16:41:57 +0100
committerPatrick Rudolph <siro@das-labor.org>2021-02-17 13:38:03 +0000
commit35f0a8fec7cd6127838fbe5a36170ba73efa49a4 (patch)
tree6eef4234394c6f54b4501d183919f704e6f857e5 /src/mainboard/prodrive/hermes/eeprom.c
parent71555955e91e1aac6c4ef32ebb7ee817fc31e31f (diff)
mb/prodrive/hermes: Write board layout
The I2C EEPROM on SMBUS needs to be updated with the current board layout, so that the BMC knows the actual configuration. Collect all needed information and update the EEPROM if something changed. Every byte written add a delay of 5 msec. Change-Id: Ic8485e6c700eede75b1e829238ee70da65118ace Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com> Signed-off-by: Angel Pons <th3fanbus@gmail.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/48810 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Patrick Rudolph <siro@das-labor.org>
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;
+}