blob: 90e8e5b90e3cdadc0cc68d0af2ea0bdc065e8846 [file] [log] [blame]
/* 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 <device.h>
#include "spira.h"
#include <cpu.h>
#include <vpd.h>
#include <ccan/str/str.h>
#include <interrupts.h>
#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,
be16_to_cpu(sp_impl->hw_version),
be16_to_cpu(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 (be16_to_cpu(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 (be16_to_cpu(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", be16_to_cpu(sp_impl->hw_version));
dt_add_property_cells(node, "sw-version", be16_to_cpu(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(be16_to_cpu(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)be64_to_cpu(iopath->psi.gxhb_base));
chip_id = pcid_to_chip_id(be32_to_cpu(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 (be16_to_cpu(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);
free(links);
}
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);
}
}