summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mainboard/prodrive/hermes/eeprom.c79
-rw-r--r--src/mainboard/prodrive/hermes/mainboard.c77
-rw-r--r--src/mainboard/prodrive/hermes/variants/baseboard/include/eeprom.h34
3 files changed, 189 insertions, 1 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;
+}
diff --git a/src/mainboard/prodrive/hermes/mainboard.c b/src/mainboard/prodrive/hermes/mainboard.c
index a5cf887d9f..c9ed7c12fb 100644
--- a/src/mainboard/prodrive/hermes/mainboard.c
+++ b/src/mainboard/prodrive/hermes/mainboard.c
@@ -1,9 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
+#include <arch/cpu.h>
#include <acpi/acpigen.h>
+#include <cbmem.h>
+#include <console/console.h>
+#include <crc_byte.h>
#include <device/device.h>
+#include <device/dram/spd.h>
#include <intelblocks/pmclib.h>
#include <types.h>
+#include <smbios.h>
#include "variants/baseboard/include/eeprom.h"
#include "gpio.h"
@@ -54,6 +60,75 @@ static void mb_usb2_fp2_pwr_enable(bool enable)
gpio_output(GPP_G4, enable);
}
+static void copy_meminfo(const struct dimm_info *dimm, union eeprom_dimm_layout *l)
+{
+ memset(l, 0, sizeof(*l));
+ if (dimm->dimm_size == 0)
+ return;
+
+ strncpy(l->name, (char *)dimm->module_part_number, sizeof(l->name) - 1);
+ l->capacity_mib = dimm->dimm_size;
+ l->data_width_bits = 8 * (1 << (dimm->bus_width & 0x7));
+ l->bus_width_bits = l->data_width_bits + 8 * ((dimm->bus_width >> 3) & 0x3);
+ l->ranks = dimm->rank_per_dimm;
+ l->controller_id = 0;
+ strncpy(l->manufacturer, spd_manufacturer_name(dimm->mod_id),
+ sizeof(l->manufacturer) - 1);
+}
+
+/*
+ * Collect board specific settings and update the CFG EEPROM if necessary.
+ * This allows the BMC webui to display the current hardware configuration.
+ */
+static void update_board_layout(void)
+{
+ struct eeprom_board_layout layout = {0};
+
+ printk(BIOS_INFO, "MB: Collecting Board Layout information\n");
+
+ /* Update CPU fields */
+ for (struct device *cpu = all_devices; cpu; cpu = cpu->next) {
+ if (cpu->path.type != DEVICE_PATH_APIC)
+ continue;
+ if (cpu->bus->dev->path.type != DEVICE_PATH_CPU_CLUSTER)
+ continue;
+ if (!cpu->enabled)
+ continue;
+ layout.cpu_count++;
+ if (!layout.cpu_name[0])
+ strcpy(layout.cpu_name, cpu->name);
+ }
+
+ if (cpuid_get_max_func() >= 0x16)
+ layout.cpu_max_non_turbo_frequency = cpuid_eax(0x16);
+
+ /* PCH */
+ strcpy(layout.pch_name, "Cannonlake-H C246");
+
+ /* DRAM */
+ struct memory_info *meminfo = cbmem_find(CBMEM_ID_MEMINFO);
+ if (meminfo) {
+ const size_t meminfo_max = MIN(meminfo->dimm_cnt, ARRAY_SIZE(meminfo->dimm));
+ for (size_t i = 0; i < MIN(meminfo_max, ARRAY_SIZE(layout.dimm)); i++)
+ copy_meminfo(&meminfo->dimm[i], &layout.dimm[i]);
+ }
+
+ /* Update CRC */
+ layout.signature = CRC(layout.raw_layout, sizeof(layout.raw_layout), crc32_byte);
+
+ printk(BIOS_DEBUG, "BOARD LAYOUT:\n");
+ printk(BIOS_DEBUG, " Signature : 0x%x\n", layout.signature);
+ printk(BIOS_DEBUG, " CPU name : %s\n", layout.cpu_name);
+ printk(BIOS_DEBUG, " CPU count : %u\n", layout.cpu_count);
+ printk(BIOS_DEBUG, " CPU freq : %u\n", layout.cpu_max_non_turbo_frequency);
+ printk(BIOS_DEBUG, " PCH name : %s\n", layout.pch_name);
+ for (size_t i = 0; i < ARRAY_SIZE(layout.dimm); i++)
+ printk(BIOS_DEBUG, " DRAM SIZE : %u\n", layout.dimm[i].capacity_mib);
+
+ if (write_board_settings(&layout))
+ printk(BIOS_ERR, "MB: Failed to update Board Layout\n");
+}
+
static void mainboard_init(void *chip_info)
{
const struct eeprom_board_settings *const board_cfg = get_board_settings();
@@ -75,6 +150,8 @@ static void mainboard_init(void *chip_info)
static void mainboard_final(struct device *dev)
{
+ update_board_layout();
+
const struct eeprom_board_settings *const board_cfg = get_board_settings();
if (!board_cfg)
diff --git a/src/mainboard/prodrive/hermes/variants/baseboard/include/eeprom.h b/src/mainboard/prodrive/hermes/variants/baseboard/include/eeprom.h
index cdb5cea1fa..56539751f3 100644
--- a/src/mainboard/prodrive/hermes/variants/baseboard/include/eeprom.h
+++ b/src/mainboard/prodrive/hermes/variants/baseboard/include/eeprom.h
@@ -2,6 +2,34 @@
#include <soc/ramstage.h>
+__packed union eeprom_dimm_layout {
+ struct {
+ char name[50];
+ char manufacturer[50];
+ uint8_t ranks;
+ uint8_t controller_id;
+ uint8_t data_width_bits;
+ uint8_t bus_width_bits;
+ uint32_t capacity_mib;
+ uint32_t max_tdp_milliwatts;
+ };
+ uint8_t raw[0x80];
+};
+
+__packed struct eeprom_board_layout {
+ uint32_t signature;
+ union {
+ struct {
+ char cpu_name[50];
+ uint8_t cpu_count;
+ uint32_t cpu_max_non_turbo_frequency;
+ char pch_name[50];
+ union eeprom_dimm_layout dimm[4];
+ };
+ uint8_t raw_layout[617];
+ };
+};
+
__packed struct eeprom_board_settings {
uint32_t signature;
union {
@@ -30,7 +58,10 @@ __packed struct eeprom_layout {
uint8_t RawFSPSUPD[0xC00];
FSPS_UPD supd;
};
- uint8_t BoardLayout[0x400];
+ union {
+ uint8_t RawBoardLayout[0x400];
+ struct eeprom_board_layout BoardLayout;
+ };
uint8_t BootOrder[0x900];
union {
uint8_t RawBoardSetting[0x100];
@@ -46,6 +77,7 @@ bool read_write_config(void *blob, size_t read_offset, size_t write_offset, size
int check_signature(const size_t offset, const uint64_t signature);
struct eeprom_board_settings *get_board_settings(void);
void report_eeprom_error(const size_t off);
+bool write_board_settings(const struct eeprom_board_layout *new_layout);
#define READ_EEPROM(section_type, section_name, dest, opt_name) \
do { \