aboutsummaryrefslogtreecommitdiff
path: root/platforms
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 /platforms
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 'platforms')
-rw-r--r--platforms/Makefile.inc10
-rw-r--r--platforms/bmc/Makefile.inc6
-rw-r--r--platforms/bmc/bmc.h24
-rw-r--r--platforms/bmc/palmetto.c186
-rw-r--r--platforms/bmc/pnor.c87
-rw-r--r--platforms/ibm-fsp/Makefile.inc6
-rw-r--r--platforms/ibm-fsp/apollo.c62
-rw-r--r--platforms/ibm-fsp/common.c196
-rw-r--r--platforms/ibm-fsp/firenze.c247
-rw-r--r--platforms/ibm-fsp/ibm-fsp.h26
-rw-r--r--platforms/ibm-fsp/lxvpd.c298
-rw-r--r--platforms/ibm-fsp/lxvpd.h111
-rw-r--r--platforms/rhesus/Makefile.inc6
-rw-r--r--platforms/rhesus/rhesus.c313
14 files changed, 1578 insertions, 0 deletions
diff --git a/platforms/Makefile.inc b/platforms/Makefile.inc
new file mode 100644
index 0000000..5061b73
--- /dev/null
+++ b/platforms/Makefile.inc
@@ -0,0 +1,10 @@
+PLATDIR = platforms
+
+SUBDIRS += $(PLATDIR)
+PLATFORMS = $(PLATDIR)/built-in.o
+
+include $(SRC)/$(PLATDIR)/ibm-fsp/Makefile.inc
+include $(SRC)/$(PLATDIR)/rhesus/Makefile.inc
+include $(SRC)/$(PLATDIR)/bmc/Makefile.inc
+
+$(PLATFORMS): $(IBM_FSP) $(RHESUS) $(BMC)
diff --git a/platforms/bmc/Makefile.inc b/platforms/bmc/Makefile.inc
new file mode 100644
index 0000000..348e594
--- /dev/null
+++ b/platforms/bmc/Makefile.inc
@@ -0,0 +1,6 @@
+SUBDIRS += $(PLATDIR)/bmc
+
+BMC_OBJS = palmetto.o pnor.o
+BMC = $(PLATDIR)/bmc/built-in.o
+$(BMC): $(BMC_OBJS:%=$(PLATDIR)/bmc/%)
+
diff --git a/platforms/bmc/bmc.h b/platforms/bmc/bmc.h
new file mode 100644
index 0000000..59d20bb
--- /dev/null
+++ b/platforms/bmc/bmc.h
@@ -0,0 +1,24 @@
+/* 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 __BMC_H
+#define __BMC_H
+
+extern void bmc_ext_irq(uint32_t chip_id);
+extern int pnor_init(void);
+
+#endif /* __BMC_H */
diff --git a/platforms/bmc/palmetto.c b/platforms/bmc/palmetto.c
new file mode 100644
index 0000000..42905de
--- /dev/null
+++ b/platforms/bmc/palmetto.c
@@ -0,0 +1,186 @@
+/* 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 <device.h>
+#include <console.h>
+#include <psi.h>
+#include <chip.h>
+#include <xscom.h>
+#include <ast.h>
+
+#include "bmc.h"
+
+/* UART1 config */
+#define UART_IO_BASE 0x3f8
+#define UART_IO_COUNT 8
+
+#define UART_LPC_IRQ 4
+
+static void palmetto_ext_irq(unsigned int chip_id __unused)
+{
+ uart_irq();
+}
+
+static void palmetto_init(void)
+{
+ /* Setup dummy console nodes */
+ if (dummy_console_enabled())
+ dummy_console_add_nodes();
+
+ /* Initialize AHB accesses via AST2400 */
+ ast_io_init();
+
+ /* Initialize PNOR/NVRAM */
+ pnor_init();
+}
+
+
+static void palmetto_fixup_dt_uart(struct dt_node *lpc)
+{
+ /*
+ * The official OF ISA/LPC binding is a bit odd, it prefixes
+ * the unit address for IO with "i". It uses 2 cells, the first
+ * one indicating IO vs. Memory space (along with bits to
+ * represent aliasing).
+ *
+ * We pickup that binding and add to it "2" as a indication
+ * of FW space.
+ */
+ struct dt_node *uart;
+ char namebuf[32];
+
+ /* First check if the UART is already there */
+ dt_for_each_child(lpc, uart) {
+ if (dt_node_is_compatible(uart, "ns16550"))
+ return;
+ }
+
+ /* Otherwise, add a node for it */
+ sprintf(namebuf, "serial@i%x", UART_IO_BASE);
+ uart = dt_new(lpc, namebuf);
+
+ dt_add_property_cells(uart, "reg",
+ 1, /* IO space */
+ UART_IO_BASE, UART_IO_COUNT);
+ dt_add_property_strings(uart, "compatible",
+ "ns16550",
+ "pnpPNP,501");
+ dt_add_property_cells(uart, "clock-frequency", 1843200);
+ dt_add_property_cells(uart, "current-speed", 115200);
+
+ /*
+ * This is needed by Linux for some obscure reasons,
+ * we'll eventually need to sanitize it but in the meantime
+ * let's make sure it's there
+ */
+ dt_add_property_strings(uart, "device_type", "serial");
+
+ /*
+ * Add interrupt. This simulates coming from HostBoot which
+ * does not know our interrupt numbering scheme. Instead, it
+ * just tells us which chip the interrupt is wired to, it will
+ * be the PSI "host error" interrupt of that chip. For now we
+ * assume the same chip as the LPC bus is on.
+ */
+ dt_add_property_cells(uart, "ibm,irq-chip-id", dt_get_chip_id(lpc));
+}
+
+static void palmetto_fixup_dt(void)
+{
+ struct dt_node *n, *primary_lpc = NULL;
+
+ /* Find the primary LPC bus */
+ dt_for_each_compatible(dt_root, n, "ibm,power8-lpc") {
+ if (!primary_lpc || dt_has_node_property(n, "primary", NULL))
+ primary_lpc = n;
+ if (dt_has_node_property(n, "#address-cells", NULL))
+ break;
+ }
+
+ if (!primary_lpc)
+ return;
+
+ /* Fixup the UART, that might be missing from HB */
+ palmetto_fixup_dt_uart(primary_lpc);
+
+ /* Force the dummy console for now */
+ force_dummy_console();
+}
+
+static void palmetto_fixup_psi_bar(void)
+{
+ struct proc_chip *chip = next_chip(NULL);
+ uint64_t psibar;
+
+ /* Read PSI BAR */
+ if (xscom_read(chip->id, 0x201090A, &psibar)) {
+ prerror("PLAT: Error reading PSI BAR\n");
+ return;
+ }
+ /* Already configured, bail out */
+ if (psibar & 1)
+ return;
+
+ /* Hard wire ... yuck */
+ psibar = 0x3fffe80000001;
+
+ printf("PLAT: Fixing up PSI BAR on chip %d BAR=%llx\n",
+ chip->id, psibar);
+
+ /* Now write it */
+ xscom_write(chip->id, 0x201090A, psibar);
+}
+
+static bool palmetto_probe(void)
+{
+ const char *model;
+
+ if (!dt_node_is_compatible(dt_root, "ibm,powernv"))
+ return false;
+
+ /* Temporary ... eventually we'll get that in compatible */
+ model = dt_prop_get_def(dt_root, "model", NULL);
+ if ((!model || !strstr(model, "palmetto")) &&
+ (!dt_node_is_compatible(dt_root, "ibm,palmetto")))
+ return false;
+
+ /* Hostboot's device-tree isn't quite right yet */
+ palmetto_fixup_dt();
+
+ /* Hostboot forgets to populate the PSI BAR */
+ palmetto_fixup_psi_bar();
+
+ /* Send external interrupts to me */
+ psi_set_external_irq_policy(EXTERNAL_IRQ_POLICY_SKIBOOT);
+
+ /* Configure UART1 on SuperIO */
+ ast_setup_uart1(UART_IO_BASE, UART_LPC_IRQ);
+
+ /* Setup UART and use it as console with interrupts */
+ uart_init(true);
+
+ return true;
+}
+
+DECLARE_PLATFORM(palmetto) = {
+ .name = "Palmetto",
+ .probe = palmetto_probe,
+ .init = palmetto_init,
+ .external_irq = palmetto_ext_irq,
+};
+
diff --git a/platforms/bmc/pnor.c b/platforms/bmc/pnor.c
new file mode 100644
index 0000000..878615d
--- /dev/null
+++ b/platforms/bmc/pnor.c
@@ -0,0 +1,87 @@
+/* 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 <device.h>
+#include <console.h>
+#include <opal.h>
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+
+#include "bmc.h"
+#include "ast.h"
+
+static struct spi_flash_ctrl *pnor_ctrl;
+static struct flash_chip *pnor_chip;
+static struct ffs_handle *pnor_ffs;
+
+int pnor_init(void)
+{
+ uint32_t nv_part, nv_start, nv_size;
+ int rc;
+
+ /* Open controller, flash and ffs */
+ rc = ast_sf_open(AST_SF_TYPE_PNOR, &pnor_ctrl);
+ if (rc) {
+ prerror("PLAT: Failed to open PNOR flash controller\n");
+ goto fail;
+ }
+ rc = flash_init(pnor_ctrl, &pnor_chip);
+ if (rc) {
+ prerror("PLAT: Failed to open init PNOR driver\n");
+ goto fail;
+ }
+ rc = ffs_open_flash(pnor_chip, 0, 0, &pnor_ffs);
+ if (rc) {
+ prerror("PLAT: Failed to parse FFS partition map\n");
+ goto fail;
+ }
+
+ /*
+ * Grab NVRAM and initialize the flash_nvram module
+ *
+ * Note: Ignore actual size for now ... some images have
+ * it setup incorrectly.
+ */
+ rc = ffs_lookup_part(pnor_ffs, "NVRAM", &nv_part);
+ if (rc) {
+ prerror("PLAT: No NVRAM partition in PNOR\n");
+ return OPAL_HARDWARE;
+ }
+ rc = ffs_part_info(pnor_ffs, nv_part, NULL,
+ &nv_start, &nv_size, NULL);
+ if (rc) {
+ prerror("PLAT: Failed to get NVRAM partition info\n");
+ return OPAL_HARDWARE;
+ }
+ flash_nvram_init(pnor_chip, nv_start, nv_size);
+
+ return 0;
+ fail:
+ if (pnor_ffs)
+ ffs_close(pnor_ffs);
+ pnor_ffs = NULL;
+ if (pnor_chip)
+ flash_exit(pnor_chip);
+ pnor_chip = NULL;
+ if (pnor_ctrl)
+ ast_sf_close(pnor_ctrl);
+ pnor_ctrl = NULL;
+
+ return rc;
+}
+
diff --git a/platforms/ibm-fsp/Makefile.inc b/platforms/ibm-fsp/Makefile.inc
new file mode 100644
index 0000000..a885cbb
--- /dev/null
+++ b/platforms/ibm-fsp/Makefile.inc
@@ -0,0 +1,6 @@
+SUBDIRS += $(PLATDIR)/ibm-fsp
+
+IBM_FSP_OBJS = common.o lxvpd.o apollo.o firenze.o
+IBM_FSP = $(PLATDIR)/ibm-fsp/built-in.o
+$(IBM_FSP): $(IBM_FSP_OBJS:%=$(PLATDIR)/ibm-fsp/%)
+
diff --git a/platforms/ibm-fsp/apollo.c b/platforms/ibm-fsp/apollo.c
new file mode 100644
index 0000000..0d8fb4b
--- /dev/null
+++ b/platforms/ibm-fsp/apollo.c
@@ -0,0 +1,62 @@
+/* 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 <device.h>
+#include <fsp.h>
+#include <pci.h>
+
+#include "ibm-fsp.h"
+#include "lxvpd.h"
+
+static bool apollo_probe(void)
+{
+ return dt_node_is_compatible(dt_root, "ibm,apollo");
+}
+
+static void apollo_setup_phb(struct phb *phb, unsigned int index)
+{
+ struct dt_node *ioc_node;
+
+ /* Grab the device-tree node of the IOC */
+ ioc_node = phb->dt_node->parent;
+ if (!ioc_node)
+ return;
+
+ /*
+ * Process the pcie slot entries from the lx vpd lid
+ *
+ * FIXME: We currently assume chip 1 always, this will have to be
+ * fixed once we understand the right way to get the BRxy/BRxy "x"
+ * "x" value. (this actually seems to work...)
+ */
+ lxvpd_process_slot_entries(phb, ioc_node, 1, index);
+}
+
+DECLARE_PLATFORM(apollo) = {
+ .name = "Apollo",
+ .probe = apollo_probe,
+ .init = ibm_fsp_init,
+ .cec_power_down = ibm_fsp_cec_power_down,
+ .cec_reboot = ibm_fsp_cec_reboot,
+ .pci_setup_phb = apollo_setup_phb,
+ .pci_get_slot_info = lxvpd_get_slot_info,
+ .nvram_info = fsp_nvram_info,
+ .nvram_start_read = fsp_nvram_start_read,
+ .nvram_write = fsp_nvram_write,
+};
+
diff --git a/platforms/ibm-fsp/common.c b/platforms/ibm-fsp/common.c
new file mode 100644
index 0000000..6d5ee17
--- /dev/null
+++ b/platforms/ibm-fsp/common.c
@@ -0,0 +1,196 @@
+/* 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 <fsp.h>
+#include <fsp-sysparam.h>
+#include <opal.h>
+#include <console.h>
+
+#include "ibm-fsp.h"
+
+static void map_debug_areas(void)
+{
+ uint64_t t, i;
+
+ /* Our memcons is in a section of its own and already
+ * aligned to 4K. The buffers are mapped as a whole
+ */
+ fsp_tce_map(PSI_DMA_MEMCONS, &memcons, 0x1000);
+ fsp_tce_map(PSI_DMA_LOG_BUF, (void*)INMEM_CON_START, INMEM_CON_LEN);
+
+ debug_descriptor.memcons_tce = PSI_DMA_MEMCONS;
+ t = memcons.obuf_phys - INMEM_CON_START + PSI_DMA_LOG_BUF;
+ debug_descriptor.memcons_obuf_tce = t;
+ t = memcons.ibuf_phys - INMEM_CON_START + PSI_DMA_LOG_BUF;
+ debug_descriptor.memcons_ibuf_tce = t;
+
+ /* We only have space in the TCE table for the trace
+ * areas on P8
+ */
+ if (proc_gen != proc_gen_p8)
+ return;
+
+ t = PSI_DMA_TRACE_BASE;
+ for (i = 0; i < debug_descriptor.num_traces; i++) {
+ /*
+ * Trace buffers are misaligned by 0x10 due to the lock
+ * in the trace structure, and their size is also not
+ * completely aligned. (They are allocated so that with
+ * the lock included, they do cover entire multiple of
+ * a 4K page however).
+ *
+ * This means we have to map the lock into the TCEs and
+ * align everything. Not a huge deal but needs to be
+ * taken into account.
+ *
+ * Note: Maybe we should map them read-only...
+ */
+ uint64_t tstart, tend, toff, tsize;
+
+ tstart = ALIGN_DOWN(debug_descriptor.trace_phys[i], 0x1000);
+ tend = ALIGN_UP(debug_descriptor.trace_phys[i] +
+ debug_descriptor.trace_size[i], 0x1000);
+ toff = debug_descriptor.trace_phys[i] - tstart;
+ tsize = tend - tstart;
+
+ fsp_tce_map(t, (void *)tstart, tsize);
+ debug_descriptor.trace_tce[i] = t + toff;
+ t += tsize;
+ }
+}
+
+
+void ibm_fsp_init(void)
+{
+ /* Early initializations of the FSP interface */
+ fsp_init();
+ map_debug_areas();
+ fsp_sysparam_init();
+
+ /* Get ready to receive E0 class messages. We need to respond
+ * to some of these for the init sequence to make forward progress
+ */
+ fsp_console_preinit();
+
+ /* Get ready to receive OCC related messages */
+ occ_fsp_init();
+
+ /* Get ready to receive Memory [Un]corretable Error messages. */
+ fsp_memory_err_init();
+
+ /* Initialize elog access */
+ fsp_elog_read_init();
+ fsp_elog_write_init();
+
+ /* Initiate dump service */
+ fsp_dump_init();
+
+ /* Start FSP/HV state controller & perform OPL */
+ fsp_opl();
+
+ /* Send MDST table notification to FSP */
+ op_display(OP_LOG, OP_MOD_INIT, 0x0000);
+ fsp_mdst_table_init();
+
+ /* Initialize the panel */
+ op_display(OP_LOG, OP_MOD_INIT, 0x0001);
+ fsp_oppanel_init();
+
+ /* Start the surveillance process */
+ op_display(OP_LOG, OP_MOD_INIT, 0x0002);
+ fsp_init_surveillance();
+
+ /* Initialize sensor access */
+ op_display(OP_LOG, OP_MOD_INIT, 0x0003);
+ fsp_init_sensor();
+
+ /* LED */
+ op_display(OP_LOG, OP_MOD_INIT, 0x0004);
+ fsp_led_init();
+
+ /* Monitor for DIAG events */
+ op_display(OP_LOG, OP_MOD_INIT, 0x0005);
+ fsp_init_diag();
+
+ /* Finish initializing the console */
+ op_display(OP_LOG, OP_MOD_INIT, 0x0006);
+ fsp_console_init();
+
+ /* Read our initial RTC value */
+ op_display(OP_LOG, OP_MOD_INIT, 0x0008);
+ fsp_rtc_init();
+
+ /* Initialize code update access */
+ op_display(OP_LOG, OP_MOD_INIT, 0x0009);
+ fsp_code_update_init();
+
+ /* Setup console */
+ if (fsp_present())
+ fsp_console_add_nodes();
+ else if (dummy_console_enabled())
+ dummy_console_add_nodes();
+}
+
+int64_t ibm_fsp_cec_reboot(void)
+{
+ uint32_t cmd = FSP_CMD_REBOOT;
+
+ if (!fsp_present())
+ return OPAL_UNSUPPORTED;
+
+ /* Flash new firmware */
+ if (fsp_flash_term_hook &&
+ fsp_flash_term_hook() == OPAL_SUCCESS)
+ cmd = FSP_CMD_DEEP_REBOOT;
+
+ printf("FSP: Sending 0x%02x reboot command to FSP...\n", cmd);
+
+ /* If that failed, talk to the FSP */
+ if (fsp_sync_msg(fsp_mkmsg(cmd, 0), true))
+ return OPAL_INTERNAL_ERROR;
+
+ return OPAL_SUCCESS;
+}
+
+int64_t ibm_fsp_cec_power_down(uint64_t request)
+{
+ /* Request is:
+ *
+ * 0 = normal
+ * 1 = immediate
+ * (we do not allow 2 for "pci cfg reset" just yet)
+ */
+
+ if (request !=0 && request != 1)
+ return OPAL_PARAMETER;
+
+ if (!fsp_present())
+ return OPAL_UNSUPPORTED;
+
+ /* Flash new firmware */
+ if (fsp_flash_term_hook)
+ fsp_flash_term_hook();
+
+ printf("FSP: Sending shutdown command to FSP...\n");
+
+ if (fsp_sync_msg(fsp_mkmsg(FSP_CMD_POWERDOWN_NORM, 1, request), true))
+ return OPAL_INTERNAL_ERROR;
+
+ return OPAL_SUCCESS;
+}
+
diff --git a/platforms/ibm-fsp/firenze.c b/platforms/ibm-fsp/firenze.c
new file mode 100644
index 0000000..ae72e21
--- /dev/null
+++ b/platforms/ibm-fsp/firenze.c
@@ -0,0 +1,247 @@
+/* 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 <device.h>
+#include <fsp.h>
+#include <pci.h>
+#include <pci-cfg.h>
+#include <chip.h>
+
+#include "ibm-fsp.h"
+#include "lxvpd.h"
+
+/* Enable that to dump the PCIe inventory table before sending to FSP */
+#define DEBUG_INVENTORY
+
+/* Structure used to send PCIe card info to FSP */
+struct fsp_pcie_entry {
+ uint32_t hw_proc_id;
+ uint16_t slot_idx;
+ uint16_t reserved;
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t subsys_vendor_id;
+ uint16_t subsys_device_id;
+};
+
+struct fsp_pcie_inventory {
+ uint32_t version; /* currently 1 */
+ uint32_t num_entries;
+ uint32_t entry_size;
+ uint32_t entry_offset;
+ struct fsp_pcie_entry entries[];
+};
+
+static struct fsp_pcie_inventory *fsp_pcie_inv;
+static unsigned int fsp_pcie_inv_alloc_count;
+#define FSP_PCIE_INV_ALLOC_CHUNK 4
+
+static bool firenze_probe(void)
+{
+ return dt_node_is_compatible(dt_root, "ibm,firenze");
+}
+
+static void firenze_send_pci_inventory(void)
+{
+ uint64_t base, abase, end, aend, offset;
+ int64_t rc;
+
+ if (!fsp_pcie_inv)
+ return;
+
+ printf("PLAT: Sending PCI inventory to FSP, table has %d entries\n",
+ fsp_pcie_inv->num_entries);
+
+#ifdef DEBUG_INVENTORY
+ {
+ unsigned int i;
+
+ printf("HWP SLT VDID DVID SVID SDID\n");
+ printf("---------------------------\n");
+ for (i = 0; i < fsp_pcie_inv->num_entries; i++) {
+ struct fsp_pcie_entry *e = &fsp_pcie_inv->entries[i];
+
+ printf("%03d %03d %04x %04x %04x %04x\n",
+ e->hw_proc_id, e->slot_idx,
+ e->vendor_id, e->device_id,
+ e->subsys_vendor_id, e->subsys_device_id);
+ }
+ }
+#endif
+
+ /*
+ * Get the location of the table in a form we can send
+ * to the FSP
+ */
+ base = (uint64_t)fsp_pcie_inv;
+ end = base + sizeof(struct fsp_pcie_inventory) +
+ fsp_pcie_inv->num_entries * fsp_pcie_inv->entry_size;
+ abase = base & ~0xffful;
+ aend = (end + 0xffful) & ~0xffful;
+ offset = PSI_DMA_PCIE_INVENTORY + (base & 0xfff);
+
+ /* We can only accomodate so many entries in the PSI map */
+ if ((aend - abase) > PSI_DMA_PCIE_INVENTORY_SIZE) {
+ prerror("PLAT: PCIe inventory too large (%lld bytes)\n",
+ aend - abase);
+ goto bail;
+ }
+
+ /* Map this in the TCEs */
+ fsp_tce_map(PSI_DMA_PCIE_INVENTORY, (void *)abase, aend - abase);
+
+ /* Send FSP message */
+ rc = fsp_sync_msg(fsp_mkmsg(FSP_CMD_PCI_POWER_CONF, 3,
+ hi32(offset), lo32(offset),
+ end - base), true);
+ if (rc)
+ prerror("PLAT: FSP error %lld sending inventory\n", rc);
+
+ /* Unmap */
+ fsp_tce_unmap(PSI_DMA_PCIE_INVENTORY, aend - abase);
+ bail:
+ /*
+ * We free the inventory. We'll have to redo that on hotplug
+ * when we support it but that isn't the case yet
+ */
+ free(fsp_pcie_inv);
+ fsp_pcie_inv = NULL;
+}
+
+static void firenze_add_pcidev_to_fsp_inventory(struct phb *phb,
+ struct pci_device *pd)
+{
+ struct fsp_pcie_entry *entry;
+ struct proc_chip *chip;
+
+ /* Check if we need to do some (Re)allocation */
+ if (!fsp_pcie_inv ||
+ fsp_pcie_inv->num_entries == fsp_pcie_inv_alloc_count) {
+ unsigned int new_count;
+ size_t new_size;
+ bool need_init = !fsp_pcie_inv;
+
+ /* (Re)allocate the block to the new size */
+ new_count = fsp_pcie_inv_alloc_count + FSP_PCIE_INV_ALLOC_CHUNK;
+ new_size = sizeof(struct fsp_pcie_inventory);
+ new_size += sizeof(struct fsp_pcie_entry) * new_count;
+ fsp_pcie_inv = realloc(fsp_pcie_inv, new_size);
+ fsp_pcie_inv_alloc_count = new_count;
+
+ /* Initialize the header for a new inventory */
+ if (need_init) {
+ fsp_pcie_inv->version = 1;
+ fsp_pcie_inv->num_entries = 0;
+ fsp_pcie_inv->entry_size =
+ sizeof(struct fsp_pcie_entry);
+ fsp_pcie_inv->entry_offset =
+ offsetof(struct fsp_pcie_inventory, entries);
+ }
+ }
+
+ /* Add entry */
+ entry = &fsp_pcie_inv->entries[fsp_pcie_inv->num_entries++];
+ chip = get_chip(dt_get_chip_id(phb->dt_node));
+ if (!chip) {
+ prerror("PLAT: Failed to get chip for PHB !\n");
+ return;
+ }
+ entry->hw_proc_id = chip->pcid;
+ entry->slot_idx = pd->parent->slot_info->slot_index;
+ entry->reserved = 0;
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_VENDOR_ID, &entry->vendor_id);
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_DEVICE_ID, &entry->device_id);
+ if (pd->is_bridge) {
+ int64_t ssvc = pci_find_cap(phb, pd->bdfn,
+ PCI_CFG_CAP_ID_SUBSYS_VID);
+ if (ssvc < 0) {
+ entry->subsys_vendor_id = 0xffff;
+ entry->subsys_device_id = 0xffff;
+ } else {
+ pci_cfg_read16(phb, pd->bdfn,
+ ssvc + PCICAP_SUBSYS_VID_VENDOR,
+ &entry->subsys_vendor_id);
+ pci_cfg_read16(phb, pd->bdfn,
+ ssvc + PCICAP_SUBSYS_VID_DEVICE,
+ &entry->subsys_device_id);
+ }
+ } else {
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_VENDOR_ID,
+ &entry->subsys_vendor_id);
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_ID,
+ &entry->subsys_device_id);
+ }
+}
+
+static void firenze_get_slot_info(struct phb *phb, struct pci_device * pd)
+{
+ /* Call the main LXVPD function first */
+ lxvpd_get_slot_info(phb, pd);
+
+ /*
+ * Do we need to add that to the FSP inventory for power management ?
+ *
+ * For now, we only add devices that:
+ *
+ * - Are function 0
+ * - Are not an RC or a downstream bridge
+ * - Have a direct parent that has a slot entry
+ * - Slot entry says pluggable
+ * - Aren't an upstream switch that has slot info
+ */
+ if (!pd || !pd->parent)
+ return;
+ if (pd->bdfn & 7)
+ return;
+ if (pd->dev_type == PCIE_TYPE_ROOT_PORT ||
+ pd->dev_type == PCIE_TYPE_SWITCH_DNPORT)
+ return;
+ if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT &&
+ pd->slot_info)
+ return;
+ if (!pd->parent->slot_info)
+ return;
+ if (!pd->parent->slot_info->pluggable)
+ return;
+ firenze_add_pcidev_to_fsp_inventory(phb, pd);
+}
+
+static void firenze_setup_phb(struct phb *phb, unsigned int index)
+{
+ uint32_t hub_id;
+
+ /* Grab Hub ID used to parse VPDs */
+ hub_id = dt_prop_get_u32_def(phb->dt_node, "ibm,hub-id", 0);
+
+ /* Process the pcie slot entries from the lx vpd lid */
+ lxvpd_process_slot_entries(phb, dt_root, hub_id, index);
+}
+
+DECLARE_PLATFORM(firenze) = {
+ .name = "Firenze",
+ .probe = firenze_probe,
+ .init = ibm_fsp_init,
+ .cec_power_down = ibm_fsp_cec_power_down,
+ .cec_reboot = ibm_fsp_cec_reboot,
+ .pci_setup_phb = firenze_setup_phb,
+ .pci_get_slot_info = firenze_get_slot_info,
+ .pci_probe_complete = firenze_send_pci_inventory,
+ .nvram_info = fsp_nvram_info,
+ .nvram_start_read = fsp_nvram_start_read,
+ .nvram_write = fsp_nvram_write,
+} ;
diff --git a/platforms/ibm-fsp/ibm-fsp.h b/platforms/ibm-fsp/ibm-fsp.h
new file mode 100644
index 0000000..f446d18
--- /dev/null
+++ b/platforms/ibm-fsp/ibm-fsp.h
@@ -0,0 +1,26 @@
+/* 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 __IBM_FSP_COMMON_H
+#define __IBM_FSP_COMMON_H
+
+extern void ibm_fsp_init(void);
+
+extern int64_t ibm_fsp_cec_power_down(uint64_t request);
+extern int64_t ibm_fsp_cec_reboot(void);
+
+#endif /* __IBM_FSP_COMMON_H */
diff --git a/platforms/ibm-fsp/lxvpd.c b/platforms/ibm-fsp/lxvpd.c
new file mode 100644
index 0000000..bcf8118
--- /dev/null
+++ b/platforms/ibm-fsp/lxvpd.c
@@ -0,0 +1,298 @@
+/* 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.
+ */
+/*
+ * LXVPD support
+ *
+ */
+
+
+#include <skiboot.h>
+#include <device.h>
+#include <vpd.h>
+#include <pci.h>
+#include <pci-cfg.h>
+
+#include "lxvpd.h"
+
+#define DBG(fmt...) do { } while(0)
+//#define DBG(fmt...) printf(fmt)
+
+/*
+ * XXX TODO: Add 1006 maps to add function loc codes and loc code maps
+ * (ie. -Tn part of the location code)
+ */
+struct lxvpd_slot_info_data {
+ uint8_t num_slots;
+ struct pci_slot_info info[];
+};
+
+static bool lxvpd_supported_slot(struct phb *phb, struct pci_device *pd)
+{
+ /* PCI/PCI-X we only support top level PHB with NULL "pd" */
+ if (phb->phb_type < phb_type_pcie_v1)
+ return pd == NULL;
+
+ /* Now we have PCI Express, we should never have a NULL "pd" */
+ if (!pd)
+ return false;
+
+ /* We support the root complex at the top level */
+ if (pd->dev_type == PCIE_TYPE_ROOT_PORT && !pd->parent)
+ return true;
+
+ /* We support an upstream switch port below the root complex */
+ if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT &&
+ pd->parent && pd->parent->dev_type == PCIE_TYPE_ROOT_PORT &&
+ !pd->parent->parent)
+ return true;
+
+ /* We support a downstream switch port below an upstream port
+ * below the root complex
+ */
+ if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT &&
+ pd->parent && pd->parent->dev_type == PCIE_TYPE_SWITCH_UPPORT &&
+ pd->parent->parent &&
+ pd->parent->parent->dev_type == PCIE_TYPE_ROOT_PORT &&
+ !pd->parent->parent->parent)
+ return true;
+
+ /* Anything else, bail */
+ return false;
+}
+
+void lxvpd_get_slot_info(struct phb *phb, struct pci_device * pd)
+{
+ struct lxvpd_slot_info_data *sdata = phb->platform_data;
+ bool is_phb = (pd && pd->parent) ? false : true;
+ bool entry_found = false;
+ uint8_t idx;
+
+ /* Check if we have slot info */
+ if (!sdata)
+ return;
+
+ DBG("LXVPD: Get Slot Info PHB%d pd=%x\n", phb->opal_id,
+ pd ? pd->bdfn : 0);
+
+ /*
+ * This code only handles PHBs and PCIe switches at the top level.
+ *
+ * We do not handle any other switch nor any other type of PCI/PCI-X
+ * bridge.
+ */
+ if (!lxvpd_supported_slot(phb, pd)) {
+ DBG("LXVPD: Unsupported slot\n");
+ return;
+ }
+
+ /* Iterate the slot map */
+ for (idx = 0; idx <= sdata->num_slots; idx++) {
+ struct pci_slot_info *info = &sdata->info[idx];
+ uint8_t pd_dev = (pd->bdfn >> 3) & 0x1f;
+
+ /* Match PHB with switch_id == 0 */
+ if (is_phb && info->switch_id == 0) {
+ entry_found = true;
+ break;
+ }
+
+ /* Match switch port with switch_id != 0 */
+ if (!is_phb && info->switch_id !=0 && info->dev_id == pd_dev) {
+ entry_found = true;
+ break;
+ }
+ }
+
+ if (entry_found) {
+ pd->slot_info = &sdata->info[idx];
+ DBG("PCI: PCIE Slot Info: \n");
+ DBG(" Label %s\n", pd->slot_info->label);
+ DBG(" Pluggable 0x%x\n", pd->slot_info->pluggable?1:0);
+ DBG(" Power Ctl 0x%x\n", pd->slot_info->power_ctl?1:0);
+ DBG(" Wired Lanes 0x%x\n", pd->slot_info->wired_lanes);
+ DBG(" Bus Clock 0x%x\n", pd->slot_info->bus_clock);
+ DBG(" Connector 0x%x\n", pd->slot_info->connector_type);
+ DBG(" Slot Index %d\n", pd->slot_info->slot_index);
+ } else
+ DBG("PCI: PCIE Slot Info Not Found\n");
+}
+
+static struct pci_slot_info *lxvpd_alloc_slot_info(struct phb *phb, int count)
+{
+ struct lxvpd_slot_info_data *data;
+
+ data = zalloc(sizeof(struct lxvpd_slot_info_data) *
+ count * sizeof(struct pci_slot_info));
+ assert(data);
+ data->num_slots = count;
+ phb->platform_data = data;
+
+ return data->info;
+}
+
+static void lxvpd_parse_1004_map(struct phb *phb, const uint8_t *sm, uint8_t sz)
+{
+ const struct pci_slot_entry_1004 *entry = NULL;
+ struct pci_slot_info *slot_info, *info;
+ uint8_t num_slots, slot, idx;
+
+ num_slots = (sz / sizeof(struct pci_slot_entry_1004));
+ slot_info = lxvpd_alloc_slot_info(phb, num_slots);
+
+ /* Iterate thru the entries in the keyword */
+ entry = (const struct pci_slot_entry_1004 *)sm;
+ for (slot = 0; slot < num_slots; slot++) {
+ info = &slot_info[slot];
+
+ /* Put slot info into pci device structure */
+ info->switch_id = entry->pba >> 4;
+ info->vswitch_id = entry->pba &0xf;
+ info->dev_id = entry->sba;
+ for (idx = 0; idx < 3; idx++)
+ info->label[idx] = entry->label[idx];
+ info->label[3] = 0;
+ info->pluggable = ((entry->p0.byte & 0x20) == 0);
+ info->power_ctl = ((entry->p0.power_ctl & 0x40) == 1);
+ switch(entry->p1.wired_lanes) {
+ case 1: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_32; break;
+ case 2: /* fall through */
+ case 3: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_64; break;
+ case 4: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X1; break;
+ case 5: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X4; break;
+ case 6: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X8; break;
+ case 7: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X16; break;
+ default:
+ info->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN;
+ }
+ info->wired_lanes = (entry->p1.wired_lanes - 3);
+ info->bus_clock = (entry->p2.bus_clock - 4);
+ info->connector_type = (entry->p2.connector_type - 5);
+ if (entry->p3.byte < 0xC0)
+ info->card_desc = ((entry->p3.byte >> 6) - 4) ;
+ else
+ info->card_desc = (entry->p3.byte >> 6);
+ info->card_mech = ((entry->p3.byte >> 4) & 0x3);
+ info->pwr_led_ctl = ((entry->p3.byte & 0xF) >> 2);
+ info->attn_led_ctl = (entry->p3.byte & 0x3);
+ info->slot_index = entry->slot_index;
+ entry++;
+ }
+}
+
+static void lxvpd_parse_1005_map(struct phb *phb, const uint8_t *sm, uint8_t sz)
+{
+ const struct pci_slot_entry_1005 *entry = NULL;
+ struct pci_slot_info *slot_info, *info;
+ uint8_t num_slots, slot, idx;
+
+ num_slots = (sz / sizeof(struct pci_slot_entry_1005));
+ slot_info = lxvpd_alloc_slot_info(phb, num_slots);
+
+ /* Iterate thru the entries in the keyword */
+ entry = (const struct pci_slot_entry_1005 *)sm;
+ for (slot = 0; slot < num_slots; slot++) {
+ info = &slot_info[slot];
+
+ /* Put slot info into pci device structure */
+ info->switch_id = entry->pba >> 4;
+ info->vswitch_id = entry->pba &0xf;
+ info->dev_id = entry->switch_device_id;
+ for (idx = 0; idx < 8; idx++)
+ info->label[idx] = entry->label[idx];
+ info->label[8] = 0;
+ info->pluggable = (entry->p0.pluggable == 0);
+ info->power_ctl = entry->p0.power_ctl;
+ info->wired_lanes = entry->p1.wired_lanes;
+ if (info->wired_lanes > PCI_SLOT_WIRED_LANES_PCIE_X32)
+ info->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN;
+ info->bus_clock = entry->p2.bus_clock;
+ info->connector_type = entry->p2.connector_type;
+ info->card_desc = (entry->p3.byte >> 6);
+ info->card_mech = ((entry->p3.byte >> 4) & 0x3);
+ info->pwr_led_ctl = ((entry->p3.byte & 0xF) >> 2);
+ info->attn_led_ctl = (entry->p3.byte & 0x3);
+ info->slot_index = entry->slot_index;
+ entry++;
+ }
+}
+
+void lxvpd_process_slot_entries(struct phb *phb,
+ struct dt_node *node,
+ uint8_t chip_id,
+ uint8_t index)
+{
+ const void *lxvpd;
+ const uint8_t *pr_rec, *pr_end, *sm;
+ size_t lxvpd_size, pr_size;
+ const uint16_t *mf = NULL;
+ char record[5] = "PR00";
+ uint8_t mf_sz, sm_sz;
+ bool found = false;
+
+ record[2] += chip_id;
+ record[3] += index;
+ record[4] = 0;
+
+ /* Get LX VPD pointer */
+ lxvpd = dt_prop_get_def_size(node, "ibm,io-vpd", NULL, &lxvpd_size);
+ if (!lxvpd) {
+ printf("LXVPD: LX VPD not found for %s in %p\n",
+ record, phb->dt_node);
+ return;
+ }
+
+ pr_rec = vpd_find_record(lxvpd, lxvpd_size, record, &pr_size);
+ if (!pr_rec) {
+ printf("LXVPD: %s record not found in LX VPD in %p\n",
+ record, phb->dt_node);
+ return;
+ }
+ pr_end = pr_rec + pr_size;
+
+ DBG("LXVPD: %s record for PHB%d is %ld bytes\n",
+ record, phb->opal_id, pr_size);
+
+ /* As long as there's still something in the PRxy record... */
+ while(pr_rec < pr_end) {
+ pr_size = pr_end - pr_rec;
+
+ /* Find the next MF keyword */
+ mf = vpd_find_keyword(pr_rec, pr_size, "MF", &mf_sz);
+ /* And the corresponding SM */
+ sm = vpd_find_keyword(pr_rec, pr_size, "SM", &sm_sz);
+ if (!mf || !sm) {
+ if (!found)
+ printf("LXVPD: Slot Map keyword %s not found\n",
+ record);
+ return;
+ }
+ DBG("LXVPD: Found 0x%04x map...\n", *mf);
+
+ switch (*mf) {
+ case 0x1004:
+ lxvpd_parse_1004_map(phb, sm + 1, sm_sz - 1);
+ found = true;
+ break;
+ case 0x1005:
+ lxvpd_parse_1005_map(phb, sm + 1, sm_sz - 1);
+ found = true;
+ break;
+ /* Add support for 0x1006 maps ... */
+ }
+ pr_rec = sm + sm_sz;
+ }
+}
+
diff --git a/platforms/ibm-fsp/lxvpd.h b/platforms/ibm-fsp/lxvpd.h
new file mode 100644
index 0000000..ce70502
--- /dev/null
+++ b/platforms/ibm-fsp/lxvpd.h
@@ -0,0 +1,111 @@
+/* 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 __LXVPD_H
+#define __LXVPD_H
+
+/* P8 PCI Slot Entry Definitions -- 1005 */
+
+struct slot_p0 {
+ union {
+ uint8_t byte;
+ struct {
+ uint8_t pluggable:1;
+ uint8_t pluggable_location:3;
+ uint8_t power_ctl:1;
+ uint8_t rsvd_5:1;
+ uint8_t upstream_port:1;
+ uint8_t alt_load_source:1;
+ };
+ };
+};
+
+struct slot_p1 {
+ uint8_t rsvd_0:1;
+ uint8_t wired_lanes:3;
+ uint8_t rsvd_4:4;
+};
+
+struct slot_p2 {
+ uint8_t rsvd_0:1;
+ uint8_t bus_clock:3;
+ uint8_t connector_type:4;
+};
+
+struct slot_p3 {
+ union {
+ uint8_t byte;
+ struct {
+ uint8_t height:1;
+ uint8_t length:1;
+ uint8_t left_mech:1;
+ uint8_t right_mech:1;
+ uint8_t pow_led_kvm:1;
+ uint8_t pow_led_fsp:1;
+ uint8_t attn_led_kvm:1;
+ uint8_t attn_led_fsp:1;
+ };
+ };
+};
+
+struct pci_slot_entry_1004 {
+ uint8_t pba;
+ uint8_t sba;
+ uint8_t phb_or_slot_type;
+ char label[3];
+ uint16_t bis;
+ struct slot_p0 p0;
+ struct slot_p1 p1;
+ struct slot_p2 p2;
+ struct slot_p3 p3;
+ uint8_t left_pitch;
+ uint8_t right_pitch;
+ uint8_t slot_index;
+ uint8_t max_slot_power;
+};
+
+struct pci_slot_entry_1005 {
+ union {
+ uint8_t pba;
+ struct {
+ uint8_t switch_id:4;
+ uint8_t vswitch_id:4;
+ };
+ };
+ uint8_t switch_device_id;
+ uint8_t slot_type:4;
+ uint8_t phb_id:4;
+ char label[8];
+ uint8_t rsvd_11[4];
+ struct slot_p0 p0;
+ struct slot_p1 p1;
+ struct slot_p2 p2;
+ struct slot_p3 p3;
+ uint8_t left_pitch;
+ uint8_t right_pitch;
+ uint8_t slot_index;
+ uint8_t rsvd_22[2];
+};
+
+struct phb;
+
+extern void lxvpd_process_slot_entries(struct phb *phb,
+ struct dt_node *node,
+ uint8_t chip_id, uint8_t index);
+
+extern void lxvpd_get_slot_info(struct phb *phb, struct pci_device * pd);
+
+#endif /* __LXVPD_H */
diff --git a/platforms/rhesus/Makefile.inc b/platforms/rhesus/Makefile.inc
new file mode 100644
index 0000000..5899ca2
--- /dev/null
+++ b/platforms/rhesus/Makefile.inc
@@ -0,0 +1,6 @@
+SUBDIRS += $(PLATDIR)/rhesus
+
+RHESUS_OBJS = rhesus.o
+RHESUS = $(PLATDIR)/rhesus/built-in.o
+$(RHESUS): $(RHESUS_OBJS:%=$(PLATDIR)/rhesus/%)
+
diff --git a/platforms/rhesus/rhesus.c b/platforms/rhesus/rhesus.c
new file mode 100644
index 0000000..41b1d5a
--- /dev/null
+++ b/platforms/rhesus/rhesus.c
@@ -0,0 +1,313 @@
+/* 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 <device.h>
+#include <lpc.h>
+#include <console.h>
+#include <opal.h>
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include <sfc-ctrl.h>
+#include <ec/config.h>
+#include <ec/gpio.h>
+
+/*
+ * EC GPIO mapping
+ */
+#define RHESUS_RST_UCD90160_N EC_GPIO_PORT_J, 3
+#define RHESUS_FM_PWR_CYCLE_N EC_GPIO_PORT_K, 2
+#define RHESUS_EN_PWR_ON_SEQ EC_GPIO_PORT_R, 1
+#define RHESUS_BOARD_REVISION0 EC_GPIO_PORT_F, 3
+#define RHESUS_BOARD_REVISION1 EC_GPIO_PORT_F, 2
+#define RHESUS_BOARD_REVISION2 EC_GPIO_PORT_E, 5
+#define RHESUS_BOARD_REVISION3 EC_GPIO_PORT_E, 4
+#define RHESUS_BOARD_REVISION4 EC_GPIO_PORT_E, 1
+
+static struct spi_flash_ctrl *pnor_ctrl;
+static struct flash_chip *pnor_chip;
+static struct ffs_handle *pnor_ffs;
+
+
+/*
+ * IO accessors for the EC driver
+ */
+void ec_outb(uint16_t addr, uint8_t data)
+{
+ lpc_outb(data, addr);
+}
+
+uint8_t ec_inb(uint16_t addr)
+{
+ return lpc_inb(addr);
+}
+
+static int rhesus_board_revision(void)
+{
+ int revision = 0, ret = 0, i = 0;
+
+ static const struct {
+ EcGpioPort port;
+ uint8_t pin;
+ } revision_gpios[] = {
+ { RHESUS_BOARD_REVISION0 },
+ { RHESUS_BOARD_REVISION1 },
+ { RHESUS_BOARD_REVISION2 },
+ { RHESUS_BOARD_REVISION3 },
+ { RHESUS_BOARD_REVISION4 },
+ };
+ for (i = 0; i < sizeof(revision_gpios) / sizeof(revision_gpios[0]); ++i)
+ {
+ ret = ec_gpio_read(revision_gpios[i].port, revision_gpios[i].pin);
+ if (ret < 0)
+ return ret;
+ revision <<= 1; revision |= ret;
+ }
+
+ return revision;
+}
+
+static int64_t rhesus_reboot(void)
+{
+ // TODO(rlippert): This should use EC_SYS_RST_N, but there is nothing to
+ // deassert that at the moment.
+ int ret = 0;
+ ret = ec_gpio_set(RHESUS_FM_PWR_CYCLE_N, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = ec_gpio_setup(RHESUS_FM_PWR_CYCLE_N,
+ EC_GPIO_OUTPUT,
+ EC_GPIO_PULLUP_DISABLE);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int64_t rhesus_power_down(uint64_t request __unused)
+{
+ int ret = 0;
+ ret = ec_gpio_set(RHESUS_EN_PWR_ON_SEQ, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = ec_gpio_setup(RHESUS_EN_PWR_ON_SEQ,
+ EC_GPIO_OUTPUT,
+ EC_GPIO_PULLUP_DISABLE);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rhesus_pnor_init(void)
+{
+ uint32_t nv_part, nv_start, nv_size;
+ int rc;
+
+ /* Open controller, flash and ffs */
+ rc = sfc_open(&pnor_ctrl);
+ if (rc) {
+ prerror("PLAT: Failed to open PNOR flash controller\n");
+ goto fail;
+ }
+ rc = flash_init(pnor_ctrl, &pnor_chip);
+ if (rc) {
+ prerror("PLAT: Failed to open init PNOR driver\n");
+ goto fail;
+ }
+ rc = ffs_open_flash(pnor_chip, 0, 0, &pnor_ffs);
+ if (rc) {
+ prerror("PLAT: Failed to parse FFS partition map\n");
+ goto fail;
+ }
+
+ /*
+ * Grab NVRAM and initialize the flash_nvram module
+ *
+ * Note: Ignore actual size for now ... some images have
+ * it setup incorrectly.
+ */
+ rc = ffs_lookup_part(pnor_ffs, "NVRAM", &nv_part);
+ if (rc) {
+ prerror("PLAT: No NVRAM partition in PNOR\n");
+ return OPAL_HARDWARE;
+ }
+ rc = ffs_part_info(pnor_ffs, nv_part, NULL,
+ &nv_start, &nv_size, NULL);
+ if (rc) {
+ prerror("PLAT: Failed to get NVRAM partition info\n");
+ return OPAL_HARDWARE;
+ }
+ flash_nvram_init(pnor_chip, nv_start, nv_size);
+
+ return 0;
+ fail:
+ if (pnor_ffs)
+ ffs_close(pnor_ffs);
+ pnor_ffs = NULL;
+ if (pnor_chip)
+ flash_exit(pnor_chip);
+ pnor_chip = NULL;
+ if (pnor_ctrl)
+ sfc_close(pnor_ctrl);
+ pnor_ctrl = NULL;
+
+ return rc;
+}
+
+static void rhesus_init(void)
+{
+ if (dummy_console_enabled())
+ dummy_console_add_nodes();
+
+ /* Initialize PNOR/NVRAM */
+ rhesus_pnor_init();
+}
+
+static void rhesus_dt_fixup_uart(struct dt_node *lpc)
+{
+ /*
+ * The official OF ISA/LPC binding is a bit odd, it prefixes
+ * the unit address for IO with "i". It uses 2 cells, the first
+ * one indicating IO vs. Memory space (along with bits to
+ * represent aliasing).
+ *
+ * We pickup that binding and add to it "2" as a indication
+ * of FW space.
+ *
+ * TODO: Probe the UART instead if the LPC bus allows for it
+ */
+ struct dt_node *uart;
+ char namebuf[32];
+#define UART_IO_BASE 0x3f8
+#define UART_IO_COUNT 8
+
+ sprintf(namebuf, "serial@i%x", UART_IO_BASE);
+ uart = dt_new(lpc, namebuf);
+
+ dt_add_property_cells(uart, "reg",
+ 1, /* IO space */
+ UART_IO_BASE, UART_IO_COUNT);
+ dt_add_property_strings(uart, "compatible",
+ "ns16550",
+ "pnpPNP,501");
+ dt_add_property_cells(uart, "clock-frequency", 1843200);
+ dt_add_property_cells(uart, "current-speed", 115200);
+
+ /*
+ * This is needed by Linux for some obscure reasons,
+ * we'll eventually need to sanitize it but in the meantime
+ * let's make sure it's there
+ */
+ dt_add_property_strings(uart, "device_type", "serial");
+
+ /*
+ * Add interrupt. This simulates coming from HostBoot which
+ * does not know our interrupt numbering scheme. Instead, it
+ * just tells us which chip the interrupt is wired to, it will
+ * be the PSI "host error" interrupt of that chip. For now we
+ * assume the same chip as the LPC bus is on.
+ */
+ dt_add_property_cells(uart, "ibm,irq-chip-id", dt_get_chip_id(lpc));
+}
+
+/*
+ * This adds the legacy RTC device to the device-tree
+ * for Linux to use
+ */
+static void rhesus_dt_fixup_rtc(struct dt_node *lpc)
+{
+ struct dt_node *rtc;
+
+ /*
+ * Follows the structure expected by the kernel file
+ * arch/powerpc/sysdev/rtc_cmos_setup.c
+ */
+ rtc = dt_new_addr(lpc, "rtc", EC_RTC_PORT_BASE);
+ dt_add_property_string(rtc, "compatible", "pnpPNP,b00");
+ dt_add_property_cells(rtc, "reg",
+ 1, /* IO space */
+ EC_RTC_PORT_BASE,
+ /* 1 index/data pair per 128 bytes */
+ (EC_RTC_BLOCK_SIZE / 128) * 2);
+}
+
+static void rhesus_dt_fixup(void)
+{
+ struct dt_node *n, *primary_lpc = NULL;
+
+ /* Find the primary LPC bus */
+ dt_for_each_compatible(dt_root, n, "ibm,power8-lpc") {
+ if (!primary_lpc || dt_has_node_property(n, "primary", NULL))
+ primary_lpc = n;
+ if (dt_has_node_property(n, "#address-cells", NULL))
+ break;
+ }
+
+ if (!primary_lpc)
+ return;
+
+ rhesus_dt_fixup_rtc(primary_lpc);
+ rhesus_dt_fixup_uart(primary_lpc);
+}
+
+static bool rhesus_probe(void)
+{
+ const char *model;
+ int rev;
+
+ if (!dt_node_is_compatible(dt_root, "ibm,powernv"))
+ return false;
+
+ model = dt_prop_get_def(dt_root, "model", NULL);
+ if (!model || !(strstr(model, "rhesus") || strstr(model, "RHESUS")))
+ return false;
+
+ /* Grab board version from EC */
+ rev = rhesus_board_revision();
+ if (rev >= 0) {
+ printf("Rhesus board rev %d\n", rev);
+ dt_add_property_cells(dt_root, "revision-id", rev);
+ } else
+ prerror("Rhesus board revision not found !\n");
+
+ /* Add missing bits of device-tree such as the UART */
+ rhesus_dt_fixup();
+
+ /*
+ * Setup UART and use it as console. For now, we
+ * don't expose the interrupt as we know it's not
+ * working properly yet
+ */
+ uart_init(false);
+
+ return true;
+}
+
+DECLARE_PLATFORM(rhesus) = {
+ .name = "Rhesus",
+ .probe = rhesus_probe,
+ .init = rhesus_init,
+ .cec_power_down = rhesus_power_down,
+ .cec_reboot = rhesus_reboot,
+};