240 lines
6.3 KiB
C
240 lines
6.3 KiB
C
/* 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 <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(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_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;
|
|
|
|
/* 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;
|
|
}
|