diff options
-rw-r--r-- | src/mainboard/prodrive/hermes/eeprom.c | 79 | ||||
-rw-r--r-- | src/mainboard/prodrive/hermes/mainboard.c | 77 | ||||
-rw-r--r-- | src/mainboard/prodrive/hermes/variants/baseboard/include/eeprom.h | 34 |
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 { \ |