summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAngel Pons <th3fanbus@gmail.com>2023-03-22 13:25:09 +0100
committerLean Sheng Tan <sheng.tan@9elements.com>2023-03-24 16:39:17 +0000
commit56c1c4dff9c7ccd8968a3e32be800375bf3d5b78 (patch)
treef8614b97e6f66a63fe48cc1558f7e4dc37b12bea
parent964079f77cced3aedc468a888b6b6f32cfbec7c9 (diff)
mb/prodrive/atlas: Implement initial VPD support
Atlas stores VPD (Vital Product Data) in an I2C EEPROM, which is only connected to the EC. In order for the host (x86) to be able to access the VPD, the EC reads the EEPROM contents into a buffer in EC RAM and provides the host with read-only access to this EC RAM buffer through EMI (Embedded Memory Interface) 0. The VPD layout is designed to be extensible yet backwards compatible. The code in coreboot uses the revision field to know which fields are valid, and will populate the rest with fallback values. Use the serial number and part number in VPD to populate SMBIOS tables. Change-Id: I2d3d70fee22548daa73ef98af56c98e950dc5e9d Signed-off-by: Angel Pons <th3fanbus@gmail.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/73937 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Lean Sheng Tan <sheng.tan@9elements.com>
-rw-r--r--src/mainboard/prodrive/atlas/Makefile.inc2
-rw-r--r--src/mainboard/prodrive/atlas/mainboard.c29
-rw-r--r--src/mainboard/prodrive/atlas/smbios.c10
-rw-r--r--src/mainboard/prodrive/atlas/vpd.c60
-rw-r--r--src/mainboard/prodrive/atlas/vpd.h59
5 files changed, 155 insertions, 5 deletions
diff --git a/src/mainboard/prodrive/atlas/Makefile.inc b/src/mainboard/prodrive/atlas/Makefile.inc
index 3826c8f654..6c11876f35 100644
--- a/src/mainboard/prodrive/atlas/Makefile.inc
+++ b/src/mainboard/prodrive/atlas/Makefile.inc
@@ -1,6 +1,7 @@
## SPDX-License-Identifier: GPL-2.0-only
all-y += ec.c
+all-y += vpd.c
bootblock-y += bootblock.c
bootblock-y += early_gpio.c
@@ -9,3 +10,4 @@ romstage-y += romstage_fsp_params.c
ramstage-y += gpio.c
ramstage-y += mainboard.c
+ramstage-y += smbios.c
diff --git a/src/mainboard/prodrive/atlas/mainboard.c b/src/mainboard/prodrive/atlas/mainboard.c
index 227e30a3c4..db808ce45a 100644
--- a/src/mainboard/prodrive/atlas/mainboard.c
+++ b/src/mainboard/prodrive/atlas/mainboard.c
@@ -1,14 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-#include <device/device.h>
#include <console/console.h>
-#include <stdint.h>
+#include <device/device.h>
#include <gpio.h>
-#include <arch/io.h>
-#include <string.h>
#include <smbios.h>
+#include <string.h>
+#include <types.h>
#include "gpio.h"
+#include "vpd.h"
void smbios_fill_dimm_locator(const struct dimm_info *dimm, struct smbios_type17 *t)
{
@@ -53,6 +53,25 @@ static void mainboard_init(void *chip_info)
printk(BIOS_INFO, "HSID: 0x%x\n", get_hsid());
}
+static const char *get_formatted_pn(void)
+{
+ static char buffer[32 + ATLAS_SN_PN_LENGTH] = {0};
+ const char *prefix = "P/N: ";
+ snprintf(buffer, sizeof(buffer), "%s%s", prefix, get_emi_eeprom_vpd()->part_number);
+ return buffer;
+}
+
+static void mainboard_smbios_strings(struct device *dev, struct smbios_type11 *t)
+{
+ t->count = smbios_add_string(t->eos, get_formatted_pn());
+}
+
+static void mainboard_enable(struct device *dev)
+{
+ dev->ops->get_smbios_strings = mainboard_smbios_strings;
+}
+
struct chip_operations mainboard_ops = {
- .init = mainboard_init,
+ .init = mainboard_init,
+ .enable_dev = mainboard_enable,
};
diff --git a/src/mainboard/prodrive/atlas/smbios.c b/src/mainboard/prodrive/atlas/smbios.c
new file mode 100644
index 0000000000..1167a807ac
--- /dev/null
+++ b/src/mainboard/prodrive/atlas/smbios.c
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <smbios.h>
+
+#include "vpd.h"
+
+const char *smbios_mainboard_serial_number(void)
+{
+ return get_emi_eeprom_vpd()->serial_number;
+}
diff --git a/src/mainboard/prodrive/atlas/vpd.c b/src/mainboard/prodrive/atlas/vpd.c
new file mode 100644
index 0000000000..12371f71e6
--- /dev/null
+++ b/src/mainboard/prodrive/atlas/vpd.c
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <console/console.h>
+#include <string.h>
+#include <types.h>
+
+#include "ec.h"
+#include "mainboard.h"
+#include "vpd.h"
+
+const union emi_eeprom_vpd *get_emi_eeprom_vpd(void)
+{
+ static union emi_eeprom_vpd vpd = {0};
+
+ /* Check if cached VPD is valid */
+ if (vpd.header.revision == VPD_LATEST_REVISION)
+ return &vpd;
+
+ ec_emi_read(vpd.raw, EMI_0_IO_BASE_ADDR, 0, 0, sizeof(vpd.raw));
+
+ /* If the magic value doesn't match, consider EEPROM VPD unreliable */
+ if (vpd.header.magic != VPD_MAGIC) {
+ printk(BIOS_WARNING, "Atlas VPD: Bad magic value, using fallback defaults\n");
+ vpd.header.revision = 0;
+ } else {
+ printk(BIOS_DEBUG, "Atlas VPD: Got revision %u from EC\n", vpd.header.revision);
+ }
+
+ /*
+ * For backwards compatibility, if the VPD from the EC is an older
+ * version, uprev it to the latest version coreboot knows about by
+ * filling in the remaining fields with default values. Should the
+ * EC provide a newer VPD revision, coreboot would downgrade it to
+ * the latest version it knows about as the VPD layout is designed
+ * to be backwards compatible.
+ *
+ * Whenever the value of `VPD_LATEST_REVISION` is incremented, add
+ * a new `case` label just before the `default` label that matches
+ * the second latest revision to initialise the newly-added fields
+ * of the VPD structure with a reasonable fallback value. Note the
+ * intentional falling through.
+ */
+ switch (vpd.header.revision) {
+ case 0:
+ memset(vpd.raw, 0, sizeof(vpd.raw));
+ vpd.header.magic = VPD_MAGIC;
+ vpd.serial_number[0] = '\0';
+ vpd.part_number[0] = '\0';
+ vpd.profile = ATLAS_PROF_UNPROGRAMMED;
+ __fallthrough;
+ default:
+ /* Ensure serial numbers are NULL-terminated, update revision last */
+ vpd.serial_number[ATLAS_SN_PN_LENGTH - 1] = '\0';
+ vpd.part_number[ATLAS_SN_PN_LENGTH - 1] = '\0';
+ vpd.header.revision = VPD_LATEST_REVISION;
+ break;
+ }
+
+ return &vpd;
+}
diff --git a/src/mainboard/prodrive/atlas/vpd.h b/src/mainboard/prodrive/atlas/vpd.h
new file mode 100644
index 0000000000..3955c119a7
--- /dev/null
+++ b/src/mainboard/prodrive/atlas/vpd.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef ATLAS_VPD_H
+#define ATLAS_VPD_H
+
+/*
+ * The Atlas COMe module stores some non-volatile vital product data in
+ * an EC-attached I2C EEPROM. The EC firmware reads the EEPROM contents
+ * and provides them to the host via EMI (Embedded Memory Interface) 0.
+ */
+
+#include <types.h>
+
+#define VPD_MAGIC 0x56504453 /* 'VPDS' */
+
+/*
+ * Increment this value whenever new fields are added to the structures.
+ * Furthermore, adapt the `get_emi_eeprom_vpd()` function accordingly to
+ * provide fallback values for newly-added fields.
+ */
+#define VPD_LATEST_REVISION 1
+
+struct __packed emi_eeprom_vpd_header {
+ uint32_t magic;
+ uint8_t revision;
+ uint8_t _rfu[15]; /* Reserved for Future Use */
+};
+
+/* For backwards compatibility reasons, do NOT reuse enum values! */
+enum atlas_profile {
+ ATLAS_PROF_UNPROGRAMMED = 0, /* EEPROM not initialised */
+ ATLAS_PROF_DEFAULT = 1,
+ ATLAS_PROF_REALTIME_PERFORMANCE = 2,
+ ATLAS_PROF_THEMIS_LED_CONFIG = 3,
+};
+
+#define ATLAS_SN_PN_LENGTH 20
+
+#define EMI_EEPROM_LAYOUT_LENGTH ( \
+ sizeof(struct emi_eeprom_vpd_header) + \
+ ATLAS_SN_PN_LENGTH + \
+ ATLAS_SN_PN_LENGTH + \
+ sizeof(uint16_t) \
+ )
+
+union emi_eeprom_vpd {
+ struct __packed {
+ struct emi_eeprom_vpd_header header;
+ char serial_number[ATLAS_SN_PN_LENGTH]; /* xx-xx-xxx-xxx */
+ char part_number[ATLAS_SN_PN_LENGTH]; /* xxx-xxxx-xxxx.Rxx */
+ uint16_t profile;
+ };
+ uint8_t raw[EMI_EEPROM_LAYOUT_LENGTH];
+};
+
+/* Always returns a non-NULL pointer to valid data */
+const union emi_eeprom_vpd *get_emi_eeprom_vpd(void);
+
+#endif /* ATLAS_VPD_H */