From 35f0a8fec7cd6127838fbe5a36170ba73efa49a4 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Thu, 12 Nov 2020 16:41:57 +0100 Subject: 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 Signed-off-by: Angel Pons Reviewed-on: https://review.coreboot.org/c/coreboot/+/48810 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Rudolph --- src/mainboard/prodrive/hermes/eeprom.c | 79 ++++++++++++++++++++++ src/mainboard/prodrive/hermes/mainboard.c | 77 +++++++++++++++++++++ .../hermes/variants/baseboard/include/eeprom.h | 34 +++++++++- 3 files changed, 189 insertions(+), 1 deletion(-) (limited to 'src/mainboard/prodrive') 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 #include +#include #include #include #include @@ -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 #include +#include +#include +#include #include +#include #include #include +#include #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 +__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 { \ -- cgit v1.2.3