| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| /* Copyright 2013-2019 IBM Corp. */ |
| |
| #include <skiboot.h> |
| #include "spira.h" |
| #include <cpu.h> |
| #include <fsp.h> |
| #include <opal.h> |
| #include <ccan/str/str.h> |
| #include <device.h> |
| |
| #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_cells(icp, "#interrupt-cells", 1); |
| 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_PCIA_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_p8) |
| 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 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; |
| |
| 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; |
| } |