diff options
Diffstat (limited to 'hdata/iohub.c')
-rw-r--r-- | hdata/iohub.c | 715 |
1 files changed, 715 insertions, 0 deletions
diff --git a/hdata/iohub.c b/hdata/iohub.c new file mode 100644 index 0000000..0dbec36 --- /dev/null +++ b/hdata/iohub.c @@ -0,0 +1,715 @@ +/* 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 "spira.h" +#include <cpu.h> +#include <fsp.h> +#include <opal.h> +#include <ccan/str/str.h> +#include <ccan/array_size/array_size.h> +#include <device.h> +#include <p5ioc2.h> +#include <p7ioc.h> +#include <vpd.h> + +#include "hdata.h" + +static void io_add_common(struct dt_node *hn, const struct cechub_io_hub *hub) +{ + dt_add_property_cells(hn, "#address-cells", 2); + dt_add_property_cells(hn, "#size-cells", 2); + dt_add_property_cells(hn, "ibm,buid-ext", hub->buid_ext); + dt_add_property_cells(hn, "ibm,chip-id", + pcid_to_chip_id(hub->proc_chip_id)); + dt_add_property_cells(hn, "ibm,gx-index", hub->gx_index); + dt_add_property_cells(hn, "revision", hub->ec_level); + + /* Instead of exposing the GX BARs as separate ranges as we *should* + * do in an ideal world, we just create a pass-through ranges and + * we use separate properties for the BARs. + * + * This is hackish but will do for now and avoids us having to + * do too complex ranges property parsing + */ + dt_add_property(hn, "ranges", NULL, 0); + dt_add_property_cells(hn, "ibm,gx-bar-1", + hi32(hub->gx_ctrl_bar1), lo32(hub->gx_ctrl_bar1)); + dt_add_property_cells(hn, "ibm,gx-bar-2", + hi32(hub->gx_ctrl_bar2), lo32(hub->gx_ctrl_bar2)); + + /* Add presence detect if valid */ + if (hub->flags & CECHUB_HUB_FLAG_FAB_BR0_PDT) + dt_add_property_cells(hn, "ibm,br0-presence-detect", + hub->fab_br0_pdt); + if (hub->flags & CECHUB_HUB_FLAG_FAB_BR1_PDT) + dt_add_property_cells(hn, "ibm,br1-presence-detect", + hub->fab_br1_pdt); +} + +static bool io_get_lx_info(const void *kwvpd, unsigned int kwvpd_sz, + int lx_idx, struct dt_node *hn) +{ + const void *lxr; + char recname[5]; + + /* Find LXRn, where n is the index passed in*/ + strcpy(recname, "LXR0"); + recname[3] += lx_idx; + lxr = vpd_find(kwvpd, kwvpd_sz, recname, "LX", NULL); + if (!lxr) { + /* Not found, try VINI */ + lxr = vpd_find(kwvpd, kwvpd_sz, "VINI", + "LX", NULL); + if (lxr) + lx_idx = VPD_LOAD_LXRN_VINI; + } + if (!lxr) { + printf("CEC: LXR%x not found !\n", lx_idx); + return false; + } + + printf("CEC: LXRn=%d LXR=%016lx\n", lx_idx, + lxr ? *(unsigned long *)lxr : 0); + printf("CEC: LX Info added to %llx\n", (long long)hn); + + /* Add the LX info */ + if (!dt_has_node_property(hn, "ibm,vpd-lx-info", NULL)) { + dt_add_property_cells(hn, "ibm,vpd-lx-info", + lx_idx, + ((uint32_t *)lxr)[0], + ((uint32_t *)lxr)[1]); + } + + return true; +} + + +static void io_get_loc_code(const void *sp_iohubs, struct dt_node *hn, const char *prop_name) +{ + const struct spira_fru_id *fru_id; + unsigned int fru_id_sz; + char loc_code[LOC_CODE_SIZE + 1]; + const char *slca_loc_code; + + /* Find SLCA Index */ + fru_id = HDIF_get_idata(sp_iohubs, CECHUB_FRU_ID_DATA, &fru_id_sz); + if (fru_id) { + memset(loc_code, 0, sizeof(loc_code)); + + /* Find LOC Code from SLCA Index */ + slca_loc_code = slca_get_loc_code_index(fru_id->slca_index); + if (slca_loc_code) { + strncpy(loc_code, slca_loc_code, LOC_CODE_SIZE); + if (!dt_has_node_property(hn, prop_name, NULL)) { + dt_add_property(hn, prop_name, loc_code, + strlen(loc_code) + 1); + } + printf("CEC: %s: %s (SLCA rsrc 0x%x)\n", + prop_name, loc_code, be16_to_cpu(fru_id->rsrc_id)); + } else { + printf("CEC: SLCA Loc not found: index %d\n", + fru_id->slca_index); + } + } else { + printf("CEC: Hub FRU ID not found...\n"); + } +} + +static struct dt_node *io_add_p5ioc2(const struct cechub_io_hub *hub, + const void *sp_iohubs) +{ + struct dt_node *hn; + uint64_t reg[2]; + + const void *kwvpd; + unsigned int kwvpd_sz; + + printf(" GX#%d BUID_Ext = 0x%x\n", + be32_to_cpu(hub->gx_index), + be32_to_cpu(hub->buid_ext)); + printf(" GX BAR 0 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar0)); + printf(" GX BAR 1 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar1)); + printf(" GX BAR 2 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar2)); + printf(" GX BAR 3 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar3)); + printf(" GX BAR 4 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar4)); + + /* We assume SBAR == GX0 + some hard coded offset */ + reg[0] = cleanup_addr(be64_to_cpu(hub->gx_ctrl_bar0) + P5IOC2_REGS_OFFSET); + reg[1] = 0x2000000; + + hn = dt_new_addr(dt_root, "io-hub", reg[0]); + if (!hn) + return NULL; + + dt_add_property(hn, "reg", reg, sizeof(reg)); + dt_add_property_strings(hn, "compatible", "ibm,p5ioc2"); + + kwvpd = HDIF_get_idata(sp_iohubs, CECHUB_ASCII_KEYWORD_VPD, &kwvpd_sz); + if (kwvpd && kwvpd != sp_iohubs) { + /* + * XX We don't know how to properly find the LXRn + * record so for now we'll just try LXR0 and if not + * found, we try LXR1 + */ + if (!io_get_lx_info(kwvpd, kwvpd_sz, 0, hn)) + io_get_lx_info(kwvpd, kwvpd_sz, 1, hn); + } else + printf("CEC: P5IOC2 Keywords not found.\n"); + + /* Get slots base loc code */ + io_get_loc_code(sp_iohubs, hn, "ibm,io-base-loc-code"); + + return hn; +} + +static struct dt_node *io_add_p7ioc(const struct cechub_io_hub *hub, + const void *sp_iohubs) +{ + struct dt_node *hn; + uint64_t reg[2]; + + const void *kwvpd; + unsigned int kwvpd_sz; + + printf(" GX#%d BUID_Ext = 0x%x\n", + be32_to_cpu(hub->gx_index), + be32_to_cpu(hub->buid_ext)); + printf(" GX BAR 0 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar0)); + printf(" GX BAR 1 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar1)); + printf(" GX BAR 2 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar2)); + printf(" GX BAR 3 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar3)); + printf(" GX BAR 4 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar4)); + + /* We only know about memory map 1 */ + if (hub->mem_map_vers != 1) { + prerror("P7IOC: Unknown memory map %d\n", hub->mem_map_vers); + /* We try to continue anyway ... */ + } + + reg[0] = cleanup_addr(be64_to_cpu(hub->gx_ctrl_bar1)); + reg[1] = 0x2000000; + + hn = dt_new_addr(dt_root, "io-hub", reg[0]); + if (!hn) + return NULL; + + dt_add_property(hn, "reg", reg, sizeof(reg)); + dt_add_property_strings(hn, "compatible", "ibm,p7ioc", "ibm,ioda-hub"); + + kwvpd = HDIF_get_idata(sp_iohubs, CECHUB_ASCII_KEYWORD_VPD, &kwvpd_sz); + if (kwvpd && kwvpd != sp_iohubs) { + /* + * XX We don't know how to properly find the LXRn + * record so for now we'll just try LXR0 and if not + * found, we try LXR1 + */ + if (!io_get_lx_info(kwvpd, kwvpd_sz, 0, hn)) + io_get_lx_info(kwvpd, kwvpd_sz, 1, hn); + } else + printf("CEC: P7IOC Keywords not found.\n"); + + io_get_loc_code(sp_iohubs, hn, "ibm,io-base-loc-code"); + + return hn; +} + +static struct dt_node *io_add_phb3(const struct cechub_io_hub *hub, + const struct HDIF_common_hdr *sp_iohubs, + unsigned int index, struct dt_node *xcom, + unsigned int pe_xscom, + unsigned int pci_xscom, + unsigned int spci_xscom) +{ + struct dt_node *pbcq; + uint32_t reg[6]; + unsigned int hdif_vers; + + /* Get HDIF version */ + hdif_vers = be16_to_cpu(sp_iohubs->version); + + /* Create PBCQ node under xscom */ + pbcq = dt_new_addr(xcom, "pbcq", pe_xscom); + if (!pbcq) + return NULL; + + /* "reg" property contains in order the PE, PCI and SPCI XSCOM + * addresses + */ + reg[0] = pe_xscom; + reg[1] = 0x20; + reg[2] = pci_xscom; + reg[3] = 0x05; + reg[4] = spci_xscom; + reg[5] = 0x15; + dt_add_property(pbcq, "reg", reg, sizeof(reg)); + + /* A couple more things ... */ + dt_add_property_strings(pbcq, "compatible", "ibm,power8-pbcq"); + dt_add_property_cells(pbcq, "ibm,phb-index", index); + dt_add_property_cells(pbcq, "ibm,hub-id", be16_to_cpu(hub->hub_num)); + + /* The loc code of the PHB itself is different from the base + * loc code of the slots (It's actually the DCM's loc code). + */ + io_get_loc_code(sp_iohubs, pbcq, "ibm,loc-code"); + + /* We indicate that this is an IBM setup, which means that + * the presence detect A/B bits are meaningful. So far we + * don't know whether they make any sense on customer setups + * so we only set that when booting with HDAT + */ + dt_add_property(pbcq, "ibm,use-ab-detect", NULL, 0); + + /* HDAT spec has these in version 0x6A and later */ + if (hdif_vers >= 0x6a) { + u64 eq0 = be64_to_cpu(hub->phb_lane_eq[index][0]); + u64 eq1 = be64_to_cpu(hub->phb_lane_eq[index][1]); + u64 eq2 = be64_to_cpu(hub->phb_lane_eq[index][2]); + u64 eq3 = be64_to_cpu(hub->phb_lane_eq[index][3]); + dt_add_property_cells(pbcq, "ibm,lane-eq", + hi32(eq0), lo32(eq0), + hi32(eq1), lo32(eq1), + hi32(eq2), lo32(eq2), + hi32(eq3), lo32(eq3)); + } + + /* Currently we only create a PBCQ node, the actual PHB nodes + * will be added by sapphire later on. + */ + return pbcq; +} + +static struct dt_node *io_add_murano(const struct cechub_io_hub *hub, + const struct HDIF_common_hdr *sp_iohubs) +{ + struct dt_node *xscom; + unsigned int i, chip_id; + + chip_id = pcid_to_chip_id(be32_to_cpu(hub->proc_chip_id)); + + printf("CEC: HW CHIP=0x%x, HW TOPO=0x%04x\n", chip_id, + be16_to_cpu(hub->hw_topology)); + + xscom = find_xscom_for_chip(chip_id); + if (!xscom) { + prerror("MURANO: Can't find XSCOM for chip %d\n", chip_id); + return NULL; + } + + /* Create PHBs, max 3 */ + for (i = 0; i < 3; i++) { + if (hub->fab_br0_pdt & (0x80 >> i)) + /* XSCOM addresses for murano DD1.0 */ + io_add_phb3(hub, sp_iohubs, i, xscom, + 0x02012000 + (i * 0x400), + 0x09012000 + (i * 0x400), + 0x09013c00 + (i * 0x40)); + } + + /* HACK: We return the XSCOM device for the VPD info */ + return xscom; +} + +static void io_add_p8_cec_vpd(const struct HDIF_common_hdr *sp_iohubs) +{ + const struct HDIF_child_ptr *iokids; + const void *iokid; + const void *kwvpd; + unsigned int kwvpd_sz; + + /* P8 LXR0 kept in IO KID Keyword VPD */ + iokids = HDIF_child_arr(sp_iohubs, CECHUB_CHILD_IO_KIDS); + if (!CHECK_SPPTR(iokids)) { + printf("CEC: No IOKID child array !\n"); + return; + } + if (!iokids->count) { + printf("CEC: IOKID count is 0 !\n"); + return; + } + if (iokids->count > 1) { + printf("CEC: WARNING ! More than 1 IO KID !!! (%d)\n", + iokids->count); + /* Ignoring the additional ones */ + } + + iokid = HDIF_child(sp_iohubs, iokids, 0, "IO KID"); + if (!iokid) { + printf("CEC: No IO KID structure in child array !\n"); + return; + } + + /* Grab base location code for slots */ + io_get_loc_code(iokid, dt_root, "ibm,io-base-loc-code"); + + kwvpd = HDIF_get_idata(iokid, CECHUB_ASCII_KEYWORD_VPD, &kwvpd_sz); + if (!kwvpd) { + printf("CEC: No VPD entry in IO KID !\n"); + return; + } + + /* Grab LX load info */ + io_get_lx_info(kwvpd, kwvpd_sz, 0, dt_root); +} + +static struct dt_node *io_add_hea(const struct cechub_io_hub *hub, + const void *sp_io) +{ + struct dt_node *np, *gnp; + uint64_t reg[2]; + unsigned int i, vpd_sz; + uint8_t kw_sz; + const void *iokid, *vpd, *ccin; + const uint8_t *mac; + const struct HDIF_child_ptr *iokids; + + /* + * We have a table of supported dauther cards looked up + * by CCIN. We don't use the 1008 slot map in the VPD. + * + * This is basically translated from BML and will do for + * now especially since we don't really support p5ioc2 + * machine, this is just for lab use + * + * This is mostly untested on 10G ... we might need more + * info about the PHY in that case + */ + const struct hea_iocard { + const char ccin[4]; + struct { + uint32_t speed; + uint16_t ports; + uint16_t phy_id; + } pg[2]; + } hea_iocards[] = { + { + .ccin = "1818", /* HV4 something */ + .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 }, + }, + { + .ccin = "1819", /* HV4 Titov Card */ + .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 }, + .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 0 }, + }, + { + .ccin = "1830", /* HV4 Sergei Card */ + .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 0 }, + .pg[1] = { .speed = 10000, .ports = 1, .phy_id = 0 }, + }, + { + .ccin = "181A", /* L4 Evans Card */ + .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 0 }, + }, + { + .ccin = "181B", /* L4 Weber Card */ + .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 0 }, + .pg[1] = { .speed = 10000, .ports = 1, .phy_id = 0 }, + }, + { + .ccin = "181C", /* HV4 Gibson Card */ + .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 }, + .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 0 }, + }, + { + .ccin = "2BC4", /* MR Riverside 2 */ + .pg[0] = { .speed = 1000, .ports = 1, .phy_id = 1 }, + .pg[1] = { .speed = 1000, .ports = 1, .phy_id = 1 }, + }, + { + .ccin = "2BC5", /* MR Lions 2 */ + .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 1 }, + .pg[1] = { .speed = 10000, .ports = 1, .phy_id = 1 }, + }, + { + .ccin = "2BC6", /* MR Onion 2 */ + .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 1 }, + .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 1 }, + }, + { + .ccin = "266D", /* Jupiter Bonzai */ + .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 1 }, + .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 1 }, + }, + /* The blade use an IO KID that's a bit oddball and seems to + * represent the backplane itself, but let's use it anyway + * + * XXX Probably want a different PHY type ! + */ + { + .ccin = "531C", /* P7 Blade */ + .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 }, + }, + }; + const struct hea_iocard *card = NULL; + + /* WARNING: This makes quite a lot of nasty assumptions + * that appear to hold true on the few machines I care + * about, which is good enough for now. We don't officially + * support p5ioc2 anyway... + */ + + /* Get first IO KID, we only support one. Real support would + * mean using the FRU ID and the SLCA to find the right "stuff" + * but at this stage it's unnecessary + */ + iokids = HDIF_child_arr(sp_io, CECHUB_CHILD_IO_KIDS); + if (!CHECK_SPPTR(iokids)) { + prerror("HEA: no IOKID in HDAT child array !\n"); + return NULL; + } + if (!iokids->count) { + prerror("HEA: IOKID count is 0 !\n"); + return NULL; + } + if (iokids->count > 1) { + printf("HEA: WARNING ! More than 1 IO KID !!! (%d)\n", + iokids->count); + } + iokid = HDIF_child(sp_io, iokids, 0, "IO KID"); + if (!iokid) { + prerror("HEA: Failed to retrieve IO KID 0 !\n"); + return NULL; + } + + /* Grab VPD */ + vpd = HDIF_get_idata(iokid, IOKID_KW_VPD, &vpd_sz); + if (!CHECK_SPPTR(vpd)) { + prerror("HEA: Failed to retrieve VPD from IO KID !\n"); + return NULL; + } + + /* Grab the MAC address */ + mac = vpd_find(vpd, vpd_sz, "VINI", "B1", &kw_sz); + if (!mac || kw_sz < 8) { + prerror("HEA: Failed to retrieve MAC Address !\n"); + return NULL; + } + + /* Grab the CCIN (card ID) */ + ccin = vpd_find(vpd, vpd_sz, "VINI", "CC", &kw_sz); + if (!ccin || kw_sz < 4) { + prerror("HEA: Failed to retrieve CCIN !\n"); + return NULL; + } + + /* Now we could try to parse the 1008 slot map etc... but instead + * we'll do like BML and grab the CCIN & use it for known cards. + * We also grab the MAC + */ + for (i = 0; i < ARRAY_SIZE(hea_iocards) && !card; i++) { + if (strncmp(hea_iocards[i].ccin, ccin, 4)) + continue; + card = &hea_iocards[i]; + } + if (!card) { + prerror("HEA: Unknown CCIN 0x%.4s!\n", (const char *)ccin); + return NULL; + } + + /* Assume base address is BAR3 + 0x4000000000 */ + reg[0] = hub->gx_ctrl_bar3 + 0x4000000000; + reg[1] = 0xc0000000; + + printf("CEC: * Adding HEA to P5IOC2, assuming GBA=0x%llx\n", + (long long)reg[0]); + np = dt_new_addr(dt_root, "ibm,hea", reg[0]); + if (!np) + return NULL; + + dt_add_property(np, "reg", reg, sizeof(reg)); + dt_add_property_strings(np, "compatible", "ibm,p5ioc2-hea"); + dt_add_property_cells(np, "#address-cells", 1); + dt_add_property_cells(np, "#size-cells", 0); + dt_add_property(np, "ibm,vpd", vpd, vpd_sz); + dt_add_property_cells(np, "#mac-address", mac[7]); + dt_add_property(np, "mac-address-base", mac, 6); + /* BUID is base + 0x30 */ + dt_add_property(np, "interrupt-controller", NULL, 0); + dt_add_property_cells(np, "interrupt-base", + ((hub->buid_ext << 9) | 0x30) << 4); + dt_add_property_cells(np, "interrupt-max-count", 128); + + /* Always 2 port groups */ + for (i = 0; i < 2; i++) { + unsigned int clause; + + switch(card->pg[i].speed) { + case 1000: + clause = 0x22; + break; + case 10000: + clause = 0x45; + break; + default: + /* Unused port group */ + continue; + } + gnp = dt_new_addr(np, "portgroup", i + 1); + if (!gnp) + continue; + + dt_add_property_cells(gnp, "reg", i + 1); + dt_add_property_cells(gnp, "speed", card->pg[i].speed); + /* XX FIXME */ + dt_add_property_strings(gnp, "phy-type", "mdio"); + dt_add_property_cells(gnp, "phy-mdio-addr", card->pg[i].phy_id); + dt_add_property_cells(gnp, "phy-mdio-clause", clause); + dt_add_property_cells(gnp, "subports", card->pg[i].ports); + } + return np; +} + +static void io_parse_fru(const void *sp_iohubs) +{ + unsigned int i; + struct dt_node *hn; + int count; + + count = HDIF_get_iarray_size(sp_iohubs, CECHUB_FRU_IO_HUBS); + if (count < 1) { + prerror("CEC: IO FRU with no chips !\n"); + return; + } + + printf("CEC: %d chips in FRU\n", count); + + /* Iterate IO hub array */ + for (i = 0; i < count; i++) { + const struct cechub_io_hub *hub; + unsigned int size, hub_id; + + hub = HDIF_get_iarray_item(sp_iohubs, CECHUB_FRU_IO_HUBS, + i, &size); + if (!hub || size < CECHUB_IOHUB_MIN_SIZE) { + prerror("CEC: IO-HUB Chip %d bad idata\n", i); + continue; + } + printf("CEC: IO Hub Chip #%d:\n", i); + switch (hub->flags & CECHUB_HUB_FLAG_STATE_MASK) { + case CECHUB_HUB_FLAG_STATE_OK: + printf("CEC: OK\n"); + break; + case CECHUB_HUB_FLAG_STATE_FAILURES: + printf("CEC: OK with failures\n"); + break; + case CECHUB_HUB_FLAG_STATE_NOT_INST: + printf("CEC: Not installed\n"); + continue; + case CECHUB_HUB_FLAG_STATE_UNUSABLE: + printf("CEC: Unusable"); + continue; + } + + hub_id = be16_to_cpu(hub->iohub_id); + + /* GX BAR assignment */ + printf("CEC: PChip: %d HUB ID: %04x [EC=0x%x] Hub#=%d)\n", + be32_to_cpu(hub->proc_chip_id), hub_id, + be32_to_cpu(hub->ec_level), be32_to_cpu(hub->hub_num)); + + switch(hub_id) { + case CECHUB_HUB_P7IOC: + printf("CEC: P7IOC !\n"); + hn = io_add_p7ioc(hub, sp_iohubs); + io_add_common(hn, hub); + break; + case CECHUB_HUB_P5IOC2: + printf("CEC: P5IOC2 !\n"); + hn = io_add_p5ioc2(hub, sp_iohubs); + io_add_common(hn, hub); + io_add_hea(hub, sp_iohubs); + break; + case CECHUB_HUB_MURANO: + case CECHUB_HUB_MURANO_SEGU: + printf("CEC: Murano !\n"); + hn = io_add_murano(hub, sp_iohubs); + break; + default: + printf("CEC: Hub ID 0x%04x unsupported !\n", + hub_id); + hn = NULL; + } + } + + /* On P8, grab the CEC VPD */ + if (proc_gen == proc_gen_p8) + io_add_p8_cec_vpd(sp_iohubs); +} + +void io_parse(void) +{ + const struct HDIF_common_hdr *sp_iohubs; + unsigned int i, size; + + /* Look for IO Hubs */ + if (!get_hdif(&spira.ntuples.cec_iohub_fru, "IO HUB")) { + prerror("CEC: Cannot locate IO Hub FRU data !\n"); + return; + } + + /* + * Note about LXRn numbering ... + * + * I can't completely make sense of what that is supposed to be, so + * for now, what we do is look for the first one we can find and + * increment it for each chip. Works for the machines I have here + */ + + for_each_ntuple_idx(&spira.ntuples.cec_iohub_fru, sp_iohubs, i, + CECHUB_FRU_HDIF_SIG) { + const struct cechub_hub_fru_id *fru_id_data; + unsigned int type; + static const char *typestr[] = { + "Reservation", + "Card", + "CPU Card", + "Backplane", + "Backplane Extension" + }; + fru_id_data = HDIF_get_idata(sp_iohubs, CECHUB_FRU_ID_DATA_AREA, + &size); + if (!fru_id_data || size < sizeof(struct cechub_hub_fru_id)) { + prerror("CEC: IO-HUB FRU %d, bad ID data\n", i); + continue; + } + type = fru_id_data->card_type; + + printf("CEC: HUB FRU %d is %s\n", + i, type > 4 ? "Unknown" : typestr[type]); + + /* + * We currently only handle the backplane (Juno) and + * processor FRU (P8 machines) + */ + if (type != CECHUB_FRU_TYPE_CEC_BKPLANE && + type != CECHUB_FRU_TYPE_CPU_CARD) { + prerror("CEC: Unsupported type\n"); + continue; + } + + /* We don't support Hubs connected to pass-through ports */ + if (fru_id_data->flags & (CECHUB_FRU_FLAG_HEADLESS | + CECHUB_FRU_FLAG_PASSTHROUGH)) { + prerror("CEC: Headless or Passthrough unsupported\n"); + continue; + } + + /* Ok, we have a reasonable candidate */ + io_parse_fru(sp_iohubs); + } +} + |