aboutsummaryrefslogtreecommitdiff
path: root/hdata
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
downloadskiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.zip
skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.gz
skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.bz2
Initial commit of Open Source release
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'hdata')
-rw-r--r--hdata/Makefile.inc8
-rw-r--r--hdata/cpu-common.c280
-rw-r--r--hdata/fsp.c200
-rw-r--r--hdata/hdata.h49
-rw-r--r--hdata/hdif.c140
-rw-r--r--hdata/hdif.h141
-rw-r--r--hdata/hostservices.c96
-rw-r--r--hdata/iohub.c715
-rw-r--r--hdata/memory.c377
-rw-r--r--hdata/paca.c322
-rw-r--r--hdata/pcia.c242
-rw-r--r--hdata/slca.c89
-rw-r--r--hdata/spira.c965
-rw-r--r--hdata/spira.h864
-rw-r--r--hdata/test/Makefile.check18
-rw-r--r--hdata/test/hdata_to_dt.c215
-rw-r--r--hdata/test/stubs.c47
-rw-r--r--hdata/vpd-common.c38
-rw-r--r--hdata/vpd.c851
19 files changed, 5657 insertions, 0 deletions
diff --git a/hdata/Makefile.inc b/hdata/Makefile.inc
new file mode 100644
index 0000000..44f8c86
--- /dev/null
+++ b/hdata/Makefile.inc
@@ -0,0 +1,8 @@
+# -*-Makefile-*-
+
+SUBDIRS += hdata
+HDATA_OBJS = spira.o paca.o pcia.o hdif.o memory.o fsp.o iohub.o vpd.o slca.o
+HDATA_OBJS += cpu-common.o vpd-common.o hostservices.o
+DEVSRC_OBJ = hdata/built-in.o
+
+$(DEVSRC_OBJ): $(HDATA_OBJS:%=hdata/%)
diff --git a/hdata/cpu-common.c b/hdata/cpu-common.c
new file mode 100644
index 0000000..f08fa27
--- /dev/null
+++ b/hdata/cpu-common.c
@@ -0,0 +1,280 @@
+/* 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 <ccan/str/str.h>
+#include <device.h>
+
+#include "hdata.h"
+
+struct dt_node * add_core_common(struct dt_node *cpus,
+ const struct sppcia_cpu_cache *cache,
+ const struct sppaca_cpu_timebase *tb,
+ uint32_t int_server, bool okay)
+{
+ const char *name;
+ struct dt_node *cpu;
+ uint32_t version;
+ uint64_t freq;
+ const uint8_t pa_features[] = {
+ 6, 0, 0xf6, 0x3f, 0xc7, 0x00, 0x80, 0xc0 };
+
+ printf(" Cache: I=%u D=%u/%u/%u/%u\n",
+ be32_to_cpu(cache->icache_size_kb),
+ be32_to_cpu(cache->l1_dcache_size_kb),
+ be32_to_cpu(cache->l2_dcache_size_kb),
+ be32_to_cpu(cache->l3_dcache_size_kb),
+ be32_to_cpu(cache->l35_dcache_size_kb));
+
+ /* Use the boot CPU PVR to make up a CPU name in the device-tree
+ * since the HDAT doesn't seem to tell....
+ */
+ version = mfspr(SPR_PVR);
+ switch(PVR_TYPE(version)) {
+ case PVR_TYPE_P7:
+ name = "PowerPC,POWER7";
+ break;
+ case PVR_TYPE_P7P:
+ name = "PowerPC,POWER7+";
+ break;
+ case PVR_TYPE_P8E:
+ case PVR_TYPE_P8:
+ name = "PowerPC,POWER8";
+ break;
+ default:
+ name = "PowerPC,Unknown";
+ }
+
+ cpu = dt_new_addr(cpus, name, int_server);
+ assert(cpu);
+ dt_add_property_string(cpu, "device_type", "cpu");
+ dt_add_property_string(cpu, "status", okay ? "okay" : "bad");
+ dt_add_property_cells(cpu, "reg", int_server);
+ dt_add_property_cells(cpu, "cpu-version", version);
+ dt_add_property(cpu, "64-bit", NULL, 0);
+ dt_add_property(cpu, "32-64-bridge", NULL, 0);
+ dt_add_property(cpu, "graphics", NULL, 0);
+ dt_add_property(cpu, "general-purpose", NULL, 0);
+ dt_add_property_cells(cpu, "ibm,processor-segment-sizes",
+ 0x1c, 0x28, 0xffffffff, 0xffffffff);
+ dt_add_property_cells(cpu, "ibm,processor-page-sizes",
+ 0xc, 0x10, 0x18, 0x22);
+
+ /* Page size encodings appear to be the same for P7 and P8 */
+ dt_add_property_cells(cpu, "ibm,segment-page-sizes",
+ 0x0c, 0x000, 3, 0x0c, 0x0000, /* 4K seg 4k pages */
+ 0x10, 0x0007, /* 4K seg 64k pages */
+ 0x18, 0x0038, /* 4K seg 16M pages */
+ 0x10, 0x110, 2, 0x10, 0x0001, /* 64K seg 64k pages */
+ 0x18, 0x0008, /* 64K seg 16M pages */
+ 0x18, 0x100, 1, 0x18, 0x0000, /* 16M seg 16M pages */
+ 0x22, 0x120, 1, 0x22, 0x0003); /* 16G seg 16G pages */
+
+ dt_add_property(cpu, "ibm,pa-features",
+ pa_features, sizeof(pa_features));
+ dt_add_property_cells(cpu, "ibm,slb-size", 0x20);
+
+ dt_add_property_cells(cpu, "ibm,vmx", 0x2);
+ dt_add_property_cells(cpu, "ibm,dfp", 0x2);
+ dt_add_property_cells(cpu, "ibm,purr", 0x1);
+ dt_add_property_cells(cpu, "ibm,spurr", 0x1);
+
+ /*
+ * Do not create "clock-frequency" if the frequency doesn't
+ * fit in a single cell
+ */
+ freq = ((uint64_t)be32_to_cpu(tb->actual_clock_speed)) * 1000000ul;
+ if (freq <= 0xfffffffful)
+ dt_add_property_cells(cpu, "clock-frequency", freq);
+ dt_add_property_cells(cpu, "ibm,extended-clock-frequency",
+ hi32(freq), lo32(freq));
+
+ /* FIXME: Hardcoding is bad. */
+ dt_add_property_cells(cpu, "timebase-frequency", 512000000);
+ dt_add_property_cells(cpu, "ibm,extended-timebase-frequency",
+ 0, 512000000);
+
+ dt_add_property_cells(cpu, "reservation-granule-size",
+ be32_to_cpu(cache->reservation_size));
+
+ dt_add_property_cells(cpu, "d-tlb-size",
+ be32_to_cpu(cache->dtlb_entries));
+ dt_add_property_cells(cpu, "i-tlb-size",
+ be32_to_cpu(cache->itlb_entries));
+ /* Assume unified TLB */
+ dt_add_property_cells(cpu, "tlb-size",
+ be32_to_cpu(cache->dtlb_entries));
+ dt_add_property_cells(cpu, "d-tlb-sets",
+ be32_to_cpu(cache->dtlb_assoc_sets));
+ dt_add_property_cells(cpu, "i-tlb-sets",
+ be32_to_cpu(cache->itlb_assoc_sets));
+ dt_add_property_cells(cpu, "tlb-sets",
+ be32_to_cpu(cache->dtlb_assoc_sets));
+
+ dt_add_property_cells(cpu, "d-cache-block-size",
+ be32_to_cpu(cache->dcache_block_size));
+ dt_add_property_cells(cpu, "i-cache-block-size",
+ be32_to_cpu(cache->icache_block_size));
+ dt_add_property_cells(cpu, "d-cache-size",
+ be32_to_cpu(cache->l1_dcache_size_kb)*1024);
+ dt_add_property_cells(cpu, "i-cache-size",
+ be32_to_cpu(cache->icache_size_kb)*1024);
+ dt_add_property_cells(cpu, "i-cache-sets",
+ be32_to_cpu(cache->icache_assoc_sets));
+ dt_add_property_cells(cpu, "d-cache-sets",
+ be32_to_cpu(cache->dcache_assoc_sets));
+
+ if (cache->icache_line_size != cache->icache_block_size)
+ dt_add_property_cells(cpu, "i-cache-line-size",
+ be32_to_cpu(cache->icache_line_size));
+ if (cache->l1_dcache_line_size != cache->dcache_block_size)
+ dt_add_property_cells(cpu, "d-cache-line-size",
+ be32_to_cpu(cache->l1_dcache_line_size));
+ return cpu;
+}
+
+void add_core_attr(struct dt_node *cpu, uint32_t attr)
+{
+ if (attr & CPU_ATTR_UNIFIED_PL1)
+ dt_add_property(cpu, "cache-unified", NULL, 0);
+ if (attr & CPU_ATTR_SPLIT_TLB)
+ dt_add_property(cpu, "tlb-split", NULL, 0);
+ if (attr & CPU_ATTR_TLBIA)
+ dt_add_property(cpu, "tlbia", NULL, 0);
+ if (attr & CPU_ATTR_PERF_MONITOR)
+ dt_add_property_cells(cpu, "performance-monitor", 0, 1);
+ if (attr & CPU_ATTR_EXTERN_CONT)
+ dt_add_property(cpu, "external-control", NULL, 0);
+}
+
+static struct dt_node *create_cache_node(struct dt_node *cpus,
+ const struct sppcia_cpu_cache *cache,
+ const char *name, uint32_t unit_addr,
+ int okay)
+{
+ struct dt_node *node;
+
+ node = dt_new_addr(cpus, name, unit_addr);
+ assert(node);
+
+ dt_add_property_string(node, "device_type", "cache");
+ dt_add_property_cells(node, "reg", unit_addr);
+ dt_add_property_string(node, "status", okay ? "okay" : "bad");
+ dt_add_property(node, "cache-unified", NULL, 0);
+
+ /* Assume cache associavitity sets is same for L2, L3 and L3.5 */
+ dt_add_property_cells(node, "d-cache-sets",
+ be32_to_cpu(cache->l2_cache_assoc_sets));
+ dt_add_property_cells(node, "i-cache-sets",
+ be32_to_cpu(cache->l2_cache_assoc_sets));
+
+ return node;
+}
+
+static struct dt_node *l35_cache_node(struct dt_node *cpus,
+ const struct sppcia_cpu_cache *cache,
+ uint32_t unit_addr, int okay)
+{
+ struct dt_node *node;
+
+ node = create_cache_node(cpus, cache, "l35-cache", unit_addr, okay);
+
+ dt_add_property_cells(node, "d-cache-size",
+ be32_to_cpu(cache->l35_dcache_size_kb) * 1024);
+ dt_add_property_cells(node, "i-cache-size",
+ be32_to_cpu(cache->l35_dcache_size_kb) * 1024);
+
+ if (cache->icache_line_size != cache->icache_block_size)
+ dt_add_property_cells(node, "i-cache-line-size",
+ be32_to_cpu(cache->icache_line_size));
+ if (cache->l35_cache_line_size != cache->dcache_block_size)
+ dt_add_property_cells(node, "d-cache-line-size",
+ be32_to_cpu(cache->l35_cache_line_size));
+
+ return node;
+}
+
+static struct dt_node *l3_cache_node(struct dt_node *cpus,
+ const struct sppcia_cpu_cache *cache,
+ uint32_t unit_addr, int okay)
+{
+ struct dt_node *node;
+
+ node = create_cache_node(cpus, cache, "l3-cache", unit_addr, okay);
+
+ dt_add_property_cells(node, "d-cache-size",
+ be32_to_cpu(cache->l3_dcache_size_kb) * 1024);
+ dt_add_property_cells(node, "i-cache-size",
+ be32_to_cpu(cache->l3_dcache_size_kb) * 1024);
+
+ if (cache->icache_line_size != cache->icache_block_size)
+ dt_add_property_cells(node, "i-cache-line-size",
+ be32_to_cpu(cache->icache_line_size));
+ if (cache->l3_line_size != cache->dcache_block_size)
+ dt_add_property_cells(node, "d-cache-line-size",
+ be32_to_cpu(cache->l3_line_size));
+
+ return node;
+}
+
+static struct dt_node *l2_cache_node(struct dt_node *cpus,
+ const struct sppcia_cpu_cache *cache,
+ uint32_t unit_addr, int okay)
+{
+ struct dt_node *node;
+
+ node = create_cache_node(cpus, cache, "l2-cache", unit_addr, okay);
+
+ dt_add_property_cells(node, "d-cache-size",
+ be32_to_cpu(cache->l2_dcache_size_kb) * 1024);
+ dt_add_property_cells(node, "i-cache-size",
+ be32_to_cpu(cache->l2_dcache_size_kb) * 1024);
+
+ if (cache->icache_line_size != cache->icache_block_size)
+ dt_add_property_cells(node, "i-cache-line-size",
+ be32_to_cpu(cache->icache_line_size));
+ if (cache->l2_line_size != cache->dcache_block_size)
+ dt_add_property_cells(node, "d-cache-line-size",
+ be32_to_cpu(cache->l2_line_size));
+
+ return node;
+}
+
+uint32_t add_core_cache_info(struct dt_node *cpus,
+ const struct sppcia_cpu_cache *cache,
+ uint32_t int_server, int okay)
+{
+ struct dt_node *l2_node, *l3_node, *l35_node;
+ uint32_t unit_addr;
+
+ /* Use Processor Interrupt Line to genarate cache unit address */
+ unit_addr = 0x20 << 24 | int_server;
+ l2_node = l2_cache_node(cpus, cache, unit_addr, okay);
+
+ unit_addr = 0x30 << 24 | int_server;
+ l3_node = l3_cache_node(cpus, cache, unit_addr, okay);
+ /* Represents the next level of cache in the memory hierarchy */
+ dt_add_property_cells(l2_node, "l2-cache", l3_node->phandle);
+
+ if (be32_to_cpu(cache->l35_dcache_size_kb)) {
+ unit_addr = 0x35 << 24 | int_server;
+ l35_node = l35_cache_node(cpus, cache, unit_addr, okay);
+ dt_add_property_cells(l3_node, "l2-cache", l35_node->phandle);
+ }
+
+ return l2_node->phandle;
+}
diff --git a/hdata/fsp.c b/hdata/fsp.c
new file mode 100644
index 0000000..cf6bc96
--- /dev/null
+++ b/hdata/fsp.c
@@ -0,0 +1,200 @@
+/* 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 <ccan/str/str.h>
+#include <device_tree.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;
+ }
+
+ printf("FSP #%d: FSP HW version %d, SW version %d, chip DD%d.%d\n",
+ i, sp_impl->hw_version, 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 (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 (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", sp_impl->hw_version);
+ dt_add_property_cells(node, "sw-version", 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(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";
+ }
+ printf("FSP #%d: IO PATH %d is %s PSI Link, GXHB at %llx\n",
+ fsp_index, index, ststr, (long long)iopath->psi.gxhb_base);
+
+ chip_id = pcid_to_chip_id(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;
+ }
+ printf("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 (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);
+}
+
+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) {
+ printf("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);
+ }
+}
+
diff --git a/hdata/hdata.h b/hdata/hdata.h
new file mode 100644
index 0000000..b9b7721
--- /dev/null
+++ b/hdata/hdata.h
@@ -0,0 +1,49 @@
+/* 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.
+ */
+
+#ifndef __HDATA_H
+#define __HDATA_H
+
+struct dt_node;
+
+extern void paca_parse(void);
+extern bool pcia_parse(void);
+extern void fsp_parse(void);
+extern void io_parse(void);
+extern struct dt_node *dt_add_vpd_node(const struct HDIF_common_hdr *hdr,
+ int indx_fru, int indx_vpd);
+extern void vpd_parse(void);
+
+extern struct dt_node *find_xscom_for_chip(uint32_t chip_id);
+extern uint32_t pcid_to_chip_id(uint32_t proc_chip_id);
+
+extern struct dt_node *add_core_common(struct dt_node *cpus,
+ const struct sppaca_cpu_cache *cache,
+ const struct sppaca_cpu_timebase *tb,
+ uint32_t int_server, bool okay);
+extern void add_core_attr(struct dt_node *cpu, uint32_t attr);
+extern uint32_t add_core_cache_info(struct dt_node *cpus,
+ const struct sppcia_cpu_cache *cache,
+ uint32_t int_server, int okay);
+extern const struct slca_entry *slca_get_entry(uint16_t slca_index);
+extern const char *slca_get_vpd_name(uint16_t slca_index);
+extern const char *slca_get_loc_code_index(uint16_t slca_index);
+extern void slca_vpd_add_loc_code(struct dt_node *node, uint16_t slca_index);
+
+extern bool hservices_from_hdat(const void *fdt, size_t size);
+
+#endif /* __HDATA_H */
+
diff --git a/hdata/hdif.c b/hdata/hdif.c
new file mode 100644
index 0000000..916b4dd
--- /dev/null
+++ b/hdata/hdif.c
@@ -0,0 +1,140 @@
+/* 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 "hdif.h"
+
+const void *HDIF_get_idata(const struct HDIF_common_hdr *hdif, unsigned int di,
+ unsigned int *size)
+{
+ const struct HDIF_common_hdr *hdr = hdif;
+ const struct HDIF_idata_ptr *iptr;
+
+ if (hdr->d1f0 != BE16_TO_CPU(0xd1f0)) {
+ prerror("HDIF: Bad header format !\n");
+ return NULL;
+ }
+
+ if (di >= be16_to_cpu(hdr->idptr_count)) {
+ prerror("HDIF: idata index out of range !\n");
+ return NULL;
+ }
+
+ iptr = (void *)hdif + be32_to_cpu(hdr->idptr_off)
+ + di * sizeof(struct HDIF_idata_ptr);
+
+ if (size)
+ *size = be32_to_cpu(iptr->size);
+
+ return (void *)hdif + be32_to_cpu(iptr->offset);
+}
+
+const void *HDIF_get_iarray_item(const struct HDIF_common_hdr *hdif,
+ unsigned int di, unsigned int ai,
+ unsigned int *size)
+{
+ const struct HDIF_array_hdr *ahdr;
+ unsigned int asize;
+ const void *arr;
+
+ arr = HDIF_get_idata(hdif, di, &asize);
+ if (!arr)
+ return NULL;
+
+ if (asize < sizeof(struct HDIF_array_hdr)) {
+ prerror("HDIF: idata block too small for array !\n");
+ return NULL;
+ }
+
+ ahdr = arr;
+
+ if (ai >= be32_to_cpu(ahdr->ecnt)) {
+ prerror("HDIF: idata array index out of range !\n");
+ return NULL;
+ }
+
+ if (size)
+ *size = be32_to_cpu(ahdr->eactsz);
+
+ return arr + be32_to_cpu(ahdr->offset) + ai * be32_to_cpu(ahdr->esize);
+}
+
+int HDIF_get_iarray_size(const struct HDIF_common_hdr *hdif, unsigned int di)
+{
+ const struct HDIF_array_hdr *ahdr;
+ unsigned int asize;
+ const void *arr;
+
+ arr = HDIF_get_idata(hdif, di, &asize);
+ if (!arr)
+ return -1;
+
+ if (asize < sizeof(struct HDIF_array_hdr)) {
+ prerror("HDIF: idata block too small for array !\n");
+ return -1;
+ }
+
+ ahdr = arr;
+ return be32_to_cpu(ahdr->ecnt);
+}
+
+struct HDIF_child_ptr *
+HDIF_child_arr(const struct HDIF_common_hdr *hdif, unsigned int idx)
+{
+ struct HDIF_child_ptr *children;
+
+ children = (void *)hdif + be32_to_cpu(hdif->child_off);
+
+ if (idx >= be16_to_cpu(hdif->child_count)) {
+ prerror("HDIF: child array idx out of range!\n");
+ return NULL;
+ }
+
+ return &children[idx];
+}
+
+struct HDIF_common_hdr *HDIF_child(const struct HDIF_common_hdr *hdif,
+ const struct HDIF_child_ptr *child,
+ unsigned int idx,
+ const char *eyecatcher)
+{
+ void *base = (void *)hdif;
+ struct HDIF_common_hdr *ret;
+ long child_off;
+
+ /* child must be in hdif's child array */
+ child_off = (void *)child - (base + be32_to_cpu(hdif->child_off));
+ assert(child_off % sizeof(struct HDIF_child_ptr) == 0);
+ assert(child_off / sizeof(struct HDIF_child_ptr)
+ < be16_to_cpu(hdif->child_count));
+
+ assert(idx < be32_to_cpu(child->count));
+
+ if (be32_to_cpu(child->size) < sizeof(struct HDIF_common_hdr)) {
+ prerror("HDIF: %s child #%i too small: %u\n",
+ eyecatcher, idx, be32_to_cpu(child->size));
+ return NULL;
+ }
+
+ ret = base + be32_to_cpu(child->offset)
+ + be32_to_cpu(child->size) * idx;
+ if (!HDIF_check(ret, eyecatcher)) {
+ prerror("HDIF: %s child #%i bad type\n",
+ eyecatcher, idx);
+ return NULL;
+ }
+
+ return ret;
+}
diff --git a/hdata/hdif.h b/hdata/hdif.h
new file mode 100644
index 0000000..fad7454
--- /dev/null
+++ b/hdata/hdif.h
@@ -0,0 +1,141 @@
+/* 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.
+ */
+
+#ifndef __HDIF_H
+#define __HDIF_H
+
+#include <skiboot.h>
+#include <types.h>
+#include <ccan/endian/endian.h>
+
+struct HDIF_common_hdr {
+ __be16 d1f0; /* 0xd1f0 */
+ char id[6]; /* eye catcher string */
+ __be16 instnum; /* instance number */
+ __be16 version; /* version */
+ __be32 total_len; /* total structure length */
+ __be32 hdr_len; /* header length (currently 0x20) */
+ __be32 idptr_off; /* offset to idata pointers */
+ __be16 idptr_count; /* number of idata pointers */
+ __be16 child_count; /* number of child structures */
+ __be32 child_off; /* offset to child structures array */
+} __packed __align(0x10);
+
+struct HDIF_idata_ptr {
+ __be32 offset;
+ __be32 size;
+} __packed __align(0x8);
+
+struct HDIF_array_hdr {
+ __be32 offset;
+ __be32 ecnt;
+ __be32 esize;
+ __be32 eactsz;
+} __packed __align(0x10);
+
+struct HDIF_child_ptr {
+ __be32 offset;
+ __be32 size;
+ __be32 count;
+} __packed;
+
+#define HDIF_HDR_LEN (sizeof(struct HDIF_common_hdr))
+#define HDIF_ARRAY_OFFSET (sizeof(struct HDIF_array_hdr))
+
+#define HDIF_ID(_id) .d1f0 = CPU_TO_BE16(0xd1f0), .id = _id
+
+#define HDIF_SIMPLE_HDR(id, vers, type) \
+{ \
+ HDIF_ID(id), \
+ .instnum = CPU_TO_BE16(0), \
+ .version = CPU_TO_BE16(vers), \
+ .total_len = CPU_TO_BE32(sizeof(type)), \
+ .hdr_len = CPU_TO_BE32(HDIF_HDR_LEN), \
+ .idptr_off = CPU_TO_BE32(HDIF_HDR_LEN), \
+ .idptr_count = CPU_TO_BE16(1), \
+ .child_count = CPU_TO_BE16(0), \
+ .child_off = CPU_TO_BE32(0), \
+}
+
+#define HDIF_IDATA_PTR(_offset, _size) \
+{ \
+ .offset = CPU_TO_BE32(_offset), \
+ .size = _size, \
+}
+
+static inline bool HDIF_check(const void *hdif, const char id[])
+{
+ const struct HDIF_common_hdr *hdr = hdif;
+
+ return hdr->d1f0 == CPU_TO_BE16(0xd1f0) &&
+ memcmp(hdr->id, id, sizeof(hdr->id)) == 0;
+}
+
+/* HDIF_get_idata - Get a pointer to internal data block
+ *
+ * @hdif : HDIF structure pointer
+ * @di : Index of the idata pointer
+ * @size : Return the data size (or NULL if ignored)
+ */
+extern const void *HDIF_get_idata(const struct HDIF_common_hdr *hdif,
+ unsigned int di,
+ unsigned int *size);
+
+/* HDIF_get_iarray - Get a pointer to an elemnt of an internal data array
+ *
+ * @hdif : HDIF structure pointer
+ * @di : Index of the idata pointer
+ * @ai : Index in the resulting array
+ * @size : Return the entry actual size (or NULL if ignored)
+ */
+extern const void *HDIF_get_iarray_item(const struct HDIF_common_hdr *hdif,
+ unsigned int di,
+ unsigned int ai, unsigned int *size);
+
+/* HDIF_get_iarray_size - Get the number of elements of an internal data array
+ *
+ * @hdif : HDIF structure pointer
+ * @di : Index of the idata pointer
+ *
+ * A negative result means an error
+ */
+extern int HDIF_get_iarray_size(const struct HDIF_common_hdr *hdif,
+ unsigned int di);
+
+/* HDIF_child_arr - Get a child array from this HDIF.
+ *
+ * @hdif : HDIF structure pointer
+ * @idx : the child to get
+ *
+ * NULL means an error (not that many children).
+ */
+extern struct HDIF_child_ptr *
+HDIF_child_arr(const struct HDIF_common_hdr *hdif, unsigned int idx);
+
+/* HDIF_child - Deref a child_ptr entry.
+ *
+ * @hdif : HDIF structure pointer
+ * @child : the child returned from HDIF_child_arr
+ * @idx : the index of the child to get (< child->count).
+ * @eyecatcher: the 6-char ID expected for this child.
+ *
+ * NULL means an error.
+ */
+extern struct HDIF_common_hdr *HDIF_child(const struct HDIF_common_hdr *hdif,
+ const struct HDIF_child_ptr *child,
+ unsigned int idx,
+ const char *eyecatcher);
+#endif /* __HDIF_H */
diff --git a/hdata/hostservices.c b/hdata/hostservices.c
new file mode 100644
index 0000000..ff2edbe
--- /dev/null
+++ b/hdata/hostservices.c
@@ -0,0 +1,96 @@
+/* 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 <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <libfdt/libfdt.h>
+#include <skiboot.h>
+#include <device.h>
+#include <compiler.h>
+#include <hostservices.h>
+
+#include "spira.h"
+#include "hdata.h"
+
+static void merge_property(const struct dt_node *src_root,
+ struct dt_node *dst_root,
+ const char *name)
+{
+ const struct dt_property *src;
+ struct dt_property *dst;
+
+ /* Nothing to merge if old one doesn't exist. */
+ src = dt_find_property(src_root, name);
+ if (!src)
+ return;
+
+ /* Just create a new one if there's none in dst. */
+ dst = __dt_find_property(dst_root, name);
+ if (!dst) {
+ dt_add_property(dst_root, name, src->prop, src->len);
+ return;
+ }
+
+ /* Append src to dst. */
+ dt_resize_property(&dst, dst->len + src->len);
+ memcpy(dst->prop + dst->len, src->prop, src->len);
+ dst->len += src->len;
+}
+
+static void hservice_parse_dt_tree(const struct dt_node *src)
+{
+ const struct dt_property *sprop;
+
+ /* Copy/merge reserved names & ranges properties. */
+ list_for_each(&src->properties, sprop, list) {
+ if (streq(sprop->name, "reserved-names") ||
+ streq(sprop->name, "reserved-ranges") ||
+ streq(sprop->name, "ibm,enabled-idle-states"))
+ merge_property(src, dt_root, sprop->name);
+ }
+}
+
+/* Get host services information from hdat. */
+bool hservices_from_hdat(const void *fdt, size_t size)
+{
+ int err;
+ struct dt_node *hservices;
+
+ printf("HBRT: Found mini-DT at 0x%p size: 0x%08lx\n", fdt, size);
+
+ /* For diagnostic purposes, we copy the whole blob over */
+ dt_add_property(dt_root, "ibm,hbrt-mini-fdt", fdt, size);
+
+ /* Parse & extract relevant properties */
+ err = fdt_check_header(fdt);
+ if (err) {
+ prerror("HBRT: fdt blob %p hdr error %d\n", fdt, err);
+ return false;
+ }
+
+ hservices = dt_new_root("ibm,hostservices");
+ err = dt_expand_node(hservices, fdt, 0);
+ if (err < 0) {
+ prerror("HBRT: fdt blob %p parse error %d\n", fdt, err);
+ return false;
+ }
+
+ hservice_parse_dt_tree(hservices);
+ return true;
+}
+
diff --git a/hdata/iohub.c b/hdata/iohub.c
new file mode 100644
index 0000000..0dbec36
--- /dev/null
+++ b/hdata/iohub.c
@@ -0,0 +1,715 @@
+/* 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 <ccan/array_size/array_size.h>
+#include <device.h>
+#include <p5ioc2.h>
+#include <p7ioc.h>
+#include <vpd.h>
+
+#include "hdata.h"
+
+static void io_add_common(struct dt_node *hn, const struct cechub_io_hub *hub)
+{
+ dt_add_property_cells(hn, "#address-cells", 2);
+ dt_add_property_cells(hn, "#size-cells", 2);
+ dt_add_property_cells(hn, "ibm,buid-ext", hub->buid_ext);
+ dt_add_property_cells(hn, "ibm,chip-id",
+ pcid_to_chip_id(hub->proc_chip_id));
+ dt_add_property_cells(hn, "ibm,gx-index", hub->gx_index);
+ dt_add_property_cells(hn, "revision", hub->ec_level);
+
+ /* Instead of exposing the GX BARs as separate ranges as we *should*
+ * do in an ideal world, we just create a pass-through ranges and
+ * we use separate properties for the BARs.
+ *
+ * This is hackish but will do for now and avoids us having to
+ * do too complex ranges property parsing
+ */
+ dt_add_property(hn, "ranges", NULL, 0);
+ dt_add_property_cells(hn, "ibm,gx-bar-1",
+ hi32(hub->gx_ctrl_bar1), lo32(hub->gx_ctrl_bar1));
+ dt_add_property_cells(hn, "ibm,gx-bar-2",
+ hi32(hub->gx_ctrl_bar2), lo32(hub->gx_ctrl_bar2));
+
+ /* Add presence detect if valid */
+ if (hub->flags & CECHUB_HUB_FLAG_FAB_BR0_PDT)
+ dt_add_property_cells(hn, "ibm,br0-presence-detect",
+ hub->fab_br0_pdt);
+ if (hub->flags & CECHUB_HUB_FLAG_FAB_BR1_PDT)
+ dt_add_property_cells(hn, "ibm,br1-presence-detect",
+ hub->fab_br1_pdt);
+}
+
+static bool io_get_lx_info(const void *kwvpd, unsigned int kwvpd_sz,
+ int lx_idx, struct dt_node *hn)
+{
+ const void *lxr;
+ char recname[5];
+
+ /* Find LXRn, where n is the index passed in*/
+ strcpy(recname, "LXR0");
+ recname[3] += lx_idx;
+ lxr = vpd_find(kwvpd, kwvpd_sz, recname, "LX", NULL);
+ if (!lxr) {
+ /* Not found, try VINI */
+ lxr = vpd_find(kwvpd, kwvpd_sz, "VINI",
+ "LX", NULL);
+ if (lxr)
+ lx_idx = VPD_LOAD_LXRN_VINI;
+ }
+ if (!lxr) {
+ printf("CEC: LXR%x not found !\n", lx_idx);
+ return false;
+ }
+
+ printf("CEC: LXRn=%d LXR=%016lx\n", lx_idx,
+ lxr ? *(unsigned long *)lxr : 0);
+ printf("CEC: LX Info added to %llx\n", (long long)hn);
+
+ /* Add the LX info */
+ if (!dt_has_node_property(hn, "ibm,vpd-lx-info", NULL)) {
+ dt_add_property_cells(hn, "ibm,vpd-lx-info",
+ lx_idx,
+ ((uint32_t *)lxr)[0],
+ ((uint32_t *)lxr)[1]);
+ }
+
+ return true;
+}
+
+
+static void io_get_loc_code(const void *sp_iohubs, struct dt_node *hn, const char *prop_name)
+{
+ const struct spira_fru_id *fru_id;
+ unsigned int fru_id_sz;
+ char loc_code[LOC_CODE_SIZE + 1];
+ const char *slca_loc_code;
+
+ /* Find SLCA Index */
+ fru_id = HDIF_get_idata(sp_iohubs, CECHUB_FRU_ID_DATA, &fru_id_sz);
+ if (fru_id) {
+ memset(loc_code, 0, sizeof(loc_code));
+
+ /* Find LOC Code from SLCA Index */
+ slca_loc_code = slca_get_loc_code_index(fru_id->slca_index);
+ if (slca_loc_code) {
+ strncpy(loc_code, slca_loc_code, LOC_CODE_SIZE);
+ if (!dt_has_node_property(hn, prop_name, NULL)) {
+ dt_add_property(hn, prop_name, loc_code,
+ strlen(loc_code) + 1);
+ }
+ printf("CEC: %s: %s (SLCA rsrc 0x%x)\n",
+ prop_name, loc_code, be16_to_cpu(fru_id->rsrc_id));
+ } else {
+ printf("CEC: SLCA Loc not found: index %d\n",
+ fru_id->slca_index);
+ }
+ } else {
+ printf("CEC: Hub FRU ID not found...\n");
+ }
+}
+
+static struct dt_node *io_add_p5ioc2(const struct cechub_io_hub *hub,
+ const void *sp_iohubs)
+{
+ struct dt_node *hn;
+ uint64_t reg[2];
+
+ const void *kwvpd;
+ unsigned int kwvpd_sz;
+
+ printf(" GX#%d BUID_Ext = 0x%x\n",
+ be32_to_cpu(hub->gx_index),
+ be32_to_cpu(hub->buid_ext));
+ printf(" GX BAR 0 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar0));
+ printf(" GX BAR 1 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar1));
+ printf(" GX BAR 2 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar2));
+ printf(" GX BAR 3 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar3));
+ printf(" GX BAR 4 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar4));
+
+ /* We assume SBAR == GX0 + some hard coded offset */
+ reg[0] = cleanup_addr(be64_to_cpu(hub->gx_ctrl_bar0) + P5IOC2_REGS_OFFSET);
+ reg[1] = 0x2000000;
+
+ hn = dt_new_addr(dt_root, "io-hub", reg[0]);
+ if (!hn)
+ return NULL;
+
+ dt_add_property(hn, "reg", reg, sizeof(reg));
+ dt_add_property_strings(hn, "compatible", "ibm,p5ioc2");
+
+ kwvpd = HDIF_get_idata(sp_iohubs, CECHUB_ASCII_KEYWORD_VPD, &kwvpd_sz);
+ if (kwvpd && kwvpd != sp_iohubs) {
+ /*
+ * XX We don't know how to properly find the LXRn
+ * record so for now we'll just try LXR0 and if not
+ * found, we try LXR1
+ */
+ if (!io_get_lx_info(kwvpd, kwvpd_sz, 0, hn))
+ io_get_lx_info(kwvpd, kwvpd_sz, 1, hn);
+ } else
+ printf("CEC: P5IOC2 Keywords not found.\n");
+
+ /* Get slots base loc code */
+ io_get_loc_code(sp_iohubs, hn, "ibm,io-base-loc-code");
+
+ return hn;
+}
+
+static struct dt_node *io_add_p7ioc(const struct cechub_io_hub *hub,
+ const void *sp_iohubs)
+{
+ struct dt_node *hn;
+ uint64_t reg[2];
+
+ const void *kwvpd;
+ unsigned int kwvpd_sz;
+
+ printf(" GX#%d BUID_Ext = 0x%x\n",
+ be32_to_cpu(hub->gx_index),
+ be32_to_cpu(hub->buid_ext));
+ printf(" GX BAR 0 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar0));
+ printf(" GX BAR 1 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar1));
+ printf(" GX BAR 2 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar2));
+ printf(" GX BAR 3 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar3));
+ printf(" GX BAR 4 = 0x%016llx\n", be64_to_cpu(hub->gx_ctrl_bar4));
+
+ /* We only know about memory map 1 */
+ if (hub->mem_map_vers != 1) {
+ prerror("P7IOC: Unknown memory map %d\n", hub->mem_map_vers);
+ /* We try to continue anyway ... */
+ }
+
+ reg[0] = cleanup_addr(be64_to_cpu(hub->gx_ctrl_bar1));
+ reg[1] = 0x2000000;
+
+ hn = dt_new_addr(dt_root, "io-hub", reg[0]);
+ if (!hn)
+ return NULL;
+
+ dt_add_property(hn, "reg", reg, sizeof(reg));
+ dt_add_property_strings(hn, "compatible", "ibm,p7ioc", "ibm,ioda-hub");
+
+ kwvpd = HDIF_get_idata(sp_iohubs, CECHUB_ASCII_KEYWORD_VPD, &kwvpd_sz);
+ if (kwvpd && kwvpd != sp_iohubs) {
+ /*
+ * XX We don't know how to properly find the LXRn
+ * record so for now we'll just try LXR0 and if not
+ * found, we try LXR1
+ */
+ if (!io_get_lx_info(kwvpd, kwvpd_sz, 0, hn))
+ io_get_lx_info(kwvpd, kwvpd_sz, 1, hn);
+ } else
+ printf("CEC: P7IOC Keywords not found.\n");
+
+ io_get_loc_code(sp_iohubs, hn, "ibm,io-base-loc-code");
+
+ return hn;
+}
+
+static struct dt_node *io_add_phb3(const struct cechub_io_hub *hub,
+ const struct HDIF_common_hdr *sp_iohubs,
+ unsigned int index, struct dt_node *xcom,
+ unsigned int pe_xscom,
+ unsigned int pci_xscom,
+ unsigned int spci_xscom)
+{
+ struct dt_node *pbcq;
+ uint32_t reg[6];
+ unsigned int hdif_vers;
+
+ /* Get HDIF version */
+ hdif_vers = be16_to_cpu(sp_iohubs->version);
+
+ /* Create PBCQ node under xscom */
+ pbcq = dt_new_addr(xcom, "pbcq", pe_xscom);
+ if (!pbcq)
+ return NULL;
+
+ /* "reg" property contains in order the PE, PCI and SPCI XSCOM
+ * addresses
+ */
+ reg[0] = pe_xscom;
+ reg[1] = 0x20;
+ reg[2] = pci_xscom;
+ reg[3] = 0x05;
+ reg[4] = spci_xscom;
+ reg[5] = 0x15;
+ dt_add_property(pbcq, "reg", reg, sizeof(reg));
+
+ /* A couple more things ... */
+ dt_add_property_strings(pbcq, "compatible", "ibm,power8-pbcq");
+ dt_add_property_cells(pbcq, "ibm,phb-index", index);
+ dt_add_property_cells(pbcq, "ibm,hub-id", be16_to_cpu(hub->hub_num));
+
+ /* The loc code of the PHB itself is different from the base
+ * loc code of the slots (It's actually the DCM's loc code).
+ */
+ io_get_loc_code(sp_iohubs, pbcq, "ibm,loc-code");
+
+ /* We indicate that this is an IBM setup, which means that
+ * the presence detect A/B bits are meaningful. So far we
+ * don't know whether they make any sense on customer setups
+ * so we only set that when booting with HDAT
+ */
+ dt_add_property(pbcq, "ibm,use-ab-detect", NULL, 0);
+
+ /* HDAT spec has these in version 0x6A and later */
+ if (hdif_vers >= 0x6a) {
+ u64 eq0 = be64_to_cpu(hub->phb_lane_eq[index][0]);
+ u64 eq1 = be64_to_cpu(hub->phb_lane_eq[index][1]);
+ u64 eq2 = be64_to_cpu(hub->phb_lane_eq[index][2]);
+ u64 eq3 = be64_to_cpu(hub->phb_lane_eq[index][3]);
+ dt_add_property_cells(pbcq, "ibm,lane-eq",
+ hi32(eq0), lo32(eq0),
+ hi32(eq1), lo32(eq1),
+ hi32(eq2), lo32(eq2),
+ hi32(eq3), lo32(eq3));
+ }
+
+ /* Currently we only create a PBCQ node, the actual PHB nodes
+ * will be added by sapphire later on.
+ */
+ return pbcq;
+}
+
+static struct dt_node *io_add_murano(const struct cechub_io_hub *hub,
+ const struct HDIF_common_hdr *sp_iohubs)
+{
+ struct dt_node *xscom;
+ unsigned int i, chip_id;
+
+ chip_id = pcid_to_chip_id(be32_to_cpu(hub->proc_chip_id));
+
+ printf("CEC: HW CHIP=0x%x, HW TOPO=0x%04x\n", chip_id,
+ be16_to_cpu(hub->hw_topology));
+
+ xscom = find_xscom_for_chip(chip_id);
+ if (!xscom) {
+ prerror("MURANO: Can't find XSCOM for chip %d\n", chip_id);
+ return NULL;
+ }
+
+ /* Create PHBs, max 3 */
+ for (i = 0; i < 3; i++) {
+ if (hub->fab_br0_pdt & (0x80 >> i))
+ /* XSCOM addresses for murano DD1.0 */
+ io_add_phb3(hub, sp_iohubs, i, xscom,
+ 0x02012000 + (i * 0x400),
+ 0x09012000 + (i * 0x400),
+ 0x09013c00 + (i * 0x40));
+ }
+
+ /* HACK: We return the XSCOM device for the VPD info */
+ return xscom;
+}
+
+static void io_add_p8_cec_vpd(const struct HDIF_common_hdr *sp_iohubs)
+{
+ const struct HDIF_child_ptr *iokids;
+ const void *iokid;
+ const void *kwvpd;
+ unsigned int kwvpd_sz;
+
+ /* P8 LXR0 kept in IO KID Keyword VPD */
+ iokids = HDIF_child_arr(sp_iohubs, CECHUB_CHILD_IO_KIDS);
+ if (!CHECK_SPPTR(iokids)) {
+ printf("CEC: No IOKID child array !\n");
+ return;
+ }
+ if (!iokids->count) {
+ printf("CEC: IOKID count is 0 !\n");
+ return;
+ }
+ if (iokids->count > 1) {
+ printf("CEC: WARNING ! More than 1 IO KID !!! (%d)\n",
+ iokids->count);
+ /* Ignoring the additional ones */
+ }
+
+ iokid = HDIF_child(sp_iohubs, iokids, 0, "IO KID");
+ if (!iokid) {
+ printf("CEC: No IO KID structure in child array !\n");
+ return;
+ }
+
+ /* Grab base location code for slots */
+ io_get_loc_code(iokid, dt_root, "ibm,io-base-loc-code");
+
+ kwvpd = HDIF_get_idata(iokid, CECHUB_ASCII_KEYWORD_VPD, &kwvpd_sz);
+ if (!kwvpd) {
+ printf("CEC: No VPD entry in IO KID !\n");
+ return;
+ }
+
+ /* Grab LX load info */
+ io_get_lx_info(kwvpd, kwvpd_sz, 0, dt_root);
+}
+
+static struct dt_node *io_add_hea(const struct cechub_io_hub *hub,
+ const void *sp_io)
+{
+ struct dt_node *np, *gnp;
+ uint64_t reg[2];
+ unsigned int i, vpd_sz;
+ uint8_t kw_sz;
+ const void *iokid, *vpd, *ccin;
+ const uint8_t *mac;
+ const struct HDIF_child_ptr *iokids;
+
+ /*
+ * We have a table of supported dauther cards looked up
+ * by CCIN. We don't use the 1008 slot map in the VPD.
+ *
+ * This is basically translated from BML and will do for
+ * now especially since we don't really support p5ioc2
+ * machine, this is just for lab use
+ *
+ * This is mostly untested on 10G ... we might need more
+ * info about the PHY in that case
+ */
+ const struct hea_iocard {
+ const char ccin[4];
+ struct {
+ uint32_t speed;
+ uint16_t ports;
+ uint16_t phy_id;
+ } pg[2];
+ } hea_iocards[] = {
+ {
+ .ccin = "1818", /* HV4 something */
+ .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 },
+ },
+ {
+ .ccin = "1819", /* HV4 Titov Card */
+ .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 },
+ .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 0 },
+ },
+ {
+ .ccin = "1830", /* HV4 Sergei Card */
+ .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 0 },
+ .pg[1] = { .speed = 10000, .ports = 1, .phy_id = 0 },
+ },
+ {
+ .ccin = "181A", /* L4 Evans Card */
+ .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 0 },
+ },
+ {
+ .ccin = "181B", /* L4 Weber Card */
+ .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 0 },
+ .pg[1] = { .speed = 10000, .ports = 1, .phy_id = 0 },
+ },
+ {
+ .ccin = "181C", /* HV4 Gibson Card */
+ .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 },
+ .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 0 },
+ },
+ {
+ .ccin = "2BC4", /* MR Riverside 2 */
+ .pg[0] = { .speed = 1000, .ports = 1, .phy_id = 1 },
+ .pg[1] = { .speed = 1000, .ports = 1, .phy_id = 1 },
+ },
+ {
+ .ccin = "2BC5", /* MR Lions 2 */
+ .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 1 },
+ .pg[1] = { .speed = 10000, .ports = 1, .phy_id = 1 },
+ },
+ {
+ .ccin = "2BC6", /* MR Onion 2 */
+ .pg[0] = { .speed = 10000, .ports = 1, .phy_id = 1 },
+ .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 1 },
+ },
+ {
+ .ccin = "266D", /* Jupiter Bonzai */
+ .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 1 },
+ .pg[1] = { .speed = 1000, .ports = 2, .phy_id = 1 },
+ },
+ /* The blade use an IO KID that's a bit oddball and seems to
+ * represent the backplane itself, but let's use it anyway
+ *
+ * XXX Probably want a different PHY type !
+ */
+ {
+ .ccin = "531C", /* P7 Blade */
+ .pg[0] = { .speed = 1000, .ports = 2, .phy_id = 0 },
+ },
+ };
+ const struct hea_iocard *card = NULL;
+
+ /* WARNING: This makes quite a lot of nasty assumptions
+ * that appear to hold true on the few machines I care
+ * about, which is good enough for now. We don't officially
+ * support p5ioc2 anyway...
+ */
+
+ /* Get first IO KID, we only support one. Real support would
+ * mean using the FRU ID and the SLCA to find the right "stuff"
+ * but at this stage it's unnecessary
+ */
+ iokids = HDIF_child_arr(sp_io, CECHUB_CHILD_IO_KIDS);
+ if (!CHECK_SPPTR(iokids)) {
+ prerror("HEA: no IOKID in HDAT child array !\n");
+ return NULL;
+ }
+ if (!iokids->count) {
+ prerror("HEA: IOKID count is 0 !\n");
+ return NULL;
+ }
+ if (iokids->count > 1) {
+ printf("HEA: WARNING ! More than 1 IO KID !!! (%d)\n",
+ iokids->count);
+ }
+ iokid = HDIF_child(sp_io, iokids, 0, "IO KID");
+ if (!iokid) {
+ prerror("HEA: Failed to retrieve IO KID 0 !\n");
+ return NULL;
+ }
+
+ /* Grab VPD */
+ vpd = HDIF_get_idata(iokid, IOKID_KW_VPD, &vpd_sz);
+ if (!CHECK_SPPTR(vpd)) {
+ prerror("HEA: Failed to retrieve VPD from IO KID !\n");
+ return NULL;
+ }
+
+ /* Grab the MAC address */
+ mac = vpd_find(vpd, vpd_sz, "VINI", "B1", &kw_sz);
+ if (!mac || kw_sz < 8) {
+ prerror("HEA: Failed to retrieve MAC Address !\n");
+ return NULL;
+ }
+
+ /* Grab the CCIN (card ID) */
+ ccin = vpd_find(vpd, vpd_sz, "VINI", "CC", &kw_sz);
+ if (!ccin || kw_sz < 4) {
+ prerror("HEA: Failed to retrieve CCIN !\n");
+ return NULL;
+ }
+
+ /* Now we could try to parse the 1008 slot map etc... but instead
+ * we'll do like BML and grab the CCIN & use it for known cards.
+ * We also grab the MAC
+ */
+ for (i = 0; i < ARRAY_SIZE(hea_iocards) && !card; i++) {
+ if (strncmp(hea_iocards[i].ccin, ccin, 4))
+ continue;
+ card = &hea_iocards[i];
+ }
+ if (!card) {
+ prerror("HEA: Unknown CCIN 0x%.4s!\n", (const char *)ccin);
+ return NULL;
+ }
+
+ /* Assume base address is BAR3 + 0x4000000000 */
+ reg[0] = hub->gx_ctrl_bar3 + 0x4000000000;
+ reg[1] = 0xc0000000;
+
+ printf("CEC: * Adding HEA to P5IOC2, assuming GBA=0x%llx\n",
+ (long long)reg[0]);
+ np = dt_new_addr(dt_root, "ibm,hea", reg[0]);
+ if (!np)
+ return NULL;
+
+ dt_add_property(np, "reg", reg, sizeof(reg));
+ dt_add_property_strings(np, "compatible", "ibm,p5ioc2-hea");
+ dt_add_property_cells(np, "#address-cells", 1);
+ dt_add_property_cells(np, "#size-cells", 0);
+ dt_add_property(np, "ibm,vpd", vpd, vpd_sz);
+ dt_add_property_cells(np, "#mac-address", mac[7]);
+ dt_add_property(np, "mac-address-base", mac, 6);
+ /* BUID is base + 0x30 */
+ dt_add_property(np, "interrupt-controller", NULL, 0);
+ dt_add_property_cells(np, "interrupt-base",
+ ((hub->buid_ext << 9) | 0x30) << 4);
+ dt_add_property_cells(np, "interrupt-max-count", 128);
+
+ /* Always 2 port groups */
+ for (i = 0; i < 2; i++) {
+ unsigned int clause;
+
+ switch(card->pg[i].speed) {
+ case 1000:
+ clause = 0x22;
+ break;
+ case 10000:
+ clause = 0x45;
+ break;
+ default:
+ /* Unused port group */
+ continue;
+ }
+ gnp = dt_new_addr(np, "portgroup", i + 1);
+ if (!gnp)
+ continue;
+
+ dt_add_property_cells(gnp, "reg", i + 1);
+ dt_add_property_cells(gnp, "speed", card->pg[i].speed);
+ /* XX FIXME */
+ dt_add_property_strings(gnp, "phy-type", "mdio");
+ dt_add_property_cells(gnp, "phy-mdio-addr", card->pg[i].phy_id);
+ dt_add_property_cells(gnp, "phy-mdio-clause", clause);
+ dt_add_property_cells(gnp, "subports", card->pg[i].ports);
+ }
+ return np;
+}
+
+static void io_parse_fru(const void *sp_iohubs)
+{
+ unsigned int i;
+ struct dt_node *hn;
+ int count;
+
+ count = HDIF_get_iarray_size(sp_iohubs, CECHUB_FRU_IO_HUBS);
+ if (count < 1) {
+ prerror("CEC: IO FRU with no chips !\n");
+ return;
+ }
+
+ printf("CEC: %d chips in FRU\n", count);
+
+ /* Iterate IO hub array */
+ for (i = 0; i < count; i++) {
+ const struct cechub_io_hub *hub;
+ unsigned int size, hub_id;
+
+ hub = HDIF_get_iarray_item(sp_iohubs, CECHUB_FRU_IO_HUBS,
+ i, &size);
+ if (!hub || size < CECHUB_IOHUB_MIN_SIZE) {
+ prerror("CEC: IO-HUB Chip %d bad idata\n", i);
+ continue;
+ }
+ printf("CEC: IO Hub Chip #%d:\n", i);
+ switch (hub->flags & CECHUB_HUB_FLAG_STATE_MASK) {
+ case CECHUB_HUB_FLAG_STATE_OK:
+ printf("CEC: OK\n");
+ break;
+ case CECHUB_HUB_FLAG_STATE_FAILURES:
+ printf("CEC: OK with failures\n");
+ break;
+ case CECHUB_HUB_FLAG_STATE_NOT_INST:
+ printf("CEC: Not installed\n");
+ continue;
+ case CECHUB_HUB_FLAG_STATE_UNUSABLE:
+ printf("CEC: Unusable");
+ continue;
+ }
+
+ hub_id = be16_to_cpu(hub->iohub_id);
+
+ /* GX BAR assignment */
+ printf("CEC: PChip: %d HUB ID: %04x [EC=0x%x] Hub#=%d)\n",
+ be32_to_cpu(hub->proc_chip_id), hub_id,
+ be32_to_cpu(hub->ec_level), be32_to_cpu(hub->hub_num));
+
+ switch(hub_id) {
+ case CECHUB_HUB_P7IOC:
+ printf("CEC: P7IOC !\n");
+ hn = io_add_p7ioc(hub, sp_iohubs);
+ io_add_common(hn, hub);
+ break;
+ case CECHUB_HUB_P5IOC2:
+ printf("CEC: P5IOC2 !\n");
+ hn = io_add_p5ioc2(hub, sp_iohubs);
+ io_add_common(hn, hub);
+ io_add_hea(hub, sp_iohubs);
+ break;
+ case CECHUB_HUB_MURANO:
+ case CECHUB_HUB_MURANO_SEGU:
+ printf("CEC: Murano !\n");
+ hn = io_add_murano(hub, sp_iohubs);
+ break;
+ default:
+ printf("CEC: Hub ID 0x%04x unsupported !\n",
+ hub_id);
+ hn = NULL;
+ }
+ }
+
+ /* On P8, grab the CEC VPD */
+ if (proc_gen == proc_gen_p8)
+ io_add_p8_cec_vpd(sp_iohubs);
+}
+
+void io_parse(void)
+{
+ const struct HDIF_common_hdr *sp_iohubs;
+ unsigned int i, size;
+
+ /* Look for IO Hubs */
+ if (!get_hdif(&spira.ntuples.cec_iohub_fru, "IO HUB")) {
+ prerror("CEC: Cannot locate IO Hub FRU data !\n");
+ return;
+ }
+
+ /*
+ * Note about LXRn numbering ...
+ *
+ * I can't completely make sense of what that is supposed to be, so
+ * for now, what we do is look for the first one we can find and
+ * increment it for each chip. Works for the machines I have here
+ */
+
+ for_each_ntuple_idx(&spira.ntuples.cec_iohub_fru, sp_iohubs, i,
+ CECHUB_FRU_HDIF_SIG) {
+ const struct cechub_hub_fru_id *fru_id_data;
+ unsigned int type;
+ static const char *typestr[] = {
+ "Reservation",
+ "Card",
+ "CPU Card",
+ "Backplane",
+ "Backplane Extension"
+ };
+ fru_id_data = HDIF_get_idata(sp_iohubs, CECHUB_FRU_ID_DATA_AREA,
+ &size);
+ if (!fru_id_data || size < sizeof(struct cechub_hub_fru_id)) {
+ prerror("CEC: IO-HUB FRU %d, bad ID data\n", i);
+ continue;
+ }
+ type = fru_id_data->card_type;
+
+ printf("CEC: HUB FRU %d is %s\n",
+ i, type > 4 ? "Unknown" : typestr[type]);
+
+ /*
+ * We currently only handle the backplane (Juno) and
+ * processor FRU (P8 machines)
+ */
+ if (type != CECHUB_FRU_TYPE_CEC_BKPLANE &&
+ type != CECHUB_FRU_TYPE_CPU_CARD) {
+ prerror("CEC: Unsupported type\n");
+ continue;
+ }
+
+ /* We don't support Hubs connected to pass-through ports */
+ if (fru_id_data->flags & (CECHUB_FRU_FLAG_HEADLESS |
+ CECHUB_FRU_FLAG_PASSTHROUGH)) {
+ prerror("CEC: Headless or Passthrough unsupported\n");
+ continue;
+ }
+
+ /* Ok, we have a reasonable candidate */
+ io_parse_fru(sp_iohubs);
+ }
+}
+
diff --git a/hdata/memory.c b/hdata/memory.c
new file mode 100644
index 0000000..8cca689
--- /dev/null
+++ b/hdata/memory.c
@@ -0,0 +1,377 @@
+/* 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 <memory.h>
+#include <cpu.h>
+#include <device_tree.h>
+#include <device.h>
+#include <vpd.h>
+#include <ccan/str/str.h>
+#include <libfdt/libfdt.h>
+#include <types.h>
+
+#include "spira.h"
+#include "hdata.h"
+
+struct HDIF_ram_area_id {
+ __be16 id;
+#define RAM_AREA_INSTALLED 0x8000
+#define RAM_AREA_FUNCTIONAL 0x4000
+ __be16 flags;
+};
+
+struct HDIF_ram_area_size {
+ __be64 mb;
+};
+
+struct ram_area {
+ const struct HDIF_ram_area_id *raid;
+ const struct HDIF_ram_area_size *rasize;
+};
+
+struct HDIF_ms_area_address_range {
+ __be64 start;
+ __be64 end;
+ __be32 chip;
+ __be32 mirror_attr;
+ __be64 mirror_start;
+};
+
+struct HDIF_ms_area_id {
+ __be16 id;
+#define MS_PTYPE_RISER_CARD 0x8000
+#define MS_PTYPE_MEM_CARD 0x4000
+#define MS_PTYPE_CEC_FRU 0x2000
+#define MS_PTYPE_HYBRID_CARD 0x1000
+ __be16 parent_type;
+#define MS_AREA_INSTALLED 0x8000
+#define MS_AREA_FUNCTIONAL 0x4000
+#define MS_AREA_SHARED 0x2000
+ __be16 flags;
+ __be16 share_id;
+};
+
+static struct dt_node *find_shared(struct dt_node *root, u16 id, u64 start, u64 len)
+{
+ struct dt_node *i;
+
+ for (i = dt_first(root); i; i = dt_next(root, i)) {
+ __be64 reg[2];
+ const struct dt_property *shared, *type;
+
+ type = dt_find_property(i, "device_type");
+ if (!type || strcmp(type->prop, "memory") != 0)
+ continue;
+
+ shared = dt_find_property(i, DT_PRIVATE "share-id");
+ if (!shared || fdt32_to_cpu(*(u32 *)shared->prop) != id)
+ continue;
+
+ memcpy(reg, dt_find_property(i, "reg")->prop, sizeof(reg));
+ if (be64_to_cpu(reg[0]) == start && be64_to_cpu(reg[1]) == len)
+ break;
+ }
+ return i;
+}
+
+static void append_chip_id(struct dt_node *mem, u32 id)
+{
+ struct dt_property *prop;
+ size_t len, i;
+ u32 *p;
+
+ prop = __dt_find_property(mem, "ibm,chip-id");
+ if (!prop)
+ return;
+ len = prop->len >> 2;
+ p = (u32 *)prop->prop;
+
+ /* Check if it exists already */
+ for (i = 0; i < len; i++) {
+ if (be32_to_cpu(p[i]) == id)
+ return;
+ }
+
+ /* Add it to the list */
+ dt_resize_property(&prop, (len + 1) << 2);
+ p = (u32 *)prop->prop;
+ p[len] = cpu_to_be32(id);
+}
+
+static bool add_address_range(struct dt_node *root,
+ const struct HDIF_ms_area_id *id,
+ const struct HDIF_ms_area_address_range *arange)
+{
+ struct dt_node *mem;
+ u64 reg[2];
+ char name[sizeof("memory@") + STR_MAX_CHARS(reg[0])];
+ u32 chip_id;
+
+ printf(" Range: 0x%016llx..0x%016llx on Chip 0x%x mattr: 0x%x\n",
+ (long long)arange->start, (long long)arange->end,
+ pcid_to_chip_id(arange->chip), arange->mirror_attr);
+
+ /* reg contains start and length */
+ reg[0] = cleanup_addr(be64_to_cpu(arange->start));
+ reg[1] = cleanup_addr(be64_to_cpu(arange->end)) - reg[0];
+
+ chip_id = pcid_to_chip_id(be32_to_cpu(arange->chip));
+
+ if (be16_to_cpu(id->flags) & MS_AREA_SHARED) {
+ /* Only enter shared nodes once. */
+ mem = find_shared(root, be16_to_cpu(id->share_id),
+ reg[0], reg[1]);
+ if (mem) {
+ append_chip_id(mem, chip_id);
+ return true;
+ }
+ }
+ sprintf(name, "memory@%llx", (long long)reg[0]);
+
+ mem = dt_new(root, name);
+ dt_add_property_string(mem, "device_type", "memory");
+ dt_add_property_cells(mem, "ibm,chip-id", chip_id);
+ dt_add_property_u64s(mem, "reg", reg[0], reg[1]);
+ if (be16_to_cpu(id->flags) & MS_AREA_SHARED)
+ dt_add_property_cells(mem, DT_PRIVATE "share-id",
+ be16_to_cpu(id->share_id));
+
+ return true;
+}
+
+static void add_chip_id_to_ram_area(const struct HDIF_common_hdr *msarea,
+ struct dt_node *ram_area)
+{
+ const struct HDIF_array_hdr *arr;
+ const struct HDIF_ms_area_address_range *arange;
+ unsigned int size;
+ u32 chip_id;
+
+ /* Safe to assume pointers are valid here. */
+ arr = HDIF_get_idata(msarea, 4, &size);
+ arange = (void *)arr + be32_to_cpu(arr->offset);
+ chip_id = pcid_to_chip_id(be32_to_cpu(arange->chip));
+ dt_add_property_cells(ram_area, "ibm,chip-id", chip_id);
+}
+
+static void add_size_to_ram_area(struct dt_node *ram_node,
+ const struct HDIF_common_hdr *hdr,
+ int indx_vpd)
+{
+ const void *fruvpd;
+ unsigned int fruvpd_sz;
+ const void *kw;
+ char *str;
+ uint8_t kwsz;
+
+ fruvpd = HDIF_get_idata(hdr, indx_vpd, &fruvpd_sz);
+ if (!CHECK_SPPTR(fruvpd))
+ return;
+
+ /* DIMM Size */
+ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "SZ", &kwsz);
+ if (!kw)
+ return;
+
+ str = zalloc(kwsz + 1);
+ memcpy(str, kw, kwsz);
+ dt_add_property_string(ram_node, "size", str);
+ free(str);
+}
+
+static void vpd_add_ram_area(const struct HDIF_common_hdr *msarea)
+{
+ unsigned int i;
+ unsigned int ram_sz;
+ const struct HDIF_common_hdr *ramarea;
+ const struct HDIF_child_ptr *ramptr;
+ const struct HDIF_ram_area_id *ram_id;
+ struct dt_node *ram_node;
+
+ ramptr = HDIF_child_arr(msarea, 0);
+ if (!CHECK_SPPTR(ramptr)) {
+ prerror("MS AREA: No RAM area at %p\n", msarea);
+ return;
+ }
+
+ for (i = 0; i < be32_to_cpu(ramptr->count); i++) {
+ ramarea = HDIF_child(msarea, ramptr, i, "RAM ");
+ if (!CHECK_SPPTR(ramarea))
+ continue;
+
+ ram_id = HDIF_get_idata(ramarea, 2, &ram_sz);
+ if (!CHECK_SPPTR(ram_id))
+ continue;
+
+ if ((be16_to_cpu(ram_id->flags) & RAM_AREA_INSTALLED) &&
+ (be16_to_cpu(ram_id->flags) & RAM_AREA_FUNCTIONAL)) {
+ ram_node = dt_add_vpd_node(ramarea, 0, 1);
+ if (ram_node) {
+ add_chip_id_to_ram_area(msarea, ram_node);
+ add_size_to_ram_area(ram_node, ramarea, 1);
+ }
+ }
+ }
+}
+
+static void get_msareas(struct dt_node *root,
+ const struct HDIF_common_hdr *ms_vpd)
+{
+ unsigned int i;
+ const struct HDIF_child_ptr *msptr;
+
+ /* First childptr refers to msareas. */
+ msptr = HDIF_child_arr(ms_vpd, MSVPD_CHILD_MS_AREAS);
+ if (!CHECK_SPPTR(msptr)) {
+ prerror("MS VPD: no children at %p\n", ms_vpd);
+ return;
+ }
+
+ for (i = 0; i < be32_to_cpu(msptr->count); i++) {
+ const struct HDIF_common_hdr *msarea;
+ const struct HDIF_array_hdr *arr;
+ const struct HDIF_ms_area_address_range *arange;
+ const struct HDIF_ms_area_id *id;
+ const void *fruid;
+ unsigned int size, j;
+ u16 flags;
+
+ msarea = HDIF_child(ms_vpd, msptr, i, "MSAREA");
+ if (!CHECK_SPPTR(msarea))
+ return;
+
+ id = HDIF_get_idata(msarea, 2, &size);
+ if (!CHECK_SPPTR(id))
+ return;
+ if (size < sizeof(*id)) {
+ prerror("MS VPD: %p msarea #%i id size too small!\n",
+ ms_vpd, i);
+ return;
+ }
+
+ flags = be16_to_cpu(id->flags);
+ printf("MS VPD: %p, area %i: %s %s %s\n",
+ ms_vpd, i,
+ flags & MS_AREA_INSTALLED ?
+ "installed" : "not installed",
+ flags & MS_AREA_FUNCTIONAL ?
+ "functional" : "not functional",
+ flags & MS_AREA_SHARED ?
+ "shared" : "not shared");
+
+ if ((flags & (MS_AREA_INSTALLED|MS_AREA_FUNCTIONAL))
+ != (MS_AREA_INSTALLED|MS_AREA_FUNCTIONAL))
+ continue;
+
+ arr = HDIF_get_idata(msarea, 4, &size);
+ if (!CHECK_SPPTR(arr))
+ continue;
+
+ if (size < sizeof(*arr)) {
+ prerror("MS VPD: %p msarea #%i arr size too small!\n",
+ ms_vpd, i);
+ return;
+ }
+
+ if (be32_to_cpu(arr->eactsz) < sizeof(*arange)) {
+ prerror("MS VPD: %p msarea #%i arange size too small!\n",
+ ms_vpd, i);
+ return;
+ }
+
+ fruid = HDIF_get_idata(msarea, 0, &size);
+ if (!CHECK_SPPTR(fruid))
+ return;
+
+ /* Add Raiser card VPD */
+ if (be16_to_cpu(id->parent_type) & MS_PTYPE_RISER_CARD)
+ dt_add_vpd_node(msarea, 0, 1);
+
+ /* Add RAM Area VPD */
+ vpd_add_ram_area(msarea);
+
+ /* This offset is from the arr, not the header! */
+ arange = (void *)arr + be32_to_cpu(arr->offset);
+ for (j = 0; j < be32_to_cpu(arr->ecnt); j++) {
+ if (!add_address_range(root, id, arange))
+ return;
+ arange = (void *)arange + be32_to_cpu(arr->esize);
+ }
+ }
+}
+
+static bool __memory_parse(struct dt_node *root)
+{
+ struct HDIF_common_hdr *ms_vpd;
+ const struct msvpd_ms_addr_config *msac;
+ const struct msvpd_total_config_ms *tcms;
+ unsigned int size;
+
+ ms_vpd = get_hdif(&spira.ntuples.ms_vpd, MSVPD_HDIF_SIG);
+ if (!ms_vpd) {
+ prerror("MS VPD: invalid\n");
+ op_display(OP_FATAL, OP_MOD_MEM, 0x0000);
+ return false;
+ }
+ if (be32_to_cpu(spira.ntuples.ms_vpd.act_len) < sizeof(*ms_vpd)) {
+ prerror("MS VPD: invalid size %u\n",
+ be32_to_cpu(spira.ntuples.ms_vpd.act_len));
+ op_display(OP_FATAL, OP_MOD_MEM, 0x0001);
+ return false;
+ }
+
+ printf("MS VPD: is at %p\n", ms_vpd);
+
+ msac = HDIF_get_idata(ms_vpd, MSVPD_IDATA_MS_ADDR_CONFIG, &size);
+ if (!CHECK_SPPTR(msac) || size < sizeof(*msac)) {
+ prerror("MS VPD: bad msac size %u @ %p\n", size, msac);
+ op_display(OP_FATAL, OP_MOD_MEM, 0x0002);
+ return false;
+ }
+ printf("MS VPD: MSAC is at %p\n", msac);
+
+ dt_add_property_u64(dt_root, DT_PRIVATE "maxmem",
+ be64_to_cpu(msac->max_configured_ms_address));
+
+ tcms = HDIF_get_idata(ms_vpd, MSVPD_IDATA_TOTAL_CONFIG_MS, &size);
+ if (!CHECK_SPPTR(tcms) || size < sizeof(*tcms)) {
+ prerror("MS VPD: Bad tcms size %u @ %p\n", size, tcms);
+ op_display(OP_FATAL, OP_MOD_MEM, 0x0003);
+ return false;
+ }
+ printf("MS VPD: TCMS is at %p\n", tcms);
+
+ printf("MS VPD: Maximum configured address: 0x%llx\n",
+ (long long)be64_to_cpu(msac->max_configured_ms_address));
+ printf("MS VPD: Maximum possible address: 0x%llx\n",
+ (long long)be64_to_cpu(msac->max_possible_ms_address));
+
+ get_msareas(root, ms_vpd);
+
+ printf("MS VPD: Total MB of RAM: 0x%llx\n",
+ (long long)be64_to_cpu(tcms->total_in_mb));
+
+ return true;
+}
+
+void memory_parse(void)
+{
+ if (!__memory_parse(dt_root)) {
+ prerror("MS VPD: Failed memory init !\n");
+ abort();
+ }
+}
+
diff --git a/hdata/paca.c b/hdata/paca.c
new file mode 100644
index 0000000..d4360e7
--- /dev/null
+++ b/hdata/paca.c
@@ -0,0 +1,322 @@
+/* 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 <types.h>
+
+#include "hdata.h"
+
+#define PACA_MAX_THREADS 4
+
+static unsigned int paca_index(const struct HDIF_common_hdr *paca)
+{
+ void *start = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
+ return ((void *)paca - start)
+ / be32_to_cpu(spira.ntuples.paca.alloc_len);
+}
+
+static struct dt_node *add_cpu_node(struct dt_node *cpus,
+ const struct HDIF_common_hdr *paca,
+ const struct sppaca_cpu_id *id,
+ bool okay)
+{
+ const struct sppaca_cpu_timebase *timebase;
+ const struct sppaca_cpu_cache *cache;
+ const struct sppaca_cpu_attr *attr;
+ struct dt_node *cpu;
+ u32 no, size, ve_flags, l2_phandle, chip_id;
+
+ /* We use the process_interrupt_line as the res id */
+ no = be32_to_cpu(id->process_interrupt_line);
+
+ ve_flags = be32_to_cpu(id->verify_exists_flags);
+ printf("CPU[%i]: PIR=%i RES=%i %s %s(%u threads)\n",
+ paca_index(paca), be32_to_cpu(id->pir), no,
+ ve_flags & CPU_ID_PACA_RESERVED
+ ? "**RESERVED**" : cpu_state(ve_flags),
+ ve_flags & CPU_ID_SECONDARY_THREAD
+ ? "[secondary] " :
+ (be32_to_cpu(id->pir) == boot_cpu->pir ? "[boot] " : ""),
+ ((ve_flags & CPU_ID_NUM_SECONDARY_THREAD_MASK)
+ >> CPU_ID_NUM_SECONDARY_THREAD_SHIFT) + 1);
+
+ timebase = HDIF_get_idata(paca, SPPACA_IDATA_TIMEBASE, &size);
+ if (!timebase || size < sizeof(*timebase)) {
+ prerror("CPU[%i]: bad timebase size %u @ %p\n",
+ paca_index(paca), size, timebase);
+ return NULL;
+ }
+
+ cache = HDIF_get_idata(paca, SPPACA_IDATA_CACHE_SIZE, &size);
+ if (!cache || size < sizeof(*cache)) {
+ prerror("CPU[%i]: bad cache size %u @ %p\n",
+ paca_index(paca), size, cache);
+ return NULL;
+ }
+
+ cpu = add_core_common(cpus, cache, timebase, no, okay);
+
+ /* Core attributes */
+ attr = HDIF_get_idata(paca, SPPACA_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, no, okay);
+ dt_add_property_cells(cpu, "l2-cache", l2_phandle);
+
+ /* We append the secondary cpus in __cpu_parse */
+ dt_add_property_cells(cpu, "ibm,ppc-interrupt-server#s", no);
+
+ dt_add_property_cells(cpu, DT_PRIVATE "hw_proc_id",
+ be32_to_cpu(id->hardware_proc_id));
+ dt_add_property_cells(cpu, "ibm,pir", be32_to_cpu(id->pir));
+
+ chip_id = pcid_to_chip_id(be32_to_cpu(id->processor_chip_id));
+ dt_add_property_cells(cpu, "ibm,chip-id", chip_id);
+
+ return cpu;
+}
+
+static struct dt_node *find_cpu_by_hardware_proc_id(struct dt_node *root,
+ u32 hw_proc_id)
+{
+ struct dt_node *i;
+
+ dt_for_each_node(root, i) {
+ const struct dt_property *prop;
+
+ if (!dt_has_node_property(i, "device_type", "cpu"))
+ continue;
+
+ prop = dt_find_property(i, DT_PRIVATE "hw_proc_id");
+ if (be32_to_cpu(*(u32 *)prop->prop) == hw_proc_id)
+ return i;
+ }
+ return NULL;
+}
+
+/* Note that numbers are small. */
+static void add_be32_sorted(__be32 arr[], __be32 new, unsigned num)
+{
+ unsigned int i;
+
+ /* Walk until we find where we belong (insertion sort). */
+ for (i = 0; i < num; i++) {
+ if (be32_to_cpu(new) < be32_to_cpu(arr[i])) {
+ __be32 tmp = arr[i];
+ arr[i] = new;
+ new = tmp;
+ }
+ }
+ arr[i] = new;
+}
+
+static void add_icps(void)
+{
+ struct dt_node *cpu;
+ unsigned int i;
+ u64 reg[PACA_MAX_THREADS * 2];
+ struct dt_node *icp;
+
+ dt_for_each_node(dt_root, cpu) {
+ u32 irange[2], size, pir;
+ const struct dt_property *intsrv;
+ const struct HDIF_common_hdr *paca;
+ u64 ibase;
+ unsigned int num_threads;
+ bool found = false;
+
+ if (!dt_has_node_property(cpu, "device_type", "cpu"))
+ continue;
+
+ intsrv = dt_find_property(cpu, "ibm,ppc-interrupt-server#s");
+ pir = dt_prop_get_u32(cpu, "ibm,pir");
+
+ /* Get ibase address */
+ paca = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
+ for_each_paca(paca) {
+ const struct sppaca_cpu_id *id;
+ id = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ID, &size);
+
+ if (pir != be32_to_cpu(id->pir))
+ continue;
+ ibase = cleanup_addr(be64_to_cpu(id->ibase));
+ found = true;
+ break;
+ }
+ if (!found)
+ return;
+
+ num_threads = intsrv->len / sizeof(u32);
+ assert(num_threads <= PACA_MAX_THREADS);
+
+ icp = dt_new_addr(dt_root, "interrupt-controller", ibase);
+ if (!icp)
+ continue;
+
+ dt_add_property_strings(icp, "compatible",
+ "IBM,ppc-xicp",
+ "IBM,power7-xicp");
+
+ irange[0] = dt_property_get_cell(intsrv, 0); /* Index */
+ irange[1] = num_threads; /* num servers */
+ dt_add_property(icp, "ibm,interrupt-server-ranges",
+ irange, sizeof(irange));
+ dt_add_property(icp, "interrupt-controller", NULL, 0);
+ dt_add_property_cells(icp, "#address-cells", 0);
+ dt_add_property_cells(icp, "#interrupt-cells", 1);
+ dt_add_property_string(icp, "device_type",
+ "PowerPC-External-Interrupt-Presentation");
+ for (i = 0; i < num_threads*2; i += 2) {
+ reg[i] = ibase;
+ /* One page is enough for a handful of regs. */
+ reg[i+1] = 4096;
+ ibase += reg[i+1];
+ }
+ dt_add_property(icp, "reg", reg, sizeof(reg));
+ }
+}
+
+static bool __paca_parse(void)
+{
+ const struct HDIF_common_hdr *paca;
+ struct dt_node *cpus;
+
+ paca = get_hdif(&spira.ntuples.paca, PACA_HDIF_SIG);
+ if (!paca) {
+ prerror("Invalid PACA (PCIA = %p)\n",
+ ntuple_addr(&spira.ntuples.pcia));
+ return false;
+ }
+
+ if (be32_to_cpu(spira.ntuples.paca.act_len) < sizeof(*paca)) {
+ prerror("PACA: invalid size %u\n",
+ be32_to_cpu(spira.ntuples.paca.act_len));
+ return false;
+ }
+
+ cpus = dt_new(dt_root, "cpus");
+ dt_add_property_cells(cpus, "#address-cells", 1);
+ dt_add_property_cells(cpus, "#size-cells", 0);
+
+ for_each_paca(paca) {
+ const struct sppaca_cpu_id *id;
+ u32 size, ve_flags;
+ bool okay;
+
+ id = HDIF_get_idata(paca, SPPACA_IDATA_CPU_ID, &size);
+
+ /* The ID structure on Blade314 is only 0x54 long. We can
+ * cope with it as we don't use all the additional fields.
+ * The minimum size we support is 0x40
+ */
+ if (!id || size < SPIRA_CPU_ID_MIN_SIZE) {
+ prerror("CPU[%i]: bad id size %u @ %p\n",
+ paca_index(paca), size, id);
+ return false;
+ }
+
+ ve_flags = be32_to_cpu(id->verify_exists_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;
+ }
+
+ printf("CPU[%i]: PIR=%i RES=%i %s\n",
+ paca_index(paca), be32_to_cpu(id->pir),
+ be32_to_cpu(id->process_interrupt_line),
+ okay ? "OK" : "UNAVAILABLE");
+
+ /* Secondary threads don't get their own node. */
+ if (ve_flags & CPU_ID_SECONDARY_THREAD)
+ continue;
+
+ if (!add_cpu_node(cpus, paca, id, okay))
+ return false;
+ }
+
+ /* Now account for secondaries. */
+ for_each_paca(paca) {
+ const struct dt_property *prop;
+ const struct sppaca_cpu_id *id;
+ u32 size, state, num, ve_flags;
+ struct dt_node *cpu;
+ __be32 *new_prop;
+
+ id = HDIF_get_idata(paca, 2, &size);
+ ve_flags = be32_to_cpu(id->verify_exists_flags);
+ state = (ve_flags & CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT;
+ switch (state) {
+ case CPU_ID_VERIFY_USABLE_NO_FAILURES:
+ case CPU_ID_VERIFY_USABLE_FAILURES:
+ break;
+ default:
+ continue;
+ }
+
+ /* Only interested in secondary threads. */
+ if (!(ve_flags & CPU_ID_SECONDARY_THREAD))
+ continue;
+
+ cpu = find_cpu_by_hardware_proc_id(cpus,
+ be32_to_cpu(id->hardware_proc_id));
+ if (!cpu) {
+ prerror("CPU[%i]: could not find primary hwid %i\n",
+ paca_index(paca),
+ be32_to_cpu(id->hardware_proc_id));
+ return false;
+ }
+
+ /* Add the cpu #. */
+ prop = dt_find_property(cpu, "ibm,ppc-interrupt-server#s");
+ num = prop->len / sizeof(u32);
+ new_prop = malloc((num + 1) * sizeof(u32));
+ if (!new_prop) {
+ prerror("Property allocation length %zu failed\n",
+ (num + 1) * sizeof(u32));
+ return false;
+ }
+ memcpy(new_prop, prop->prop, prop->len);
+ add_be32_sorted(new_prop, id->process_interrupt_line, num);
+ dt_del_property(cpu, (struct dt_property *)prop);
+ dt_add_property(cpu, "ibm,ppc-interrupt-server#s",
+ new_prop, (num + 1) * sizeof(__be32));
+ free(new_prop);
+ }
+
+ add_icps();
+
+ return true;
+}
+
+void paca_parse(void)
+{
+ if (!__paca_parse()) {
+ prerror("CPU: Initial CPU parsing failed\n");
+ abort();
+ }
+}
diff --git a/hdata/pcia.c b/hdata/pcia.c
new file mode 100644
index 0000000..d128a31
--- /dev/null
+++ b/hdata/pcia.c
@@ -0,0 +1,242 @@
+/* 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"))
+ / spira.ntuples.pcia.alloc_len;
+}
+
+static const struct sppcia_cpu_thread *find_tada(const void *pcia,
+ unsigned int thread)
+{
+ unsigned int count = HDIF_get_iarray_size(pcia,
+ SPPCIA_IDATA_THREAD_ARRAY);
+ unsigned int i;
+
+ 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_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->proc_int_line);
+ 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_cells(icp, "#interrupt-cells", 1);
+ 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);
+
+ printf("CORE[%i]: PIR=%i RES=%i %s %s(%u threads)\n",
+ pcia_index(pcia), t->pir, t->proc_int_line,
+ 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->proc_int_line), 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->proc_int_line), 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->proc_int_line;
+ assert(t->proc_int_line == t->pir);
+ }
+
+ dt_add_property(cpu, "ibm,ppc-interrupt-server#s", iserv, 4 * threads);
+
+ /* Add the ICP node for this CPU */
+ add_icp(pcia, threads, icp_compat);
+
+ return cpu;
+}
+
+bool pcia_parse(void)
+{
+ const void *pcia;
+ struct dt_node *cpus;
+ bool got_pcia = false;
+
+ /* Check PCIA exists... if not, maybe we are getting a PACA ? */
+ pcia = get_hdif(&spira.ntuples.pcia, "SPPCIA");
+ if (!pcia)
+ return false;
+
+ printf("Got PCIA !\n");
+
+ got_pcia = true;
+
+ 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;
+ }
+
+ printf("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 got_pcia;
+}
diff --git a/hdata/slca.c b/hdata/slca.c
new file mode 100644
index 0000000..a709aaa
--- /dev/null
+++ b/hdata/slca.c
@@ -0,0 +1,89 @@
+/* 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 "hdata.h"
+
+const struct slca_entry *slca_get_entry(uint16_t slca_index)
+{
+ struct HDIF_common_hdr *slca_hdr;
+ int count;
+
+ slca_hdr = get_hdif(&spira.ntuples.slca, SLCA_HDIF_SIG);
+ if (!slca_hdr) {
+ prerror("SLCA Invalid\n");
+ return NULL;
+ }
+
+ count = HDIF_get_iarray_size(slca_hdr, SLCA_IDATA_ARRAY);
+ if (count < 0) {
+ prerror("SLCA: Can't find SLCA array size!\n");
+ return NULL;
+ }
+
+ if (slca_index < count) {
+ const struct slca_entry *s_entry;
+ unsigned int entry_sz;
+ s_entry = HDIF_get_iarray_item(slca_hdr, SLCA_IDATA_ARRAY,
+ slca_index, &entry_sz);
+
+ if (s_entry && entry_sz >= sizeof(*s_entry))
+ return s_entry;
+ } else
+ printf("SLCA: Can't find slca_entry for index %d\n", slca_index);
+ return NULL;
+}
+
+const char *slca_get_vpd_name(uint16_t slca_index)
+{
+ const struct slca_entry *s_entry;
+
+ s_entry = slca_get_entry(slca_index);
+ if (s_entry)
+ return (const char *)s_entry->fru_id;
+ else
+ printf("SLCA: Can't find fru_id for index %d\n", slca_index);
+ return NULL;
+}
+
+const char *slca_get_loc_code_index(uint16_t slca_index)
+{
+ const struct slca_entry *s_entry;
+
+ s_entry = slca_get_entry(slca_index);
+ if (s_entry)
+ return s_entry->loc_code;
+ else
+ printf("SLCA: Entry %d bad idata\n", slca_index);
+
+ return NULL;
+}
+
+void slca_vpd_add_loc_code(struct dt_node *node, uint16_t slca_index)
+{
+ const char *fru_loc_code;
+ char loc_code[LOC_CODE_SIZE + 1];
+
+ memset(loc_code, 0, sizeof(loc_code));
+ fru_loc_code = slca_get_loc_code_index(slca_index);
+ if (!fru_loc_code)
+ return;
+
+ strncpy(loc_code, fru_loc_code, LOC_CODE_SIZE);
+ dt_add_property(node, "ibm,loc-code", loc_code, strlen(loc_code) + 1);
+}
diff --git a/hdata/spira.c b/hdata/spira.c
new file mode 100644
index 0000000..39a78e8
--- /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");
+}
diff --git a/hdata/spira.h b/hdata/spira.h
new file mode 100644
index 0000000..93239d0
--- /dev/null
+++ b/hdata/spira.h
@@ -0,0 +1,864 @@
+/* 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.
+ */
+
+#ifndef __SPIRA_H
+#define __SPIRA_H
+
+#include "hdif.h"
+
+/*
+ * The SPIRA structure
+ *
+ * NOTE: This is one of the only HDIF structure that we layout entirely
+ * as a C struct because it's provided by us to the FSP. Almost everything
+ * else is generated by the FSP, and thus must be "parsed" since the various
+ * offsets and alignments might change.
+ */
+
+#define SPIRA_VERSION 0x20 /* Like 730 ? */
+
+struct spira_ntuple {
+ __be64 addr;
+ __be16 alloc_cnt;
+ __be16 act_cnt;
+ __be32 alloc_len;
+ __be32 act_len;
+ __be32 tce_off;
+ __be64 padding;
+} __packed;
+
+#define SPIRA_NTUPLES_COUNT 0x18
+
+struct spira_ntuples {
+ struct HDIF_array_hdr array_hdr;
+ struct spira_ntuple sp_subsys; /* 0x040 */
+ struct spira_ntuple ipl_parms; /* 0x060 */
+ struct spira_ntuple nt_enclosure_vpd; /* 0x080 */
+ struct spira_ntuple slca; /* 0x0a0 */
+ struct spira_ntuple backplane_vpd; /* 0x0c0 */
+ struct spira_ntuple system_vpd; /* 0x0e0 */
+ struct spira_ntuple chip_tod; /* 0x100 */
+ struct spira_ntuple proc_init; /* 0x120 */
+ struct spira_ntuple clock_vpd; /* 0x140 */
+ struct spira_ntuple anchor_vpd; /* 0x160 */
+ struct spira_ntuple op_panel_vpd; /* 0x180 */
+ struct spira_ntuple ext_cache_fru_vpd; /* 0x1a0 */
+ struct spira_ntuple misc_cec_fru_vpd; /* 0x1c0 */
+ struct spira_ntuple paca; /* 0x1e0 */
+ struct spira_ntuple ms_vpd; /* 0x200 */
+ struct spira_ntuple cec_iohub_fru; /* 0x220 */
+ struct spira_ntuple cpu_ctrl; /* 0x240 */
+ struct spira_ntuple mdump_src; /* 0x260 */
+ struct spira_ntuple mdump_dst; /* 0x280 */
+ struct spira_ntuple mdump_res; /* 0x2a0 */
+ struct spira_ntuple heap; /* 0x2c0 */
+ struct spira_ntuple pcia; /* 0x2e0 */
+ struct spira_ntuple proc_chip; /* 0x300 */
+ struct spira_ntuple hs_data; /* 0x320 */
+};
+
+struct spira {
+ struct HDIF_common_hdr hdr;
+ struct HDIF_idata_ptr ntuples_ptr;
+ __be64 pad;
+ struct spira_ntuples ntuples;
+ u8 reserved[0x4c0];
+} __packed __align(0x100);
+
+extern struct spira spira;
+
+/* This macro can be used to check the validity of a pointer returned
+ * by one of the HDIF API functions. It returns true if the pointer
+ * appears valid. If it's not valid and not NULL, it will print some
+ * error in the log as well.
+ */
+#define CHECK_SPPTR(_ptr) spira_check_ptr(_ptr, __FILE__, __LINE__)
+
+#define get_hdif(ntuple, id) __get_hdif((ntuple), (id), __FILE__, __LINE__)
+
+extern struct HDIF_common_hdr *__get_hdif(struct spira_ntuple *n,
+ const char id[],
+ const char *file, int line);
+
+#define for_each_ntuple_idx(_ntuples, _p, _idx, _id) \
+ for (_p = get_hdif((_ntuples), _id ""), _idx = 0; \
+ _p && _idx < be16_to_cpu((_ntuples)->act_cnt); \
+ _p = (void *)_p + be32_to_cpu((_ntuples)->alloc_len), _idx++)
+
+#define for_each_ntuple(_ntuples, _p, _id) \
+ for (_p = get_hdif((_ntuples), _id ""); \
+ _p && (void *)_p < ntuple_addr(_ntuples) \
+ + (be16_to_cpu((_ntuples)->act_cnt) * \
+ be32_to_cpu((_ntuples)->alloc_len)); \
+ _p = (void *)_p + be32_to_cpu((_ntuples)->alloc_len))
+
+#define for_each_paca(p) for_each_ntuple(&spira.ntuples.paca, p, PACA_HDIF_SIG)
+
+#define for_each_pcia(p) for_each_ntuple(&spira.ntuples.pcia, p, SPPCIA_HDIF_SIG)
+
+
+/* We override these for testing. */
+#ifndef ntuple_addr
+#define ntuple_addr(_ntuples) ((void *)BE64_TO_CPU((_ntuples)->addr))
+#endif
+
+#ifndef spira_check_ptr
+extern bool spira_check_ptr(const void *ptr, const char *file,
+ unsigned int line);
+#endif
+
+struct proc_init_data {
+ struct HDIF_common_hdr hdr;
+ struct HDIF_idata_ptr regs_ptr;
+ struct {
+ __be64 nia;
+ __be64 msr;
+ } regs;
+} __packed __align(0x10);
+
+/*
+ * The FRU ID structure is used in several tuples, so we
+ * define it generically here
+ */
+struct spira_fru_id {
+ __be16 slca_index;
+ __be16 rsrc_id; /* formerly VPD port number */
+};
+
+/*
+ * The FRU operational status structure is used in several
+ * tuples, so we define it generically here
+ */
+struct spira_fru_op_status {
+ uint8_t flags;
+#define FRU_OP_STATUS_FLAG_USED 0x02 /* If 0 -> not used (redundant) */
+#define FRU_OP_STATUS_FLAG_FUNCTIONAL 0x01 /* If 0 -> non-functional */
+ uint8_t reserved[3];
+};
+
+/*
+ * Move VPD related stuff to another file ...
+ */
+#define VPD_ID(_a, _b) ((_a) << 8 | (_b))
+
+/*
+ * Service Processor Subsystem Structure
+ *
+ * This structure contains several internal data blocks
+ * describing the service processor(s) in the system
+ */
+
+#define SPSS_HDIF_SIG "SPINFO"
+
+/* Idata index 0 : FRU ID Data */
+#define SPSS_IDATA_FRU_ID 0
+
+/* Idata index 1 : Keyword VPD for the FSP instance */
+#define SPSS_IDATA_KEYWORD_VPD 1
+
+/* Idata index 2 : SP Implementation */
+#define SPSS_IDATA_SP_IMPL 2
+
+struct spss_sp_impl {
+ __be16 hw_version;
+ __be16 sw_version;
+ __be16 func_flags;
+#define SPSS_SP_IMPL_FLAGS_INSTALLED 0x8000
+#define SPSS_SP_IMPL_FLAGS_FUNCTIONAL 0x4000
+#define SPSS_SP_IMPL_FLAGS_PRIMARY 0x2000
+ u8 chip_version;
+ u8 reserved;
+};
+
+/* Idata index 3 is deprecated */
+
+/* Idata index 4 : SP Memory Locator */
+#define SPSS_IDATA_SP_MEMLOC 4
+
+/* Idata index 5 : SP I/O path array */
+#define SPSS_IDATA_SP_IOPATH 5
+
+/* An HDIF array of IO path */
+struct spss_iopath {
+ __be16 iopath_type;
+#define SPSS_IOPATH_TYPE_IOHUB_PHB 0x0001
+#define SPSS_IOPATH_TYPE_PSI 0x0002
+ union {
+ struct {
+ __be16 iohub_chip_inst;
+ __be16 iohub_chip_port;
+ __be16 phb_id;
+ } __packed iohub_phb;
+
+ struct {
+ __be16 link_status;
+#define SPSS_IO_PATH_PSI_LINK_BAD_FRU 0x0000
+#define SPSS_IO_PATH_PSI_LINK_CURRENT 0x0001
+#define SPSS_IO_PATH_PSI_LINK_BACKUP 0x0002
+ u8 ml2_version;
+ u8 reserved;
+ __be16 slca_count;
+ u8 slca_idx[16];
+ __be32 proc_chip_id;
+ __be32 reserved2;
+ __be64 gxhb_base;
+ } __packed psi;
+ };
+} __packed;
+
+/*
+ * IPL Parms structure
+ *
+ */
+
+/* Idata index 0: System Parameters */
+#define IPLPARAMS_SYSPARAMS 0
+
+struct iplparams_sysparams {
+ char sys_model[4];
+ char cpu_feature_code[4];
+ __be32 effective_pvr;
+ __be32 system_type;
+ uint8_t num_lpar_oct[8];
+ __be32 abc_bus_speed;
+ __be32 wxyz_bus_speed;
+ __be32 sys_eco_mode;
+ __be32 sys_attributes;
+ __be32 mem_scrubbing;
+ __be16 cur_spl_value;
+ uint8_t pump_mode;
+ uint8_t use_pore_sleep;
+ __be32 pore_image_size;
+} __packed;
+
+/* Idata index 1: IPL parameters */
+#define IPLPARAMS_IPLPARAMS 1
+
+struct iplparams_iplparams {
+ uint8_t reserved;
+ uint8_t hv_ipl_dest;
+ uint8_t ipl_side;
+#define IPLPARAMS_CEC_FW_IPL_SIDE_TEMP 0x10
+#define IPLPARAMS_FSP_FW_IPL_SIDE_TEMP 0x01
+ uint8_t ipl_speed;
+ __be16 cec_ipl_attrib;
+ uint8_t cec_ipl_maj_type;
+ uint8_t cec_ipl_min_type;
+ uint8_t os_ipl_mode;
+ uint8_t keylock_pos;
+ uint8_t lmb_size;
+ uint8_t deprecated;
+ __be32 max_hsl_opticonnect;
+ __be32 other_attrib;
+#define IPLPARAMS_OATTR_RST_PCI_BUSNO 0x08000000
+#define IPLPARAMS_OATTR_CLEAR_NVRAM 0x04000000
+ __be16 huge_page_count;
+ uint8_t huge_page_size;
+#define IPLPARAMS_HUGE_PG_SIZE_16G 0
+ uint8_t num_vlan_switches;
+ __be64 reserved2;
+};
+
+/* Idata index 4: Platform Dump Descriptor */
+#define IPLPARAMS_PLATFORM_DUMP 4
+
+struct iplparams_dump {
+ __be16 flags;
+ uint8_t reserved1;
+ uint8_t policy;
+#define HYP_DUMP_POLICY_NORMAL 0x00
+ __be32 dump_id;
+ __be64 reserved2;
+ __be64 act_dump_sz;
+ __be32 max_hw_dump_sz;
+ __be32 act_hw_dump_sz;
+ __be32 max_sp_dump_sz;
+ __be32 plid;
+};
+
+/* Idata index 8: serial ports */
+#define IPLPARMS_IDATA_SERIAL 8
+
+/* An HDIF array of serial descriptions */
+struct iplparms_serial {
+ uint8_t loc_code[LOC_CODE_SIZE];
+ __be16 rsrc_id;
+ __be16 flags;
+#define PLPARMS_SERIAL_FLAGS_CALLHOME 0x8000
+} __packed;
+
+/*
+ * Chip TOD structure
+ *
+ * This is an array of 32 entries (I assume per possible chip)
+ */
+
+/* Idata index 0: Chip ID data (array) */
+#define CHIPTOD_IDATA_CHIPID 0
+
+struct chiptod_chipid {
+ __be32 chip_id;
+ __be32 flags;
+#define CHIPTOD_ID_FLAGS_PRIMARY 0x02
+#define CHIPTOD_ID_FLAGS_SECONDARY 0x01
+#define CHIPTOD_ID_FLAGS_STATUS_MASK 0x0c
+#define CHIPTOD_ID_FLAGS_STATUS_OK 0x04
+#define CHIPTOD_ID_FLAGS_STATUS_NOK 0x08
+} __packed;
+
+/* Idata index 0: Chip Initialization data */
+#define CHIPTOD_IDATA_CHIPINIT 1
+
+struct chiptod_chipinit {
+ __be32 ctrl_reg_internal;
+ __be32 tod_ctrl_reg;
+} __packed;
+
+/*
+ * MS VPD - Memory Description Tree
+ *
+ * One such structure pointing to the various memory arrays
+ * along with other infos about the BCRs, Page Mover, XSCOM,...
+ */
+#define MSVPD_HDIF_SIG "MS VPD"
+
+/* Idata index 0: Mainstore address config */
+#define MSVPD_IDATA_MS_ADDR_CONFIG 0
+
+/* Mainstore Address Configuration */
+struct msvpd_ms_addr_config {
+ __be64 max_configured_ms_address;
+ __be64 max_possible_ms_address;
+ __be32 deprecated;
+ __be64 mirrorable_memory_starting_address;
+} __attribute__((packed));
+
+/* Idata index 1: Total configured mainstore */
+#define MSVPD_IDATA_TOTAL_CONFIG_MS 1
+
+struct msvpd_total_config_ms {
+ __be64 total_in_mb;
+};
+
+/* Idata index 2: Page mover and sync structure */
+#define MSVPD_IDATA_PMOVER_SYNCHRO 2
+
+struct msvpd_pmover_bsr_synchro {
+ __be32 flags;
+#define MSVPD_PMS_FLAG_HWLOCK_EN 0x80000000
+#define MSVPD_PMS_FLAG_PMOVER_EN 0x40000000
+#define MSVPD_PMS_FLAG_BSR_EN 0x20000000
+#define MSVPD_PMS_FLAG_XSCOMBASE_VALID 0x10000000
+ /* P7 values for BSR mode */
+#define MSVPD_PMS_FLAG_P7BSR_1M_MODE 0x00000000
+#define MSVPD_PMS_FLAG_P7BSR_2M_MODE 0x02000000
+#define MSVPD_PMS_FLAG_P7BSR_4M_MODE 0x04000000
+#define MSVPD_PMS_FLAG_P7BSR_8M_MODE 0x06000000
+ __be32 hwlocks_per_page;
+ __be64 hwlock_addr;
+ __be64 pmover_addr;
+ __be64 bsr_addr;
+ __be64 xscom_addr;
+
+};
+
+/* Idata index 3: Memory Trace Array */
+
+/* Idata index 4: UE Address Array */
+
+/* Child index 0: MS area child structure */
+#define MSVPD_CHILD_MS_AREAS 0
+
+/*
+ * CEC I/O Hub FRU
+ *
+ * This is an array of CEC Hub FRU HDIF structures
+ *
+ * Each of these has some idata pointers to generic info about the
+ * hub and a possible child pointer for daughter card.
+ *
+ * Actual ports are in the SLCA and need to be cross referenced
+ *
+ * Note that slots meant for the addition of GX+ adapters that
+ * are currently unpopulated but support hotplug will have a
+ * minimum "placeholder" entry, which will be fully populated
+ * when the array is rebuild during concurrent maintainance.
+ * This "placeholder" is called a "reservation".
+ *
+ * WARNING: The array rebuild by concurrent maintainance is not
+ * guaranteed to be in the same order as the IPL array, not is
+ * the order stable between concurrent maintainance operations.
+ *
+ * There's also a child pointer to daugher card structures but
+ * we aren't going to handle that just yet.
+ */
+#define CECHUB_FRU_HDIF_SIG "IO HUB"
+#define IOKID_FRU_HDIF_SIG "IO KID"
+
+/* Idata index 0: FRU ID data
+ *
+ * This is a generic struct spira_fru_id defined above
+ */
+#define CECHUB_FRU_ID_DATA 0
+
+/* Idata index 1: ASCII Keyword VPD */
+#define CECHUB_ASCII_KEYWORD_VPD 1
+
+/* Idata index 2: Hub FRU ID data area */
+#define CECHUB_FRU_ID_DATA_AREA 2
+
+struct cechub_hub_fru_id {
+ __be32 card_type;
+#define CECHUB_FRU_TYPE_IOHUB_RSRV 0
+#define CECHUB_FRU_TYPE_IOHUB_CARD 1
+#define CECHUB_FRU_TYPE_CPU_CARD 2
+#define CECHUB_FRU_TYPE_CEC_BKPLANE 3
+#define CECHUB_FRU_TYPE_BKPLANE_EXT 4
+ __be32 unused;
+ __be16 total_chips;
+ uint8_t flags;
+#define CECHUB_FRU_FLAG_HEADLESS 0x80 /* not connected to CPU */
+#define CECHUB_FRU_FLAG_PASSTHROUGH 0x40 /* connected to passhtrough
+ port of another hub */
+ uint8_t reserved;
+ __be16 parent_hub_id; /* chip instance number of the
+ hub that contains the passthrough
+ port this one is connected to */
+ __be16 reserved2;
+} __packed;
+
+
+/* Idata index 3: IO HUB array */
+
+#define CECHUB_FRU_IO_HUBS 3
+
+/* This is an HDIF array of IO Hub structures */
+struct cechub_io_hub {
+ __be64 fmtc_address;
+ __be32 fmtc_tce_size;
+ __be16 hub_num; /* unique hub number (I/O Hub ID) */
+ uint8_t flags;
+#define CECHUB_HUB_FLAG_STATE_MASK 0xc0
+#define CECHUB_HUB_FLAG_STATE_OK 0x00
+#define CECHUB_HUB_FLAG_STATE_FAILURES 0x40
+#define CECHUB_HUB_FLAG_STATE_NOT_INST 0x80
+#define CECHUB_HUB_FLAG_STATE_UNUSABLE 0xc0
+#define CECHUB_HUB_FLAG_MASTER_HUB 0x20 /* HDAT < v9.x only */
+#define CECHUB_HUB_FLAG_GARD_MASK_VALID 0x08 /* HDAT < v9.x only */
+#define CECHUB_HUB_FLAG_SWITCH_MASK_PDT 0x04 /* HDAT < v9.x only */
+#define CECHUB_HUB_FLAG_FAB_BR0_PDT 0x02 /* HDAT < v9.x only */
+#define CECHUB_HUB_FLAG_FAB_BR1_PDT 0x01 /* HDAT < v9.x only */
+ uint8_t nr_ports; /* HDAT < v9.x only */
+ uint8_t fab_br0_pdt; /* p5ioc2 PCI-X or P8 PHB3's */
+#define CECHUB_HUB_FAB_BR0_PDT_PHB0 0x80
+#define CECHUB_HUB_FAB_BR0_PDT_PHB1 0x40
+#define CECHUB_HUB_FAB_BR0_PDT_PHB2 0x20
+#define CECHUB_HUB_FAB_BR0_PDT_PHB3 0x10
+ uint8_t fab_br1_pdt; /* p5ioc2 & p7ioc PCI-E */
+#define CECHUB_HUB_FAB_BR1_PDT_PHB0 0x80
+#define CECHUB_HUB_FAB_BR1_PDT_PHB1 0x40
+#define CECHUB_HUB_FAB_BR1_PDT_PHB2 0x20
+#define CECHUB_HUB_FAB_BR1_PDT_PHB3 0x10
+#define CECHUB_HUB_FAB_BR1_PDT_PHB4 0x08 /* p7ioc only */
+#define CECHUB_HUB_FAB_BR1_PDT_PHB5 0x04 /* p7ioc only */
+ __be16 iohub_id; /* the type of hub */
+#define CECHUB_HUB_P5IOC2 0x1061 /* from VPL1 */
+#define CECHUB_HUB_P7IOC 0x60e7 /* from VPL3 */
+#define CECHUB_HUB_MURANO 0x20ef /* Murano from spec */
+#define CECHUB_HUB_MURANO_SEGU 0x0001 /* Murano+Seguso from spec */
+#define CECHUB_HUB_VENICE_WYATT 0x0010 /* Venice+Wyatt from spec */
+ __be32 ec_level;
+ __be32 aff_dom2; /* HDAT < v9.x only */
+ __be32 aff_dom3; /* HDAT < v9.x only */
+ __be64 reserved;
+ __be32 proc_chip_id;
+
+ union {
+ /* HDAT < v9.x */
+ struct {
+ __be32 gx_index; /* GX bus index on cpu */
+ __be32 buid_ext; /* BUID Extension */
+ __be32 xscom_chip_id; /* TORRENT ONLY */
+ };
+ /* HDAT >= v9.x */
+ struct {
+ __be32 reserved1;
+ __be32 reserved2;
+ __be16 reserved3;
+ __be16 hw_topology;
+ };
+ };
+ __be32 mrid;
+ __be32 mem_map_vers;
+ union {
+ /* HDAT < v9.x */
+ struct {
+ __be64 gx_ctrl_bar0;
+ __be64 gx_ctrl_bar1;
+ __be64 gx_ctrl_bar2;
+ __be64 gx_ctrl_bar3;
+ __be64 gx_ctrl_bar4;
+ __be32 sw_mask_pdt;
+ __be16 gard_mask;
+ __be16 gx_bus_speed; /* Version 0x58 */
+ };
+
+ /* HDAT >= v9.x, HDIF version 0x6A or later */
+ struct {
+ /* 4 values per PHB, 4 PHBs */
+ __be64 phb_lane_eq[4][4];
+ };
+ };
+} __packed;
+
+/* We support structures as small as 0x68 bytes */
+#define CECHUB_IOHUB_MIN_SIZE 0x68
+
+/* Child index 0: IO Daugther Card */
+#define CECHUB_CHILD_IO_KIDS 0
+
+/*
+ * IO KID is a dauther card structure
+ */
+#define IOKID_FRU_ID_DATA 0
+#define IOKID_KW_VPD 1
+
+/*
+ * Slot Location Code Array (aka SLCA)
+ *
+ * This is a pile of location codes referenced by various other
+ * structures such as the IO Hubs for things on the CEC. Not
+ * everything in there is a physical port. The SLCA is actually
+ * a tree which represent the topology of the system.
+ *
+ * The tree works as follow: A parent has a pointer to the first
+ * child. A child has a pointer to its parent. Siblings are
+ * consecutive entries.
+ *
+ * Note: If we ever support concurrent maintainance... this is
+ * completely rebuilt, invalidating all indices, though other
+ * structures that may reference SLCA by index will be rebuilt
+ * as well.
+ *
+ * Note that a lot of that stuff is based on VPD documentation
+ * such as the identification keywords. I will list the ones
+ * I manage to figure out without the doc separately.
+ */
+#define SLCA_HDIF_SIG "SLCA "
+
+/* Idata index 0 : SLCA root pointer
+ *
+ * The SLCA array is an HDIF array of all the entries. The tree
+ * structure is based on indices inside the entries and order of
+ * the entries
+ */
+#define SLCA_IDATA_ARRAY 0
+
+/* Note: An "index" (or idx) is always an index into the SLCA array
+ * and "id" is a reference to some other object.
+ */
+struct slca_entry {
+ __be16 my_index; /* redundant, useful */
+ __be16 rsrc_id; /* formerly VPD port number */
+ uint8_t fru_id[2]; /* ASCII VPD ID */
+#define SLCA_ROOT_VPD_ID VPD_ID('V','V')
+#define SLCA_SYSTEM_VPD_ID VPD_ID('S','V')
+ __be16 parent_index; /* Parent entry index */
+ uint8_t flags;
+#define SLCA_FLAG_NON_FUNCTIONAL 0x02 /* For redundant entries */
+#define SLCA_FLAG_IMBEDDED 0x01 /* not set => pluggable */
+ uint8_t old_nr_child; /* Legacy: Nr of children */
+ __be16 child_index; /* First child index */
+ __be16 child_rsrc_id; /* Resource ID of first child */
+ uint8_t loc_code_allen; /* Alloc len of loc code */
+ uint8_t loc_code_len; /* Loc code len */
+ uint8_t loc_code[LOC_CODE_SIZE]; /* NULL terminated (thus max 79 chr) */
+ __be16 first_dup_idx; /* First redundant resource index */
+ uint8_t nr_dups; /* Number of redundant entries */
+ uint8_t reserved;
+ __be16 nr_child; /* New version */
+ uint8_t install_indic; /* Installed indicator */
+#define SLCA_INSTALL_NO_HW_PDT 1 /* No HW presence detect */
+#define SLCA_INSTALL_INSTALLED 2
+#define SLCA_INSTALL_NOT_INSTALLED 3
+ uint8_t vpd_collected;
+#define SLCA_VPD_COLLECTED 2
+#define SLCA_VPD_NOT_COLLECTED 3
+} __packed;
+
+/*
+ * System VPD
+ */
+#define SYSVPD_HDIF_SIG "SYSVPD"
+
+/* Idata index 0 : FRU ID Data */
+#define SYSVPD_IDATA_FRU_ID 0
+
+/* Idata index 1 : Keyword VPD */
+#define SYSVPD_IDATA_KW_VPD 1
+
+/* Idata index 2 : Operational status */
+#define SYSVPD_IDATA_OP_STATUS 2
+
+/*
+ * FRU keyword VPD structure
+ */
+#define FRUVPD_HDIF_SIG "FRUVPD"
+
+/* Idata index 0 : FRU ID Data */
+#define FRUVPD_IDATA_FRU_ID 0
+
+/* Idata index 1 : Keyword VPD */
+#define FRUVPD_IDATA_KW_VPD 1
+
+/* Idata index 2 : Operational status */
+#define FRUVPD_IDATA_OP_STATUS 2
+
+
+/*
+ * SPPACA structure. The SPIRA contain an array of these, one
+ * per processor thread
+ */
+#define PACA_HDIF_SIG "SPPACA"
+
+/* Idata index 0 : FRU ID Data */
+#define SPPACA_IDATA_FRU_ID 0
+
+/* Idata index 1 : Keyword VPD */
+#define SPPACA_IDATA_KW_VPD 1
+
+/* Idata index 2 : CPU ID data area */
+#define SPPACA_IDATA_CPU_ID 2
+
+struct sppaca_cpu_id {
+ __be32 pir;
+ __be32 fru_id;
+ __be32 hardware_proc_id;
+#define CPU_ID_VERIFY_MASK 0xC0000000
+#define CPU_ID_VERIFY_SHIFT 30
+#define CPU_ID_VERIFY_USABLE_NO_FAILURES 0
+#define CPU_ID_VERIFY_USABLE_FAILURES 1
+#define CPU_ID_VERIFY_NOT_INSTALLED 2
+#define CPU_ID_VERIFY_UNUSABLE 3
+#define CPU_ID_SECONDARY_THREAD 0x20000000
+#define CPU_ID_PACA_RESERVED 0x10000000
+#define CPU_ID_NUM_SECONDARY_THREAD_MASK 0x00FF0000
+#define CPU_ID_NUM_SECONDARY_THREAD_SHIFT 16
+ __be32 verify_exists_flags;
+ __be32 chip_ec_level;
+ __be32 processor_chip_id;
+ __be32 logical_processor_id;
+ /* This is the resource number, too. */
+ __be32 process_interrupt_line;
+ __be32 reserved1;
+ __be32 hardware_module_id;
+ __be64 ibase;
+ __be32 deprecated1;
+ __be32 physical_thread_id;
+ __be32 deprecated2;
+ __be32 ccm_node_id;
+ /* This fields are not always present, check struct size */
+#define SPIRA_CPU_ID_MIN_SIZE 0x40
+ __be32 hw_card_id;
+ __be32 internal_drawer_node_id;
+ __be32 drawer_book_octant_blade_id;
+ __be32 memory_interleaving_scope;
+ __be32 lco_target;
+} __packed;
+
+/* Idata index 3 : Timebase data */
+#define SPPACA_IDATA_TIMEBASE 3
+
+struct sppaca_cpu_timebase {
+ __be32 cycle_time;
+ __be32 time_base;
+ __be32 actual_clock_speed;
+ __be32 memory_bus_frequency;
+} __packed;
+
+/* Idata index 4 : Cache size structure */
+#define SPPACA_IDATA_CACHE_SIZE 4
+
+struct sppaca_cpu_cache {
+ __be32 icache_size_kb;
+ __be32 icache_line_size;
+ __be32 l1_dcache_size_kb;
+ __be32 l1_dcache_line_size;
+ __be32 l2_dcache_size_kb;
+ __be32 l2_line_size;
+ __be32 l3_dcache_size_kb;
+ __be32 l3_line_size;
+ __be32 dcache_block_size;
+ __be32 icache_block_size;
+ __be32 dcache_assoc_sets;
+ __be32 icache_assoc_sets;
+ __be32 dtlb_entries;
+ __be32 dtlb_assoc_sets;
+ __be32 itlb_entries;
+ __be32 itlb_assoc_sets;
+ __be32 reservation_size;
+ __be32 l2_cache_assoc_sets;
+ __be32 l35_dcache_size_kb;
+ __be32 l35_cache_line_size;
+};
+
+/* Idata index 6 : CPU Attributes */
+#define SPPACA_IDATA_CPU_ATTR 6
+
+#define sppaca_cpu_attr sppcia_cpu_attr
+
+/*
+ * SPPCIA structure. The SPIRA contain an array of these, one
+ * per processor core
+ */
+#define SPPCIA_HDIF_SIG "SPPCIA"
+
+/* Idata index 0 : Core unique data */
+#define SPPCIA_IDATA_CORE_UNIQUE 0
+
+/* NOTE: This is the same layout as "struct sppaca_cpu_id",
+ * with essentially some fields removed and a reserved
+ * field added
+ */
+struct sppcia_core_unique {
+ __be32 reserved;
+ __be32 proc_fru_id;
+ __be32 hw_proc_id;
+ __be32 verif_exist_flags; /* Same as PACA */
+ __be32 chip_ec_level;
+ __be32 proc_chip_id;
+ __be32 reserved2;
+ __be32 reserved3;
+ __be32 reserved4;
+ __be32 hw_module_id;
+ __be64 reserved5;
+ __be32 reserved6;
+ __be32 reserved7;
+ __be32 reserved8;
+ __be32 ccm_node_id;
+ __be32 hw_card_id;
+ __be32 internal_drawer_node_id;
+ __be32 drawer_book_octant_blade_id;
+ __be32 memory_interleaving_scope;
+ __be32 lco_target;
+ __be32 reserved9;
+} __packed;
+
+/* Idata index 1 : CPU Time base structure */
+#define SPPCIA_IDATA_TIMEBASE 1
+
+#define sppcia_cpu_timebase sppaca_cpu_timebase
+
+/* Idata index 2 : CPU Cache Size Structure */
+#define SPPCIA_IDATA_CPU_CACHE 2
+
+#define sppcia_cpu_cache sppaca_cpu_cache
+
+/* Idata index 3 : Thread Array Data
+ *
+ * HDIF array of
+ */
+#define SPPCIA_IDATA_THREAD_ARRAY 3
+
+struct sppcia_cpu_thread {
+ __be32 proc_int_line;
+ __be32 phys_thread_id;
+ __be64 ibase;
+ __be32 pir;
+} __packed;
+
+/* Idata index 4 : CPU Attributes */
+#define SPPCIA_IDATA_CPU_ATTR 4
+
+struct sppcia_cpu_attr {
+#define CPU_ATTR_UNIFIED_PL1 0x80
+#define CPU_ATTR_SPLIT_TLB 0x40
+#define CPU_ATTR_TLBIA 0x20
+#define CPU_ATTR_PERF_MONITOR 0x10
+#define CPU_ATTR_EXTERN_CONT 0x02
+ __be32 attr;
+} __packed;
+
+/*
+ * Processor Chip Related Data. The SPIRA contain an array of these, one
+ * per chip
+ */
+#define SPPCRD_HDIF_SIG "SPPCRD"
+
+/* Idata index 0 : Chip info */
+#define SPPCRD_IDATA_CHIP_INFO 0
+
+struct sppcrd_chip_info {
+ __be32 proc_chip_id;
+ __be32 verif_exist_flags;
+#define CHIP_VERIFY_MASK 0xC0000000
+#define CHIP_VERIFY_SHIFT 30
+#define CHIP_VERIFY_USABLE_NO_FAILURES 0
+#define CHIP_VERIFY_USABLE_FAILURES 1
+#define CHIP_VERIFY_NOT_INSTALLED 2
+#define CHIP_VERIFY_UNUSABLE 3
+ __be32 nx_state;
+ __be32 pore_state;
+ __be32 xscom_id;
+ /* Version 0xA */
+ __be32 reserved;
+ __be32 dbob_id;
+ __be32 occ_state;
+} __packed;
+
+/* Idata index 1 : Chip TOD */
+#define SPPCRD_IDATA_CHIP_TOD 1
+
+struct sppcrd_chip_tod {
+ __be32 flags;
+ /* CHIPTOD_ID_... values */
+ __be32 ctrl_reg_internal;
+ __be32 tod_ctrl_reg;
+} __packed;
+
+/* Idata index 2 : FRU ID */
+#define SPPCRD_IDATA_FRU_ID 2
+
+/* Idata index 3 : ASCII Keyword data */
+#define SPPCRD_IDATA_KW_VPD 3
+
+/* Idata index 4 : Module VPD */
+#define SPPCRD_IDATA_MODULE_VPD 4
+
+
+/*
+ * Host Services Data.
+ */
+#define HSERV_HDIF_SIG "HOSTSR"
+
+/* Idata index 0 : System attribute data */
+#define HSERV_IDATA_SYS_ATTR 0
+
+static inline const char *cpu_state(u32 flags)
+{
+ switch ((flags & CPU_ID_VERIFY_MASK) >> CPU_ID_VERIFY_SHIFT) {
+ case CPU_ID_VERIFY_USABLE_NO_FAILURES:
+ return "OK";
+ case CPU_ID_VERIFY_USABLE_FAILURES:
+ return "FAILURES";
+ case CPU_ID_VERIFY_NOT_INSTALLED:
+ return "NOT-INSTALLED";
+ case CPU_ID_VERIFY_UNUSABLE:
+ return "UNUSABLE";
+ }
+ return "**UNKNOWN**";
+}
+#endif /* __SPIRA_H */
diff --git a/hdata/test/Makefile.check b/hdata/test/Makefile.check
new file mode 100644
index 0000000..e03a021
--- /dev/null
+++ b/hdata/test/Makefile.check
@@ -0,0 +1,18 @@
+# -*-Makefile-*-
+
+check: hdata-check
+
+# Add some test ntuples for open source version...
+hdata-check: hdata/test/hdata_to_dt
+# $(VALGRIND) hdata/test/hdata_to_dt -q hdata/test/spira.bin hdata/test/ntuples.bin
+
+hdata/test/stubs.o: hdata/test/stubs.c
+ $(HOSTCC) $(HOSTCFLAGS) -g -c -o $@ $<
+
+hdata/test/hdata_to_dt: hdata/test/hdata_to_dt.c hdata/test/stubs.o
+ $(HOSTCC) $(HOSTCFLAGS) -O0 -g -I hdata -I include -I . -I libfdt -o $@ $< hdata/test/stubs.o
+
+clean: hdata-test-clean
+
+hdata-test-clean:
+ $(RM) hdata/test/*.o hdata/test/hdata_to_dt
diff --git a/hdata/test/hdata_to_dt.c b/hdata/test/hdata_to_dt.c
new file mode 100644
index 0000000..4215740
--- /dev/null
+++ b/hdata/test/hdata_to_dt.c
@@ -0,0 +1,215 @@
+/* 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.
+ */
+/* Given a hdata dump, output the device tree. */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include <interrupts.h>
+
+/* Our actual map. */
+static void *spira_heap;
+static size_t spira_heap_size;
+static uint64_t base_addr;
+
+/* Override ntuple_addr. */
+#define ntuple_addr ntuple_addr
+struct spira_ntuple;
+static void *ntuple_addr(const struct spira_ntuple *n);
+
+/* Stuff which core expects. */
+#define __this_cpu ((struct cpu_thread *)NULL)
+#define zalloc(expr) calloc(1, (expr))
+
+/* Don't include processor-specific stuff. */
+#define __PROCESSOR_H
+#define PVR_TYPE(_pvr) _pvr
+
+/* PVR definitions */
+#define PVR_TYPE_P7 0x003f
+#define PVR_TYPE_P7P 0x004a
+#define PVR_TYPE_P8E 0x004b
+#define PVR_TYPE_P8 0x004d
+
+#define SPR_PVR 0x11f /* RO: Processor version register */
+
+#define __CPU_H
+struct cpu_thread {
+ uint32_t pir;
+};
+
+struct cpu_thread __boot_cpu, *boot_cpu = &__boot_cpu;
+static unsigned long fake_pvr_type = PVR_TYPE_P7;
+
+static inline unsigned long mfspr(unsigned int spr)
+{
+ assert(spr == SPR_PVR);
+ return fake_pvr_type;
+}
+
+struct dt_node *add_ics_node(void)
+{
+ return NULL;
+}
+
+#include <config.h>
+#include <bitutils.h>
+
+/* Your pointers won't be correct, that's OK. */
+#define spira_check_ptr(ptr, file, line) ((ptr) != NULL)
+
+#include "../cpu-common.c"
+#include "../fsp.c"
+#include "../hdif.c"
+#include "../iohub.c"
+#include "../memory.c"
+#include "../paca.c"
+#include "../pcia.c"
+#include "../spira.c"
+#include "../vpd.c"
+#include "../vpd-common.c"
+#include "../slca.c"
+#include "../hostservices.c"
+#include "../../core/vpd.c"
+#include "../../core/device.c"
+#include "../../core/chip.c"
+
+#include <err.h>
+
+char __rodata_start[1], __rodata_end[1];
+
+enum proc_gen proc_gen = proc_gen_p7;
+
+static void *ntuple_addr(const struct spira_ntuple *n)
+{
+ uint64_t addr = be64_to_cpu(n->addr);
+ if (n->addr == 0)
+ return NULL;
+ assert(addr >= base_addr);
+ assert(addr < base_addr + spira_heap_size);
+ return spira_heap + ((unsigned long)addr - base_addr);
+}
+
+static void indent_num(unsigned indent)
+{
+ unsigned int i;
+
+ for (i = 0; i < indent; i++)
+ putc(' ', stdout);
+}
+
+static void dump_val(const void *prop, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ printf("%02x ", ((unsigned char *)prop)[i]);
+}
+
+/* Make sure valgrind knows these are undefined bytes. */
+static void undefined_bytes(void *p, size_t len)
+{
+ void *undef = malloc(len);
+ memcpy(p, undef, len);
+ free(undef);
+}
+
+static void dump_dt(const struct dt_node *root, unsigned indent)
+{
+ const struct dt_node *i;
+ const struct dt_property *p;
+
+ list_for_each(&root->properties, p, list) {
+ indent_num(indent);
+ printf("prop: %s size: %zu val: ", p->name, p->len);
+ dump_val(p->prop, p->len);
+ printf("\n");
+ }
+
+ list_for_each(&root->children, i, list)
+ dump_dt(i, indent + 2);
+}
+
+int main(int argc, char *argv[])
+{
+ int fd, r;
+ bool verbose = false, quiet = false;
+
+ while (argv[1]) {
+ if (strcmp(argv[1], "-v") == 0) {
+ verbose = true;
+ argv++;
+ argc--;
+ } else if (strcmp(argv[1], "-q") == 0) {
+ quiet = true;
+ argv++;
+ argc--;
+ } else
+ break;
+ }
+
+ if (argc != 3)
+ errx(1, "Usage: hdata [-v|-q] <spira-dump> <heap-dump>");
+
+ /* Copy in spira dump (assumes little has changed!). */
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0)
+ err(1, "opening %s", argv[1]);
+ r = read(fd, &spira, sizeof(spira));
+ if (r < sizeof(spira.hdr))
+ err(1, "reading %s gave %i", argv[1], r);
+ if (verbose)
+ printf("verbose: read spira %u bytes\n", r);
+ close(fd);
+
+ undefined_bytes((void *)&spira + r, sizeof(spira) - r);
+
+ base_addr = be64_to_cpu(spira.ntuples.heap.addr);
+ if (!base_addr)
+ errx(1, "Invalid base addr");
+ if (verbose)
+ printf("verbose: map.base_addr = %llx\n", (long long)base_addr);
+
+ fd = open(argv[2], O_RDONLY);
+ if (fd < 0)
+ err(1, "opening %s", argv[2]);
+ spira_heap_size = lseek(fd, 0, SEEK_END);
+ spira_heap = mmap(NULL, spira_heap_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (spira_heap == MAP_FAILED)
+ err(1, "mmaping %s", argv[3]);
+ if (verbose)
+ printf("verbose: mapped %zu at %p\n",
+ spira_heap_size, spira_heap);
+ close(fd);
+
+ if (quiet) {
+ fclose(stdout);
+ fclose(stderr);
+ }
+
+ parse_hdat(false, 0);
+
+ if (!quiet)
+ dump_dt(dt_root, 0);
+
+ dt_free(dt_root);
+ return 0;
+}
diff --git a/hdata/test/stubs.c b/hdata/test/stubs.c
new file mode 100644
index 0000000..8e1bd1b
--- /dev/null
+++ b/hdata/test/stubs.c
@@ -0,0 +1,47 @@
+/* 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.
+ */
+/* Add any stub functions required for linking here. */
+#include <stdlib.h>
+
+static void stub_function(void)
+{
+ abort();
+}
+
+#define STUB(fnname) \
+ void fnname(void) __attribute__((weak, alias ("stub_function")))
+
+STUB(fdt_begin_node);
+STUB(fdt_property);
+STUB(fdt_end_node);
+STUB(fdt_create);
+STUB(fdt_add_reservemap_entry);
+STUB(fdt_finish_reservemap);
+STUB(fdt_strerror);
+STUB(fdt_check_header);
+STUB(_fdt_check_node_offset);
+STUB(fdt_next_tag);
+STUB(fdt_string);
+STUB(fdt_get_name);
+STUB(dt_first);
+STUB(dt_next);
+STUB(dt_has_node_property);
+STUB(dt_get_address);
+STUB(op_display);
+STUB(fsp_fetch_data);
+STUB(get_ics_phandle);
+STUB(get_psi_interrupt);
+STUB(fsp_adjust_lid_side);
diff --git a/hdata/vpd-common.c b/hdata/vpd-common.c
new file mode 100644
index 0000000..eecda7e
--- /dev/null
+++ b/hdata/vpd-common.c
@@ -0,0 +1,38 @@
+/* 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 <vpd.h>
+#include <string.h>
+#include <device.h>
+
+static const struct machine_info machine_table[] = {
+ {"8247-21L", "IBM Power System S812L"},
+ {"8247-22L", "IBM Power System S822L"},
+ {"8247-24L", "IBM Power System S824L"},
+ {"8286-41A", "IBM Power System S814"},
+ {"8286-22A", "IBM Power System S822"},
+ {"8286-42A", "IBM Power System S824"},
+};
+
+const struct machine_info *machine_info_lookup(char *mtm)
+{
+ int i;
+ for(i = 0; i < ARRAY_SIZE(machine_table); i++)
+ if (!strcmp(machine_table[i].mtm, mtm))
+ return &machine_table[i];
+ return NULL;
+}
diff --git a/hdata/vpd.c b/hdata/vpd.c
new file mode 100644
index 0000000..e568a06
--- /dev/null
+++ b/hdata/vpd.c
@@ -0,0 +1,851 @@
+/* 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 <vpd.h>
+#include <string.h>
+#include "spira.h"
+#include "hdata.h"
+#include <device.h>
+#include "hdata.h"
+
+struct card_info {
+ const char *ccin; /* Customer card identification number */
+ const char *description;
+};
+
+static const struct card_info card_table[] = {
+ {"2B06", "System planar 2S4U"},
+ {"2B07", "System planar 1S4U"},
+ {"2B2E", "System planar 2S2U"},
+ {"2B2F", "System planar 1S2U"},
+ {"2CD4", "System planar 2S4U"},
+ {"2CD5", "System planar 1S4U"},
+ {"2CD6", "System planar 2S2U"},
+ {"2CD7", "System planar 1S2U"},
+ {"2CD7", "System planar 1S2U"},
+ {"2B09", "Base JBOD, RAID and Backplane HD"},
+ {"57D7", "Split JBOD, RAID Card"},
+ {"2B0B", "Native I/O Card"},
+
+ /* Anchor cards */
+ {"52FE", "System Anchor Card - IBM Power 824"},
+ {"52F2", "System Anchor Card - IBM Power 814"},
+ {"52F5", "System Anchor Card - IBM Power 822"},
+ {"561A", "System Anchor Card - IBM Power 824L"},
+ {"524D", "System Anchor Card - IBM Power 822L"},
+ {"560F", "System Anchor Card - IBM Power 812L"},
+ {"561C", "System Anchor Card - DS8870"},
+
+ /* Memory DIMMs */
+ {"31E0", "16GB CDIMM"},
+ {"31E8", "16GB CDIMM"},
+ {"31E1", "32GB CDIMM"},
+ {"31E9", "32GB CDIMM"},
+ {"31E2", "64GB CDIMM"},
+ {"31EA", "64GB CDIMM"},
+
+ /* Power supplies */
+ {"2B1D", "Power Supply 900W AC"},
+ {"2B1E", "Power Supply 1400W AC"},
+ {"2B75", "Power Supply 1400W HVDC"},
+
+ /* Fans */
+ {"2B1F", "Fan 4U (A1, A2, A3, A4)"},
+ {"2B29", "Fan 2U (A1, A2, A3, A4, A5, A6)"},
+
+ /* Other cards */
+};
+
+static const struct card_info *card_info_lookup(char *ccin)
+{
+ int i;
+ for(i = 0; i < ARRAY_SIZE(card_table); i++)
+ if (!strcmp(card_table[i].ccin, ccin))
+ return &card_table[i];
+ return NULL;
+}
+
+static void vpd_vini_parse(struct dt_node *node,
+ const void *fruvpd, unsigned int fruvpd_sz)
+{
+ const void *kw;
+ char *str;
+ uint8_t kwsz;
+ const struct card_info *cinfo;
+
+ /* FRU Stocking Part Number */
+ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "FN", &kwsz);
+ if (kw) {
+ str = zalloc(kwsz + 1);
+ if (!str)
+ goto no_memory;
+ memcpy(str, kw, kwsz);
+ dt_add_property_string(node, "fru-number", str);
+ free(str);
+ }
+
+ /* Serial Number */
+ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "SN", &kwsz);
+ if (kw) {
+ str = zalloc(kwsz + 1);
+ if (!str)
+ goto no_memory;
+ memcpy(str, kw, kwsz);
+ dt_add_property_string(node, "serial-number", str);
+ free(str);
+ }
+
+ /* Part Number */
+ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "PN", &kwsz);
+ if (kw) {
+ str = zalloc(kwsz + 1);
+ if (!str)
+ goto no_memory;
+ memcpy(str, kw, kwsz);
+ dt_add_property_string(node, "part-number", str);
+ free(str);
+ }
+
+ /* Customer Card Identification Number (CCIN) */
+ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CC", &kwsz);
+ if (kw) {
+ str = zalloc(kwsz + 1);
+ if (!str)
+ goto no_memory;
+ memcpy(str, kw, kwsz);
+ dt_add_property_string(node, "ccin", str);
+ cinfo = card_info_lookup(str);
+ if (cinfo)
+ dt_add_property_string(node,
+ "description", cinfo->description);
+ free(str);
+ }
+ return;
+no_memory:
+ prerror("VPD: memory allocation failure in VINI parsing\n");
+}
+
+static const char *vpd_map_name(const char *vpd_name)
+{
+ /* vpd_name is a 2 char array */
+ switch (vpd_name[0]) {
+ case 'A':
+ switch (vpd_name[1]) {
+ case 'A':
+ return "ac-power-supply";
+ case 'M':
+ return "air-mover";
+ case 'V':
+ return "anchor-card";
+ }
+ break;
+ case 'B':
+ switch (vpd_name[1]) {
+ case 'A':
+ return "bus-adapter-card";
+ case 'C':
+ return "battery-charger";
+ case 'D':
+ return "bus-daughter-card";
+ case 'E':
+ return "bus-expansion-card";
+ case 'P':
+ return "backplane";
+ case 'R':
+ return "backplane-riser";
+ case 'X':
+ return "backplane-extender";
+ }
+ break;
+ case 'C':
+ switch (vpd_name[1]) {
+ case 'A':
+ return "calgary-bridge";
+ case 'B':
+ return "infiniband-connector";
+ case 'C':
+ return "clock-card";
+ case 'D':
+ return "card-connector";
+ case 'E':
+ return "ethernet-connector";
+ case 'L':
+ return "calgary-phb";
+ case 'I':
+ return "capacity-card";
+ case 'O':
+ return "sma-connector";
+ case 'P':
+ return "processor-capacity-card";
+ case 'R':
+ return "rio-connector";
+ case 'S':
+ return "serial-connector";
+ case 'U':
+ return "usb-connector";
+ }
+ break;
+ case 'D':
+ switch (vpd_name[1]) {
+ case 'B':
+ return "dasd-backplane";
+ case 'C':
+ return "drawer-card";
+ case 'E':
+ return "drawer-extension";
+ case 'I':
+ return "drawer-interposer";
+ case 'L':
+ return "p7ih-dlink-connector";
+ case 'T':
+ return "legacy-pci-card";
+ case 'V':
+ return "media-drawer-led";
+ }
+ break;
+ case 'E':
+ switch (vpd_name[1]) {
+ case 'I':
+ return "enclosure-led";
+ case 'F':
+ return "enclosure-fault-led";
+ case 'S':
+ return "embedded-sas";
+ case 'T':
+ return "ethernet-riser";
+ case 'V':
+ return "enclosure";
+ }
+ break;
+ case 'F':
+ switch (vpd_name[1]) {
+ case 'M':
+ return "frame";
+ }
+ break;
+ case 'H':
+ switch (vpd_name[1]) {
+ case 'B':
+ return "host-rio-pci-card";
+ case 'D':
+ return "high-speed-card";
+ case 'M':
+ return "hmc-connector";
+ }
+ break;
+ case 'I':
+ switch (vpd_name[1]) {
+ case 'B':
+ return "io-backplane";
+ case 'C':
+ return "io-card";
+ case 'D':
+ return "ide-connector";
+ case 'I':
+ return "io-drawer-led";
+ case 'P':
+ return "interplane-card";
+ case 'S':
+ return "smp-vbus-cable";
+ case 'T':
+ return "enclosure-cable";
+ case 'V':
+ return "io-enclosure";
+ }
+ break;
+ case 'K':
+ switch (vpd_name[1]) {
+ case 'V':
+ return "keyboard-led";
+ }
+ break;
+ case 'L':
+ switch (vpd_name[1]) {
+ case '2':
+ return "l2-cache-module";
+ case '3':
+ return "l3-cache-module";
+ case 'C':
+ return "squadrons-light-connector";
+ case 'R':
+ return "p7ih-connector";
+ case 'O':
+ return "system-locate-led";
+ case 'T':
+ return "squadrons-light-strip";
+ }
+ break;
+ case 'M':
+ switch (vpd_name[1]) {
+ case 'B':
+ return "media-backplane";
+ case 'E':
+ return "map-extension";
+ case 'M':
+ return "mip-meter";
+ case 'S':
+ return "ms-dimm";
+ }
+ break;
+ case 'N':
+ switch (vpd_name[1]) {
+ case 'B':
+ return "nvram-battery";
+ case 'C':
+ return "sp-node-controller";
+ case 'D':
+ return "numa-dimm";
+ }
+ break;
+ case 'O':
+ switch (vpd_name[1]) {
+ case 'D':
+ return "cuod-card";
+ case 'P':
+ return "op-panel";
+ case 'S':
+ return "oscillator";
+ }
+ break;
+ case 'P':
+ switch (vpd_name[1]) {
+ case '2':
+ return "ioc";
+ case '5':
+ return "ioc-bridge";
+ case 'B':
+ return "io-drawer-backplane";
+ case 'C':
+ return "power-capacitor";
+ case 'D':
+ return "processor-card";
+ case 'F':
+ return "processor";
+ case 'I':
+ return "ioc-phb";
+ case 'O':
+ return "spcn";
+ case 'N':
+ return "spcn-connector";
+ case 'R':
+ return "pci-riser-card";
+ case 'S':
+ return "power-supply";
+ case 'T':
+ return "pass-through-card";
+ case 'X':
+ return "psc-sync-card";
+ case 'W':
+ return "power-connector";
+ }
+ break;
+ case 'R':
+ switch (vpd_name[1]) {
+ case 'G':
+ return "regulator";
+ case 'I':
+ return "riser";
+ case 'K':
+ return "rack-indicator";
+ case 'W':
+ return "riscwatch-connector";
+ }
+ break;
+ case 'S':
+ switch (vpd_name[1]) {
+ case 'A':
+ return "sys-attn-led";
+ case 'B':
+ return "backup-sysvpd";
+ case 'C':
+ return "scsi-connector";
+ case 'D':
+ return "sas-connector";
+ case 'I':
+ return "scsi-ide-converter";
+ case 'L':
+ return "phb-slot";
+ case 'P':
+ return "service-processor";
+ case 'R':
+ return "service-card";
+ case 'S':
+ return "soft-switch";
+ case 'V':
+ return "system-vpd";
+ case 'Y':
+ return "legacy-sysvpd";
+ }
+ break;
+ case 'T':
+ switch (vpd_name[1]) {
+ case 'D':
+ return "tod-clock";
+ case 'I':
+ return "torrent-pcie-phb";
+ case 'L':
+ return "torrent-riser";
+ case 'M':
+ return "thermal-sensor";
+ case 'P':
+ return "tpmd-adapter";
+ case 'R':
+ return "torrent-bridge";
+ }
+ break;
+ case 'V':
+ switch (vpd_name[1]) {
+ case 'V':
+ return "root-node-vpd";
+ }
+ break;
+ case 'W':
+ switch (vpd_name[1]) {
+ case 'D':
+ return "water_device";
+ }
+ break;
+ }
+ return "Unknown";
+}
+
+static bool valid_child_entry(const struct slca_entry *entry)
+{
+ if (!entry)
+ return false;
+
+ /*
+ * Skip entries with independent ntuple FRUVPD/MSVPD, etc.,
+ * representations, since they have a unique PN, FN, SN, et al.
+ * We add details for those devices via the ntuple walk.
+ */
+ switch (entry->fru_id[0]) {
+ case 'A':
+ switch (entry->fru_id[1]) {
+ case 'V': /* AV */
+ return false;
+ }
+ break;
+ case 'B':
+ switch (entry->fru_id[1]) {
+ case 'P': /* BP */
+ case 'X': /* BX */
+ return false;
+ }
+ break;
+ case 'C':
+ switch (entry->fru_id[1]) {
+ case 'C': /* CC */
+ return false;
+ }
+ break;
+ case 'D':
+ switch (entry->fru_id[1]) {
+ case 'B': /* DB */
+ return false;
+ }
+ break;
+ case 'E':
+ switch (entry->fru_id[1]) {
+ case 'V': /* EV */
+ return false;
+ }
+ break;
+ case 'M':
+ switch (entry->fru_id[1]) {
+ case 'S': /* MS */
+ return false;
+ }
+ break;
+ case 'O':
+ switch (entry->fru_id[1]) {
+ case 'P': /* OP */
+ return false;
+ }
+ break;
+ case 'R':
+ switch (entry->fru_id[1]) {
+ case 'I': /* RI */
+ return false;
+ }
+ break;
+ case 'P':
+ switch (entry->fru_id[1]) {
+ case '2': /* P2 */
+ case '5': /* P5 */
+ case 'F': /* PF */
+ return false;
+ }
+ break;
+ case 'S':
+ switch (entry->fru_id[1]) {
+ case 'P': /* SP */
+ return false;
+ }
+ break;
+ case 'T':
+ switch (entry->fru_id[1]) {
+ case 'P': /* TP */
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if ((entry->install_indic == SLCA_INSTALL_INSTALLED) &&
+ (entry->vpd_collected == SLCA_VPD_COLLECTED))
+ return true;
+
+ return false;
+}
+
+static void vpd_add_children(struct dt_node *parent, uint16_t slca_index)
+{
+ const struct slca_entry *s_entry, *child;
+ uint16_t current_child_index, max_index;
+
+ s_entry = slca_get_entry(slca_index);
+ if (!s_entry || (s_entry->nr_child == 0))
+ return;
+
+ /*
+ * This slca_entry has children. Parse the children array
+ * and add nodes for valid entries.
+ *
+ * A child entry is valid if all of the following criteria is met
+ * a. SLCA_INSTALL_INSTALLED is set in s_entry->install_indic
+ * b. SLCA_VPD_COLLECTED is set in s_entry->vpd_collected
+ * c. The SLCA is not a duplicate entry.
+ */
+
+ /* current_index tracks where we are right now in the array */
+ current_child_index = be16_to_cpu(s_entry->child_index);
+
+ /* max_index tracks how far down the array we must traverse */
+ max_index = be16_to_cpu(s_entry->child_index)
+ + be16_to_cpu(s_entry->nr_child);
+
+ while (current_child_index < max_index) {
+ child = slca_get_entry(current_child_index);
+ if (!child)
+ return;
+
+ if (valid_child_entry(child)) {
+ const char *name;
+ uint64_t addr;
+ struct dt_node *node;
+
+ /* create new node, add location code */
+ name = vpd_map_name(child->fru_id);
+ addr = (uint64_t)be16_to_cpu(child->rsrc_id);
+ node = dt_new_addr(parent, name, addr);
+ if (!node) {
+ prerror("VPD: Creating node at %s@%llx failed\n",
+ name, addr);
+ return;
+ }
+ slca_vpd_add_loc_code(node, be16_to_cpu(child->my_index));
+
+ /* Add child FRU type */
+ dt_add_property(node, "fru-type", child->fru_id, 2);
+
+ /* recursively add children */
+ vpd_add_children(node, be16_to_cpu(child->my_index));
+ }
+
+ /* Skip dups -- currently we presume dups are contiguous */
+ if (child->nr_dups > 0)
+ current_child_index += child->nr_dups;
+ current_child_index++;
+ }
+ return;
+}
+
+struct dt_node *dt_add_vpd_node(const struct HDIF_common_hdr *hdr,
+ int indx_fru, int indx_vpd)
+{
+ const void *fruvpd;
+ unsigned int fruvpd_sz;
+ unsigned int fru_id_sz;
+ uint64_t addr;
+ struct dt_node *dt_vpd;
+ struct dt_node *node;
+ const struct spira_fru_id *fru_id;
+ const struct slca_entry *s_entry;
+ const char *vpd_name;
+ const char *name;
+ int len;
+ char *lname;
+
+ fru_id = HDIF_get_idata(hdr, indx_fru, &fru_id_sz);
+ if (!fru_id)
+ return NULL;
+
+ s_entry = slca_get_entry(be16_to_cpu(fru_id->slca_index));
+ if (valid_child_entry(s_entry)) /* Don't populate child VPD here */
+ return NULL;
+
+ fruvpd = HDIF_get_idata(hdr, indx_vpd, &fruvpd_sz);
+ if (!CHECK_SPPTR(fruvpd))
+ return NULL;
+
+ vpd_name = slca_get_vpd_name(be16_to_cpu(fru_id->slca_index));
+ if (!vpd_name) {
+ prerror("VPD: VPD name at index %d couldn't be found\n",
+ fru_id->slca_index);
+ return NULL;
+ }
+
+ dt_vpd = dt_find_by_path(dt_root, "/vpd");
+ if (!dt_vpd)
+ return NULL;
+
+ /* Get node name */
+ name = vpd_map_name(vpd_name);
+ addr = (uint64_t)be16_to_cpu(fru_id->rsrc_id);
+ len = strlen(name) + STR_MAX_CHARS(addr) + 2;
+ lname = zalloc(len);
+ if (!lname) {
+ prerror("VPD: Failed to allocate memory\n");
+ return NULL;
+ }
+ snprintf(lname, len, "%s@%llx", name, (long long)addr);
+
+ /*
+ * FRU can be a child of some other FRU. Make sure
+ * we have not added this node already.
+ */
+ node = dt_find_by_path(dt_vpd, lname);
+ if (node) {
+ free(lname);
+ return NULL;
+ }
+
+ node = dt_new(dt_vpd, lname);
+ if (!node) {
+ free(lname);
+ return NULL;
+ }
+
+ /* Parse VPD fields */
+ dt_add_property(node, "ibm,vpd", fruvpd, fruvpd_sz);
+ vpd_vini_parse(node, fruvpd, fruvpd_sz);
+
+ /* Location code */
+ slca_vpd_add_loc_code(node, be16_to_cpu(fru_id->slca_index));
+ /* Add FRU label */
+ dt_add_property(node, "fru-type", vpd_name, 2);
+ vpd_add_children(node, be16_to_cpu(fru_id->slca_index));
+
+ free(lname);
+ return node;
+}
+
+static void sysvpd_parse(void)
+{
+ const char *model;
+ const char *system_id;
+ const char *brand;
+ char *str;
+ uint8_t sz;
+ const void *sysvpd;
+ unsigned int sysvpd_sz;
+ unsigned int fru_id_sz;
+ struct dt_node *dt_vpd;
+ const struct spira_fru_id *fru_id;
+ struct HDIF_common_hdr *sysvpd_hdr;
+ const struct machine_info *mi;
+
+ sysvpd_hdr = get_hdif(&spira.ntuples.system_vpd, SYSVPD_HDIF_SIG);
+ if (!sysvpd_hdr)
+ goto no_sysvpd;
+
+ fru_id = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_FRU_ID, &fru_id_sz);
+ if (!fru_id)
+ goto no_sysvpd;;
+
+ sysvpd = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_KW_VPD, &sysvpd_sz);
+ if (!CHECK_SPPTR(sysvpd))
+ goto no_sysvpd;
+
+ /* Add system VPD */
+ dt_vpd = dt_find_by_path(dt_root, "/vpd");
+ if (dt_vpd) {
+ dt_add_property(dt_vpd, "ibm,vpd", sysvpd, sysvpd_sz);
+ slca_vpd_add_loc_code(dt_vpd, be16_to_cpu(fru_id->slca_index));
+ }
+
+ model = vpd_find(sysvpd, sysvpd_sz, "VSYS", "TM", &sz);
+ if (!model)
+ goto no_sysvpd;
+ str = zalloc(sz + 1);
+ if (!str)
+ goto no_sysvpd;
+ memcpy(str, model, sz);
+ dt_add_property_string(dt_root, "model", str);
+ mi = machine_info_lookup(str);
+ if (mi)
+ dt_add_property_string(dt_root, "model-name", mi->name);
+ free(str);
+ dt_add_property_string(dt_root, "vendor", "IBM");
+
+ system_id = vpd_find(sysvpd, sysvpd_sz, "VSYS", "SE", &sz);
+ if (!system_id)
+ goto no_sysid;
+ str = zalloc(sz + 1);
+ if (!str)
+ goto no_sysid;
+ memcpy(str, system_id, sz);
+ dt_add_property_string(dt_root, "system-id", str);
+ free(str);
+
+ brand = vpd_find(sysvpd, sysvpd_sz, "VSYS", "BR", &sz);
+ if (!brand)
+ goto no_brand;
+ str = zalloc(sz + 1);
+ if (!str)
+ goto no_brand;
+ memcpy(str, brand, sz);
+ dt_add_property_string(dt_root, "system-brand", str);
+ free(str);
+
+ return;
+
+no_brand:
+ dt_add_property_string(dt_root, "system-brand", "Unknown");
+ return;
+
+no_sysid:
+ dt_add_property_string(dt_root, "system-id", "Unknown");
+ return;
+
+ no_sysvpd:
+ dt_add_property_string(dt_root, "model", "Unknown");
+}
+
+static void iokid_vpd_parse(const struct HDIF_common_hdr *iohub_hdr)
+{
+ const struct HDIF_child_ptr *iokids;
+ const struct HDIF_common_hdr *iokid;
+ unsigned int i;
+
+ iokids = HDIF_child_arr(iohub_hdr, CECHUB_CHILD_IO_KIDS);
+ if (!CHECK_SPPTR(iokids)) {
+ prerror("VPD: No IOKID child array\n");
+ return;
+ }
+
+ for (i = 0; i < be32_to_cpu(iokids->count); i++) {
+ iokid = HDIF_child(iohub_hdr, iokids, i,
+ IOKID_FRU_HDIF_SIG);
+ /* IO KID VPD structure layout is similar to FRUVPD */
+ if (iokid)
+ dt_add_vpd_node(iokid,
+ FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
+ }
+}
+
+static void iohub_vpd_parse(void)
+{
+ const struct HDIF_common_hdr *iohub_hdr;
+ const struct cechub_hub_fru_id *fru_id_data;
+ unsigned int i, vpd_sz, fru_id_sz;
+
+ if (!get_hdif(&spira.ntuples.cec_iohub_fru, CECHUB_FRU_HDIF_SIG)) {
+ prerror("VPD: Could not find IO HUB FRU data\n");
+ return;
+ }
+
+ for_each_ntuple_idx(&spira.ntuples.cec_iohub_fru, iohub_hdr,
+ i, CECHUB_FRU_HDIF_SIG) {
+
+ fru_id_data = HDIF_get_idata(iohub_hdr,
+ CECHUB_FRU_ID_DATA_AREA,
+ &fru_id_sz);
+
+ /* P8, IO HUB is on processor card and we have a
+ * daughter card array
+ */
+ if (fru_id_data &&
+ be32_to_cpu(fru_id_data->card_type) == CECHUB_FRU_TYPE_CPU_CARD) {
+ iokid_vpd_parse(iohub_hdr);
+ continue;
+ }
+
+ /* On P7, the keyword VPD will not be NULL */
+ if (HDIF_get_idata(iohub_hdr,
+ CECHUB_ASCII_KEYWORD_VPD, &vpd_sz))
+ dt_add_vpd_node(iohub_hdr, CECHUB_FRU_ID_DATA,
+ CECHUB_ASCII_KEYWORD_VPD);
+ }
+}
+
+static void _vpd_parse(struct spira_ntuple tuple)
+{
+ const struct HDIF_common_hdr *fruvpd_hdr;
+ unsigned int i;
+
+ if (!get_hdif(&tuple, FRUVPD_HDIF_SIG))
+ return;
+
+ for_each_ntuple_idx(&tuple, fruvpd_hdr, i, FRUVPD_HDIF_SIG)
+ dt_add_vpd_node(fruvpd_hdr,
+ FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
+}
+
+void vpd_parse(void)
+{
+ const struct HDIF_common_hdr *fruvpd_hdr;
+
+ /* Enclosure */
+ _vpd_parse(spira.ntuples.nt_enclosure_vpd);
+
+ /* Backplane */
+ _vpd_parse(spira.ntuples.backplane_vpd);
+
+ /* System VPD uses the VSYS record, so its special */
+ sysvpd_parse();
+
+ /* clock card -- does this use the FRUVPD sig? */
+ _vpd_parse(spira.ntuples.clock_vpd);
+
+ /* Anchor card */
+ _vpd_parse(spira.ntuples.anchor_vpd);
+
+ /* Op panel -- does this use the FRUVPD sig? */
+ _vpd_parse(spira.ntuples.op_panel_vpd);
+
+ /* External cache FRU vpd -- does this use the FRUVPD sig? */
+ _vpd_parse(spira.ntuples.ext_cache_fru_vpd);
+
+ /* Misc CEC FRU */
+ _vpd_parse(spira.ntuples.misc_cec_fru_vpd);
+
+ /* CEC IO HUB FRU */
+ iohub_vpd_parse();
+
+ /*
+ * SP subsystem -- while the rest of the SPINFO structure is
+ * different, the FRU ID data and pointer pair to keyword VPD
+ * are the same offset as a FRUVPD entry. So reuse it
+ */
+ fruvpd_hdr = get_hdif(&spira.ntuples.sp_subsys, SPSS_HDIF_SIG);
+ if (fruvpd_hdr)
+ dt_add_vpd_node(fruvpd_hdr,
+ FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
+}