summaryrefslogtreecommitdiffstats
path: root/hdata/vpd.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-07-02 15:36:20 +1000
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-07-02 15:36:20 +1000
commit1d880992fd8c8457a2d990ac6622cfd58fb1b261 (patch)
treec4c843b12e96b5612c315db5a23c5da1a900618c /hdata/vpd.c
downloadtalos-skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.gz
talos-skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.zip
Initial commit of Open Source release
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'hdata/vpd.c')
-rw-r--r--hdata/vpd.c851
1 files changed, 851 insertions, 0 deletions
diff --git a/hdata/vpd.c b/hdata/vpd.c
new file mode 100644
index 00000000..e568a065
--- /dev/null
+++ b/hdata/vpd.c
@@ -0,0 +1,851 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <skiboot.h>
+#include <vpd.h>
+#include <string.h>
+#include "spira.h"
+#include "hdata.h"
+#include <device.h>
+#include "hdata.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 */
+};
+
+static const struct card_info *card_info_lookup(char *ccin)
+{
+ int i;
+ for(i = 0; i < ARRAY_SIZE(card_table); i++)
+ if (!strcmp(card_table[i].ccin, ccin))
+ return &card_table[i];
+ return NULL;
+}
+
+static void vpd_vini_parse(struct dt_node *node,
+ const void *fruvpd, unsigned int fruvpd_sz)
+{
+ const void *kw;
+ char *str;
+ uint8_t kwsz;
+ const struct card_info *cinfo;
+
+ /* FRU Stocking Part Number */
+ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "FN", &kwsz);
+ if (kw) {
+ str = zalloc(kwsz + 1);
+ if (!str)
+ goto no_memory;
+ memcpy(str, kw, kwsz);
+ dt_add_property_string(node, "fru-number", str);
+ free(str);
+ }
+
+ /* Serial Number */
+ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "SN", &kwsz);
+ if (kw) {
+ str = zalloc(kwsz + 1);
+ if (!str)
+ goto no_memory;
+ memcpy(str, kw, kwsz);
+ dt_add_property_string(node, "serial-number", str);
+ free(str);
+ }
+
+ /* Part Number */
+ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "PN", &kwsz);
+ if (kw) {
+ str = zalloc(kwsz + 1);
+ if (!str)
+ goto no_memory;
+ memcpy(str, kw, kwsz);
+ dt_add_property_string(node, "part-number", str);
+ free(str);
+ }
+
+ /* Customer Card Identification Number (CCIN) */
+ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CC", &kwsz);
+ if (kw) {
+ str = zalloc(kwsz + 1);
+ if (!str)
+ goto no_memory;
+ memcpy(str, kw, kwsz);
+ dt_add_property_string(node, "ccin", str);
+ cinfo = card_info_lookup(str);
+ if (cinfo)
+ dt_add_property_string(node,
+ "description", cinfo->description);
+ free(str);
+ }
+ return;
+no_memory:
+ prerror("VPD: memory allocation failure in VINI parsing\n");
+}
+
+static const char *vpd_map_name(const char *vpd_name)
+{
+ /* vpd_name is a 2 char array */
+ switch (vpd_name[0]) {
+ case 'A':
+ switch (vpd_name[1]) {
+ case 'A':
+ return "ac-power-supply";
+ case 'M':
+ return "air-mover";
+ case 'V':
+ return "anchor-card";
+ }
+ break;
+ case 'B':
+ switch (vpd_name[1]) {
+ case 'A':
+ return "bus-adapter-card";
+ case 'C':
+ return "battery-charger";
+ case 'D':
+ return "bus-daughter-card";
+ case 'E':
+ return "bus-expansion-card";
+ case 'P':
+ return "backplane";
+ case 'R':
+ return "backplane-riser";
+ case 'X':
+ return "backplane-extender";
+ }
+ break;
+ case 'C':
+ switch (vpd_name[1]) {
+ case 'A':
+ return "calgary-bridge";
+ case 'B':
+ return "infiniband-connector";
+ case 'C':
+ return "clock-card";
+ case 'D':
+ return "card-connector";
+ case 'E':
+ return "ethernet-connector";
+ case 'L':
+ return "calgary-phb";
+ case 'I':
+ return "capacity-card";
+ case 'O':
+ return "sma-connector";
+ case 'P':
+ return "processor-capacity-card";
+ case 'R':
+ return "rio-connector";
+ case 'S':
+ return "serial-connector";
+ case 'U':
+ return "usb-connector";
+ }
+ break;
+ case 'D':
+ switch (vpd_name[1]) {
+ case 'B':
+ return "dasd-backplane";
+ case 'C':
+ return "drawer-card";
+ case 'E':
+ return "drawer-extension";
+ case 'I':
+ return "drawer-interposer";
+ case 'L':
+ return "p7ih-dlink-connector";
+ case 'T':
+ return "legacy-pci-card";
+ case 'V':
+ return "media-drawer-led";
+ }
+ break;
+ case 'E':
+ switch (vpd_name[1]) {
+ case 'I':
+ return "enclosure-led";
+ case 'F':
+ return "enclosure-fault-led";
+ case 'S':
+ return "embedded-sas";
+ case 'T':
+ return "ethernet-riser";
+ case 'V':
+ return "enclosure";
+ }
+ break;
+ case 'F':
+ switch (vpd_name[1]) {
+ case 'M':
+ return "frame";
+ }
+ break;
+ case 'H':
+ switch (vpd_name[1]) {
+ case 'B':
+ return "host-rio-pci-card";
+ case 'D':
+ return "high-speed-card";
+ case 'M':
+ return "hmc-connector";
+ }
+ break;
+ case 'I':
+ switch (vpd_name[1]) {
+ case 'B':
+ return "io-backplane";
+ case 'C':
+ return "io-card";
+ case 'D':
+ return "ide-connector";
+ case 'I':
+ return "io-drawer-led";
+ case 'P':
+ return "interplane-card";
+ case 'S':
+ return "smp-vbus-cable";
+ case 'T':
+ return "enclosure-cable";
+ case 'V':
+ return "io-enclosure";
+ }
+ break;
+ case 'K':
+ switch (vpd_name[1]) {
+ case 'V':
+ return "keyboard-led";
+ }
+ break;
+ case 'L':
+ switch (vpd_name[1]) {
+ case '2':
+ return "l2-cache-module";
+ case '3':
+ return "l3-cache-module";
+ case 'C':
+ return "squadrons-light-connector";
+ case 'R':
+ return "p7ih-connector";
+ case 'O':
+ return "system-locate-led";
+ case 'T':
+ return "squadrons-light-strip";
+ }
+ break;
+ case 'M':
+ switch (vpd_name[1]) {
+ case 'B':
+ return "media-backplane";
+ case 'E':
+ return "map-extension";
+ case 'M':
+ return "mip-meter";
+ case 'S':
+ return "ms-dimm";
+ }
+ break;
+ case 'N':
+ switch (vpd_name[1]) {
+ case 'B':
+ return "nvram-battery";
+ case 'C':
+ return "sp-node-controller";
+ case 'D':
+ return "numa-dimm";
+ }
+ break;
+ case 'O':
+ switch (vpd_name[1]) {
+ case 'D':
+ return "cuod-card";
+ case 'P':
+ return "op-panel";
+ case 'S':
+ return "oscillator";
+ }
+ break;
+ case 'P':
+ switch (vpd_name[1]) {
+ case '2':
+ return "ioc";
+ case '5':
+ return "ioc-bridge";
+ case 'B':
+ return "io-drawer-backplane";
+ case 'C':
+ return "power-capacitor";
+ case 'D':
+ return "processor-card";
+ case 'F':
+ return "processor";
+ case 'I':
+ return "ioc-phb";
+ case 'O':
+ return "spcn";
+ case 'N':
+ return "spcn-connector";
+ case 'R':
+ return "pci-riser-card";
+ case 'S':
+ return "power-supply";
+ case 'T':
+ return "pass-through-card";
+ case 'X':
+ return "psc-sync-card";
+ case 'W':
+ return "power-connector";
+ }
+ break;
+ case 'R':
+ switch (vpd_name[1]) {
+ case 'G':
+ return "regulator";
+ case 'I':
+ return "riser";
+ case 'K':
+ return "rack-indicator";
+ case 'W':
+ return "riscwatch-connector";
+ }
+ break;
+ case 'S':
+ switch (vpd_name[1]) {
+ case 'A':
+ return "sys-attn-led";
+ case 'B':
+ return "backup-sysvpd";
+ case 'C':
+ return "scsi-connector";
+ case 'D':
+ return "sas-connector";
+ case 'I':
+ return "scsi-ide-converter";
+ case 'L':
+ return "phb-slot";
+ case 'P':
+ return "service-processor";
+ case 'R':
+ return "service-card";
+ case 'S':
+ return "soft-switch";
+ case 'V':
+ return "system-vpd";
+ case 'Y':
+ return "legacy-sysvpd";
+ }
+ break;
+ case 'T':
+ switch (vpd_name[1]) {
+ case 'D':
+ return "tod-clock";
+ case 'I':
+ return "torrent-pcie-phb";
+ case 'L':
+ return "torrent-riser";
+ case 'M':
+ return "thermal-sensor";
+ case 'P':
+ return "tpmd-adapter";
+ case 'R':
+ return "torrent-bridge";
+ }
+ break;
+ case 'V':
+ switch (vpd_name[1]) {
+ case 'V':
+ return "root-node-vpd";
+ }
+ break;
+ case 'W':
+ switch (vpd_name[1]) {
+ case 'D':
+ return "water_device";
+ }
+ break;
+ }
+ return "Unknown";
+}
+
+static bool valid_child_entry(const struct slca_entry *entry)
+{
+ if (!entry)
+ return false;
+
+ /*
+ * Skip entries with independent ntuple FRUVPD/MSVPD, etc.,
+ * representations, since they have a unique PN, FN, SN, et al.
+ * We add details for those devices via the ntuple walk.
+ */
+ switch (entry->fru_id[0]) {
+ case 'A':
+ switch (entry->fru_id[1]) {
+ case 'V': /* AV */
+ return false;
+ }
+ break;
+ case 'B':
+ switch (entry->fru_id[1]) {
+ case 'P': /* BP */
+ case 'X': /* BX */
+ return false;
+ }
+ break;
+ case 'C':
+ switch (entry->fru_id[1]) {
+ case 'C': /* CC */
+ return false;
+ }
+ break;
+ case 'D':
+ switch (entry->fru_id[1]) {
+ case 'B': /* DB */
+ return false;
+ }
+ break;
+ case 'E':
+ switch (entry->fru_id[1]) {
+ case 'V': /* EV */
+ return false;
+ }
+ break;
+ case 'M':
+ switch (entry->fru_id[1]) {
+ case 'S': /* MS */
+ return false;
+ }
+ break;
+ case 'O':
+ switch (entry->fru_id[1]) {
+ case 'P': /* OP */
+ return false;
+ }
+ break;
+ case 'R':
+ switch (entry->fru_id[1]) {
+ case 'I': /* RI */
+ return false;
+ }
+ break;
+ case 'P':
+ switch (entry->fru_id[1]) {
+ case '2': /* P2 */
+ case '5': /* P5 */
+ case 'F': /* PF */
+ return false;
+ }
+ break;
+ case 'S':
+ switch (entry->fru_id[1]) {
+ case 'P': /* SP */
+ return false;
+ }
+ break;
+ case 'T':
+ switch (entry->fru_id[1]) {
+ case 'P': /* TP */
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if ((entry->install_indic == SLCA_INSTALL_INSTALLED) &&
+ (entry->vpd_collected == SLCA_VPD_COLLECTED))
+ return true;
+
+ return false;
+}
+
+static void vpd_add_children(struct dt_node *parent, uint16_t slca_index)
+{
+ const struct slca_entry *s_entry, *child;
+ uint16_t current_child_index, max_index;
+
+ s_entry = slca_get_entry(slca_index);
+ if (!s_entry || (s_entry->nr_child == 0))
+ return;
+
+ /*
+ * This slca_entry has children. Parse the children array
+ * and add nodes for valid entries.
+ *
+ * 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.
+ */
+
+ /* current_index tracks where we are right now in the array */
+ current_child_index = be16_to_cpu(s_entry->child_index);
+
+ /* max_index tracks how far down the array we must traverse */
+ max_index = be16_to_cpu(s_entry->child_index)
+ + be16_to_cpu(s_entry->nr_child);
+
+ while (current_child_index < max_index) {
+ child = slca_get_entry(current_child_index);
+ if (!child)
+ return;
+
+ if (valid_child_entry(child)) {
+ const char *name;
+ uint64_t addr;
+ struct dt_node *node;
+
+ /* create new node, add location code */
+ name = vpd_map_name(child->fru_id);
+ addr = (uint64_t)be16_to_cpu(child->rsrc_id);
+ node = dt_new_addr(parent, name, addr);
+ if (!node) {
+ prerror("VPD: Creating node at %s@%llx failed\n",
+ name, addr);
+ return;
+ }
+ slca_vpd_add_loc_code(node, be16_to_cpu(child->my_index));
+
+ /* Add child FRU type */
+ dt_add_property(node, "fru-type", child->fru_id, 2);
+
+ /* recursively add children */
+ vpd_add_children(node, be16_to_cpu(child->my_index));
+ }
+
+ /* Skip dups -- currently we presume dups are contiguous */
+ if (child->nr_dups > 0)
+ current_child_index += child->nr_dups;
+ current_child_index++;
+ }
+ return;
+}
+
+struct dt_node *dt_add_vpd_node(const struct HDIF_common_hdr *hdr,
+ int indx_fru, int indx_vpd)
+{
+ const void *fruvpd;
+ unsigned int fruvpd_sz;
+ unsigned int fru_id_sz;
+ uint64_t addr;
+ struct dt_node *dt_vpd;
+ struct dt_node *node;
+ const struct spira_fru_id *fru_id;
+ const struct slca_entry *s_entry;
+ const char *vpd_name;
+ const char *name;
+ int len;
+ char *lname;
+
+ fru_id = HDIF_get_idata(hdr, indx_fru, &fru_id_sz);
+ if (!fru_id)
+ return NULL;
+
+ s_entry = slca_get_entry(be16_to_cpu(fru_id->slca_index));
+ if (valid_child_entry(s_entry)) /* Don't populate child VPD here */
+ return NULL;
+
+ fruvpd = HDIF_get_idata(hdr, indx_vpd, &fruvpd_sz);
+ if (!CHECK_SPPTR(fruvpd))
+ return NULL;
+
+ vpd_name = slca_get_vpd_name(be16_to_cpu(fru_id->slca_index));
+ if (!vpd_name) {
+ prerror("VPD: VPD name at index %d couldn't be found\n",
+ fru_id->slca_index);
+ return NULL;
+ }
+
+ dt_vpd = dt_find_by_path(dt_root, "/vpd");
+ if (!dt_vpd)
+ return NULL;
+
+ /* Get node name */
+ name = vpd_map_name(vpd_name);
+ addr = (uint64_t)be16_to_cpu(fru_id->rsrc_id);
+ len = strlen(name) + STR_MAX_CHARS(addr) + 2;
+ lname = zalloc(len);
+ if (!lname) {
+ prerror("VPD: Failed to allocate memory\n");
+ return NULL;
+ }
+ snprintf(lname, len, "%s@%llx", name, (long long)addr);
+
+ /*
+ * FRU can be a child of some other FRU. Make sure
+ * we have not added this node already.
+ */
+ node = dt_find_by_path(dt_vpd, lname);
+ if (node) {
+ free(lname);
+ return NULL;
+ }
+
+ node = dt_new(dt_vpd, lname);
+ if (!node) {
+ free(lname);
+ return NULL;
+ }
+
+ /* Parse VPD fields */
+ dt_add_property(node, "ibm,vpd", fruvpd, fruvpd_sz);
+ vpd_vini_parse(node, fruvpd, fruvpd_sz);
+
+ /* Location code */
+ slca_vpd_add_loc_code(node, be16_to_cpu(fru_id->slca_index));
+ /* Add FRU label */
+ dt_add_property(node, "fru-type", vpd_name, 2);
+ vpd_add_children(node, be16_to_cpu(fru_id->slca_index));
+
+ free(lname);
+ return node;
+}
+
+static void sysvpd_parse(void)
+{
+ const char *model;
+ const char *system_id;
+ const char *brand;
+ char *str;
+ uint8_t sz;
+ 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;
+ const struct machine_info *mi;
+
+ sysvpd_hdr = get_hdif(&spira.ntuples.system_vpd, SYSVPD_HDIF_SIG);
+ if (!sysvpd_hdr)
+ goto no_sysvpd;
+
+ fru_id = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_FRU_ID, &fru_id_sz);
+ if (!fru_id)
+ goto no_sysvpd;;
+
+ sysvpd = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_KW_VPD, &sysvpd_sz);
+ if (!CHECK_SPPTR(sysvpd))
+ goto no_sysvpd;
+
+ /* 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));
+ }
+
+ model = vpd_find(sysvpd, sysvpd_sz, "VSYS", "TM", &sz);
+ if (!model)
+ goto no_sysvpd;
+ str = zalloc(sz + 1);
+ if (!str)
+ goto no_sysvpd;
+ memcpy(str, model, sz);
+ dt_add_property_string(dt_root, "model", str);
+ mi = machine_info_lookup(str);
+ if (mi)
+ dt_add_property_string(dt_root, "model-name", mi->name);
+ free(str);
+ dt_add_property_string(dt_root, "vendor", "IBM");
+
+ system_id = vpd_find(sysvpd, sysvpd_sz, "VSYS", "SE", &sz);
+ if (!system_id)
+ goto no_sysid;
+ str = zalloc(sz + 1);
+ if (!str)
+ goto no_sysid;
+ memcpy(str, system_id, sz);
+ dt_add_property_string(dt_root, "system-id", str);
+ free(str);
+
+ brand = vpd_find(sysvpd, sysvpd_sz, "VSYS", "BR", &sz);
+ if (!brand)
+ goto no_brand;
+ str = zalloc(sz + 1);
+ if (!str)
+ goto no_brand;
+ memcpy(str, brand, sz);
+ dt_add_property_string(dt_root, "system-brand", str);
+ free(str);
+
+ return;
+
+no_brand:
+ dt_add_property_string(dt_root, "system-brand", "Unknown");
+ return;
+
+no_sysid:
+ dt_add_property_string(dt_root, "system-id", "Unknown");
+ return;
+
+ no_sysvpd:
+ dt_add_property_string(dt_root, "model", "Unknown");
+}
+
+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;
+ }
+
+ /* On P7, the keyword VPD will not be NULL */
+ 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;
+
+ /* Enclosure */
+ _vpd_parse(spira.ntuples.nt_enclosure_vpd);
+
+ /* Backplane */
+ _vpd_parse(spira.ntuples.backplane_vpd);
+
+ /* System VPD uses the VSYS record, so its special */
+ sysvpd_parse();
+
+ /* 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);
+}
OpenPOWER on IntegriCloud