From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001
From: Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com>
Date: Tue, 10 Oct 2023 14:33:42 +0000
Subject: Add submodule dependency files

Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
---
 roms/skiboot/hdata/vpd.c | 767 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 767 insertions(+)
 create mode 100644 roms/skiboot/hdata/vpd.c

(limited to 'roms/skiboot/hdata/vpd.c')

diff --git a/roms/skiboot/hdata/vpd.c b/roms/skiboot/hdata/vpd.c
new file mode 100644
index 000000000..c778de346
--- /dev/null
+++ b/roms/skiboot/hdata/vpd.c
@@ -0,0 +1,767 @@
+// SPDX-License-Identifier: Apache-2.0
+/* Copyright 2013-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <vpd.h>
+#include <string.h>
+#include "spira.h"
+#include "hdata.h"
+#include <device.h>
+#include "hdata.h"
+#include <inttypes.h>
+#include <mem_region-malloc.h>
+
+struct card_info {
+	const char *ccin; 	/* Customer card identification number */
+	const char *description;
+};
+
+static const struct card_info card_table[] = {
+	{"2B06", "System planar 2S4U"},
+	{"2B07", "System planar 1S4U"},
+	{"2B2E", "System planar 2S2U"},
+	{"2B2F", "System planar 1S2U"},
+	{"2CD4", "System planar 2S4U"},
+	{"2CD5", "System planar 1S4U"},
+	{"2CD6", "System planar 2S2U"},
+	{"2CD7", "System planar 1S2U"},
+	{"2CD7", "System planar 1S2U"},
+	{"2B09", "Base JBOD, RAID and Backplane HD"},
+	{"57D7", "Split JBOD, RAID Card"},
+	{"2B0B", "Native I/O Card"},
+
+	/* Anchor cards */
+	{"52FE", "System Anchor Card - IBM Power 824"},
+	{"52F2", "System Anchor Card - IBM Power 814"},
+	{"52F5", "System Anchor Card - IBM Power 822"},
+	{"561A", "System Anchor Card - IBM Power 824L"},
+	{"524D", "System Anchor Card - IBM Power 822L"},
+	{"560F", "System Anchor Card - IBM Power 812L"},
+	{"561C", "System Anchor Card - DS8870"},
+
+	/* Memory DIMMs */
+	{"31E0", "16GB CDIMM"},
+	{"31E8", "16GB CDIMM"},
+	{"31E1", "32GB CDIMM"},
+	{"31E9", "32GB CDIMM"},
+	{"31E2", "64GB CDIMM"},
+	{"31EA", "64GB CDIMM"},
+
+	/* Power supplies */
+	{"2B1D", "Power Supply 900W AC"},
+	{"2B1E", "Power Supply 1400W AC"},
+	{"2B75", "Power Supply 1400W HVDC"},
+
+	/* Fans */
+	{"2B1F", "Fan 4U (A1, A2, A3, A4)"},
+	{"2B29", "Fan 2U (A1, A2, A3, A4, A5, A6)"},
+
+	/* Other cards */
+};
+
+struct vpd_key_map {
+	const char *keyword;		/* 2 char keyword  */
+	const char *description;
+};
+
+static const struct vpd_key_map vpd_key_table[] = {
+	{"AA", "ac-power-supply"},
+	{"AM", "air-mover"},
+	{"AV", "anchor-card"},
+
+	{"BA", "bus-adapter-card"},
+	{"BC", "battery-charger"},
+	{"BD", "bus-daughter-card"},
+	{"BE", "bus-expansion-card"},
+	{"BP", "backplane"},
+	{"BR", "backplane-riser"},
+	{"BX", "backplane-extender"},
+
+	{"CA", "calgary-bridge"},
+	{"CB", "infiniband-connector"},
+	{"CC", "clock-card"},
+	{"CD", "card-connector"},
+	{"CE", "ethernet-connector"},
+	{"CL", "calgary-phb"},
+	{"CI", "capacity-card"},
+	{"CO", "sma-connector"},
+	{"CP", "processor-capacity-card"},
+	{"CR", "rio-connector"},
+	{"CS", "serial-connector"},
+	{"CU", "usb-connector"},
+
+	{"DB", "dasd-backplane"},
+	{"DC", "drawer-card"},
+	{"DE", "drawer-extension"},
+	{"DI", "drawer-interposer"},
+	{"DL", "p7ih-dlink-connector"},
+	{"DT", "legacy-pci-card"},
+	{"DV", "media-drawer-led"},
+
+	{"EI", "enclosure-led"},
+	{"EF", "enclosure-fault-led"},
+	{"ES", "embedded-sas"},
+	{"ET", "ethernet-riser"},
+	{"EV", "enclosure"},
+
+	{"FM", "frame"},
+	{"FN", "fru-stocking-part-number"},
+
+	{"HB", "host-rio-pci-card"},
+	{"HD", "high-speed-card"},
+	{"HM", "hmc-connector"},
+
+	{"IB", "io-backplane"},
+	{"IC", "io-card"},
+	{"ID", "ide-connector"},
+	{"II", "io-drawer-led"},
+	{"IP", "interplane-card"},
+	{"IS", "smp-vbus-cable"},
+	{"IT", "enclosure-cable"},
+	{"IV", "io-enclosure"},
+
+	{"KV", "keyboard-led"},
+
+	{"L2", "l2-cache-module"},
+	{"L3", "l3-cache-module"},
+	{"LC", "squadrons-light-connector"},
+	{"LR", "p7ih-connector"},
+	{"LO", "system-locate-led"},
+	{"LT", "squadrons-light-strip"},
+
+	{"MB", "media-backplane"},
+	{"ME", "map-extension"},
+	{"MM", "mip-meter"},
+	{"MS", "ms-dimm"},
+
+	{"NB", "nvram-battery"},
+	{"NC", "sp-node-controller"},
+	{"ND", "numa-dimm"},
+
+	{"OD", "cuod-card"},
+	{"OP", "op-panel"},
+	{"OS", "oscillator"},
+
+	{"P2", "ioc"},
+	{"P5", "ioc-bridge"},
+	{"PB", "io-drawer-backplane"},
+	{"PC", "power-capacitor"},
+	{"PD", "processor-card"},
+	{"PF", "processor"},
+	{"PI", "ioc-phb"},
+	{"PO", "spcn"},
+	{"PN", "spcn-connector"},
+	{"PR", "pci-riser-card"},
+	{"PS", "power-supply"},
+	{"PT", "pass-through-card"},
+	{"PX", "psc-sync-card"},
+	{"PW", "power-connector"},
+
+	{"RG", "regulator"},
+	{"RI", "riser"},
+	{"RK", "rack-indicator"},
+	{"RW", "riscwatch-connector"},
+
+	{"SA", "sys-attn-led"},
+	{"SB", "backup-sysvpd"},
+	{"SC", "scsi-connector"},
+	{"SD", "sas-connector"},
+	{"SI", "scsi-ide-converter"},
+	{"SL", "phb-slot"},
+	{"SN", "smp-cable-connector"},
+	{"SP", "service-processor"},
+	{"SR", "service-card"},
+	{"SS", "soft-switch"},
+	{"SV", "system-vpd"},
+	{"SY", "legacy-sysvpd"},
+
+	{"TD", "tod-clock"},
+	{"TI", "torrent-pcie-phb"},
+	{"TL", "torrent-riser"},
+	{"TM", "thermal-sensor"},
+	{"TP", "tpmd-adapter"},
+	{"TR", "torrent-bridge"},
+
+	{"VV", "root-node-vpd"},
+
+	{"WD", "water_device"},
+};
+
+static const char *vpd_map_name(const char *vpd_name)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vpd_key_table); i++)
+		if (!strcmp(vpd_key_table[i].keyword, vpd_name))
+			return vpd_key_table[i].description;
+
+	prlog(PR_WARNING, "VPD: Could not map FRU ID %s to a known name\n",
+	      vpd_name);
+
+	return "Unknown";
+}
+
+static const struct card_info *card_info_lookup(char *ccin)
+{
+	int i;
+	for(i = 0; i < ARRAY_SIZE(card_table); i++)
+		/* CCIN is always 4 bytes in size */
+		if (!strncmp(card_table[i].ccin, ccin, 4))
+			return &card_table[i];
+	return NULL;
+}
+
+/* Discard trailing spaces and populate device tree */
+static struct dt_property *dt_add_prop_sanitize_val(struct dt_node *node,
+			     const char *name, const char *val, int vlen)
+{
+	char *prop = zalloc(vlen + 1);
+	int i;
+	struct dt_property *p = NULL;
+
+	if (!prop)
+		return p;
+
+	memcpy(prop, val, vlen);
+	for (i = vlen - 1; i >= 0; i--) {
+		if (prop[i] != 0x20) {
+			prop[i + 1] = '\0';
+			break;
+		}
+	}
+
+	if (i >= 0 && !dt_find_property(node, name))
+		p = dt_add_property_string(node, name, prop);
+
+	free(prop);
+	return p;
+}
+
+/*
+ * OpenPower system does not provide processor vendor name under FRU VPD.
+ * Parse processor module VPD to get vendor detail
+ */
+void dt_add_proc_vendor(struct dt_node *proc_node,
+			const void *mvpd, unsigned int mvpd_sz)
+{
+	const void *kw;
+	uint8_t sz;
+
+	kw = vpd_find(mvpd, mvpd_sz, "VINI", "VN", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(proc_node, "vendor", kw, sz);
+}
+
+/*
+ * For OpenPOWER, we only decipher OPFR records. While OP HDAT have VINI
+ * records too, populating the fields in there is optional. Also, there
+ * is an overlap in the fields contained therein.
+ */
+static void vpd_opfr_parse(struct dt_node *node,
+		const void *fruvpd, unsigned int fruvpd_sz)
+{
+	const void *kw;
+	uint8_t sz;
+
+	/* Vendor Name */
+	kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VN", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "vendor", kw, sz);
+
+	/* FRU Description */
+	kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "DR", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "description", kw, sz);
+
+	/* Part number */
+	kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VP", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "part-number", kw, sz);
+
+	/* Serial number */
+	kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VS", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "serial-number", kw, sz);
+
+	/* Build date in BCD */
+	kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "MB", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "build-date", kw, sz);
+
+	return;
+}
+
+/*
+ * For CPUs, parse the VRML data.
+ */
+static void vpd_vrml_parse(struct dt_node *node,
+		const void *fruvpd, unsigned int fruvpd_sz)
+{
+	const void *kw;
+	uint8_t sz;
+
+	/* Part number */
+	kw = vpd_find(fruvpd, fruvpd_sz, "VRML", "PN", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "part-number", kw, sz);
+
+	/* Serial number */
+	kw = vpd_find(fruvpd, fruvpd_sz, "VRML", "SN", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "serial-number", kw, sz);
+
+	return;
+}
+
+static void vpd_vini_parse(struct dt_node *node,
+			   const void *fruvpd, unsigned int fruvpd_sz)
+{
+	const void *kw;
+	const char *desc;
+	uint8_t sz;
+	const struct card_info *cinfo;
+
+	/* FRU Stocking Part Number */
+	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "FN", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "fru-number", kw, sz);
+
+	/* Serial Number */
+	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "SN", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "serial-number", kw, sz);
+
+	/* Part Number */
+	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "PN", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "part-number", kw, sz);
+
+	/* Vendor Name */
+	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "VN", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "vendor", kw, sz);
+
+	/* CCIN Extension */
+	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CE", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "ccin-extension", kw, sz);
+
+	/* HW Version info */
+	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "HW", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "hw-version", kw, sz);
+
+	/* Card type info */
+	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CT", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "card-type", kw, sz);
+
+	/* HW characteristics info */
+	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "B3", &sz);
+	if (kw)
+		dt_add_prop_sanitize_val(node, "hw-characteristics", kw, sz);
+
+	/* Customer Card Identification Number (CCIN) */
+	kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CC", &sz);
+	if (kw) {
+		dt_add_prop_sanitize_val(node, "ccin", kw, sz);
+
+		cinfo = card_info_lookup((char *)kw);
+		if (cinfo) {
+			dt_add_property_string(node,
+				       "description", cinfo->description);
+		} else {
+			desc = vpd_find(fruvpd, fruvpd_sz, "VINI", "DR", &sz);
+			if (desc) {
+				dt_add_prop_sanitize_val(node,
+						     "description", desc, sz);
+			} else {
+				dt_add_property_string(node, "description", "Unknown");
+				prlog(PR_WARNING,
+				      "VPD: CCIN desc not available for: %s\n",
+				      (char*)kw);
+			}
+		}
+	}
+
+	return;
+}
+
+static bool valid_child_entry(const struct slca_entry *entry)
+{
+	if ((entry->install_indic == SLCA_INSTALL_INSTALLED) &&
+		(entry->vpd_collected == SLCA_VPD_COLLECTED))
+		return true;
+
+	return false;
+}
+
+void vpd_data_parse(struct dt_node *node, const void *fruvpd, u32 fruvpd_sz)
+{
+	if (vpd_find_record(fruvpd, fruvpd_sz, "OPFR", NULL))
+		vpd_opfr_parse(node, fruvpd, fruvpd_sz);
+	else if (vpd_find_record(fruvpd, fruvpd_sz, "VRML", NULL))
+		vpd_vrml_parse(node, fruvpd, fruvpd_sz);
+	else
+		vpd_vini_parse(node, fruvpd, fruvpd_sz);
+}
+
+/* Create the /vpd node and add its children */
+void dt_init_vpd_node(void)
+{
+	const char *name, *p_name;
+	int count, index;
+	uint64_t addr, p_addr;
+	struct dt_node *dt_vpd;
+	struct HDIF_common_hdr *slca_hdr;
+	struct dt_node *parent, *node;
+	const struct slca_entry *entry, *p_entry;
+
+	dt_vpd = dt_new(dt_root, "vpd");
+	assert(dt_vpd);
+	dt_add_property_string(dt_vpd, "compatible", "ibm,opal-v3-vpd");
+	dt_add_property_cells(dt_vpd, "#size-cells", 0);
+	dt_add_property_cells(dt_vpd, "#address-cells", 1);
+
+	slca_hdr = get_hdif(&spira.ntuples.slca, SLCA_HDIF_SIG);
+	if (!slca_hdr) {
+		prerror("SLCA Invalid\n");
+		return;
+	}
+
+	count = HDIF_get_iarray_size(slca_hdr, SLCA_IDATA_ARRAY);
+	if (count < 0) {
+		prerror("SLCA: Can't find SLCA array size!\n");
+		return;
+	}
+
+	for (index = 0; index < count; index++) {
+		/* Get SLCA entry */
+		entry = slca_get_entry(index);
+		if (!entry)
+			continue;
+
+		/*
+		 * A child entry is valid if all of the following criteria is met
+		 *   a. SLCA_INSTALL_INSTALLED is set in s_entry->install_indic
+		 *   b. SLCA_VPD_COLLECTED is set in s_entry->vpd_collected
+		 *   c. The SLCA is not a duplicate entry.
+		 */
+		if (!valid_child_entry(entry))
+			goto next_entry;
+
+		name = vpd_map_name(entry->fru_id);
+		addr = be16_to_cpu(entry->rsrc_id);
+		/* Check node is already created or not */
+		if (dt_find_by_name_addr(dt_vpd, name, addr))
+			goto next_entry;
+
+		/* Get parent node */
+		if (index == SLCA_ROOT_INDEX) {
+			parent = dt_vpd;
+		} else {
+			p_entry = slca_get_entry(be16_to_cpu(entry->parent_index));
+			if (!p_entry)
+				goto next_entry;
+			p_name = vpd_map_name(p_entry->fru_id);
+			p_addr = be16_to_cpu(p_entry->rsrc_id);
+			parent = dt_find_by_name_addr(dt_vpd, p_name, p_addr);
+		}
+		if (!parent)
+			goto next_entry;
+
+		node = dt_new_addr(parent, name, addr);
+		if (!node) {
+			prerror("VPD: Creating node at %s@%"PRIx64" failed\n",
+				name, addr);
+			goto next_entry;
+		}
+
+		/* Add location code */
+		slca_vpd_add_loc_code(node, be16_to_cpu(entry->my_index));
+		/* Add FRU label */
+		dt_add_property(node, "fru-type", entry->fru_id, 2);
+		dt_add_property_cells(node, "reg", addr);
+		dt_add_property_cells(node, "#size-cells", 0);
+		dt_add_property_cells(node, "#address-cells", 1);
+
+next_entry:
+		/* Skip dups -- dups are contiguous */
+		index += entry->nr_dups;
+	}
+}
+
+struct dt_node *dt_add_vpd_node(const struct HDIF_common_hdr *hdr,
+				int indx_fru, int indx_vpd)
+{
+	const struct spira_fru_id *fru_id;
+	unsigned int fruvpd_sz, fru_id_sz;
+	const struct slca_entry *entry;
+	struct dt_node *dt_vpd, *node;
+	const void *fruvpd;
+	const char *name;
+	uint64_t addr;
+
+	fru_id = HDIF_get_idata(hdr, indx_fru, &fru_id_sz);
+	if (!fru_id)
+		return NULL;
+
+	fruvpd = HDIF_get_idata(hdr, indx_vpd, &fruvpd_sz);
+	if (!CHECK_SPPTR(fruvpd))
+		return NULL;
+
+	dt_vpd = dt_find_by_path(dt_root, "/vpd");
+	if (!dt_vpd)
+		return NULL;
+
+	entry = slca_get_entry(be16_to_cpu(fru_id->slca_index));
+	if (!entry)
+		return NULL;
+
+	name = vpd_map_name(entry->fru_id);
+	addr = be16_to_cpu(entry->rsrc_id);
+	/* Get the node already created */
+	node = dt_find_by_name_addr(dt_vpd, name, addr);
+	/*
+	 * It is unlikely that node not found because vpd nodes have the
+	 * corresponding slca entry which we would have used to populate the vpd
+	 * tree during the 'first' pass above so that we just need to perform
+	 * VINI parse and add the vpd data..
+	 */
+	if (!node)
+		return NULL;
+
+	/* Parse VPD fields, ensure that it has not been added already */
+	if (vpd_valid(fruvpd, fruvpd_sz)
+	    && !dt_find_property(node, "ibm,vpd")) {
+		dt_add_property(node, "ibm,vpd", fruvpd, fruvpd_sz);
+		vpd_data_parse(node, fruvpd, fruvpd_sz);
+	}
+
+	return node;
+}
+
+static void dt_add_model_name(void)
+{
+	const char *model_name = NULL;
+	const struct machine_info *mi;
+	const struct iplparams_sysparams *p;
+	const struct HDIF_common_hdr *iplp;
+	const struct dt_property *model;
+
+	model = dt_find_property(dt_root, "model");
+
+	iplp = get_hdif(&spira.ntuples.ipl_parms, "IPLPMS");
+	if (!iplp)
+		goto def_model;
+
+	p = HDIF_get_idata(iplp, IPLPARAMS_SYSPARAMS, NULL);
+	if (!CHECK_SPPTR(p))
+		goto def_model;
+
+	if (be16_to_cpu(iplp->version) >= 0x60)
+		model_name = p->sys_type_str;
+
+def_model:
+	if ((!model_name || model_name[0] == '\0') && model) {
+		mi = machine_info_lookup(model->prop);
+		if (mi)
+			model_name = mi->name;
+	}
+
+	if(model_name)
+		dt_add_property_string(dt_root, "model-name", model_name);
+}
+
+static void sysvpd_parse_opp(const void *sysvpd, unsigned int sysvpd_sz)
+{
+	const char *v;
+	uint8_t sz;
+
+	v = vpd_find(sysvpd, sysvpd_sz, "OSYS", "MM", &sz);
+	if (v)
+		dt_add_prop_sanitize_val(dt_root, "model", v, sz);
+	else
+		dt_add_property_string(dt_root, "model", "Unknown");
+
+	v = vpd_find(sysvpd, sysvpd_sz, "OSYS", "SS", &sz);
+	if (v)
+		dt_add_prop_sanitize_val(dt_root, "system-id", v, sz);
+	else
+		dt_add_property_string(dt_root, "system-id", "Unknown");
+}
+
+
+static void sysvpd_parse_legacy(const void *sysvpd, unsigned int sysvpd_sz)
+{
+	const char *model;
+	const char *system_id;
+	const char *brand;
+	uint8_t sz;
+
+	model = vpd_find(sysvpd, sysvpd_sz, "VSYS", "TM", &sz);
+	if (model)
+		dt_add_prop_sanitize_val(dt_root, "model", model, sz);
+	else
+		dt_add_property_string(dt_root, "model", "Unknown");
+
+	system_id = vpd_find(sysvpd, sysvpd_sz, "VSYS", "SE", &sz);
+	if (system_id)
+		dt_add_prop_sanitize_val(dt_root, "system-id", system_id, sz);
+	else
+		dt_add_property_string(dt_root, "system-id", "Unknown");
+
+	brand = vpd_find(sysvpd, sysvpd_sz, "VSYS", "BR", &sz);
+	if (brand)
+		dt_add_prop_sanitize_val(dt_root, "system-brand", brand, sz);
+	else
+		dt_add_property_string(dt_root, "brand", "Unknown");
+}
+
+static void sysvpd_parse(void)
+{
+	const void *sysvpd;
+	unsigned int sysvpd_sz;
+	unsigned int fru_id_sz;
+	struct dt_node *dt_vpd;
+	const struct spira_fru_id *fru_id;
+	struct HDIF_common_hdr *sysvpd_hdr;
+
+	sysvpd_hdr = get_hdif(&spira.ntuples.system_vpd, SYSVPD_HDIF_SIG);
+	if (!sysvpd_hdr)
+		return;
+
+	fru_id = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_FRU_ID, &fru_id_sz);
+	if (!fru_id)
+		return;
+
+	sysvpd = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_KW_VPD, &sysvpd_sz);
+	if (!CHECK_SPPTR(sysvpd))
+		return;
+
+	/* Add system VPD */
+	dt_vpd = dt_find_by_path(dt_root, "/vpd");
+	if (dt_vpd) {
+		dt_add_property(dt_vpd, "ibm,vpd", sysvpd, sysvpd_sz);
+		slca_vpd_add_loc_code(dt_vpd, be16_to_cpu(fru_id->slca_index));
+	}
+
+	/* Look for the new OpenPower "OSYS" first */
+	if (vpd_find_record(sysvpd, sysvpd_sz, "OSYS", NULL))
+		sysvpd_parse_opp(sysvpd, sysvpd_sz);
+	else
+		sysvpd_parse_legacy(sysvpd, sysvpd_sz);
+
+	dt_add_model_name();
+}
+
+static void iokid_vpd_parse(const struct HDIF_common_hdr *iohub_hdr)
+{
+	const struct HDIF_child_ptr *iokids;
+	const struct HDIF_common_hdr *iokid;
+	unsigned int i;
+
+	iokids = HDIF_child_arr(iohub_hdr, CECHUB_CHILD_IO_KIDS);
+	if (!CHECK_SPPTR(iokids)) {
+		prerror("VPD: No IOKID child array\n");
+		return;
+	}
+
+	for (i = 0; i < be32_to_cpu(iokids->count); i++) {
+		iokid = HDIF_child(iohub_hdr, iokids, i,
+					IOKID_FRU_HDIF_SIG);
+		/* IO KID VPD structure layout is similar to FRUVPD */
+		if (iokid)
+			dt_add_vpd_node(iokid,
+				FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
+	}
+}
+
+static void iohub_vpd_parse(void)
+{
+	const struct HDIF_common_hdr *iohub_hdr;
+	const struct cechub_hub_fru_id *fru_id_data;
+	unsigned int i, vpd_sz, fru_id_sz;
+
+	if (!get_hdif(&spira.ntuples.cec_iohub_fru, CECHUB_FRU_HDIF_SIG)) {
+		prerror("VPD: Could not find IO HUB FRU data\n");
+		return;
+	}
+
+	for_each_ntuple_idx(&spira.ntuples.cec_iohub_fru, iohub_hdr,
+					i, CECHUB_FRU_HDIF_SIG) {
+
+		fru_id_data = HDIF_get_idata(iohub_hdr,
+					     CECHUB_FRU_ID_DATA_AREA,
+					     &fru_id_sz);
+
+		/* P8, IO HUB is on processor card and we have a
+		 * daughter card array
+		 */
+		if (fru_id_data &&
+		    be32_to_cpu(fru_id_data->card_type) == CECHUB_FRU_TYPE_CPU_CARD) {
+			iokid_vpd_parse(iohub_hdr);
+			continue;
+		}
+
+		if (HDIF_get_idata(iohub_hdr,
+				   CECHUB_ASCII_KEYWORD_VPD, &vpd_sz))
+			dt_add_vpd_node(iohub_hdr, CECHUB_FRU_ID_DATA,
+					CECHUB_ASCII_KEYWORD_VPD);
+	}
+}
+
+static void _vpd_parse(struct spira_ntuple tuple)
+{
+	const struct HDIF_common_hdr *fruvpd_hdr;
+	unsigned int i;
+
+	if (!get_hdif(&tuple, FRUVPD_HDIF_SIG))
+		return;
+
+	for_each_ntuple_idx(&tuple, fruvpd_hdr, i, FRUVPD_HDIF_SIG)
+		dt_add_vpd_node(fruvpd_hdr,
+				FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
+}
+
+void vpd_parse(void)
+{
+	const struct HDIF_common_hdr *fruvpd_hdr;
+
+	/* System VPD uses the VSYS record, so its special */
+	sysvpd_parse();
+
+	/* Enclosure */
+	_vpd_parse(spira.ntuples.nt_enclosure_vpd);
+
+	/* Backplane */
+	_vpd_parse(spira.ntuples.backplane_vpd);
+
+	/* clock card -- does this use the FRUVPD sig? */
+	_vpd_parse(spira.ntuples.clock_vpd);
+
+	/* Anchor card */
+	_vpd_parse(spira.ntuples.anchor_vpd);
+
+	/* Op panel -- does this use the FRUVPD sig? */
+	_vpd_parse(spira.ntuples.op_panel_vpd);
+
+	/* External cache FRU vpd -- does this use the FRUVPD sig? */
+	_vpd_parse(spira.ntuples.ext_cache_fru_vpd);
+
+	/* Misc CEC FRU */
+	_vpd_parse(spira.ntuples.misc_cec_fru_vpd);
+
+	/* CEC IO HUB FRU */
+	iohub_vpd_parse();
+
+	/*
+	 * SP subsystem -- while the rest of the SPINFO structure is
+	 * different, the FRU ID data and pointer pair to keyword VPD
+	 * are the same offset as a FRUVPD entry. So reuse it
+	 */
+	fruvpd_hdr = get_hdif(&spira.ntuples.sp_subsys, SPSS_HDIF_SIG);
+	if (fruvpd_hdr)
+		dt_add_vpd_node(fruvpd_hdr,
+				FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
+}
-- 
cgit