779 lines
19 KiB
C
779 lines
19 KiB
C
/* Copyright 2013-2017 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"
|
|
#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"},
|
|
|
|
{"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"},
|
|
{"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;
|
|
}
|
|
|
|
/* 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;
|
|
|
|
/* 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);
|
|
}
|