/* 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" #define PCIA_MAX_THREADS 8 static unsigned int pcia_index(const void *pcia) { return (pcia - (void *)get_hdif(&spira.ntuples.pcia, "SPPCIA")) / be32_to_cpu(spira.ntuples.pcia.alloc_len); } static const struct sppcia_cpu_thread *find_tada(const void *pcia, unsigned int thread) { int count = HDIF_get_iarray_size(pcia, SPPCIA_IDATA_THREAD_ARRAY); int i; if (count < 0) return NULL; for (i = 0; i < count; i++) { const struct sppcia_cpu_thread *t; unsigned int size; t = HDIF_get_iarray_item(pcia, SPPCIA_IDATA_THREAD_ARRAY, i, &size); if (!t || size < sizeof(*t)) continue; if (be32_to_cpu(t->phys_thread_id) == thread) return t; } return NULL; } static void add_xics_icp(const void *pcia, u32 tcount, const char *compat) { const struct sppcia_cpu_thread *t; struct dt_node *icp; __be64 *reg; u32 i, irange[2], rsize; rsize = tcount * 2 * sizeof(__be64); reg = malloc(rsize); assert(reg); /* Suppresses uninitialized warning from gcc */ irange[0] = 0; for (i = 0; i < tcount; i++) { t = find_tada(pcia, i); assert(t); if (i == 0) irange[0] = be32_to_cpu(t->pir); reg[i * 2] = cpu_to_be64(cleanup_addr(be64_to_cpu(t->ibase))); reg[i * 2 + 1] = cpu_to_be64(0x1000); } irange[1] = tcount; icp = dt_new_addr(dt_root, "interrupt-controller", be64_to_cpu(reg[0])); if (!icp) { free(reg); return; } if (compat) dt_add_property_strings(icp, "compatible", "ibm,ppc-xicp", compat); else dt_add_property_strings(icp, "compatible", "ibm,ppc-xicp"); dt_add_property_cells(icp, "ibm,interrupt-server-ranges", irange[0], irange[1]); dt_add_property(icp, "interrupt-controller", NULL, 0); dt_add_property(icp, "reg", reg, rsize); dt_add_property_cells(icp, "#address-cells", 0); dt_add_property_string(icp, "device_type", "PowerPC-External-Interrupt-Presentation"); free(reg); } static struct dt_node *add_core_node(struct dt_node *cpus, const void *pcia, const struct sppcia_core_unique *id, bool okay) { const struct sppcia_cpu_thread *t; const struct sppcia_cpu_timebase *timebase; const struct sppcia_cpu_cache *cache; const struct sppcia_cpu_attr *attr; struct dt_node *cpu; const char *icp_compat; u32 i, size, threads, ve_flags, l2_phandle, chip_id; __be32 iserv[PCIA_MAX_THREADS]; /* Look for thread 0 */ t = find_tada(pcia, 0); if (!t) { prerror("CORE[%i]: Failed to find thread 0 !\n", pcia_index(pcia)); return NULL; } ve_flags = be32_to_cpu(id->verif_exist_flags); threads = ((ve_flags & CPU_ID_NUM_SECONDARY_THREAD_MASK) >> CPU_ID_NUM_SECONDARY_THREAD_SHIFT) + 1; assert(threads <= PCIA_MAX_THREADS); prlog(PR_INFO, "CORE[%i]: PIR=%.8x %s %s(%u threads)\n", pcia_index(pcia), be32_to_cpu(t->pir), ve_flags & CPU_ID_PACA_RESERVED ? "**RESERVED**" : cpu_state(ve_flags), be32_to_cpu(t->pir) == boot_cpu->pir ? "[boot] " : "", threads); timebase = HDIF_get_idata(pcia, SPPCIA_IDATA_TIMEBASE, &size); if (!timebase || size < sizeof(*timebase)) { prerror("CORE[%i]: bad timebase size %u @ %p\n", pcia_index(pcia), size, timebase); return NULL; } cache = HDIF_get_idata(pcia, SPPCIA_IDATA_CPU_CACHE, &size); if (!cache || size < sizeof(*cache)) { prerror("CORE[%i]: bad cache size %u @ %p\n", pcia_index(pcia), size, cache); return NULL; } cpu = add_core_common(cpus, cache, timebase, be32_to_cpu(t->pir), okay); /* Core attributes */ attr = HDIF_get_idata(pcia, SPPCIA_IDATA_CPU_ATTR, &size); if (attr) add_core_attr(cpu, be32_to_cpu(attr->attr)); /* Add cache info */ l2_phandle = add_core_cache_info(cpus, cache, be32_to_cpu(t->pir), okay); dt_add_property_cells(cpu, "l2-cache", l2_phandle); if (proc_gen == proc_gen_p7) icp_compat = "IBM,power7-icp"; else icp_compat = "IBM,power8-icp"; /* Get HW Chip ID */ chip_id = pcid_to_chip_id(be32_to_cpu(id->proc_chip_id)); dt_add_property_cells(cpu, "ibm,pir", be32_to_cpu(t->pir)); dt_add_property_cells(cpu, "ibm,chip-id", chip_id); /* Build ibm,ppc-interrupt-server#s with all threads */ for (i = 0; i < threads; i++) { t = find_tada(pcia, i); if (!t) { threads = i; break; } iserv[i] = t->pir; } dt_add_property(cpu, "ibm,ppc-interrupt-server#s", iserv, 4 * threads); /* Add the ICP node for this CPU for P7 / P8 */ if (proc_gen <= proc_gen_p8) add_xics_icp(pcia, threads, icp_compat); return cpu; } bool pcia_parse(void) { const void *pcia; struct dt_node *cpus; /* Check PCIA exists... if not, maybe we are getting a PACA ? */ pcia = get_hdif(&spira.ntuples.pcia, "SPPCIA"); if (!pcia) return false; prlog(PR_INFO, "Got PCIA !\n"); cpus = dt_new(dt_root, "cpus"); dt_add_property_cells(cpus, "#address-cells", 1); dt_add_property_cells(cpus, "#size-cells", 0); for_each_pcia(pcia) { const struct sppcia_core_unique *id; u32 size, ve_flags; bool okay; id = HDIF_get_idata(pcia, SPPCIA_IDATA_CORE_UNIQUE, &size); if (!id || size < sizeof(*id)) { prerror("CORE[%i]: bad id size %u @ %p\n", pcia_index(pcia), size, id); return false; } ve_flags = be32_to_cpu(id->verif_exist_flags); switch ((ve_flags & CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT) { case CPU_ID_VERIFY_USABLE_NO_FAILURES: case CPU_ID_VERIFY_USABLE_FAILURES: okay = true; break; default: okay = false; } prlog(okay ? PR_INFO : PR_WARNING, "CORE[%i]: HW_PROC_ID=%i PROC_CHIP_ID=%i EC=0x%x %s\n", pcia_index(pcia), be32_to_cpu(id->hw_proc_id), be32_to_cpu(id->proc_chip_id), be32_to_cpu(id->chip_ec_level), okay ? "OK" : "UNAVAILABLE"); if (!add_core_node(cpus, pcia, id, okay)) break; } return true; }