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>
This commit is contained in:
Patrick Rudolph 2020-11-12 16:41:57 +01:00 committed by Patrick Rudolph
parent 71555955e9
commit 35f0a8fec7
3 changed files with 189 additions and 1 deletions

View File

@ -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;
}

View File

@ -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)

View File

@ -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 { \