David.Milosevic@9elements.com has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/68137 )
Change subject: [WIP] mb/prodrive/atlas: Populate smbios table with VPD from ECs EMI ......................................................................
[WIP] mb/prodrive/atlas: Populate smbios table with VPD from ECs EMI
The embedded controller stores serial-number and part-number within an EEPROM which is unreachable to the Host CPU. Therefore, on the EC side, we set up a shared memory region with the help of EMI and copy the VPDs to it. On the coreboot side, we can read those back and populate the SMBios table type 1 with the serial-number and type 11 with the part-number.
emi.*: small interface to access the EMI regions vpd.*: uses the emi interface in order to read the VPDs smbios.c: populates smbios type 1 with the serial-number by using vpd.h mainboard.c: uses vpd.h to add a string to smbios type 11 ld_config.*: offers functionality to configure logical devices devicetree.cb: enables address range 0xc00-0xcff for the EMI runtime registers Makefile.inc: adds emi.c, vpd.c, smbios.c, ld_config.c to the ramstage compilation
TODO: ld_config.* is redundant. We can replace this with pnp_device.h or pnp_ops.h
Signed-off-by: Milo-D David.Milosevic@9elements.com Change-Id: I47bb4883c43ff344a9bda92c3106dd025533b391 --- M src/mainboard/prodrive/atlas/Makefile.inc M src/mainboard/prodrive/atlas/devicetree.cb A src/mainboard/prodrive/atlas/emi.c A src/mainboard/prodrive/atlas/emi.h A src/mainboard/prodrive/atlas/ld_config.c A src/mainboard/prodrive/atlas/ld_config.h M src/mainboard/prodrive/atlas/mainboard.c A src/mainboard/prodrive/atlas/smbios.c A src/mainboard/prodrive/atlas/vpd.c A src/mainboard/prodrive/atlas/vpd.h 10 files changed, 503 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/37/68137/1
diff --git a/src/mainboard/prodrive/atlas/Makefile.inc b/src/mainboard/prodrive/atlas/Makefile.inc index 1446e54..1152771 100644 --- a/src/mainboard/prodrive/atlas/Makefile.inc +++ b/src/mainboard/prodrive/atlas/Makefile.inc @@ -7,3 +7,7 @@
ramstage-y += gpio.c ramstage-y += mainboard.c +ramstage-y += ld_config.c +ramstage-y += smbios.c +ramstage-y += emi.c +ramstage-y += vpd.c diff --git a/src/mainboard/prodrive/atlas/devicetree.cb b/src/mainboard/prodrive/atlas/devicetree.cb index 7ca5c88..f15ceee 100644 --- a/src/mainboard/prodrive/atlas/devicetree.cb +++ b/src/mainboard/prodrive/atlas/devicetree.cb @@ -14,6 +14,9 @@ # EC memory map range is 0x900-0x9ff register "gen3_dec" = "0x00fc0901"
+ # EC EMI-0 range is 0xc00 - 0xcff + register "gen4_dec" = "0x00fc0c01" + # SaGv Configuration register "sagv" = "CONFIG(ATLAS_ENABLE_SAGV) ? SaGv_Enabled : SaGv_Disabled"
diff --git a/src/mainboard/prodrive/atlas/emi.c b/src/mainboard/prodrive/atlas/emi.c new file mode 100644 index 0000000..cf03687 --- /dev/null +++ b/src/mainboard/prodrive/atlas/emi.c @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <arch/io.h> +#include <console/console.h> + +#include "ld_config.h" +#include "emi.h" + +#define ESPI_IO_COMPONENT 0x0d + +#define EMI_0_CONFIG_INDEX 0x68 +#define EMI_1_CONFIG_INDEX 0x6c + +#define EMI_0_BASE 0xc00 +#define EMI_1_BASE EMI_0_BASE + APPLICATION_ID + 0x1 + +#define encode_address_lsb(addr, access) ((((addr) & 0x00fc) >> 0) | (access)) +#define encode_address_msb(addr, region) ((((addr) & 0x7f00) >> 8) | ((region) << 7)) + +/* --- Forward Declaration of internal Functionality --- */ + +static inline u16 emi_base(const EMI_INSTANCE instance); +static inline u8 emi_index(const EMI_INSTANCE instance); + +/* --- Exposed Functionality --- */ + +void emi_init(const EMI_INSTANCE instance) { + + ld_config_enter(); + + const u16 base = emi_base(instance); + const u8 index = emi_index(instance); + + ld_config_write(ESPI_IO_COMPONENT, index + 2, (base & 0x00ff) >> 0); + ld_config_write(ESPI_IO_COMPONENT, index + 3, (base & 0xff00) >> 8); + ld_config_write(ESPI_IO_COMPONENT, index + 0, 0b00000001); // valid bit + + ld_config_leave(); +} + +int emi_read_region(const EMI_INSTANCE instance, const EMI_REGION region, const EMI_ACCESS access, const u16 addr, u8 *buff) { + + const u16 base = emi_base(instance); + + outb(encode_address_lsb(addr, access), base + EC_ADDRESS_LSB); + outb(encode_address_msb(addr, region), base + EC_ADDRESS_MSB); + + switch(access) { + + case EMI_ACCESS_8BIT: + + *((u8*) buff) = inb(base + EC_DATA_BYTE_0); + + break; + + case EMI_ACCESS_16BIT: + + *((u16*) buff) = inw(base + EC_DATA_BYTE_0); + + break; + + case EMI_ACCESS_32BIT: + case EMI_ACCESS_32BIT_AUTO_INC: + + *((u32*) buff) = inl(base + EC_DATA_BYTE_0); + + break; + + default: printk(BIOS_NOTICE, "emi: invalid access.\n"); return EMI_FAILURE; + } + + return EMI_SUCCESS; +} + +int emi_write_region(const EMI_INSTANCE instance, const EMI_REGION region, const EMI_ACCESS access, const u16 addr, u8 *bytes) { + + const u16 base = emi_base(instance); + + outb(encode_address_lsb(addr, access), base + EC_ADDRESS_LSB); + outb(encode_address_msb(addr, region), base + EC_ADDRESS_MSB); + + switch(access) { + + case EMI_ACCESS_8BIT: + + outb(*((u8*) bytes), base + EC_DATA_BYTE_0); + + break; + + case EMI_ACCESS_16BIT: + + outw(*((u16*) bytes), base + EC_DATA_BYTE_0); + + break; + + case EMI_ACCESS_32BIT: + case EMI_ACCESS_32BIT_AUTO_INC: + + outl(*((u32*) bytes), base + EC_DATA_BYTE_0); + + break; + + default: printk(BIOS_NOTICE, "emi: invalid access.\n"); return EMI_FAILURE; + } + + return EMI_SUCCESS; +} + +void emi_read_region_block(const EMI_INSTANCE instance, const EMI_REGION region, const u16 addr, const u16 n, u8 *buff) { + + u16 idx = 0; + + for(;;) { + + u8 bytes[4] = { 0x0, 0x0, 0x0, 0x0 }; + emi_read_region(instance, region, EMI_ACCESS_32BIT, addr + idx, bytes); + + for(u8 i = 0; i < 4; i++) { + + if(idx == n) + return; + + buff[idx++] = bytes[i]; + } + } +} + +inline void emi_write_register(const EMI_INSTANCE instance, const u8 offs, const u8 value) { + + outb(value, emi_base(instance) + offs); +} + +inline u8 emi_read_register(const EMI_INSTANCE instance, const u8 offs) { + + return inb(emi_base(instance) + offs); +} + +/* --- Internal Functionality --- */ + +static inline u16 emi_base(const EMI_INSTANCE instance) { + + return (instance == EMI_INSTANCE_0) ? EMI_0_BASE : EMI_1_BASE; +} + +static inline u8 emi_index(const EMI_INSTANCE instance) { + + return (instance == EMI_INSTANCE_0) ? EMI_0_CONFIG_INDEX : EMI_1_CONFIG_INDEX; +} diff --git a/src/mainboard/prodrive/atlas/emi.h b/src/mainboard/prodrive/atlas/emi.h new file mode 100644 index 0000000..e01de45 --- /dev/null +++ b/src/mainboard/prodrive/atlas/emi.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __EMI_H__ +#define __EMI_H__ + +#include <stdint.h> + +#define EMI_SUCCESS 0 +#define EMI_FAILURE 1 + +#define HOST_EC_MBOX 0x00 +#define EC_HOST_MBOX 0x01 +#define EC_ADDRESS_LSB 0x02 +#define EC_ADDRESS_MSB 0x03 +#define EC_DATA_BYTE_0 0x04 +#define EC_DATA_BYTE_1 0x05 +#define EC_DATA_BYTE_2 0x06 +#define EC_DATA_BYTE_3 0x07 +#define INTERRUPT_SOURCE_LSB 0x08 +#define INTERRUPT_SOURCE_MSB 0x09 +#define INTERRUPT_MASK_LSB 0x0a +#define INTERRUPT_MASK_MSB 0x0b +#define APPLICATION_ID 0x0c + +/* + * === Embedded Memory Interface Library === + * + * A small library for accessing EMI regions within + * the embedded controller (MEC152x). + * + * */ + +typedef enum { + + EMI_INSTANCE_0, ///< EMI instance 0 (EMI0) + EMI_INSTANCE_1 ///< EMI instance 1 (EMI1) + +} EMI_INSTANCE; + +typedef enum { + + EMI_REGION_0, ///< EMI region 0 + EMI_REGION_1 ///< EMI region 1 + +} EMI_REGION; + +typedef enum { + + EMI_ACCESS_8BIT = 0b00, ///< 8-bit read/write access + EMI_ACCESS_16BIT = 0b01, ///< 16-bit read/write access + EMI_ACCESS_32BIT = 0b10, ///< 32-bit read/write access + EMI_ACCESS_32BIT_AUTO_INC = 0b11 ///< 32-bit read/write access (auto increment) + +} EMI_ACCESS; + +/* + * emi_init - initialize a specific EMI instance + * @instance: the EMI instance to initialize + * + * [note] it is mandatory to call this function before using + * the rest of the interface. + * */ +extern void emi_init(const EMI_INSTANCE instance); + +/* + * emi_read_region - read from an EMI region at a specific address + * @instance: the desired EMI instance + * @region: the EMI region to read from + * @access: read access type (8-bit read, 16-bit read, ...) + * @addr: the target address (dword aligned) + * @buff: a byte buffer to store contents of the EMI region + * => returns EMI_SUCCESS on success and EMI_FAILURE on failure + * */ +extern int emi_read_region(const EMI_INSTANCE instance, const EMI_REGION region, const EMI_ACCESS access, const u16 addr, u8 *buff); + +/* + * emi_write_region - write to an EMI region at a specific address + * @instance: the desired EMI instance + * @region: the EMI region to write to + * @access: write access type (8-bit write, 16-bit write, ...) + * @addr: the target address (dword aligned) + * @bytes: bytes to write + * => returns EMI_SUCCESS on success and EMI_FAILURE on failure + * */ +extern int emi_write_region(const EMI_INSTANCE instance, const EMI_REGION region, const EMI_ACCESS access, const u16 addr, u8 *bytes); + +/* + * emi_read_region_block - read a sequential block from an EMI region + * @instance: the desired EMI instance + * @region: the EMI region to read from + * @addr: base address for block read (dword aligned) + * @n: the number of bytes to read from the region + * @buff: a byte buffer to store contents of the EMI region + * */ +extern void emi_read_region_block(const EMI_INSTANCE instance, const EMI_REGION region, const u16 addr, const u16 n, u8 *buff); + +/* + * emi_write_register - write to an EMI runtime register + * @instance: the desired EMI instance + * @offs: runtime register offset + * @value: the value to write to the runtime register + * */ +extern void emi_write_register(const EMI_INSTANCE instance, const u8 offs, const u8 value); + +/* + * emi_read_register - read from an EMI runtime register + * @instance: the desired EMI instance + * @offs: runtime register offset + * => returns a byte from a runtime register + * */ +extern u8 emi_read_register(const EMI_INSTANCE instance, const u8 offs); + +#endif diff --git a/src/mainboard/prodrive/atlas/ld_config.c b/src/mainboard/prodrive/atlas/ld_config.c new file mode 100644 index 0000000..c03ada4 --- /dev/null +++ b/src/mainboard/prodrive/atlas/ld_config.c @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <arch/io.h> +#include "ld_config.h" + +#define CONFIG_PORT_INDEX 0x2e +#define CONFIG_PORT_DATA 0x2f + +#define CONFIG_ENTRY_KEY 0x55 +#define CONFIG_EXIT_KEY 0xaa + +#define LDN_CONFIG_REGISTER 0x07 + +/* + * This file is redundant and should only be used temporarily within + * the WIP. We can use things like pnp_device.h instead. + * */ + + +void ld_config_enter(void) { + + outb(CONFIG_ENTRY_KEY, CONFIG_PORT_INDEX); +} + +void ld_config_leave(void) { + + outb(CONFIG_EXIT_KEY, CONFIG_PORT_INDEX); +} + +void ld_config_write(const u8 ldn, const u8 index, const u8 value) { + + outb(LDN_CONFIG_REGISTER, CONFIG_PORT_INDEX); + outb(ldn, CONFIG_PORT_DATA); + outb(index, CONFIG_PORT_INDEX); + outb(value, CONFIG_PORT_DATA); +} + +u8 ld_config_read(const u8 ldn, const u8 index) { + + outb(LDN_CONFIG_REGISTER, CONFIG_PORT_INDEX); + outb(ldn, CONFIG_PORT_DATA); + outb(index, CONFIG_PORT_INDEX); + + return inb(CONFIG_PORT_DATA); +} \ No newline at end of file diff --git a/src/mainboard/prodrive/atlas/ld_config.h b/src/mainboard/prodrive/atlas/ld_config.h new file mode 100644 index 0000000..bc2306b --- /dev/null +++ b/src/mainboard/prodrive/atlas/ld_config.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __LD_CONFIG_H__ +#define __LD_CONFIG_H__ + +/* + * This file is redundant and should only be used temporarily within + * the WIP. We can use things like pnp_device.h instead. + * */ + +/* + * ld_config_enter - enter logical device config mode + * */ +extern void ld_config_enter(void); + +/* + * ld_config_leave - leave logical device config mode + * */ +extern void ld_config_leave(void); + +/* + * ld_config_write - write to a logical device configuration register + * @ldn: logical device number + * @index: index of configuration register + * @value: value to be written to @index + * */ +extern void ld_config_write(const u8 ldn, const u8 index, const u8 value); + +/* + * ld_config_read - read from a logical device configuration register + * @ldn: logical device number + * @index: index of configuration register + * => returns the content of the configuration register + * */ +extern u8 ld_config_read(const u8 ldn, const u8 index); + +#endif diff --git a/src/mainboard/prodrive/atlas/mainboard.c b/src/mainboard/prodrive/atlas/mainboard.c index 036208a..1587d6b 100644 --- a/src/mainboard/prodrive/atlas/mainboard.c +++ b/src/mainboard/prodrive/atlas/mainboard.c @@ -1,15 +1,50 @@ /* SPDX-License-Identifier: GPL-2.0-only */
-#include <device/device.h> #include <stdint.h> +#include <string.h> +#include <smbios.h> +#include <device/device.h>
+#include "vpd.h" #include "gpio.h"
+/* <--- Forward Declaration of smbios Helpers ---> */ + +static void get_smbios_strings(struct device *dev, struct smbios_type11 *t); +static const char* get_smbios_part_number(void); + +/* <--- Mainboard Chip Operations ---> */ + static void mainboard_init(void *chip_info) { configure_gpio_pads(); }
+static void mainboard_enable(struct device *dev) { + + dev->ops->get_smbios_strings = get_smbios_strings; +} + struct chip_operations mainboard_ops = { - .init = mainboard_init, + + .init = mainboard_init, + .enable_dev = mainboard_enable }; + +/* <--- smbios Helpers ---> */ + +static void get_smbios_strings(struct device *dev, struct smbios_type11 *t) { + + t->count = smbios_add_string(t->eos, get_smbios_part_number()); +} + +static const char* get_smbios_part_number(void) { + + const char prefix[5] = "P/N: "; + static u8 pn[sizeof(prefix) + PN_LENGTH + 1] = { '\x00' }; + + strncpy((char*) pn, prefix, sizeof(prefix)); + vpd_get(vpd(part_number), pn + sizeof(prefix)); + + return (char*) pn; +} diff --git a/src/mainboard/prodrive/atlas/smbios.c b/src/mainboard/prodrive/atlas/smbios.c new file mode 100644 index 0000000..8d66d8c --- /dev/null +++ b/src/mainboard/prodrive/atlas/smbios.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <string.h> +#include <smbios.h> + +#include "vpd.h" + +const char* smbios_mainboard_serial_number(void) { + + static u8 sn[SN_LENGTH + 1] = { '\x00' }; + vpd_get(vpd(serial_number), sn); + + return (char*) sn; +} diff --git a/src/mainboard/prodrive/atlas/vpd.c b/src/mainboard/prodrive/atlas/vpd.c new file mode 100644 index 0000000..01e6219 --- /dev/null +++ b/src/mainboard/prodrive/atlas/vpd.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <types.h> + +#include "emi.h" +#include "vpd.h" + +/* <--- Globals ---> */ + +static bool emi_initialized = false; + +/* <--- Forward Declaration of Internal Functionality ---> */ + +static u16 vpd_length(const u16 vpd); + +/* <--- Exposed Functionality ---> */ + +void vpd_get(const u16 vpd, u8 *buff) { + + if(emi_initialized == false) { + + emi_init(EMI_INSTANCE_0); + emi_initialized = true; + } + + const u16 len = vpd_length(vpd); + emi_read_region_block(EMI_INSTANCE_0, EMI_REGION_0, vpd, len, buff); +} + +/* <--- Internal Functionality ---> */ + +static u16 vpd_length(const u16 vpd) { + + switch(vpd) { + + case vpd(part_number): return PN_LENGTH; + case vpd(serial_number): return SN_LENGTH; + + default: return 0; + } +} diff --git a/src/mainboard/prodrive/atlas/vpd.h b/src/mainboard/prodrive/atlas/vpd.h new file mode 100644 index 0000000..e0f0bf5 --- /dev/null +++ b/src/mainboard/prodrive/atlas/vpd.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __VPD_H__ +#define __VPD_H__ + +#include <stddef.h> + +#define PAGESIZE 16 +#define PN_LENGTH 12 ///< part_number, without nullbyte +#define SN_LENGTH 13 ///< serial_number, without nullbyte + +typedef struct __attribute__((__packed__)) vpd_section { + + char part_number [PAGESIZE]; + char serial_number[PAGESIZE]; + +} vpd_section_t; + +/* + * pass this macro as vpd argument, where + * x is a member of vpd_section_t. For example, + * vpd(serial_number) to access the serial number + * within the vpd section. + * */ +#define vpd(x) offsetof(vpd_section_t, x) + +/* + * vpd_get - retrieve a vpd member + * @vpd: target vpd. use vpd(x) macro + * @buff: byte buffer to store the vpd + * */ +extern void vpd_get(const u16 vpd, u8 *buff); + +#endif \ No newline at end of file