/* 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 #include "spira.h" #include #include #include #include #include #include "hdata.h" static struct dt_node *fsp_create_node(const void *spss, int i, struct dt_node *parent) { const struct spss_sp_impl *sp_impl; struct dt_node *node; unsigned int mask; /* Find an check the SP Implementation structure */ sp_impl = HDIF_get_idata(spss, SPSS_IDATA_SP_IMPL, NULL); if (!CHECK_SPPTR(sp_impl)) { prerror("FSP #%d: SPSS/SP_Implementation not found !\n", i); return NULL; } prlog(PR_INFO, "FSP #%d: FSP HW version %d, SW version %d," " chip DD%d.%d\n", i, sp_impl->hw_version, sp_impl->sw_version, sp_impl->chip_version >> 4, sp_impl->chip_version & 0xf); mask = SPSS_SP_IMPL_FLAGS_INSTALLED | SPSS_SP_IMPL_FLAGS_FUNCTIONAL; if ((be16_to_cpu(sp_impl->func_flags) & mask) != mask) { prerror("FSP #%d: FSP not installed or not functional\n", i); return NULL; } node = dt_new_addr(parent, "fsp", i); assert(node); dt_add_property_cells(node, "reg", i); if (sp_impl->hw_version == 1) { dt_add_property_strings(node, "compatible", "ibm,fsp", "ibm,fsp1"); /* Offset into the FSP MMIO space where the mailbox * registers are */ /* seen in the FSP1 spec */ dt_add_property_cells(node, "reg-offset", 0xb0016000); } else if (sp_impl->hw_version == 2) { dt_add_property_strings(node, "compatible", "ibm,fsp", "ibm,fsp2"); dt_add_property_cells(node, "reg-offset", 0xb0011000); } dt_add_property_cells(node, "hw-version", sp_impl->hw_version); dt_add_property_cells(node, "sw-version", sp_impl->sw_version); if (be16_to_cpu(sp_impl->func_flags) & SPSS_SP_IMPL_FLAGS_PRIMARY) dt_add_property(node, "primary", NULL, 0); return node; } static uint32_t fsp_create_link(const struct spss_iopath *iopath, int index, int fsp_index) { struct dt_node *node; const char *ststr; bool current = false; bool working = false; uint32_t chip_id; switch(iopath->psi.link_status) { case SPSS_IO_PATH_PSI_LINK_BAD_FRU: ststr = "Broken"; break; case SPSS_IO_PATH_PSI_LINK_CURRENT: ststr = "Active"; current = working = true; break; case SPSS_IO_PATH_PSI_LINK_BACKUP: ststr = "Backup"; working = true; break; default: ststr = "Unknown"; } prlog(PR_DEBUG, "FSP #%d: IO PATH %d is %s PSI Link, GXHB at %llx\n", fsp_index, index, ststr, (long long)iopath->psi.gxhb_base); chip_id = pcid_to_chip_id(iopath->psi.proc_chip_id); node = dt_find_compatible_node_on_chip(dt_root, NULL, "ibm,psihb-x", chip_id); if (!node) { prerror("FSP #%d: Can't find psihb node for link %d\n", fsp_index, index); } else { if (current) dt_add_property(node, "boot-link", NULL, 0); dt_add_property_strings(node, "status", working ? "ok" : "bad"); } return chip_id; } static void fsp_create_links(const void *spss, int index, struct dt_node *fsp_node) { uint32_t *links = NULL; unsigned int i, lp, lcount = 0; int count; count = HDIF_get_iarray_size(spss, SPSS_IDATA_SP_IOPATH); if (count < 0) { prerror("FSP #%d: Can't find IO PATH array size !\n", index); return; } prlog(PR_DEBUG, "FSP #%d: Found %d IO PATH\n", index, count); /* Iterate all links */ for (i = 0; i < count; i++) { const struct spss_iopath *iopath; unsigned int iopath_sz; uint32_t chip; iopath = HDIF_get_iarray_item(spss, SPSS_IDATA_SP_IOPATH, i, &iopath_sz); if (!CHECK_SPPTR(iopath)) { prerror("FSP #%d: Can't find IO PATH %d\n", index, i); break; } if (iopath->iopath_type != SPSS_IOPATH_TYPE_PSI) { prerror("FSP #%d: Unsupported IO PATH %d type 0x%04x\n", index, i, iopath->iopath_type); continue; } chip = fsp_create_link(iopath, i, index); lp = lcount++; links = realloc(links, 4 * lcount); links[lp] = chip; } if (links) dt_add_property(fsp_node, "ibm,psi-links", links, lcount * 4); } void fsp_parse(void) { const void *base_spss, *spss; struct dt_node *fsp_root, *fsp_node; int i; /* * Note on DT representation of the PSI links and FSPs: * * We create a XSCOM node for each PSI host bridge(one per chip), * * This is done in spira.c * * We do not create the /psi MMIO variant at this stage, it will * be added by the psi driver in skiboot. * * We do not put the FSP(s) as children of these. Instead, we create * a top-level /fsps node with the FSPs as children. * * Each FSP then has a "links" property which is an array of chip IDs */ /* Find SPSS in SPIRA */ base_spss = get_hdif(&spira.ntuples.sp_subsys, SPSS_HDIF_SIG); if (!base_spss) { prlog(PR_WARNING, "FSP: No SPSS in SPIRA !\n"); return; } fsp_root = dt_new(dt_root, "fsps"); assert(fsp_root); dt_add_property_cells(fsp_root, "#address-cells", 1); dt_add_property_cells(fsp_root, "#size-cells", 0); /* Iterate FSPs in SPIRA */ for_each_ntuple_idx(&spira.ntuples.sp_subsys, spss, i, SPSS_HDIF_SIG) { fsp_node = fsp_create_node(spss, i, fsp_root); if (fsp_node) fsp_create_links(spss, i, fsp_node); } }