summaryrefslogtreecommitdiffstats
path: root/hdata/spira.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-07-02 15:36:20 +1000
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-07-02 15:36:20 +1000
commit1d880992fd8c8457a2d990ac6622cfd58fb1b261 (patch)
treec4c843b12e96b5612c315db5a23c5da1a900618c /hdata/spira.c
downloadblackbird-skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.gz
blackbird-skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.zip
Initial commit of Open Source release
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'hdata/spira.c')
-rw-r--r--hdata/spira.c965
1 files changed, 965 insertions, 0 deletions
diff --git a/hdata/spira.c b/hdata/spira.c
new file mode 100644
index 00000000..39a78e88
--- /dev/null
+++ b/hdata/spira.c
@@ -0,0 +1,965 @@
+/* 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 <memory.h>
+#include <vpd.h>
+#include <interrupts.h>
+#include <ccan/str/str.h>
+#include <chip.h>
+#include <fsp-mdst-table.h>
+
+#include "hdata.h"
+#include "hostservices.h"
+
+/* Processor Initialization structure, contains
+ * the initial NIA and MSR values for the entry
+ * point
+ *
+ * Note: It appears to be ignoring the entry point
+ * and always going to 0x180
+ */
+
+static int cpu_type;
+
+__section(".procin.data") struct proc_init_data proc_init_data = {
+ .hdr = HDIF_SIMPLE_HDR("PROCIN", 1, struct proc_init_data),
+ .regs_ptr = HDIF_IDATA_PTR(offsetof(struct proc_init_data, regs), 0x10),
+ .regs = {
+ .nia = CPU_TO_BE64(0x180),
+ .msr = CPU_TO_BE64(0x9000000000000000ULL), /* SF | HV */
+ },
+};
+
+/* Populate MDST table
+ *
+ * Note that we only pass sapphire console buffer here so that we can
+ * capture early failure logs. Later dump component (fsp_dump_mdst_init)
+ * creates new table with all the memory sections we are interested and
+ * sends updated table to FSP via MBOX.
+ *
+ * To help the FSP distinguishing between TCE tokens and actual physical
+ * addresses, we set the top bit to 1 on physical addresses
+ */
+#define ADDR_TOP_BIT (1ul << 63)
+
+__section(".mdst.data") struct dump_mdst_table init_mdst_table[2] = {
+ {
+ .addr = CPU_TO_BE64(INMEM_CON_START | ADDR_TOP_BIT),
+ .type = CPU_TO_BE32(DUMP_SECTION_CONSOLE),
+ .size = CPU_TO_BE32(INMEM_CON_LEN),
+ },
+ {
+ .addr = CPU_TO_BE64(HBRT_CON_START | ADDR_TOP_BIT),
+ .type = CPU_TO_BE32(DUMP_SECTION_HBRT_LOG),
+ .size = CPU_TO_BE32(HBRT_CON_LEN),
+ },
+};
+
+/* SP Interface Root Array, aka SPIRA */
+__section(".spira.data") struct spira spira = {
+ .hdr = HDIF_SIMPLE_HDR("SPIRA ", SPIRA_VERSION, struct spira),
+ .ntuples_ptr = HDIF_IDATA_PTR(offsetof(struct spira, ntuples),
+ sizeof(struct spira_ntuples)),
+ .ntuples = {
+ .array_hdr = {
+ .offset = CPU_TO_BE32(HDIF_ARRAY_OFFSET),
+ .ecnt = CPU_TO_BE32(SPIRA_NTUPLES_COUNT),
+ .esize
+ = CPU_TO_BE32(sizeof(struct spira_ntuple)),
+ .eactsz = CPU_TO_BE32(0x18),
+ },
+ /* We only populate some n-tuples */
+ .proc_init = {
+ .addr = CPU_TO_BE64(PROCIN_OFF),
+ .alloc_cnt = CPU_TO_BE16(1),
+ .act_cnt = CPU_TO_BE16(1),
+ .alloc_len
+ = CPU_TO_BE32(sizeof(struct proc_init_data)),
+ },
+ .heap = {
+ .addr = CPU_TO_BE64(SPIRA_HEAP_BASE),
+ .alloc_cnt = CPU_TO_BE16(1),
+ .alloc_len = CPU_TO_BE32(SPIRA_HEAP_SIZE),
+ },
+ .mdump_src = {
+ .addr = CPU_TO_BE64(MDST_TABLE_OFF),
+ .alloc_cnt = CPU_TO_BE16(ARRAY_SIZE(init_mdst_table)),
+ .act_cnt = CPU_TO_BE16(ARRAY_SIZE(init_mdst_table)),
+ .alloc_len =
+ CPU_TO_BE32(sizeof(init_mdst_table)),
+ },
+ },
+};
+
+/* Overridden for testing. */
+#ifndef spira_check_ptr
+bool spira_check_ptr(const void *ptr, const char *file, unsigned int line)
+{
+ if (!ptr)
+ return false;
+ if (((unsigned long)ptr) >= SPIRA_HEAP_BASE &&
+ ((unsigned long)ptr) < (SPIRA_HEAP_BASE + SPIRA_HEAP_SIZE))
+ return true;
+
+ prerror("SPIRA: Bad pointer %p at %s line %d\n", ptr, file, line);
+ return false;
+}
+#endif
+
+struct HDIF_common_hdr *__get_hdif(struct spira_ntuple *n, const char id[],
+ const char *file, int line)
+{
+ struct HDIF_common_hdr *h = ntuple_addr(n);
+ if (!spira_check_ptr(h, file, line))
+ return NULL;
+
+ if (!HDIF_check(h, id)) {
+ prerror("SPIRA: bad tuple %p: expected %s at %s line %d\n",
+ h, id, file, line);
+ return NULL;
+ }
+ return h;
+}
+
+static struct dt_node *add_xscom_node(uint64_t base, uint32_t hw_id,
+ uint32_t proc_chip_id)
+{
+ struct dt_node *node;
+ uint64_t addr, size;
+
+ addr = base | ((uint64_t)hw_id << PPC_BITLSHIFT(28));
+ size = (u64)1 << PPC_BITLSHIFT(28);
+
+ printf("XSCOM: Found HW ID 0x%x (PCID 0x%x) @ 0x%llx\n",
+ hw_id, proc_chip_id, (long long)addr);
+
+ node = dt_new_addr(dt_root, "xscom", addr);
+ if (!node)
+ return NULL;
+
+ dt_add_property_cells(node, "ibm,chip-id", hw_id);
+ dt_add_property_cells(node, "ibm,proc-chip-id", proc_chip_id);
+ dt_add_property_cells(node, "#address-cells", 1);
+ dt_add_property_cells(node, "#size-cells", 1);
+ dt_add_property(node, "scom-controller", NULL, 0);
+
+ switch(proc_gen) {
+ case proc_gen_p7:
+ dt_add_property_strings(node, "compatible",
+ "ibm,xscom", "ibm,power7-xscom");
+ break;
+ case proc_gen_p8:
+ dt_add_property_strings(node, "compatible",
+ "ibm,xscom", "ibm,power8-xscom");
+ break;
+ default:
+ dt_add_property_strings(node, "compatible", "ibm,xscom");
+ }
+ dt_add_property_u64s(node, "reg", addr, size);
+
+ return node;
+}
+
+struct dt_node *find_xscom_for_chip(uint32_t chip_id)
+{
+ struct dt_node *node;
+ uint32_t id;
+
+ dt_for_each_compatible(dt_root, node, "ibm,xscom") {
+ id = dt_get_chip_id(node);
+ if (id == chip_id)
+ return node;
+ }
+
+ return NULL;
+}
+
+static void add_psihb_node(struct dt_node *np)
+{
+ u32 psi_scom, psi_slen;
+ const char *psi_comp;
+
+ /*
+ * We add a few things under XSCOM that aren't added
+ * by any other HDAT path
+ */
+
+ /* PSI host bridge */
+ switch(proc_gen) {
+ case proc_gen_p7:
+ psi_scom = 0x2010c00;
+ psi_slen = 0x10;
+ psi_comp = "ibm,power7-psihb-x";
+ break;
+ case proc_gen_p8:
+ psi_scom = 0x2010900;
+ psi_slen = 0x20;
+ psi_comp = "ibm,power8-psihb-x";
+ break;
+ default:
+ psi_comp = NULL;
+ }
+ if (psi_comp) {
+ struct dt_node *psi_np;
+
+ psi_np = dt_new_addr(np, "psihb", psi_scom);
+ if (!psi_np)
+ return;
+
+ dt_add_property_cells(psi_np, "reg", psi_scom, psi_slen);
+ dt_add_property_strings(psi_np, "compatible", psi_comp,
+ "ibm,psihb-x");
+ }
+}
+
+static void add_xscom_add_pcia_assoc(struct dt_node *np, uint32_t pcid)
+{
+ const struct HDIF_common_hdr *hdr;
+ u32 size;
+
+
+ /*
+ * The SPPCRD doesn't contain all the affinity data, we have
+ * to dig it out of a core. I assume this is so that node
+ * affinity can be different for groups of cores within the
+ * chip, but for now we are going to ignore that
+ */
+ hdr = get_hdif(&spira.ntuples.pcia, SPPCIA_HDIF_SIG);
+ if (!hdr)
+ return;
+
+ for_each_pcia(hdr) {
+ const struct sppcia_core_unique *id;
+
+ id = HDIF_get_idata(hdr, SPPCIA_IDATA_CORE_UNIQUE, &size);
+ if (!id || size < sizeof(*id))
+ continue;
+
+ if (be32_to_cpu(id->proc_chip_id) != pcid)
+ continue;
+
+ dt_add_property_cells(np, "ibm,ccm-node-id",
+ be32_to_cpu(id->ccm_node_id));
+ dt_add_property_cells(np, "ibm,hw-card-id",
+ be32_to_cpu(id->hw_card_id));
+ dt_add_property_cells(np, "ibm,hw-module-id",
+ be32_to_cpu(id->hw_module_id));
+ if (!dt_find_property(np, "ibm,dbob-id"))
+ dt_add_property_cells(np, "ibm,dbob-id",
+ be32_to_cpu(id->drawer_book_octant_blade_id));
+ dt_add_property_cells(np, "ibm,mem-interleave-scope",
+ be32_to_cpu(id->memory_interleaving_scope));
+ return;
+ }
+}
+
+static bool add_xscom_sppcrd(uint64_t xscom_base)
+{
+ const struct HDIF_common_hdr *hdif;
+ unsigned int i, vpd_sz;
+ const void *vpd;
+ struct dt_node *np;
+
+ for_each_ntuple_idx(&spira.ntuples.proc_chip, hdif, i,
+ SPPCRD_HDIF_SIG) {
+ const struct sppcrd_chip_info *cinfo;
+ u32 ve, version;
+
+ cinfo = HDIF_get_idata(hdif, SPPCRD_IDATA_CHIP_INFO, NULL);
+ if (!CHECK_SPPTR(cinfo)) {
+ prerror("XSCOM: Bad ChipID data %d\n", i);
+ continue;
+ }
+
+ ve = be32_to_cpu(cinfo->verif_exist_flags) & CHIP_VERIFY_MASK;
+ ve >>= CHIP_VERIFY_SHIFT;
+ if (ve == CHIP_VERIFY_NOT_INSTALLED ||
+ ve == CHIP_VERIFY_UNUSABLE)
+ continue;
+
+ /* Create the XSCOM node */
+ np = add_xscom_node(xscom_base,
+ be32_to_cpu(cinfo->xscom_id),
+ be32_to_cpu(cinfo->proc_chip_id));
+ if (!np)
+ continue;
+
+ version = be16_to_cpu(hdif->version);
+
+ /* Version 0A has additional OCC related stuff */
+ if (version >= 0x000a) {
+ if (!dt_find_property(np, "ibm,dbob-id"))
+ dt_add_property_cells(np, "ibm,dbob-id",
+ be32_to_cpu(cinfo->dbob_id));
+ dt_add_property_cells(np, "ibm,occ-functional-state",
+ be32_to_cpu(cinfo->occ_state));
+ }
+
+ /* Add chip VPD */
+ dt_add_vpd_node(hdif, SPPCRD_IDATA_FRU_ID, SPPCRD_IDATA_KW_VPD);
+
+ /* Add module VPD on version A and later */
+ if (version >= 0x000a) {
+ vpd = HDIF_get_idata(hdif, SPPCRD_IDATA_MODULE_VPD,
+ &vpd_sz);
+ if (CHECK_SPPTR(vpd))
+ dt_add_property(np, "ibm,module-vpd", vpd,
+ vpd_sz);
+ }
+
+ /*
+ * Extract additional associativity information from
+ * the core data. Pick one core on that chip
+ */
+ add_xscom_add_pcia_assoc(np, be32_to_cpu(cinfo->proc_chip_id));
+
+ /* Add PSI Host bridge */
+ add_psihb_node(np);
+ }
+
+ return i > 0;
+}
+
+static void add_xscom_sppaca(uint64_t xscom_base)
+{
+ const struct HDIF_common_hdr *hdif;
+ unsigned int i;
+ struct dt_node *np;
+
+ for_each_ntuple_idx(&spira.ntuples.paca, hdif, i, PACA_HDIF_SIG) {
+ const struct sppaca_cpu_id *id;
+ unsigned int chip_id, size;
+ int ve;
+
+ /* We only suport old style PACA on P7 ! */
+ assert(proc_gen == proc_gen_p7);
+
+ id = HDIF_get_idata(hdif, SPPACA_IDATA_CPU_ID, &size);
+
+ if (!CHECK_SPPTR(id)) {
+ prerror("XSCOM: Bad processor data %d\n", i);
+ continue;
+ }
+
+ ve = be32_to_cpu(id->verify_exists_flags) & CPU_ID_VERIFY_MASK;
+ ve >>= CPU_ID_VERIFY_SHIFT;
+ if (ve == CPU_ID_VERIFY_NOT_INSTALLED ||
+ ve == CPU_ID_VERIFY_UNUSABLE)
+ continue;
+
+ /* Convert to HW chip ID */
+ chip_id = P7_PIR2GCID(be32_to_cpu(id->pir));
+
+ /* do we already have an XSCOM for this chip? */
+ if (find_xscom_for_chip(chip_id))
+ continue;
+
+ /* Create the XSCOM node */
+ np = add_xscom_node(xscom_base, chip_id,
+ be32_to_cpu(id->processor_chip_id));
+ if (!np)
+ continue;
+
+ /* Add chip VPD */
+ dt_add_vpd_node(hdif, SPPACA_IDATA_FRU_ID, SPPACA_IDATA_KW_VPD);
+
+ /* Add chip associativity data */
+ dt_add_property_cells(np, "ibm,ccm-node-id",
+ be32_to_cpu(id->ccm_node_id));
+ if (size > SPIRA_CPU_ID_MIN_SIZE) {
+ dt_add_property_cells(np, "ibm,hw-card-id",
+ be32_to_cpu(id->hw_card_id));
+ dt_add_property_cells(np, "ibm,hw-module-id",
+ be32_to_cpu(id->hardware_module_id));
+ if (!dt_find_property(np, "ibm,dbob-id"))
+ dt_add_property_cells(np, "ibm,dbob-id",
+ be32_to_cpu(id->drawer_book_octant_blade_id));
+ dt_add_property_cells(np, "ibm,mem-interleave-scope",
+ be32_to_cpu(id->memory_interleaving_scope));
+ }
+
+ /* Add PSI Host bridge */
+ add_psihb_node(np);
+ }
+}
+
+static void add_xscom(void)
+{
+ const void *ms_vpd;
+ const struct msvpd_pmover_bsr_synchro *pmbs;
+ unsigned int size;
+ uint64_t xscom_base;
+
+ ms_vpd = get_hdif(&spira.ntuples.ms_vpd, MSVPD_HDIF_SIG);
+ if (!ms_vpd) {
+ prerror("XSCOM: Can't find MS VPD\n");
+ return;
+ }
+
+ pmbs = HDIF_get_idata(ms_vpd, MSVPD_IDATA_PMOVER_SYNCHRO, &size);
+ if (!CHECK_SPPTR(pmbs) || size < sizeof(*pmbs)) {
+ prerror("XSCOM: absent or bad PMBS size %u @ %p\n", size, pmbs);
+ return;
+ }
+
+ if (!(be32_to_cpu(pmbs->flags) & MSVPD_PMS_FLAG_XSCOMBASE_VALID)) {
+ prerror("XSCOM: No XSCOM base in PMBS, using default\n");
+ return;
+ }
+
+ xscom_base = be64_to_cpu(pmbs->xscom_addr);
+
+ /* Some FSP (on P7) give me a crap base address for XSCOM (it has
+ * spurious bits set as far as I can tell). Since only 5 bits 18:22 can
+ * be programmed in hardware, let's isolate these. This seems to give
+ * me the right value on VPL1
+ */
+ if (cpu_type == PVR_TYPE_P7)
+ xscom_base &= 0x80003e0000000000ul;
+
+ /* Get rid of the top bits */
+ xscom_base = cleanup_addr(xscom_base);
+
+ /* First, try the new proc_chip ntuples for chip data */
+ if (add_xscom_sppcrd(xscom_base))
+ return;
+
+ /* Otherwise, check the old-style PACA, looking for unique chips */
+ add_xscom_sppaca(xscom_base);
+}
+
+static void add_chiptod_node(unsigned int chip_id, int flags)
+{
+ struct dt_node *node, *xscom_node;
+ const char *compat_str;
+ uint32_t addr, len;
+
+ if ((flags & CHIPTOD_ID_FLAGS_STATUS_MASK) !=
+ CHIPTOD_ID_FLAGS_STATUS_OK)
+ return;
+
+ xscom_node = find_xscom_for_chip(chip_id);
+ if (!xscom_node) {
+ prerror("CHIPTOD: No xscom for chiptod %d?\n", chip_id);
+ return;
+ }
+
+ addr = 0x40000;
+ len = 0x34;
+
+ switch(proc_gen) {
+ case proc_gen_p7:
+ compat_str = "ibm,power7-chiptod";
+ break;
+ case proc_gen_p8:
+ compat_str = "ibm,power8-chiptod";
+ break;
+ default:
+ return;
+ }
+
+ printf("CHIPTOD: Found on chip 0x%x %s\n", chip_id,
+ (flags & CHIPTOD_ID_FLAGS_PRIMARY) ? "[primary]" :
+ ((flags & CHIPTOD_ID_FLAGS_SECONDARY) ? "[secondary]" : ""));
+
+ node = dt_new_addr(xscom_node, "chiptod", addr);
+ if (!node)
+ return;
+
+ dt_add_property_cells(node, "reg", addr, len);
+ dt_add_property_strings(node, "compatible", "ibm,power-chiptod",
+ compat_str);
+
+ if (flags & CHIPTOD_ID_FLAGS_PRIMARY)
+ dt_add_property(node, "primary", NULL, 0);
+ if (flags & CHIPTOD_ID_FLAGS_SECONDARY)
+ dt_add_property(node, "secondary", NULL, 0);
+}
+
+static bool add_chiptod_old(void)
+{
+ const void *hdif;
+ unsigned int i;
+ bool found = false;
+
+ /*
+ * Locate chiptod ID structures in SPIRA
+ */
+ if (!get_hdif(&spira.ntuples.chip_tod, "TOD "))
+ return found;
+
+ for_each_ntuple_idx(&spira.ntuples.chip_tod, hdif, i, "TOD ") {
+ const struct chiptod_chipid *id;
+
+ id = HDIF_get_idata(hdif, CHIPTOD_IDATA_CHIPID, NULL);
+ if (!CHECK_SPPTR(id)) {
+ prerror("CHIPTOD: Bad ChipID data %d\n", i);
+ continue;
+ }
+
+ add_chiptod_node(pcid_to_chip_id(be32_to_cpu(id->chip_id)),
+ be32_to_cpu(id->flags));
+ found = true;
+ }
+ return found;
+}
+
+static bool add_chiptod_new(uint32_t master_cpu)
+{
+ const void *hdif;
+ unsigned int i, master_chip;
+ bool found = false;
+
+ /*
+ * Locate Proc Chip ID structures in SPIRA
+ */
+ if (!get_hdif(&spira.ntuples.proc_chip, SPPCRD_HDIF_SIG))
+ return found;
+
+ master_chip = pir_to_chip_id(master_cpu);
+
+ for_each_ntuple_idx(&spira.ntuples.proc_chip, hdif, i,
+ SPPCRD_HDIF_SIG) {
+ const struct sppcrd_chip_info *cinfo;
+ const struct sppcrd_chip_tod *tinfo;
+ unsigned int size;
+ u32 ve, flags;
+
+ cinfo = HDIF_get_idata(hdif, SPPCRD_IDATA_CHIP_INFO, NULL);
+ if (!CHECK_SPPTR(cinfo)) {
+ prerror("CHIPTOD: Bad ChipID data %d\n", i);
+ continue;
+ }
+
+ ve = be32_to_cpu(cinfo->verif_exist_flags) & CHIP_VERIFY_MASK;
+ ve >>= CHIP_VERIFY_SHIFT;
+ if (ve == CHIP_VERIFY_NOT_INSTALLED ||
+ ve == CHIP_VERIFY_UNUSABLE)
+ continue;
+
+ tinfo = HDIF_get_idata(hdif, SPPCRD_IDATA_CHIP_TOD, &size);
+ if (!CHECK_SPPTR(tinfo)) {
+ prerror("CHIPTOD: Bad TOD data %d\n", i);
+ continue;
+ }
+
+ flags = be32_to_cpu(tinfo->flags);
+
+ /* The FSP may strip the chiptod info from HDAT; if we find
+ * a zero-ed out entry, assume that the chiptod is
+ * present, but we don't have any primary/secondary info. In
+ * this case, pick the primary based on the CPU that was
+ * assigned master.
+ */
+ if (!size) {
+ flags = CHIPTOD_ID_FLAGS_STATUS_OK;
+ if (be32_to_cpu(cinfo->xscom_id) == master_chip)
+ flags |= CHIPTOD_ID_FLAGS_PRIMARY;
+ }
+
+ add_chiptod_node(be32_to_cpu(cinfo->xscom_id), flags);
+ found = true;
+ }
+ return found;
+}
+
+static void add_nx_node(u32 gcid)
+{
+ struct dt_node *nx;
+ const char *cp_str;
+ u32 addr;
+ u32 size;
+ struct dt_node *xscom;
+
+ xscom = find_xscom_for_chip(gcid);
+ if (xscom == NULL) {
+ prerror("NX%d: did not found xscom node.\n", gcid);
+ return;
+ }
+
+ /*
+ * The NX register space is relatively self contained on P7+ but
+ * a bit more messy on P8. However it's all contained within the
+ * PB chiplet port 1 so we'll stick to that in the "reg" property
+ * and let the NX "driver" deal with the details.
+ */
+ addr = 0x2010000;
+ size = 0x0004000;
+
+ switch (proc_gen) {
+ case proc_gen_p7:
+ cp_str = "ibm,power7-nx";
+ break;
+ case proc_gen_p8:
+ cp_str = "ibm,power8-nx";
+ break;
+ default:
+ return;
+ }
+ nx = dt_new_addr(xscom, "nx", addr);
+ if (!nx)
+ return;
+
+ dt_add_property_cells(nx, "reg", addr, size);
+ dt_add_property_strings(nx, "compatible", "ibm,power-nx", cp_str);
+}
+
+static void add_nx(void)
+{
+ unsigned int i;
+ void *hdif;
+
+ for_each_ntuple_idx(&spira.ntuples.proc_chip, hdif, i,
+ SPPCRD_HDIF_SIG) {
+ const struct sppcrd_chip_info *cinfo;
+ u32 ve;
+
+ cinfo = HDIF_get_idata(hdif, SPPCRD_IDATA_CHIP_INFO, NULL);
+ if (!CHECK_SPPTR(cinfo)) {
+ prerror("NX: Bad ChipID data %d\n", i);
+ continue;
+ }
+
+ ve = be32_to_cpu(cinfo->verif_exist_flags) & CHIP_VERIFY_MASK;
+ ve >>= CHIP_VERIFY_SHIFT;
+ if (ve == CHIP_VERIFY_NOT_INSTALLED ||
+ ve == CHIP_VERIFY_UNUSABLE)
+ continue;
+
+ if (cinfo->nx_state)
+ add_nx_node(be32_to_cpu(cinfo->xscom_id));
+ }
+}
+
+
+static void add_iplparams_sys_params(const void *iplp, struct dt_node *node)
+{
+ const struct iplparams_sysparams *p;
+ u32 sys_type;
+ const char *sys_family;
+
+ p = HDIF_get_idata(iplp, IPLPARAMS_SYSPARAMS, NULL);
+ if (!CHECK_SPPTR(p)) {
+ prerror("IPLPARAMS: No SYS Parameters\n");
+ /* Create a generic compatible property */
+ dt_add_property_string(dt_root, "compatible", "ibm,powernv");
+ return;
+ }
+
+ node = dt_new(node, "sys-params");
+ assert(node);
+ dt_add_property_cells(node, "#address-cells", 0);
+ dt_add_property_cells(node, "#size-cells", 0);
+
+ dt_add_property_nstr(node, "ibm,sys-model", p->sys_model, 4);
+
+ /* Compatible is 2 entries: ibm,powernv and ibm,<platform>
+ */
+ sys_type = be32_to_cpu(p->system_type);
+ switch(sys_type >> 28) {
+ case 0:
+ sys_family = "ibm,squadrons";
+ break;
+ case 1:
+ sys_family = "ibm,eclipz";
+ break;
+ case 2:
+ sys_family = "ibm,apollo";
+ break;
+ case 3:
+ sys_family = "ibm,firenze";
+ break;
+ default:
+ sys_family = NULL;
+ prerror("IPLPARAMS: Unknown system family\n");
+ break;
+ }
+ dt_add_property_strings(dt_root, "compatible", "ibm,powernv",
+ sys_family);
+}
+
+static void add_iplparams_ipl_params(const void *iplp, struct dt_node *node)
+{
+ const struct iplparams_iplparams *p;
+
+ p = HDIF_get_idata(iplp, IPLPARAMS_IPLPARAMS, NULL);
+ if (!CHECK_SPPTR(p)) {
+ prerror("IPLPARAMS: No IPL Parameters\n");
+ return;
+ }
+
+ node = dt_new(node, "ipl-params");
+ assert(node);
+ dt_add_property_cells(node, "#address-cells", 0);
+ dt_add_property_cells(node, "#size-cells", 0);
+
+ /* On an ASM initiated factory reset, this bit will be set
+ * and the FSP expects the firmware to reset the PCI bus
+ * numbers and respond with a Power Down (CE,4D,02) message
+ */
+ if (p->other_attrib & IPLPARAMS_OATTR_RST_PCI_BUSNO)
+ dt_add_property_cells(node, "pci-busno-reset-ipl", 1);
+ dt_add_property_strings(node, "cec-ipl-side",
+ (p->ipl_side & IPLPARAMS_CEC_FW_IPL_SIDE_TEMP) ?
+ "temp" : "perm");
+ dt_add_property_strings(node, "fsp-ipl-side",
+ (p->ipl_side & IPLPARAMS_FSP_FW_IPL_SIDE_TEMP) ?
+ "temp" : "perm");
+ dt_add_property_cells(node, "os-ipl-mode", p->os_ipl_mode);
+ dt_add_property_strings(node, "cec-major-type",
+ p->cec_ipl_maj_type ? "hot" : "cold");
+}
+
+static void add_iplparams_serials(const void *iplp, struct dt_node *node)
+{
+ const struct iplparms_serial *ipser;
+ struct dt_node *ser_node;
+ int count, i;
+
+ count = HDIF_get_iarray_size(iplp, IPLPARMS_IDATA_SERIAL);
+ if (!count) {
+ prerror("IPLPARAMS: No serial ports\n");
+ return;
+ }
+ prerror("IPLPARAMS: %d serial ports in array\n", count);
+
+ node = dt_new(node, "fsp-serial");
+ assert(node);
+ dt_add_property_cells(node, "#address-cells", 1);
+ dt_add_property_cells(node, "#size-cells", 0);
+
+ for (i = 0; i < count; i++) {
+ u16 rsrc_id;
+ ipser = HDIF_get_iarray_item(iplp, IPLPARMS_IDATA_SERIAL,
+ i, NULL);
+ if (!CHECK_SPPTR(ipser))
+ continue;
+ rsrc_id = be16_to_cpu(ipser->rsrc_id);
+ printf("IPLPARAMS: Serial %d rsrc: %04x loc: %s\n",
+ i, rsrc_id, ipser->loc_code);
+ ser_node = dt_new_addr(node, "serial", rsrc_id);
+ if (!ser_node)
+ continue;
+
+ dt_add_property_cells(ser_node, "reg", rsrc_id);
+ dt_add_property_nstr(ser_node, "ibm,loc-code",
+ ipser->loc_code, LOC_CODE_SIZE);
+ dt_add_property_string(ser_node, "compatible",
+ "ibm,fsp-serial");
+ /* XXX handle CALLHOME flag ? */
+ }
+}
+
+/*
+ * Check for platform dump, if present populate DT
+ */
+static void add_iplparams_platform_dump(const void *iplp, struct dt_node *node)
+{
+ const struct iplparams_dump *ipl_dump;
+
+ ipl_dump = HDIF_get_idata(iplp, IPLPARAMS_PLATFORM_DUMP, NULL);
+ if (!CHECK_SPPTR(ipl_dump))
+ return;
+
+ node = dt_new(node, "platform-dump");
+ assert(node);
+
+ if (be32_to_cpu(ipl_dump->dump_id)) {
+ dt_add_property_cells(node, "dump-id",
+ be32_to_cpu(ipl_dump->dump_id));
+ dt_add_property_u64(node, "total-size",
+ be64_to_cpu(ipl_dump->act_dump_sz));
+ dt_add_property_u64(node, "hw-dump-size",
+ be32_to_cpu(ipl_dump->act_hw_dump_sz));
+ dt_add_property_cells(node, "plog-id",
+ be32_to_cpu(ipl_dump->plid));
+ }
+}
+
+static void add_iplparams(void)
+{
+ struct dt_node *iplp_node;
+ const void *ipl_parms;
+
+ ipl_parms = get_hdif(&spira.ntuples.ipl_parms, "IPLPMS");
+ if (!ipl_parms) {
+ prerror("IPLPARAMS: Cannot find IPL Parms in SPIRA\n");
+ return;
+ }
+
+ iplp_node = dt_new(dt_root, "ipl-params");
+ assert(iplp_node);
+ dt_add_property_cells(iplp_node, "#address-cells", 0);
+ dt_add_property_cells(iplp_node, "#size-cells", 0);
+
+ add_iplparams_sys_params(ipl_parms, iplp_node);
+ add_iplparams_ipl_params(ipl_parms, iplp_node);
+ add_iplparams_serials(ipl_parms, iplp_node);
+ add_iplparams_platform_dump(ipl_parms, iplp_node);
+}
+
+/* Various structure contain a "proc_chip_id" which is an arbitrary
+ * numbering used by HDAT to reference chips, which doesn't correspond
+ * to the HW IDs. We want to use the HW IDs everywhere in the DT so
+ * we convert using this.
+ *
+ * Note: On P7, the HW ID is the XSCOM "GCID" including the T bit which
+ * is *different* from the chip ID portion of the interrupt server#
+ * (or PIR). See the explanations in chip.h
+ */
+uint32_t pcid_to_chip_id(uint32_t proc_chip_id)
+{
+ unsigned int i;
+ const void *hdif;
+
+ /* First, try the proc_chip ntuples for chip data */
+ for_each_ntuple_idx(&spira.ntuples.proc_chip, hdif, i,
+ SPPCRD_HDIF_SIG) {
+ const struct sppcrd_chip_info *cinfo;
+
+ cinfo = HDIF_get_idata(hdif, SPPCRD_IDATA_CHIP_INFO,
+ NULL);
+ if (!CHECK_SPPTR(cinfo)) {
+ prerror("XSCOM: Bad ChipID data %d\n", i);
+ continue;
+ }
+ if (proc_chip_id == be32_to_cpu(cinfo->proc_chip_id))
+ return be32_to_cpu(cinfo->xscom_id);
+ }
+
+ /* Otherwise, check the old-style PACA, looking for unique chips */
+ for_each_ntuple_idx(&spira.ntuples.paca, hdif, i, PACA_HDIF_SIG) {
+ const struct sppaca_cpu_id *id;
+
+ /* We only suport old style PACA on P7 ! */
+ assert(proc_gen == proc_gen_p7);
+
+ id = HDIF_get_idata(hdif, SPPACA_IDATA_CPU_ID, NULL);
+
+ if (!CHECK_SPPTR(id)) {
+ prerror("XSCOM: Bad processor data %d\n", i);
+ continue;
+ }
+
+ if (proc_chip_id == be32_to_cpu(id->processor_chip_id))
+ return P7_PIR2GCID(be32_to_cpu(id->pir));
+ }
+
+ /* Not found, what to do ? Assert ? For now return a number
+ * guaranteed to not exist
+ */
+ return (uint32_t)-1;
+}
+
+static void dt_init_vpd_node(void)
+{
+ struct dt_node *dt_vpd;
+
+ dt_vpd = dt_new(dt_root, "vpd");
+ assert(dt_vpd);
+}
+
+static void hostservices_parse(void)
+{
+ struct HDIF_common_hdr *hs_hdr;
+ const void *dt_blob;
+ unsigned int size;
+ unsigned int ntuples_size;
+
+ ntuples_size = sizeof(struct HDIF_array_hdr) +
+ be32_to_cpu(spira.ntuples.array_hdr.ecnt) *
+ sizeof(struct spira_ntuple);
+
+ if (offsetof(struct spira_ntuples, hs_data) >= ntuples_size) {
+ prerror("SPIRA: No host services data found\n");
+ return;
+ }
+
+ hs_hdr = get_hdif(&spira.ntuples.hs_data, HSERV_HDIF_SIG);
+ if (!hs_hdr) {
+ prerror("SPIRA: No host services data found\n");
+ return;
+ }
+
+ dt_blob = HDIF_get_idata(hs_hdr, 0, &size);
+ if (!dt_blob) {
+ prerror("SPIRA: No host services idata found\n");
+ return;
+ }
+ hservices_from_hdat(dt_blob, size);
+}
+
+void parse_hdat(bool is_opal, uint32_t master_cpu)
+{
+ cpu_type = PVR_TYPE(mfspr(SPR_PVR));
+
+ printf("\n");
+ printf("-----------------------------------------------\n");
+ printf("-------------- Parsing HDAT ... ---------------\n");
+ printf("-----------------------------------------------\n");
+ printf("\n");
+
+ dt_root = dt_new_root("");
+
+ /*
+ * Basic DT root stuff
+ */
+ dt_add_property_cells(dt_root, "#address-cells", 2);
+ dt_add_property_cells(dt_root, "#size-cells", 2);
+ dt_add_property_string(dt_root, "lid-type", is_opal ? "opal" : "phyp");
+
+ /* Create /vpd node */
+ dt_init_vpd_node();
+
+ /* Parse SPPACA and/or PCIA */
+ if (!pcia_parse())
+ paca_parse();
+
+ /* IPL params */
+ add_iplparams();
+
+ /* Parse MS VPD */
+ memory_parse();
+
+ /* Add XSCOM node (must be before chiptod & IO ) */
+ add_xscom();
+
+ /* Add FSP */
+ fsp_parse();
+
+ /* Add ChipTOD's */
+ if (!add_chiptod_old() && !add_chiptod_new(master_cpu))
+ prerror("CHIPTOD: No ChipTOD found !\n");
+
+ /* Add NX */
+ add_nx();
+
+ /* Add IO HUBs and/or PHBs */
+ io_parse();
+
+ /* Parse VPD */
+ vpd_parse();
+
+ /* Host services information. */
+ hostservices_parse();
+
+ printf("\n");
+ printf("-----------------------------------------------\n");
+ printf("\n");
+}
OpenPOWER on IntegriCloud