summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 3826c8f654bd..6c11876f350e 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 227e30a3c4fe..db808ce45a9f 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 000000000000..1167a807ace0
--- /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 000000000000..12371f71e6b7
--- /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 000000000000..3955c119a760
--- /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 */