aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/platforms
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/platforms')
-rw-r--r--roms/skiboot/platforms/Makefile.inc12
-rw-r--r--roms/skiboot/platforms/astbmc/Makefile.inc13
-rw-r--r--roms/skiboot/platforms/astbmc/astbmc.h113
-rw-r--r--roms/skiboot/platforms/astbmc/barreleye.c165
-rw-r--r--roms/skiboot/platforms/astbmc/blackbird.c106
-rw-r--r--roms/skiboot/platforms/astbmc/common.c564
-rw-r--r--roms/skiboot/platforms/astbmc/firestone.c149
-rw-r--r--roms/skiboot/platforms/astbmc/garrison.c285
-rw-r--r--roms/skiboot/platforms/astbmc/habanero.c140
-rw-r--r--roms/skiboot/platforms/astbmc/mihawk.c567
-rw-r--r--roms/skiboot/platforms/astbmc/mowgli.c107
-rw-r--r--roms/skiboot/platforms/astbmc/nicole.c90
-rw-r--r--roms/skiboot/platforms/astbmc/p8dnu.c344
-rw-r--r--roms/skiboot/platforms/astbmc/p8dtu.c276
-rw-r--r--roms/skiboot/platforms/astbmc/p9dsu.c725
-rw-r--r--roms/skiboot/platforms/astbmc/palmetto.c123
-rw-r--r--roms/skiboot/platforms/astbmc/pnor.c98
-rw-r--r--roms/skiboot/platforms/astbmc/rainier.c136
-rw-r--r--roms/skiboot/platforms/astbmc/romulus.c73
-rw-r--r--roms/skiboot/platforms/astbmc/slots.c259
-rw-r--r--roms/skiboot/platforms/astbmc/swift.c125
-rw-r--r--roms/skiboot/platforms/astbmc/talos.c76
-rw-r--r--roms/skiboot/platforms/astbmc/vesnin.c346
-rw-r--r--roms/skiboot/platforms/astbmc/witherspoon.c604
-rw-r--r--roms/skiboot/platforms/astbmc/zaius.c258
-rw-r--r--roms/skiboot/platforms/ibm-fsp/Makefile.inc9
-rw-r--r--roms/skiboot/platforms/ibm-fsp/common.c279
-rw-r--r--roms/skiboot/platforms/ibm-fsp/firenze-pci.c1044
-rw-r--r--roms/skiboot/platforms/ibm-fsp/firenze.c223
-rw-r--r--roms/skiboot/platforms/ibm-fsp/fsp-vpd.c152
-rw-r--r--roms/skiboot/platforms/ibm-fsp/hostservices.c912
-rw-r--r--roms/skiboot/platforms/ibm-fsp/ibm-fsp.h45
-rw-r--r--roms/skiboot/platforms/ibm-fsp/lxvpd.c371
-rw-r--r--roms/skiboot/platforms/ibm-fsp/lxvpd.h164
-rw-r--r--roms/skiboot/platforms/ibm-fsp/zz.c213
-rw-r--r--roms/skiboot/platforms/mambo/Makefile.inc6
-rw-r--r--roms/skiboot/platforms/mambo/console.c74
-rw-r--r--roms/skiboot/platforms/mambo/mambo.c321
-rw-r--r--roms/skiboot/platforms/mambo/mambo.h46
-rw-r--r--roms/skiboot/platforms/qemu/Makefile.inc6
-rw-r--r--roms/skiboot/platforms/qemu/qemu.c152
-rw-r--r--roms/skiboot/platforms/rhesus/Makefile.inc6
-rw-r--r--roms/skiboot/platforms/rhesus/ec/config.h70
-rw-r--r--roms/skiboot/platforms/rhesus/ec/gpio.h41
-rw-r--r--roms/skiboot/platforms/rhesus/gpio.c75
-rw-r--r--roms/skiboot/platforms/rhesus/rhesus.c274
46 files changed, 10237 insertions, 0 deletions
diff --git a/roms/skiboot/platforms/Makefile.inc b/roms/skiboot/platforms/Makefile.inc
new file mode 100644
index 000000000..3269532a3
--- /dev/null
+++ b/roms/skiboot/platforms/Makefile.inc
@@ -0,0 +1,12 @@
+PLATDIR = platforms
+
+SUBDIRS += $(PLATDIR)
+PLATFORMS = $(PLATDIR)/built-in.a
+
+include $(SRC)/$(PLATDIR)/ibm-fsp/Makefile.inc
+include $(SRC)/$(PLATDIR)/rhesus/Makefile.inc
+include $(SRC)/$(PLATDIR)/astbmc/Makefile.inc
+include $(SRC)/$(PLATDIR)/mambo/Makefile.inc
+include $(SRC)/$(PLATDIR)/qemu/Makefile.inc
+
+$(PLATFORMS): $(IBM_FSP) $(RHESUS) $(ASTBMC) $(MAMBO) $(QEMU)
diff --git a/roms/skiboot/platforms/astbmc/Makefile.inc b/roms/skiboot/platforms/astbmc/Makefile.inc
new file mode 100644
index 000000000..070813231
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/Makefile.inc
@@ -0,0 +1,13 @@
+SUBDIRS += $(PLATDIR)/astbmc
+
+ASTBMC_OBJS = pnor.o common.o slots.o \
+ palmetto.o habanero.o firestone.o \
+ p8dtu.o p8dnu.o \
+ garrison.o barreleye.o \
+ witherspoon.o zaius.o romulus.o p9dsu.o \
+ vesnin.o nicole.o mihawk.o mowgli.o \
+ talos.o blackbird.o \
+ swift.o rainier.o
+
+ASTBMC = $(PLATDIR)/astbmc/built-in.a
+$(ASTBMC): $(ASTBMC_OBJS:%=$(PLATDIR)/astbmc/%)
diff --git a/roms/skiboot/platforms/astbmc/astbmc.h b/roms/skiboot/platforms/astbmc/astbmc.h
new file mode 100644
index 000000000..00f221230
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/astbmc.h
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#ifndef __ASTBMC_H
+#define __ASTBMC_H
+
+#include <platform.h>
+
+#define ST_LOC_PHB(chip_id, phb_idx) ((chip_id) << 16 | (phb_idx))
+#define ST_LOC_DEVFN(dev, fn) ((dev) << 3 | (fn))
+/*
+ * NPU groups are used to allocate device numbers. There is a 1 to 1
+ * correlation between a NPU group and a physical GPU. Links within a group
+ * are allocated as functions within a device, so groups must be numbered
+ * sequentially starting at 0.
+ */
+#define ST_LOC_NPU_GROUP(group_id) (group_id << 3)
+
+struct slot_table_entry {
+ enum slot_table_etype {
+ st_end, /* End of list */
+ st_phb,
+ st_pluggable_slot,
+ st_builtin_dev,
+ st_npu_slot
+ } etype;
+ uint32_t location;
+ const char *name;
+ const struct slot_table_entry *children;
+ uint8_t power_limit;
+};
+
+/*
+ * Helper to reduce the noise in the PHB table
+ */
+#define ST_PHB_ENTRY(chip_id, phb_id, child_table) \
+{ \
+ .etype = st_phb, \
+ .location = ST_LOC_PHB(chip_id, phb_id), \
+ .children = child_table \
+}
+
+/*
+ * For the most part the "table" isn't really a table and only contains
+ * a single real entry and the etype = st_end terminator. In these cases
+ * we can use these helpers. If you need something special in the slot
+ * table for each slot (e.g. power limit, devfn != 0) then you need to
+ * define the actual structure.
+ */
+#define ST_BUILTIN_DEV(st_name, slot_name, ...) \
+static struct slot_table_entry st_name[] = \
+{ \
+ { \
+ .etype = st_pluggable_slot, \
+ .name = slot_name, \
+ ##__VA_ARGS__ \
+ }, \
+ { .etype = st_end }, \
+}
+
+#define ST_PLUGGABLE(st_name, slot_name, ...) \
+static struct slot_table_entry st_name[] = \
+{ \
+ { \
+ .etype = st_pluggable_slot, \
+ .name = slot_name, \
+ ##__VA_ARGS__ \
+ }, \
+ { .etype = st_end }, \
+}
+
+#define SW_PLUGGABLE(slot_name, port, ...) \
+{ \
+ .etype = st_pluggable_slot, \
+ .name = slot_name, \
+ .location = ST_LOC_DEVFN(port, 0), \
+ ##__VA_ARGS__ \
+}
+
+#define SW_BUILTIN(slot_name, port, ...) \
+{ \
+ .etype = st_builtin_dev, \
+ .name = slot_name, \
+ .location = ST_LOC_DEVFN(port, 0), \
+ ##__VA_ARGS__ \
+}
+
+extern const struct bmc_hw_config bmc_hw_ast2400;
+extern const struct bmc_hw_config bmc_hw_ast2500;
+extern const struct bmc_hw_config bmc_hw_ast2600;
+extern const struct bmc_platform bmc_plat_ast2400_ami;
+extern const struct bmc_platform bmc_plat_ast2500_ami;
+extern const struct bmc_platform bmc_plat_ast2500_openbmc;
+extern const struct bmc_platform bmc_plat_ast2600_openbmc;
+
+extern void astbmc_early_init(void);
+extern int64_t astbmc_ipmi_reboot(void);
+extern int64_t astbmc_ipmi_power_down(uint64_t request);
+extern void astbmc_init(void);
+extern void astbmc_ext_irq_serirq_cpld(unsigned int chip_id);
+extern int pnor_init(void);
+extern void check_all_slot_table(void);
+extern void astbmc_exit(void);
+extern void astbmc_seeprom_update(void);
+
+extern void slot_table_init(const struct slot_table_entry *top_table);
+extern void slot_table_get_slot_info(struct phb *phb, struct pci_device * pd);
+void slot_table_add_slot_info(struct pci_device *pd,
+ const struct slot_table_entry *ent);
+
+void dt_slot_get_slot_info(struct phb *phb, struct pci_device *pd);
+
+#endif /* __ASTBMC_H */
diff --git a/roms/skiboot/platforms/astbmc/barreleye.c b/roms/skiboot/platforms/astbmc/barreleye.c
new file mode 100644
index 000000000..f7542d66a
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/barreleye.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright 2016 Ingrasys.
+ * Copyright 2016-2019 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+
+#include "astbmc.h"
+
+static const struct slot_table_entry barreleye_phb0_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry barreleye_plx_slots[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(1,0),
+ .name = "IO Board SATA",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(2,0),
+ .name = "IO Board USB",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(3,0),
+ .name = "IO Board BMC",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(4,0),
+ .name = "IO Board NIC",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry barreleye_plx_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = barreleye_plx_slots,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry barreleye_phb0_1_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "PLX Switch",
+ .children = barreleye_plx_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry barreleye_phb0_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Network Mezz",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry barreleye_phb8_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Storage Mezz",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry barreleye_phb8_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot3",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry barreleye_phb8_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot2",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry barreleye_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,0),
+ .children = barreleye_phb0_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,1),
+ .children = barreleye_phb0_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,2),
+ .children = barreleye_phb0_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,0),
+ .children = barreleye_phb8_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,1),
+ .children = barreleye_phb8_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,2),
+ .children = barreleye_phb8_2_slot,
+ },
+ { .etype = st_end },
+};
+
+static bool barreleye_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "ingrasys,barreleye"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+ slot_table_init(barreleye_phb_table);
+
+ return true;
+}
+
+
+DECLARE_PLATFORM(barreleye) = {
+ .name = "Barreleye",
+ .probe = barreleye_probe,
+ .init = astbmc_init,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .external_irq = astbmc_ext_irq_serirq_cpld,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/astbmc/blackbird.c b/roms/skiboot/platforms/astbmc/blackbird.c
new file mode 100644
index 000000000..d7a1f81d6
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/blackbird.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: Apache-2.0
+/* Copyright 2017 IBM Corp.
+ * Copyright 2018-2019 Raptor Engineering, LLC
+ * Copyright 2019 Stewart Smith
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+#include <psi.h>
+#include <lpc.h>
+
+#include "astbmc.h"
+
+ST_PLUGGABLE(blackbird_cpu1_slot1, "SLOT1 PCIE 4.0 X16");
+ST_PLUGGABLE(blackbird_cpu1_slot2, "SLOT2 PCIE 4.0 X8");
+
+ST_BUILTIN_DEV(blackbird_builtin_sata, "Builtin SATA");
+ST_BUILTIN_DEV(blackbird_builtin_usb, "Builtin USB");
+ST_BUILTIN_DEV(blackbird_builtin_ethernet, "Builtin Ethernet");
+ST_BUILTIN_DEV(blackbird_builtin_bmc, "BMC");
+
+static const struct slot_table_entry blackbird_phb_table[] = {
+ ST_PHB_ENTRY(0, 0, blackbird_cpu1_slot1),
+ ST_PHB_ENTRY(0, 1, blackbird_cpu1_slot2),
+
+ ST_PHB_ENTRY(0, 2, blackbird_builtin_sata),
+ ST_PHB_ENTRY(0, 3, blackbird_builtin_usb),
+ ST_PHB_ENTRY(0, 4, blackbird_builtin_ethernet),
+ ST_PHB_ENTRY(0, 5, blackbird_builtin_bmc),
+
+ { .etype = st_end },
+};
+
+static bool blackbird_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "rcs,blackbird"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Setup UART for use by OPAL (Linux hvc) */
+ uart_set_console_policy(UART_CONSOLE_OPAL);
+
+ slot_table_init(blackbird_phb_table);
+
+ return true;
+}
+
+static int64_t blackbird_write_oppanel_async(uint64_t async_token __unused,
+ oppanel_line_t *lines,
+ uint64_t num_lines)
+{
+ uint8_t *line;
+ int len;
+
+ if (num_lines != 1)
+ return OPAL_PARAMETER;
+
+ line = (void *) be64_to_cpu(lines[0].line);
+ len = be64_to_cpu(lines[0].line_len);
+
+ if (len > 0)
+ lpc_probe_write(OPAL_LPC_IO, 0x80, line[0], 1);
+ if (len > 1)
+ lpc_probe_write(OPAL_LPC_IO, 0x81, line[1], 1);
+ if (len > 2)
+ lpc_probe_write(OPAL_LPC_IO, 0x82, line[2], 1);
+
+ return OPAL_SUCCESS;
+}
+
+static void blackbird_init(void)
+{
+ struct dt_node *oppanel;
+
+ astbmc_init();
+
+ opal_register(OPAL_WRITE_OPPANEL_ASYNC, blackbird_write_oppanel_async, 3);
+
+ oppanel = dt_new(opal_node, "oppanel");
+ dt_add_property_cells(oppanel, "#length", 3);
+ dt_add_property_cells(oppanel, "#lines", 1);
+ dt_add_property_strings(oppanel, "compatible", "ibm,opal-oppanel", "rcs,ipl-observer");
+
+}
+
+DECLARE_PLATFORM(blackbird) = {
+ .name = "Blackbird",
+ .probe = blackbird_probe,
+ .init = blackbird_init,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .bmc = &bmc_plat_ast2500_openbmc,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .exit = astbmc_exit,
+ .terminate = ipmi_terminate,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/astbmc/common.c b/roms/skiboot/platforms/astbmc/common.c
new file mode 100644
index 000000000..83ef70ad3
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/common.c
@@ -0,0 +1,564 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <psi.h>
+#include <chip.h>
+#include <xscom.h>
+#include <ast.h>
+#include <ipmi.h>
+#include <bt.h>
+#include <errorlog.h>
+#include <lpc.h>
+#include <timebase.h>
+
+#include "astbmc.h"
+
+/* UART1 config */
+#define UART_IO_BASE 0x3f8
+#define UART_IO_COUNT 8
+#define UART_LPC_IRQ 4
+
+/* BT config */
+#define BT_IO_BASE 0xe4
+#define BT_IO_COUNT 3
+#define BT_LPC_IRQ 10
+
+/* MBOX config */
+#define MBOX_IO_BASE 0x1000
+#define MBOX_IO_COUNT 6
+#define MBOX_LPC_IRQ 9
+
+void astbmc_ext_irq_serirq_cpld(unsigned int chip_id)
+{
+ lpc_all_interrupts(chip_id);
+}
+
+static void astbmc_ipmi_error(struct ipmi_msg *msg)
+{
+ prlog(PR_DEBUG, "ASTBMC: error sending msg. cc = %02x\n", msg->cc);
+
+ ipmi_free_msg(msg);
+}
+
+static void astbmc_ipmi_setenables(void)
+{
+ struct ipmi_msg *msg;
+
+ struct {
+ uint8_t oem2_en : 1;
+ uint8_t oem1_en : 1;
+ uint8_t oem0_en : 1;
+ uint8_t reserved : 1;
+ uint8_t sel_en : 1;
+ uint8_t msgbuf_en : 1;
+ uint8_t msgbuf_full_int_en : 1;
+ uint8_t rxmsg_queue_int_en : 1;
+ } data;
+
+ memset(&data, 0, sizeof(data));
+
+ /* The spec says we need to read-modify-write to not clobber
+ * the state of the other flags. These are set on by the bmc */
+ data.rxmsg_queue_int_en = 1;
+ data.sel_en = 1;
+
+ /* These are the ones we want to set on */
+ data.msgbuf_en = 1;
+
+ msg = ipmi_mkmsg_simple(IPMI_SET_ENABLES, &data, sizeof(data));
+ if (!msg) {
+ /**
+ * @fwts-label ASTBMCFailedSetEnables
+ * @fwts-advice AST BMC is likely to be non-functional
+ * when accessed from host.
+ */
+ prlog(PR_ERR, "ASTBMC: failed to set enables\n");
+ return;
+ }
+
+ msg->error = astbmc_ipmi_error;
+
+ ipmi_queue_msg(msg);
+
+}
+
+static int astbmc_fru_init(void)
+{
+ const struct dt_property *prop;
+ struct dt_node *node;
+ uint8_t fru_id;
+
+ node = dt_find_by_path(dt_root, "bmc");
+ if (!node)
+ return -1;
+
+ prop = dt_find_property(node, "firmware-fru-id");
+ if (!prop)
+ return -1;
+
+ fru_id = dt_property_get_cell(prop, 0) & 0xff;
+ ipmi_fru_init(fru_id);
+ return 0;
+}
+
+
+void astbmc_init(void)
+{
+ /* Register the BT interface with the IPMI layer
+ *
+ * Initialise this first to enable PNOR access
+ */
+ bt_init();
+
+ /* Initialize PNOR/NVRAM */
+ pnor_init();
+
+ /* Initialize elog */
+ elog_init();
+ ipmi_sel_init();
+ ipmi_wdt_init();
+ ipmi_rtc_init();
+ ipmi_opal_init();
+ astbmc_fru_init();
+ ipmi_sensor_init();
+
+ /* Request BMC information */
+ ipmi_get_bmc_info_request();
+
+ /* As soon as IPMI is up, inform BMC we are in "S0" */
+ ipmi_set_power_state(IPMI_PWR_SYS_S0_WORKING, IPMI_PWR_NOCHANGE);
+
+ /* Enable IPMI OEM message interrupts */
+ astbmc_ipmi_setenables();
+
+ ipmi_set_fw_progress_sensor(IPMI_FW_MOTHERBOARD_INIT);
+
+ /* Setup UART console for use by Linux via OPAL API */
+ set_opal_console(&uart_opal_con);
+}
+
+int64_t astbmc_ipmi_power_down(uint64_t request)
+{
+ if (request != IPMI_CHASSIS_PWR_DOWN) {
+ prlog(PR_WARNING, "PLAT: unexpected shutdown request %llx\n",
+ request);
+ }
+
+ return ipmi_chassis_control(request);
+}
+
+int64_t astbmc_ipmi_reboot(void)
+{
+ return ipmi_chassis_control(IPMI_CHASSIS_HARD_RESET);
+}
+
+void astbmc_seeprom_update(void)
+{
+ int flag_set, counter, rc;
+
+ rc = ipmi_get_chassis_boot_opt_request();
+
+ if (rc) {
+ prlog(PR_WARNING, "Failed to check SBE validation flag\n");
+ return;
+ }
+
+ flag_set = ipmi_chassis_check_sbe_validation();
+
+ if (flag_set <= 0) {
+ prlog(PR_DEBUG, "SBE validation flag unset or invalid\n");
+ return;
+ }
+
+ /*
+ * Flag is set, wait until SBE validation is complete and the flag
+ * has been reset.
+ */
+ prlog(PR_WARNING, "SBE validation required, waiting for completion\n");
+ prlog(PR_WARNING, "System will be powered off if validation fails\n");
+ counter = 0;
+
+ while (flag_set > 0) {
+ time_wait_ms(10000);
+ if (++counter % 3 == 0) {
+ /* Let the user know we're alive every 30s */
+ prlog(PR_WARNING, "waiting for completion...\n");
+ }
+ if (counter == 180) {
+ /* This is longer than expected and we have no way of
+ * checking if it's still running. Apologies if you
+ * ever see this message.
+ */
+ prlog(PR_WARNING, "30 minutes has elapsed, this is longer than expected for verification\n");
+ prlog(PR_WARNING, "If no progress is made a power reset of the BMC and Host may be required\n");
+ counter = 0;
+ }
+
+ /* As above, loop anyway if we fail to check the flag */
+ rc = ipmi_get_chassis_boot_opt_request();
+ if (rc == 0)
+ flag_set = ipmi_chassis_check_sbe_validation();
+ else
+ prlog(PR_WARNING, "Failed to check SBE validation flag\n");
+ }
+
+ /*
+ * The SBE validation can (will) leave the SBE in a bad state,
+ * preventing timers from working properly. Reboot so that we
+ * can boot normally with everything intact.
+ */
+ prlog(PR_WARNING, "SBE validation complete, rebooting\n");
+ if (platform.cec_reboot)
+ platform.cec_reboot();
+ else
+ abort();
+ while(true);
+}
+
+static void astbmc_fixup_dt_system_id(void)
+{
+ /* Make sure we don't already have one */
+ if (dt_find_property(dt_root, "system-id"))
+ return;
+
+ dt_add_property_strings(dt_root, "system-id", "unavailable");
+}
+
+static void astbmc_fixup_dt_bt(struct dt_node *lpc)
+{
+ struct dt_node *bt;
+ char namebuf[32];
+
+ /* First check if the BT interface is already there */
+ dt_for_each_child(lpc, bt) {
+ if (dt_node_is_compatible(bt, "bt"))
+ return;
+ }
+
+ snprintf(namebuf, sizeof(namebuf), "ipmi-bt@i%x", BT_IO_BASE);
+ bt = dt_new(lpc, namebuf);
+
+ dt_add_property_cells(bt, "reg",
+ 1, /* IO space */
+ BT_IO_BASE, BT_IO_COUNT);
+ dt_add_property_strings(bt, "compatible", "ipmi-bt");
+
+ /* Mark it as reserved to avoid Linux trying to claim it */
+ dt_add_property_strings(bt, "status", "reserved");
+
+ dt_add_property_cells(bt, "interrupts", BT_LPC_IRQ);
+ dt_add_property_cells(bt, "interrupt-parent", lpc->phandle);
+}
+
+static void astbmc_fixup_dt_mbox(struct dt_node *lpc)
+{
+ struct dt_node *mbox;
+ char namebuf[32];
+
+ if (!lpc)
+ return;
+
+ /*
+ * P9 machines always use hiomap, either by ipmi or mbox. P8 machines
+ * can indicate they support mbox using the scratch register, or ipmi
+ * by configuring the hiomap ipmi command. If neither are configured
+ * for P8 then skiboot will drive the flash controller directly.
+ * XXX P10
+ */
+ if (proc_gen == proc_gen_p8 && !ast_scratch_reg_is_mbox())
+ return;
+
+ /* First check if the mbox interface is already there */
+ dt_for_each_child(lpc, mbox) {
+ if (dt_node_is_compatible(mbox, "mbox"))
+ return;
+ }
+
+ snprintf(namebuf, sizeof(namebuf), "mbox@i%x", MBOX_IO_BASE);
+ mbox = dt_new(lpc, namebuf);
+
+ dt_add_property_cells(mbox, "reg",
+ 1, /* IO space */
+ MBOX_IO_BASE, MBOX_IO_COUNT);
+ dt_add_property_strings(mbox, "compatible", "mbox");
+
+ /* Mark it as reserved to avoid Linux trying to claim it */
+ dt_add_property_strings(mbox, "status", "reserved");
+
+ dt_add_property_cells(mbox, "interrupts", MBOX_LPC_IRQ);
+ dt_add_property_cells(mbox, "interrupt-parent", lpc->phandle);
+}
+
+static void astbmc_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 */
+ snprintf(namebuf, sizeof(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 */
+ dt_add_property_cells(uart, "interrupts", UART_LPC_IRQ);
+ dt_add_property_cells(uart, "interrupt-parent", lpc->phandle);
+}
+
+static void del_compatible(struct dt_node *node)
+{
+ struct dt_property *prop;
+
+ prop = __dt_find_property(node, "compatible");
+ if (prop)
+ dt_del_property(node, prop);
+}
+
+
+static void astbmc_fixup_bmc_sensors(void)
+{
+ struct dt_node *parent, *node;
+
+ parent = dt_find_by_path(dt_root, "bmc");
+ if (!parent)
+ return;
+ del_compatible(parent);
+
+ parent = dt_find_by_name(parent, "sensors");
+ if (!parent)
+ return;
+ del_compatible(parent);
+
+ dt_for_each_child(parent, node) {
+ if (dt_find_property(node, "compatible"))
+ continue;
+ dt_add_property_string(node, "compatible", "ibm,ipmi-sensor");
+ }
+}
+
+static struct dt_node *dt_find_primary_lpc(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;
+ }
+ dt_for_each_compatible(dt_root, n, "ibm,power9-lpc") {
+ if (!primary_lpc || dt_has_node_property(n, "primary", NULL))
+ primary_lpc = n;
+ if (dt_has_node_property(n, "#address-cells", NULL))
+ break;
+ }
+
+ return primary_lpc;
+}
+
+static void astbmc_fixup_dt(void)
+{
+ struct dt_node *primary_lpc;
+
+ primary_lpc = dt_find_primary_lpc();
+
+ if (!primary_lpc)
+ return;
+
+ /* Fixup the UART, that might be missing from HB */
+ astbmc_fixup_dt_uart(primary_lpc);
+
+ /* BT is not in HB either */
+ astbmc_fixup_dt_bt(primary_lpc);
+
+ /* The pel logging code needs a system-id property to work so
+ make sure we have one. */
+ astbmc_fixup_dt_system_id();
+
+ if (proc_gen == proc_gen_p8)
+ astbmc_fixup_bmc_sensors();
+}
+
+static void astbmc_fixup_psi_bar(void)
+{
+ struct proc_chip *chip = next_chip(NULL);
+ uint64_t psibar;
+
+ /* This is P8 specific */
+ if (proc_gen != proc_gen_p8)
+ return;
+
+ /* 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 = 0x3fffe80000001UL;
+
+ 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 void astbmc_fixup_uart(void)
+{
+ /*
+ * Depending on which image we are running, it may be configuring the
+ * virtual UART or not. Check if VUART is enabled and use SIO if not.
+ * We also correct the configuration of VUART as some BMC images don't
+ * setup the interrupt properly
+ */
+ if (ast_is_vuart1_enabled()) {
+ printf("PLAT: Using virtual UART\n");
+ ast_disable_sio_uart1();
+ ast_setup_vuart1(UART_IO_BASE, UART_LPC_IRQ);
+ } else {
+ printf("PLAT: Using SuperIO UART\n");
+ ast_setup_sio_uart1(UART_IO_BASE, UART_LPC_IRQ);
+ }
+}
+
+void astbmc_early_init(void)
+{
+ /* Hostboot's device-tree isn't quite right yet */
+ astbmc_fixup_dt();
+
+ /* Hostboot forgets to populate the PSI BAR */
+ astbmc_fixup_psi_bar();
+
+ if (ast_sio_init()) {
+ if (ast_io_init()) {
+ astbmc_fixup_uart();
+ ast_setup_ibt(BT_IO_BASE, BT_LPC_IRQ);
+ } else
+ prerror("PLAT: AST IO initialisation failed!\n");
+
+ /*
+ * P9 prefers IPMI for HIOMAP but will use MBOX if IPMI is not
+ * supported. P8 either uses IPMI HIOMAP or direct IO, and
+ * never MBOX. Thus only populate the MBOX node on P9 to allow
+ * fallback.
+ */
+ if (proc_gen >= proc_gen_p9) {
+ astbmc_fixup_dt_mbox(dt_find_primary_lpc());
+ ast_setup_sio_mbox(MBOX_IO_BASE, MBOX_LPC_IRQ);
+ }
+ } else {
+ /*
+ * This may or may not be an error depending on if we set up
+ * hiomap or not. In the old days it *was* an error, but now
+ * with the way we configure the BMC hardware, this is actually
+ * the not error case.
+ */
+ prlog(PR_INFO, "PLAT: AST SIO unavailable!\n");
+ }
+
+ /* Setup UART and use it as console */
+ uart_init();
+
+ prd_init();
+}
+
+void astbmc_exit(void)
+{
+ ipmi_wdt_final_reset();
+}
+
+static const struct bmc_sw_config bmc_sw_ami = {
+ .ipmi_oem_partial_add_esel = IPMI_CODE(0x3a, 0xf0),
+ .ipmi_oem_pnor_access_status = IPMI_CODE(0x3a, 0x07),
+ .ipmi_oem_hiomap_cmd = IPMI_CODE(0x3a, 0x5a),
+};
+
+static const struct bmc_sw_config bmc_sw_openbmc = {
+ .ipmi_oem_partial_add_esel = IPMI_CODE(0x3a, 0xf0),
+ .ipmi_oem_hiomap_cmd = IPMI_CODE(0x3a, 0x5a),
+};
+
+/* Extracted from a Palmetto */
+const struct bmc_hw_config bmc_hw_ast2400 = {
+ .scu_revision_id = 0x2010303,
+ .mcr_configuration = 0x00000577,
+ .mcr_scu_mpll = 0x000050c0,
+ .mcr_scu_strap = 0x00000000,
+};
+
+/* Extracted from a Witherspoon */
+const struct bmc_hw_config bmc_hw_ast2500 = {
+ .scu_revision_id = 0x04030303,
+ .mcr_configuration = 0x11200756,
+ .mcr_scu_mpll = 0x000071C1,
+ .mcr_scu_strap = 0x00000000,
+};
+
+/* XXX P10: Update with Rainier values */
+const struct bmc_hw_config bmc_hw_ast2600 = {
+ .scu_revision_id = 0x05000303,
+ .mcr_configuration = 0x11200756,
+ .mcr_scu_mpll = 0x1008405F,
+ .mcr_scu_strap = 0x000030E0,
+};
+
+const struct bmc_platform bmc_plat_ast2400_ami = {
+ .name = "ast2400:ami",
+ .hw = &bmc_hw_ast2400,
+ .sw = &bmc_sw_ami,
+};
+
+const struct bmc_platform bmc_plat_ast2500_ami = {
+ .name = "ast2500:ami",
+ .hw = &bmc_hw_ast2500,
+ .sw = &bmc_sw_ami,
+};
+
+const struct bmc_platform bmc_plat_ast2500_openbmc = {
+ .name = "ast2500:openbmc",
+ .hw = &bmc_hw_ast2500,
+ .sw = &bmc_sw_openbmc,
+};
+
+const struct bmc_platform bmc_plat_ast2600_openbmc = {
+ .name = "ast2600:openbmc",
+ .hw = &bmc_hw_ast2600,
+ .sw = &bmc_sw_openbmc,
+};
diff --git a/roms/skiboot/platforms/astbmc/firestone.c b/roms/skiboot/platforms/astbmc/firestone.c
new file mode 100644
index 000000000..ae5603ebc
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/firestone.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+
+#include "astbmc.h"
+
+static const struct slot_table_entry firestone_phb0_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot5",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry firestone_phb0_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot4",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry firestone_phb8_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot2",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry firestone_plx_slots[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(1,0),
+ .name = "Slot3",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(9,0),
+ .name = "Backplane USB",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0xa,0),
+ .name = "Backplane SATA",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0xb,0),
+ .name = "Backplane BMC",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry firestone_plx_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = firestone_plx_slots,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry firestone_phb8_1_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Backplane PLX",
+ .children = firestone_plx_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry firestone_phb8_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry firestone_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,0),
+ .children = firestone_phb0_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,1),
+ .children = firestone_phb0_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,0),
+ .children = firestone_phb8_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,1),
+ .children = firestone_phb8_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,2),
+ .children = firestone_phb8_2_slot,
+ },
+ { .etype = st_end },
+};
+
+static bool firestone_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "ibm,firestone"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+ slot_table_init(firestone_phb_table);
+
+ return true;
+}
+
+
+DECLARE_PLATFORM(firestone) = {
+ .name = "Firestone",
+ .bmc = &bmc_plat_ast2400_ami,
+ .probe = firestone_probe,
+ .init = astbmc_init,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .external_irq = astbmc_ext_irq_serirq_cpld,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/astbmc/garrison.c b/roms/skiboot/platforms/astbmc/garrison.c
new file mode 100644
index 000000000..e698e2f68
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/garrison.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+#include <psi.h>
+#include <npu-regs.h>
+
+#include "astbmc.h"
+
+static const struct slot_table_entry garrison_phb0_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot3",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_phb0_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot2",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_phb0_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "GPU1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_phb0_3_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "GPU2",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_npu0_slots[] = {
+ {
+ .etype = st_npu_slot,
+ .location = ST_LOC_NPU_GROUP(0),
+ .name = "GPU2",
+ },
+ {
+ .etype = st_npu_slot,
+ .location = ST_LOC_NPU_GROUP(1),
+ .name = "GPU1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_phb1_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_plx_slots[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(1,0),
+ .name = "Backplane USB",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(2,0),
+ .name = "Backplane SATA",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(3,0),
+ .name = "Backplane BMC",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_plx_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = garrison_plx_slots,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_phb1_1_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Backplane PLX",
+ .children = garrison_plx_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_phb1_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "GPU3",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_phb1_3_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "GPU4",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_npu1_slots[] = {
+ {
+ .etype = st_npu_slot,
+ .location = ST_LOC_NPU_GROUP(0),
+ .name = "GPU4",
+ },
+ {
+ .etype = st_npu_slot,
+ .location = ST_LOC_NPU_GROUP(1),
+ .name = "GPU3",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry garrison_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,0),
+ .children = garrison_phb0_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,1),
+ .children = garrison_phb0_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,2),
+ .children = garrison_phb0_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,3),
+ .children = garrison_phb0_3_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,4),
+ .children = garrison_npu0_slots,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(1,0),
+ .children = garrison_phb1_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(1,1),
+ .children = garrison_phb1_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(1,2),
+ .children = garrison_phb1_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(1,3),
+ .children = garrison_phb1_3_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(1,4),
+ .children = garrison_npu1_slots,
+ },
+ { .etype = st_end },
+};
+
+#define NPU_BASE 0x8013c00
+#define NPU_SIZE 0x2c
+#define NPU_INDIRECT0 0x8000000008010c3fUL
+#define NPU_INDIRECT1 0x8000000008010c7fUL
+
+static void create_link(struct dt_node *npu, int group, int index)
+{
+ struct dt_node *link;
+ uint32_t lane_mask;
+ uint64_t phy;
+ char namebuf[32];
+
+ snprintf(namebuf, sizeof(namebuf), "link@%x", index);
+ link = dt_new(npu, namebuf);
+
+ dt_add_property_string(link, "compatible", "ibm,npu-link");
+ dt_add_property_cells(link, "ibm,npu-link-index", index);
+
+ if (index < 4) {
+ phy = NPU_INDIRECT0;
+ lane_mask = 0xff << (index * 8);
+ } else {
+ phy = NPU_INDIRECT1;
+ lane_mask = 0xff0000 >> (index - 3) * 8;
+ }
+ dt_add_property_u64s(link, "ibm,npu-phy", phy);
+ dt_add_property_cells(link, "ibm,npu-lane-mask", lane_mask);
+ dt_add_property_cells(link, "ibm,npu-group-id", group);
+}
+
+static void dt_create_npu(void)
+{
+ struct dt_node *xscom, *npu;
+ char namebuf[32];
+
+ dt_for_each_compatible(dt_root, xscom, "ibm,xscom") {
+ snprintf(namebuf, sizeof(namebuf), "npu@%x", NPU_BASE);
+ npu = dt_new(xscom, namebuf);
+ dt_add_property_cells(npu, "reg", NPU_BASE, NPU_SIZE);
+ dt_add_property_strings(npu, "compatible", "ibm,power8-npu");
+
+ /* Use the first available PHB index which is 4 given
+ * there are three normal PHBs. */
+ dt_add_property_cells(npu, "ibm,phb-index", 4);
+ dt_add_property_cells(npu, "ibm,npu-index", 0);
+ dt_add_property_cells(npu, "ibm,npu-links", 4);
+
+ /* On Garrison we have 2 links per GPU device. These are
+ * grouped together as per the slot tables above. */
+ create_link(npu, 0, 0);
+ create_link(npu, 0, 1);
+ create_link(npu, 1, 4);
+ create_link(npu, 1, 5);
+ }
+}
+
+static bool garrison_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "ibm,garrison"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Fixups until HB get the NPU bindings */
+ dt_create_npu();
+
+ slot_table_init(garrison_phb_table);
+
+ return true;
+}
+
+DECLARE_PLATFORM(garrison) = {
+ .name = "Garrison",
+ .bmc = &bmc_plat_ast2400_ami,
+ .probe = garrison_probe,
+ .init = astbmc_init,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .seeprom_update = astbmc_seeprom_update,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/astbmc/habanero.c b/roms/skiboot/platforms/astbmc/habanero.c
new file mode 100644
index 000000000..b98ff8960
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/habanero.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+
+#include "astbmc.h"
+
+static const struct slot_table_entry habanero_phb0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot3",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry habanero_plx_slots[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(1,0),
+ .name = "Network Mezz",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(8,0),
+ .name = "Storage Mezz",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(9,0),
+ .name = "Backplane USB",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0xa,0),
+ .name = "Backplane BMC",
+ },
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0x10,0),
+ .name = "Slot2",
+ },
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0x11,0),
+ .name = "Slot1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry habanero_plx_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = habanero_plx_slots,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry habanero_phb1_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Backplane PLX",
+ .children = habanero_plx_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry habanero_phb2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot4",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry habanero_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,0),
+ .children = habanero_phb0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,1),
+ .children = habanero_phb1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,2),
+ .children = habanero_phb2_slot,
+ },
+ { .etype = st_end },
+};
+
+static bool habanero_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, "habanero")) &&
+ (!dt_node_is_compatible(dt_root, "tyan,habanero")))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ slot_table_init(habanero_phb_table);
+
+ return true;
+}
+
+DECLARE_PLATFORM(habanero) = {
+ .name = "Habanero",
+ .bmc = &bmc_plat_ast2400_ami,
+ .probe = habanero_probe,
+ .init = astbmc_init,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .external_irq = astbmc_ext_irq_serirq_cpld,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .seeprom_update = astbmc_seeprom_update,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/astbmc/mihawk.c b/roms/skiboot/platforms/astbmc/mihawk.c
new file mode 100644
index 000000000..54c288416
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/mihawk.c
@@ -0,0 +1,567 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright 2019 Wistron Corp.
+ * Copyright 2017 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+#include <psi.h>
+#include <npu-regs.h>
+#include <npu2.h>
+#include <pci.h>
+#include <pci-cfg.h>
+
+#include <timebase.h>
+
+#include "astbmc.h"
+
+/* IPMI message code for Riser-F query (OEM). */
+#define IPMI_RISERF_QUERY IPMI_CODE(0x32, 0x01)
+
+static bool mihawk_riserF_found = false;
+static bool bmc_query_waiting = false;
+
+#define OPAL_ID_SLOT2 0x01
+#define OPAL_ID_SLOT4 0x03
+#define OPAL_ID_SLOT7 0x31
+#define OPAL_ID_SLOT9 0x33
+
+/* nvme backplane slots */
+static const struct slot_table_entry hdd_bay_s2_slots[] = {
+ SW_PLUGGABLE("nvme13", 0x0),
+ SW_PLUGGABLE("nvme14", 0x1),
+ SW_PLUGGABLE("nvme15", 0x2),
+ SW_PLUGGABLE("nvme16", 0x3),
+
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry hdd_bay_s4_slots[] = {
+ SW_PLUGGABLE("nvme17", 0x0),
+ SW_PLUGGABLE("nvme18", 0x1),
+ SW_PLUGGABLE("nvme19", 0x2),
+ SW_PLUGGABLE("nvme20", 0x3),
+ SW_PLUGGABLE("nvme21", 0x4),
+ SW_PLUGGABLE("nvme22", 0x5),
+ SW_PLUGGABLE("nvme23", 0x6),
+ SW_PLUGGABLE("nvme24", 0x7),
+
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry hdd_bay_s7_slots[] = {
+ SW_PLUGGABLE("nvme9", 0x0),
+ SW_PLUGGABLE("nvme10", 0x1),
+ SW_PLUGGABLE("nvme11", 0x2),
+ SW_PLUGGABLE("nvme12", 0x3),
+
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry hdd_bay_s9_slots[] = {
+ SW_PLUGGABLE("nvme1", 0x0),
+ SW_PLUGGABLE("nvme2", 0x1),
+ SW_PLUGGABLE("nvme3", 0x2),
+ SW_PLUGGABLE("nvme4", 0x3),
+ SW_PLUGGABLE("nvme5", 0x4),
+ SW_PLUGGABLE("nvme6", 0x5),
+ SW_PLUGGABLE("nvme7", 0x6),
+ SW_PLUGGABLE("nvme8", 0x7),
+
+ { .etype = st_end },
+};
+
+static void mihawk_get_slot_info(struct phb *phb, struct pci_device *pd)
+{
+ const struct slot_table_entry *ent = NULL;
+
+ if (!pd || pd->slot)
+ return;
+
+ /*
+ * If we find a 8533 or c012 switch then assume it's the NVMe Rack.
+ * This might break if we have another switch with the same vdid in
+ * the system for some reason. This is a really dumb hack, but until
+ * we get query the BMC about wether we have a HDD rack or not we
+ * don't have much of a choice.
+ */
+ if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT) {
+ if (pd->vdid == 0x853311f8) { // for microsemi controller
+ for (ent = hdd_bay_s9_slots; ent->etype != st_end; ent++)
+ if (ent->location == (pd->bdfn & 0xff))
+ break;
+ } else if (pd->vdid == 0xc0121000) { // for broadcom nvme hba
+ switch (phb->opal_id) {
+ case OPAL_ID_SLOT2:
+ ent = hdd_bay_s2_slots;
+ break;
+ case OPAL_ID_SLOT4:
+ ent = hdd_bay_s4_slots;
+ break;
+ case OPAL_ID_SLOT7:
+ ent = hdd_bay_s7_slots;
+ break;
+ case OPAL_ID_SLOT9:
+ default:
+ ent = hdd_bay_s9_slots;
+ break;
+ }
+
+ for (; ent->etype != st_end; ent++)
+ if (ent->location == (pd->bdfn & 0xff))
+ break;
+ }
+ }
+
+ if (ent)
+ slot_table_add_slot_info(pd, ent);
+ else
+ slot_table_get_slot_info(phb, pd);
+}
+
+static const char *mihawk_ocapi_slot_label(uint32_t chip_id,
+ uint32_t brick_index)
+{
+ const char *name = NULL;
+
+ if (chip_id == 0) {
+ if (brick_index == 2)
+ name = "JP90NVB1";
+ else
+ name = "JP90NVT1";
+ } else {
+ if (brick_index == 2)
+ name = "JP91NVB1";
+ else
+ name = "JP91NVT1";
+ }
+ return name;
+}
+
+static const struct ocapi_phy_setup mihawk_phy = {
+ .tx_ffe_pre_coeff = 0x3,
+ .tx_ffe_post_coeff = 0x14,
+ .tx_ffe_boost_en = 0,
+};
+
+static const struct platform_ocapi mihawk_ocapi = {
+ .i2c_engine = 1,
+ .i2c_port = 4,
+ .i2c_reset_addr = 0x20,
+ .i2c_reset_brick2 = (1 << 1),
+ .i2c_reset_brick3 = (1 << 6),
+ .i2c_reset_brick4 = 0, /* unused */
+ .i2c_reset_brick5 = 0, /* unused */
+ .i2c_presence_addr = 0x20,
+ .i2c_presence_brick2 = (1 << 2), /* bottom connector */
+ .i2c_presence_brick3 = (1 << 7), /* top connector */
+ .i2c_presence_brick4 = 0, /* unused */
+ .i2c_presence_brick5 = 0, /* unused */
+ .odl_phy_swap = true,
+ .ocapi_slot_label = mihawk_ocapi_slot_label,
+ .phy_setup = &mihawk_phy,
+};
+
+static const struct slot_table_entry P1E1A_x8_PLX8748_RiserA_down[] = {
+ SW_PLUGGABLE("Slot7", 0x10),
+ SW_PLUGGABLE("Slot8", 0x8),
+ SW_PLUGGABLE("Slot10", 0x9),
+
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry P1E1A_x8_PLX8748_RiserA_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P1E1A_x8_PLX8748_RiserA_down,
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry p1phb1_rA_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P1E1A_x8_PLX8748_RiserA_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry P0E1A_x8_PLX8748_RiserA_down[] = {
+ SW_PLUGGABLE("Slot2", 0x10),
+ SW_PLUGGABLE("Slot3", 0x8),
+ SW_PLUGGABLE("Slot5", 0x9),
+
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry P0E1A_x8_PLX8748_RiserA_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P0E1A_x8_PLX8748_RiserA_down,
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry p0phb1_rA_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P0E1A_x8_PLX8748_RiserA_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry P1E1A_x8_PLX8748_RiserF_down[] = {
+ SW_PLUGGABLE("Slot7", 0x10),
+ SW_PLUGGABLE("Slot10", 0x9),
+
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry P1E1A_x8_PLX8748_RiserF_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P1E1A_x8_PLX8748_RiserF_down,
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry p1phb1_rF_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P1E1A_x8_PLX8748_RiserF_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry P0E1A_x8_PLX8748_RiserF_down[] = {
+ SW_PLUGGABLE("Slot2", 0x10),
+ SW_PLUGGABLE("Slot5", 0x9),
+
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry P0E1A_x8_PLX8748_RiserF_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P0E1A_x8_PLX8748_RiserF_down,
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry p0phb1_rF_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P0E1A_x8_PLX8748_RiserF_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry P1E2_x16_Switch_down[] = {
+ SW_PLUGGABLE("Slot8", 0x1),
+ SW_PLUGGABLE("Slot9", 0x0),
+
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry P1E2_x16_Switch_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P1E2_x16_Switch_down,
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry p1phb3_switch_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P1E2_x16_Switch_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry P0E2_x16_Switch_down[] = {
+ SW_PLUGGABLE("Slot3", 0x1),
+ SW_PLUGGABLE("Slot4", 0x0),
+
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry P0E2_x16_Switch_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P0E2_x16_Switch_down,
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry p0phb3_switch_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = P0E2_x16_Switch_up,
+ },
+ { .etype = st_end },
+};
+
+ST_PLUGGABLE(p0phb0_slot, "Slot1");
+ST_PLUGGABLE(p0phb3_slot, "Slot4");
+ST_PLUGGABLE(p1phb0_slot, "Slot6");
+ST_PLUGGABLE(p1phb3_slot, "Slot9");
+
+static const struct slot_table_entry mihawk_riserA_phb_table[] = {
+ /* ==== CPU0 ==== */
+ ST_PHB_ENTRY(0, 0, p0phb0_slot), /* P0E0_x16_Slot1 */
+ ST_PHB_ENTRY(0, 1, p0phb1_rA_slot), /* P0E1A_x8_PLX8748-1_Slot2-3-5 */
+ //ST_PHB_ENTRY(0, 2, p0phb2_slot), /* P0E1B_x8_USBTI7340 */
+ ST_PHB_ENTRY(0, 3, p0phb3_slot), /* P0E2_x16_Slot4 */
+
+ /* ==== CPU1 ==== */
+ ST_PHB_ENTRY(8, 0, p1phb0_slot), /* P1E0_x16_Slot6 */
+ ST_PHB_ENTRY(8, 1, p1phb1_rA_slot), /* P1E1A_x8_PLX8748-2_Slot7-8-10 */
+ //ST_PHB_ENTRY(8, 2, p1phb2_slot), /* P1E1B_x8_NA */
+ ST_PHB_ENTRY(8, 3, p1phb3_slot), /* P1E2_x16_Slot9 */
+
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry mihawk_riserF_phb_table[] = {
+ /* ==== CPU0 ==== */
+ ST_PHB_ENTRY(0, 0, p0phb0_slot), /* P0E0_x16_Slot1 */
+ ST_PHB_ENTRY(0, 1, p0phb1_rF_slot), /* P0E1A_x8_PLX8748-1_Slot2-5 */
+ //ST_PHB_ENTRY(0, 2, p0phb2_slot), /* P0E1B_x8_USBTI7340 */
+ ST_PHB_ENTRY(0, 3, p0phb3_switch_slot),/* P0E2_x16_SWITCH_Slot3-4 */
+
+ /* ==== CPU1 ==== */
+ ST_PHB_ENTRY(8, 0, p1phb0_slot), /* P1E0_x16_Slot6 */
+ ST_PHB_ENTRY(8, 1, p1phb1_rF_slot), /* P1E1A_x8_PLX8748-2_Slot7-10 */
+ //ST_PHB_ENTRY(8, 2, p1phb2_slot), /* P1E1B_x8_NA */
+ ST_PHB_ENTRY(8, 3, p1phb3_switch_slot),/* P1E2_x16_SWITCH_Slot8-9 */
+
+ { .etype = st_end },
+};
+
+#define NPU_BASE 0x5011000
+#define NPU_SIZE 0x2c
+#define NPU_INDIRECT0 0x8000000009010c3fUL /* OB0 - no OB3 on Mihawk */
+
+/* OpenCAPI only */
+static void create_link(struct dt_node *npu, int group, int index)
+{
+ struct dt_node *link;
+ uint32_t lane_mask;
+ char namebuf[32];
+
+ snprintf(namebuf, sizeof(namebuf), "link@%x", index);
+ link = dt_new(npu, namebuf);
+ assert(link);
+
+ dt_add_property_string(link, "compatible", "ibm,npu-link");
+ dt_add_property_cells(link, "ibm,npu-link-index", index);
+
+ switch (index) {
+ case 2:
+ lane_mask = 0xf1e000; /* 0-3, 7-10 */
+ break;
+ case 3:
+ lane_mask = 0x00078f; /* 13-16, 20-23 */
+ break;
+ default:
+ assert(0);
+ }
+
+ dt_add_property_u64s(link, "ibm,npu-phy", NPU_INDIRECT0);
+ dt_add_property_cells(link, "ibm,npu-lane-mask", lane_mask);
+ dt_add_property_cells(link, "ibm,npu-group-id", group);
+ dt_add_property_u64s(link, "ibm,link-speed", 25000000000ul);
+}
+
+/* FIXME: Get rid of this after we get NPU information properly via HDAT/MRW */
+static void mihawk_create_npu(void)
+{
+ struct dt_node *xscom, *npu;
+ int npu_index = 0;
+ char namebuf[32];
+
+ /* Return if there's already an NPU in the device tree */
+ if (dt_find_compatible_node(dt_root, NULL, "ibm,power9-npu"))
+ return;
+
+ prlog(PR_DEBUG, "OCAPI: Adding NPU device nodes\n");
+ dt_for_each_compatible(dt_root, xscom, "ibm,xscom") {
+ snprintf(namebuf, sizeof(namebuf), "npu@%x", NPU_BASE);
+ npu = dt_new(xscom, namebuf);
+ dt_add_property_cells(npu, "reg", NPU_BASE, NPU_SIZE);
+ dt_add_property_strings(npu, "compatible", "ibm,power9-npu");
+ dt_add_property_cells(npu, "ibm,npu-index", npu_index++);
+ dt_add_property_cells(npu, "ibm,npu-links", 2);
+ create_link(npu, 1, 2);
+ create_link(npu, 2, 3);
+ }
+}
+
+/* FIXME: Get rid of this after we get NPU information properly via HDAT/MRW */
+static void mihawk_create_ocapi_i2c_bus(void)
+{
+ struct dt_node *xscom, *i2cm, *i2c_bus;
+ prlog(PR_DEBUG, "OCAPI: Adding I2C bus device node for OCAPI reset\n");
+ dt_for_each_compatible(dt_root, xscom, "ibm,xscom") {
+ i2cm = dt_find_by_name(xscom, "i2cm@a1000");
+ if (!i2cm) {
+ prlog(PR_ERR, "OCAPI: Failed to get I2C bus device node\n");
+ continue;
+ }
+
+ if (dt_find_by_name(i2cm, "i2c-bus@4"))
+ continue;
+
+ i2c_bus = dt_new_addr(i2cm, "i2c-bus", 4);
+ dt_add_property_cells(i2c_bus, "reg", 4);
+ dt_add_property_cells(i2c_bus, "bus-frequency", 0x61a80);
+ dt_add_property_strings(i2c_bus, "compatible",
+ "ibm,opal-i2c", "ibm,power8-i2c-port",
+ "ibm,power9-i2c-port");
+ }
+}
+
+/*
+ * HACK: Hostboot doesn't export the correct data for the system VPD EEPROM
+ * for this system. So we need to work around it here.
+ */
+static void vpd_dt_fixup(void)
+{
+ struct dt_node *n = dt_find_by_path(dt_root,
+ "/xscom@603fc00000000/i2cm@a2000/i2c-bus@0/eeprom@50");
+
+ if (n) {
+ dt_check_del_prop(n, "compatible");
+ dt_add_property_string(n, "compatible", "atmel,24c512");
+
+ dt_check_del_prop(n, "label");
+ dt_add_property_string(n, "label", "system-vpd");
+ }
+}
+
+static bool mihawk_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "ibm,mihawk") &&
+ !dt_node_is_compatible(dt_root, "wistron,mihawk"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Setup UART for use by OPAL (Linux hvc) */
+ uart_set_console_policy(UART_CONSOLE_OPAL);
+
+ vpd_dt_fixup();
+
+ mihawk_create_npu();
+ mihawk_create_ocapi_i2c_bus();
+
+ return true;
+}
+
+static void mihawk_riser_query_complete(struct ipmi_msg *msg)
+{
+ uint8_t *riser_state;
+
+ if (msg->cc != IPMI_CC_NO_ERROR) {
+ prlog(PR_ERR, "Mihawk: IPMI riser query returned error. cmd=0x%02x,"
+ " netfn=0x%02x, rc=0x%x\n", msg->cmd, msg->netfn, msg->cc);
+ bmc_query_waiting = false;
+ ipmi_free_msg(msg);
+ return;
+ }
+
+ prlog(PR_DEBUG, "Mihawk: IPMI Got riser query result. p0:%02x, p1:%02x\n"
+ , msg->data[0], msg->data[1]);
+
+ riser_state = (uint8_t*)msg->user_data;
+ lwsync();
+ *riser_state = msg->data[0] << 4 | msg->data[1];
+
+ bmc_query_waiting = false;
+ ipmi_free_msg(msg);
+}
+
+static void mihawk_init(void)
+{
+ struct ipmi_msg *ipmi_msg;
+ uint8_t riser_state = 0;
+ int timeout_ms = 3000;
+
+ astbmc_init();
+
+ /*
+ * We use IPMI to ask BMC if Riser-F is installed and set up the
+ * corresponding slot table.
+ */
+ ipmi_msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
+ IPMI_RISERF_QUERY,
+ mihawk_riser_query_complete,
+ &riser_state, NULL, 0, 2);
+
+ if (!ipmi_msg) {
+ prlog(PR_ERR, "Mihawk: Couldn't create ipmi msg.");
+ } else {
+ ipmi_msg->error = mihawk_riser_query_complete;
+ ipmi_queue_msg(ipmi_msg);
+ bmc_query_waiting = true;
+
+ prlog(PR_DEBUG, "Mihawk: Requesting IPMI_RISERF_QUERY (netfn "
+ "%02x, cmd %02x)\n", ipmi_msg->netfn, ipmi_msg->cmd);
+
+ while (bmc_query_waiting) {
+ time_wait_ms(10);
+ timeout_ms -= 10;
+
+ if (timeout_ms == 0)
+ break;
+ }
+ }
+
+ prlog(PR_DEBUG, "Mihawk: IPMI_RISERF_QUERY finish. riser_state: %02x"
+ ", waiting: %d\n", riser_state, bmc_query_waiting);
+
+ if (riser_state != 0) {
+ mihawk_riserF_found = true;
+ slot_table_init(mihawk_riserF_phb_table);
+ prlog(PR_DEBUG, "Mihawk: Detect Riser-F via IPMI\n");
+ } else {
+ slot_table_init(mihawk_riserA_phb_table);
+ prlog(PR_DEBUG, "Mihawk: No Riser-F found, use Riser-A table\n");
+ }
+}
+
+DECLARE_PLATFORM(mihawk) = {
+ .name = "Mihawk",
+ .probe = mihawk_probe,
+ .init = mihawk_init,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .bmc = &bmc_plat_ast2500_openbmc,
+ .pci_get_slot_info = mihawk_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .ocapi = &mihawk_ocapi,
+ .npu2_device_detect = npu2_i2c_presence_detect,
+};
diff --git a/roms/skiboot/platforms/astbmc/mowgli.c b/roms/skiboot/platforms/astbmc/mowgli.c
new file mode 100644
index 000000000..df83319de
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/mowgli.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Copyright 2020 Wistron Corp.
+ * Copyright 2017-2019 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+#include <psi.h>
+#include <npu-regs.h>
+#include <secvar.h>
+
+#include "astbmc.h"
+
+ST_PLUGGABLE(mowgli_slot1, "Pcie Slot1");
+ST_PLUGGABLE(mowgli_builtin_SAS, "Builtin SAS");
+ST_BUILTIN_DEV(mowgli_builtin_bmc, "BMC");
+ST_PLUGGABLE(mowgli_builtin_ethernet, "Builtin Ethernet");
+ST_BUILTIN_DEV(mowgli_builtin_usb, "Builtin USB");
+
+static const struct slot_table_entry mowgli_phb_table[] = {
+ ST_PHB_ENTRY(0, 0, mowgli_slot1),
+ ST_PHB_ENTRY(0, 1, mowgli_builtin_SAS),
+ ST_PHB_ENTRY(0, 2, mowgli_builtin_bmc),
+ ST_PHB_ENTRY(0, 3, mowgli_builtin_ethernet),
+ ST_PHB_ENTRY(0, 4, mowgli_builtin_usb),
+
+ { .etype = st_end },
+};
+
+/*
+ * HACK: Hostboot doesn't export the correct data for the system VPD EEPROM
+ * for this system. So we need to work around it here.
+ */
+static void vpd_dt_fixup(void)
+{
+ struct dt_node *n = dt_find_by_path(dt_root,
+ "/xscom@603fc00000000/i2cm@a2000/i2c-bus@0/eeprom@50");
+
+ if (n) {
+ dt_check_del_prop(n, "compatible");
+ dt_add_property_string(n, "compatible", "atmel,24c512");
+
+ dt_check_del_prop(n, "label");
+ dt_add_property_string(n, "label", "system-vpd");
+ }
+}
+
+static void phb0_fixup(void)
+{
+ struct dt_node *stk;
+ u32 phb_index;
+
+ /* Limit PHB0/(pec0) to gen3 speed */
+ dt_for_each_compatible(dt_root, stk, "ibm,power9-phb-stack") {
+ phb_index = dt_prop_get_u32_def(stk, "ibm,phb-index", -1);
+ if (phb_index == 0) {
+ dt_check_del_prop(stk, "ibm,max-link-speed");
+ dt_add_property_cells(stk, "ibm,max-link-speed", 3);
+ }
+ }
+}
+
+static bool mowgli_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "ibm,mowgli"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Setup UART for use by OPAL (Linux hvc) */
+ uart_set_console_policy(UART_CONSOLE_OPAL);
+
+ vpd_dt_fixup();
+
+ slot_table_init(mowgli_phb_table);
+ phb0_fixup();
+
+ return true;
+}
+static int mowgli_secvar_init(void)
+{
+ return secvar_main(secboot_tpm_driver, edk2_compatible_v1);
+}
+
+
+DECLARE_PLATFORM(mowgli) = {
+ .name = "Mowgli",
+ .probe = mowgli_probe,
+ .init = astbmc_init,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .bmc = &bmc_plat_ast2500_openbmc,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .exit = astbmc_exit,
+ .terminate = ipmi_terminate,
+ .op_display = op_display_lpc,
+ .secvar_init = mowgli_secvar_init,
+};
diff --git a/roms/skiboot/platforms/astbmc/nicole.c b/roms/skiboot/platforms/astbmc/nicole.c
new file mode 100644
index 000000000..f21d0361a
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/nicole.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 YADRO
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <ipmi.h>
+
+#include "astbmc.h"
+
+#define CHIP_ID_CPU0 0x00
+#define CHIP_ID_CPU1 0x08
+
+ST_PLUGGABLE(nicole_backplane0, "Backplane0 (16x)");
+ST_PLUGGABLE(nicole_backplane1, "Backplane1 (16x)");
+
+ST_BUILTIN_DEV(nicole_builtin_net, "Builtin Network");
+ST_BUILTIN_DEV(nicole_builtin_ssd0, "Builtin SSD0");
+ST_BUILTIN_DEV(nicole_builtin_ssd1, "Builtin SSD1");
+ST_BUILTIN_DEV(nicole_builtin_vga, "Builtin VGA");
+ST_BUILTIN_DEV(nicole_builtin_usb, "Builtin USB");
+
+static const struct slot_table_entry nicole_phb_table[] = {
+ ST_PHB_ENTRY(CHIP_ID_CPU0, 0, nicole_backplane0),
+ ST_PHB_ENTRY(CHIP_ID_CPU0, 1, nicole_builtin_net),
+ ST_PHB_ENTRY(CHIP_ID_CPU0, 2, nicole_builtin_ssd0),
+ ST_PHB_ENTRY(CHIP_ID_CPU0, 3, nicole_backplane1),
+
+ ST_PHB_ENTRY(CHIP_ID_CPU1, 3, nicole_builtin_ssd1),
+ ST_PHB_ENTRY(CHIP_ID_CPU1, 4, nicole_builtin_vga),
+ ST_PHB_ENTRY(CHIP_ID_CPU1, 5, nicole_builtin_usb),
+
+ { .etype = st_end },
+};
+
+/* Fixup the system VPD EEPROM size.
+ *
+ * Hostboot doesn't export the correct description for EEPROMs, as a result,
+ * all EEPROMs in the system work in "atmel,24c128" compatibility mode (16KiB).
+ * Nicole platform has 32KiB EEPROM for the system VPD.
+ */
+static void vpd_dt_fixup(void)
+{
+ struct dt_node* vpd_eeprom = dt_find_by_path(dt_root,
+ "/xscom@603fc00000000/i2cm@a2000/i2c-bus@0/eeprom@50");
+
+ if (vpd_eeprom) {
+ dt_check_del_prop(vpd_eeprom, "compatible");
+ dt_add_property_string(vpd_eeprom, "compatible", "atmel,24c256");
+
+ dt_check_del_prop(vpd_eeprom, "label");
+ dt_add_property_string(vpd_eeprom, "label", "system-vpd");
+ }
+}
+
+static bool nicole_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "YADRO,nicole"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Setup UART for use by OPAL (Linux hvc) */
+ uart_set_console_policy(UART_CONSOLE_OPAL);
+
+ /* Fixup system VPD EEPROM size */
+ vpd_dt_fixup();
+
+ slot_table_init(nicole_phb_table);
+
+ return true;
+}
+
+DECLARE_PLATFORM(nicole) = {
+ .name = "Nicole",
+ .probe = nicole_probe,
+ .init = astbmc_init,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .bmc = &bmc_plat_ast2500_openbmc,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .exit = astbmc_exit,
+ .terminate = ipmi_terminate,
+};
diff --git a/roms/skiboot/platforms/astbmc/p8dnu.c b/roms/skiboot/platforms/astbmc/p8dnu.c
new file mode 100644
index 000000000..e223d158b
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/p8dnu.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright 2017 Supermicro
+ * Copyright 2017-2019 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+#include <psi.h>
+#include <npu-regs.h>
+
+#include "astbmc.h"
+
+static const struct slot_table_entry p8dnu_phb0_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "UIO SLOT1",
+ },
+ { .etype = st_end },
+};
+
+
+static const struct slot_table_entry p8dnu_plx_slots_00[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(1,0),
+ .name = "Onboard SATA Marvell 88SE9230",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(2,0),
+ .name = "Slot_DUIO ",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(8,0),
+ .name = "Intel LAN X710/X557-AT",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(9,0),
+ .name = "Onboard VGA AST2400",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0xa,0),
+ .name = "Onboard USB TI TUSB7340",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_plx_up_00[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = p8dnu_plx_slots_00,
+ },
+ { .etype = st_end },
+};
+
+
+
+static const struct slot_table_entry p8dnu_phb0_1_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Backplane PLX VS0",
+ .children = p8dnu_plx_up_00,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_phb0_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "GPU1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_phb0_3_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "GPU2",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_npu0_slots[] = {
+ {
+ .etype = st_npu_slot,
+ .location = ST_LOC_NPU_GROUP(0),
+ .name = "GPU2",
+ },
+ {
+ .etype = st_npu_slot,
+ .location = ST_LOC_NPU_GROUP(1),
+ .name = "GPU1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_phb1_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO SLOT1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_plx_slots[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(5,0),
+ .name = "RSC-R1UW-E8R SLOT1",
+ },
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0xd,0),
+ .name = "WIO SLOT2",
+ },
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0xc,0),
+ .name = "WIO SLOT3",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_plx_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = p8dnu_plx_slots,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_phb1_1_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Backplane PLX VS1",
+ .children = p8dnu_plx_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_phb1_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "GPU3",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_phb1_3_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "GPU4",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_npu1_slots[] = {
+ {
+ .etype = st_npu_slot,
+ .location = ST_LOC_NPU_GROUP(0),
+ .name = "GPU4",
+ },
+ {
+ .etype = st_npu_slot,
+ .location = ST_LOC_NPU_GROUP(1),
+ .name = "GPU3",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dnu_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,0),
+ .children = p8dnu_phb0_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,1),
+ .children = p8dnu_phb0_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,2),
+ .children = p8dnu_phb0_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,3),
+ .children = p8dnu_phb0_3_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,4),
+ .children = p8dnu_npu0_slots,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(1,0),
+ .children = p8dnu_phb1_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(1,1),
+ .children = p8dnu_phb1_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(1,2),
+ .children = p8dnu_phb1_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(1,3),
+ .children = p8dnu_phb1_3_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(1,4),
+ .children = p8dnu_npu1_slots,
+ },
+ { .etype = st_end },
+};
+
+#define NPU_BASE 0x8013c00
+#define NPU_SIZE 0x2c
+#define NPU_INDIRECT0 0x8000000008010c3fUL
+#define NPU_INDIRECT1 0x8000000008010c7fUL
+
+static void create_link(struct dt_node *npu, int group, int index)
+{
+ struct dt_node *link;
+ uint32_t lane_mask;
+ uint64_t phy;
+ char namebuf[32];
+
+ snprintf(namebuf, sizeof(namebuf), "link@%x", index);
+ link = dt_new(npu, namebuf);
+
+ dt_add_property_string(link, "compatible", "ibm,npu-link");
+ dt_add_property_cells(link, "ibm,npu-link-index", index);
+
+ if (index < 4) {
+ phy = NPU_INDIRECT0;
+ lane_mask = 0xff << (index * 8);
+ } else {
+ phy = NPU_INDIRECT1;
+ lane_mask = 0xff0000 >> (index - 3) * 8;
+ }
+ dt_add_property_u64s(link, "ibm,npu-phy", phy);
+ dt_add_property_cells(link, "ibm,npu-lane-mask", lane_mask);
+ dt_add_property_cells(link, "ibm,npu-group-id", group);
+}
+
+static void dt_create_npu(void)
+{
+ struct dt_node *xscom, *npu;
+ char namebuf[32];
+
+ dt_for_each_compatible(dt_root, xscom, "ibm,xscom") {
+ snprintf(namebuf, sizeof(namebuf), "npu@%x", NPU_BASE);
+ npu = dt_new(xscom, namebuf);
+ dt_add_property_cells(npu, "reg", NPU_BASE, NPU_SIZE);
+ dt_add_property_strings(npu, "compatible", "ibm,power8-npu");
+
+ /*
+ * Use the first available PHB index which is 4 given
+ * there are three normal PHBs.
+ */
+ dt_add_property_cells(npu, "ibm,phb-index", 4);
+ dt_add_property_cells(npu, "ibm,npu-index", 0);
+ dt_add_property_cells(npu, "ibm,npu-links", 4);
+
+ /*
+ * On p8dnu we have 2 links per GPU device. These are
+ * grouped together as per the slot tables above.
+ */
+ create_link(npu, 0, 0);
+ create_link(npu, 0, 1);
+ create_link(npu, 1, 4);
+ create_link(npu, 1, 5);
+ }
+}
+
+static bool p8dnu_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "supermicro,p8dnu"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Fixups until HB get the NPU bindings */
+ dt_create_npu();
+
+ slot_table_init(p8dnu_phb_table);
+
+ return true;
+}
+
+static const struct bmc_sw_config bmc_sw_smc = {
+ .ipmi_oem_partial_add_esel = IPMI_CODE(0x3a, 0xf0),
+ .ipmi_oem_pnor_access_status = IPMI_CODE(0x3a, 0x07),
+};
+
+static const struct bmc_platform bmc_plat_ast2400_smc = {
+ .name = "SMC",
+ .hw = &bmc_hw_ast2400,
+ .sw = &bmc_sw_smc,
+};
+
+DECLARE_PLATFORM(p8dnu) = {
+ .name = "P8DNU",
+ .probe = p8dnu_probe,
+ .bmc = &bmc_plat_ast2400_smc,
+ .init = astbmc_init,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .seeprom_update = astbmc_seeprom_update,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/astbmc/p8dtu.c b/roms/skiboot/platforms/astbmc/p8dtu.c
new file mode 100644
index 000000000..a9d8dc068
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/p8dtu.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright 2016 Supermicro.
+ * Copyright 2016-2019 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+
+#include "astbmc.h"
+
+static const struct slot_table_entry p8dtu_phb0_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "UIO Slot1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dtu_phb0_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "UIO Network",
+ },
+ { .etype = st_end },
+};
+
+
+static const struct slot_table_entry p8dtu_plx_slots[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(1,0),
+ .name = "PLX Slot1",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0x9,0),
+ .name = "Onboard USB",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0xa,0),
+ .name = "Onboard SATA1",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0xb,0),
+ .name = "Onboard BMC",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0xc,0),
+ .name = "Onboard SATA2",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dtu_plx_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = p8dtu_plx_slots,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dtu_phb0_1_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "PLX Switch",
+ .children = p8dtu_plx_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dtu_phb8_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dtu2u_phb8_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot3",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dtu2u_phb8_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot2",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dtu1u_phb8_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot2",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dtu1u_phb8_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot3",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dtu2u_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,0),
+ .children = p8dtu_phb0_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,1),
+ .children = p8dtu_phb0_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,2),
+ .children = p8dtu_phb0_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,0),
+ .children = p8dtu_phb8_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,1),
+ .children = p8dtu2u_phb8_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,2),
+ .children = p8dtu2u_phb8_2_slot,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p8dtu1u_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,0),
+ .children = p8dtu_phb0_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,1),
+ .children = p8dtu_phb0_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,2),
+ .children = p8dtu_phb0_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,0),
+ .children = p8dtu_phb8_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,1),
+ .children = p8dtu1u_phb8_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,2),
+ .children = p8dtu1u_phb8_2_slot,
+ },
+ { .etype = st_end },
+};
+
+static bool p8dtu1u_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "supermicro,p8dtu1u"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+ slot_table_init(p8dtu1u_phb_table);
+
+ return true;
+}
+
+static bool p8dtu2u_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "supermicro,p8dtu2u"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+ slot_table_init(p8dtu2u_phb_table);
+
+ return true;
+}
+
+static const struct bmc_sw_config bmc_sw_smc = {
+ .ipmi_oem_partial_add_esel = IPMI_CODE(0x3a, 0xf0),
+ .ipmi_oem_pnor_access_status = IPMI_CODE(0x3a, 0x07),
+ .ipmi_oem_hiomap_cmd = IPMI_CODE(0x3a, 0x5a),
+};
+
+/* Provided by Eric Chen (SMC) */
+static const struct bmc_hw_config p8dtu_bmc_hw = {
+ .scu_revision_id = 0x02010303,
+ .mcr_configuration = 0x00000577,
+ .mcr_scu_mpll = 0x000050c0,
+ .mcr_scu_strap = 0x00000000,
+};
+
+static const struct bmc_platform bmc_plat_ast2400_smc = {
+ .name = "SMC",
+ .hw = &p8dtu_bmc_hw,
+ .sw = &bmc_sw_smc,
+};
+
+DECLARE_PLATFORM(p8dtu1u) = {
+ .name = "p8dtu1u",
+ .probe = p8dtu1u_probe,
+ .bmc = &bmc_plat_ast2400_smc,
+ .init = astbmc_init,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .external_irq = astbmc_ext_irq_serirq_cpld,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .seeprom_update = astbmc_seeprom_update,
+ .op_display = op_display_lpc,
+};
+
+DECLARE_PLATFORM(p8dtu2u) = {
+ .name = "p8dtu2u",
+ .probe = p8dtu2u_probe,
+ .bmc = &bmc_plat_ast2400_smc,
+ .init = astbmc_init,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .external_irq = astbmc_ext_irq_serirq_cpld,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .seeprom_update = astbmc_seeprom_update,
+ .op_display = op_display_lpc,
+};
+
diff --git a/roms/skiboot/platforms/astbmc/p9dsu.c b/roms/skiboot/platforms/astbmc/p9dsu.c
new file mode 100644
index 000000000..5c9756ec6
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/p9dsu.c
@@ -0,0 +1,725 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright 2017 Supermicro Inc.
+ * Copyright 2018-2019 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+#include <psi.h>
+#include <npu-regs.h>
+#include <opal-internal.h>
+#include <cpu.h>
+#include <timebase.h>
+
+#include "astbmc.h"
+
+static bool p9dsu_riser_found = false;
+
+static const struct slot_table_entry p9dsu1u_phb0_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "UIO Slot1",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu1u_phb0_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "UIO Slot2",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu1u_phb0_2_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Onboard LAN",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu1u_phb0_3_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Onboard SAS",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu1u_phb0_4_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Onboard BMC",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu1u_phb0_5_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Onboard USB",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu1u_phb8_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot1",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu1u_phb8_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO-R Slot",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu1u_phb8_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot3",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu1u_phb8_3_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot2",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu1u_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,0),
+ .children = p9dsu1u_phb0_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,1),
+ .children = p9dsu1u_phb0_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,2),
+ .children = p9dsu1u_phb0_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,3),
+ .children = p9dsu1u_phb0_3_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,4),
+ .children = p9dsu1u_phb0_4_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,5),
+ .children = p9dsu1u_phb0_5_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,0),
+ .children = p9dsu1u_phb8_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,1),
+ .children = p9dsu1u_phb8_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,2),
+ .children = p9dsu1u_phb8_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,3),
+ .children = p9dsu1u_phb8_3_slot,
+ },
+ { .etype = st_end },
+};
+
+
+static const struct slot_table_entry p9dsu2u_phb0_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "UIO Slot1",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2u_phb0_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "UIO Slot2",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2u_phb0_2_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Onboard LAN",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2u_phb0_3_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Onboard SAS",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2u_phb0_4_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Onboard BMC",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2u_phb0_5_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Onboard USB",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2u_phb8_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot1",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2u_phb8_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO-R Slot",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2u_phb8_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot3",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2u_phb8_3_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot3",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2u_phb8_4_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot2",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+
+static const struct slot_table_entry p9dsu2u_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,0),
+ .children = p9dsu2u_phb0_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,1),
+ .children = p9dsu2u_phb0_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,2),
+ .children = p9dsu2u_phb0_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,3),
+ .children = p9dsu2u_phb0_3_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,4),
+ .children = p9dsu2u_phb0_4_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,5),
+ .children = p9dsu2u_phb0_5_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,0),
+ .children = p9dsu2u_phb8_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,1),
+ .children = p9dsu2u_phb8_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,2),
+ .children = p9dsu2u_phb8_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,3),
+ .children = p9dsu2u_phb8_3_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,4),
+ .children = p9dsu2u_phb8_4_slot,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_uio_plx_down[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0x1,0),
+ .name = "UIO Slot2",
+ .power_limit = 75,
+ },
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0x8,0),
+ .name = "PLX switch",
+ .power_limit = 75,
+ },
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0x9,0),
+ .name = "Onboard LAN",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_uio_plx_up[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = p9dsu2uess_uio_plx_down,
+ .name = "PLX up",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_wio_plx_down[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0x1,0),
+ .name = "WIO Slot1",
+ .power_limit = 75,
+ },
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0x8,0),
+ .name = "PLX switch",
+ .power_limit = 75,
+ },
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0x9,0),
+ .name = "WIO Slot2",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_wio_plx_up[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = p9dsu2uess_wio_plx_down,
+ .name = "PLX up",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb0_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "UIO Slot1",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb0_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = p9dsu2uess_uio_plx_up,
+ .name = "PLX",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb0_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "UIO Slot3",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb0_3_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Onboard SAS",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb0_4_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Onboard BMC",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb0_5_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Onboard USB",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb8_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot3",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb8_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO-R Slot",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb8_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = p9dsu2uess_wio_plx_up,
+ .name = "PLX",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb8_3_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot4",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb8_4_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "WIO Slot5",
+ .power_limit = 75,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry p9dsu2uess_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,0),
+ .children = p9dsu2uess_phb0_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,1),
+ .children = p9dsu2uess_phb0_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,2),
+ .children = p9dsu2uess_phb0_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,3),
+ .children = p9dsu2uess_phb0_3_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,4),
+ .children = p9dsu2uess_phb0_4_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,5),
+ .children = p9dsu2uess_phb0_5_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,0),
+ .children = p9dsu2uess_phb8_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,1),
+ .children = p9dsu2uess_phb8_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,2),
+ .children = p9dsu2uess_phb8_2_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,3),
+ .children = p9dsu2uess_phb8_3_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(8,4),
+ .children = p9dsu2uess_phb8_4_slot,
+ },
+ { .etype = st_end },
+};
+
+
+/*
+ * HACK: Hostboot doesn't export the correct data for the system VPD EEPROM
+ * for this system. So we need to work around it here.
+ */
+static void p9dsu_dt_fixups(void)
+{
+ struct dt_node *n = dt_find_by_path(dt_root,
+ "/xscom@603fc00000000/i2cm@a2000/i2c-bus@0/eeprom@50");
+
+ if (n) {
+ dt_check_del_prop(n, "compatible");
+ dt_add_property_string(n, "compatible", "atmel,24c256");
+
+ dt_check_del_prop(n, "label");
+ dt_add_property_string(n, "label", "system-vpd");
+ }
+}
+
+static bool p9dsu_probe(void)
+{
+ if (!(dt_node_is_compatible(dt_root, "supermicro,p9dsu") ||
+ dt_node_is_compatible(dt_root, "supermicro,p9dsu1u") ||
+ dt_node_is_compatible(dt_root, "supermicro,p9dsu2u") ||
+ dt_node_is_compatible(dt_root, "supermicro,p9dsu2uess")))
+ return false;
+
+ p9dsu_riser_found = true;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Setup UART for use by OPAL (Linux hvc) */
+ uart_set_console_policy(UART_CONSOLE_OPAL);
+
+ p9dsu_dt_fixups();
+
+ if (dt_node_is_compatible(dt_root, "supermicro,p9dsu1u")) {
+ prlog(PR_INFO, "Detected p9dsu1u variant\n");
+ slot_table_init(p9dsu1u_phb_table);
+ } else if (dt_node_is_compatible(dt_root, "supermicro,p9dsu2u")) {
+ prlog(PR_INFO, "Detected p9dsu2u variant\n");
+ slot_table_init(p9dsu2u_phb_table);
+ } else if (dt_node_is_compatible(dt_root, "supermicro,p9dsu2uess")) {
+ prlog(PR_INFO, "Detected p9dsu2uess variant\n");
+ slot_table_init(p9dsu2uess_phb_table);
+ } else {
+ /*
+ * else we need to ask the BMC what subtype we are, but we need IPMI
+ * which we don't get until astbmc_init(), so we delay setting up the
+ * slot table until later.
+ *
+ * This only applies if you're using a Hostboot that doesn't do this
+ * for us.
+ */
+ p9dsu_riser_found = false;
+ }
+
+ return true;
+}
+
+static void p9dsu_riser_query_complete(struct ipmi_msg *m)
+{
+ u8 *riser_id = (u8*)m->user_data;
+ lwsync();
+ *riser_id = m->data[0];
+ ipmi_free_msg(m);
+}
+
+static void p9dsu_init(void)
+{
+ u8 smc_riser_req[] = {0x03, 0x70, 0x01, 0x02};
+ struct ipmi_msg *ipmi_msg;
+ u8 riser_id = 0;
+ const char *p9dsu_variant;
+ int timeout_ms = 3000;
+
+ astbmc_init();
+ /*
+ * Now we have IPMI up and running we can ask the BMC for what p9dsu
+ * variant we are if Hostboot isn't the patched one that does this
+ * for us.
+ */
+ if (!p9dsu_riser_found) {
+ ipmi_msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
+ IPMI_CODE(IPMI_NETFN_APP, 0x52),
+ p9dsu_riser_query_complete,
+ &riser_id,
+ smc_riser_req, sizeof(smc_riser_req), 1);
+ ipmi_queue_msg(ipmi_msg);
+ while(riser_id==0 && timeout_ms > 0) {
+ time_wait_ms(10);
+ timeout_ms -= 10;
+ }
+ switch (riser_id) {
+ case 0x9:
+ p9dsu_variant = "supermicro,p9dsu1u";
+ slot_table_init(p9dsu1u_phb_table);
+ break;
+ case 0x19:
+ p9dsu_variant = "supermicro,p9dsu2u";
+ slot_table_init(p9dsu2u_phb_table);
+ break;
+ case 0x1D:
+ p9dsu_variant = "supermicro,p9dsu2uess";
+ slot_table_init(p9dsu2uess_phb_table);
+ break;
+ default:
+ prlog(PR_ERR, "Defaulting to p9dsu2uess\n");
+ p9dsu_variant = "supermicro,p9dsu2uess";
+ slot_table_init(p9dsu2uess_phb_table);
+ break;
+ }
+ prlog(PR_INFO,"Detected %s variant via IPMI\n", p9dsu_variant);
+ dt_check_del_prop(dt_root, "compatible");
+ dt_add_property_strings(dt_root, "compatible", "ibm,powernv",
+ "supermicro,p9dsu", p9dsu_variant);
+ }
+}
+
+static const struct bmc_sw_config bmc_sw_smc = {
+ .ipmi_oem_partial_add_esel = IPMI_CODE(0x3a, 0xf0),
+ .ipmi_oem_hiomap_cmd = IPMI_CODE(0x3a, 0x5a),
+};
+
+/* Provided by Eric Chen (SMC) */
+static const struct bmc_hw_config p9dsu_bmc_hw = {
+ .scu_revision_id = 0x04030303,
+ .mcr_configuration = 0x11000756,
+ .mcr_scu_mpll = 0x000071c1,
+ .mcr_scu_strap = 0x00000000,
+};
+
+static const struct bmc_platform bmc_plat_ast2500_smc = {
+ .name = "SMC",
+ .hw = &p9dsu_bmc_hw,
+ .sw = &bmc_sw_smc,
+};
+
+DECLARE_PLATFORM(p9dsu1u) = {
+ .name = "p9dsu",
+ .probe = p9dsu_probe,
+ .init = p9dsu_init,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .bmc = &bmc_plat_ast2500_smc,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/astbmc/palmetto.c b/roms/skiboot/platforms/astbmc/palmetto.c
new file mode 100644
index 000000000..546d51197
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/palmetto.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+
+#include "astbmc.h"
+
+static const struct slot_table_entry palmetto_phb0_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot2",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry palmetto_plx_slots[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(1,0),
+ .name = "Backplane BMC",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(2,0),
+ .name = "Backplane USB",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(3,0),
+ .name = "Backplane Network",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(4,0),
+ .name = "Backplane SATA",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry palmetto_plx_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = palmetto_plx_slots,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry palmetto_phb0_1_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Backplane PLX",
+ .children = palmetto_plx_up,
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry palmetto_phb0_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Slot1",
+ },
+ { .etype = st_end },
+};
+
+static const struct slot_table_entry palmetto_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,0),
+ .children = palmetto_phb0_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,1),
+ .children = palmetto_phb0_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(0,2),
+ .children = palmetto_phb0_2_slot,
+ },
+ { .etype = st_end },
+};
+
+static bool palmetto_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "ibm,powernv") ||
+ !dt_node_is_compatible(dt_root, "tyan,palmetto"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ slot_table_init(palmetto_phb_table);
+
+ return true;
+}
+
+
+DECLARE_PLATFORM(palmetto) = {
+ .name = "Palmetto",
+ .probe = palmetto_probe,
+ .bmc = &bmc_plat_ast2400_ami,
+ .init = astbmc_init,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .external_irq = astbmc_ext_irq_serirq_cpld,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/astbmc/pnor.c b/roms/skiboot/platforms/astbmc/pnor.c
new file mode 100644
index 000000000..64f2249d1
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/pnor.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <opal.h>
+#include <libflash/ipmi-hiomap.h>
+#include <libflash/mbox-flash.h>
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include <libflash/blocklevel.h>
+#include <ast.h>
+
+#include "astbmc.h"
+
+enum ast_flash_style {
+ raw_flash,
+ raw_mem,
+ ipmi_hiomap,
+ mbox_hiomap,
+};
+
+static enum ast_flash_style ast_flash_get_fallback_style(void)
+{
+ if (ast_lpc_fw_mbox_hiomap())
+ return mbox_hiomap;
+
+ if (ast_lpc_fw_maps_flash())
+ return raw_flash;
+
+ return raw_mem;
+}
+
+int pnor_init(void)
+{
+ struct spi_flash_ctrl *pnor_ctrl = NULL;
+ struct blocklevel_device *bl = NULL;
+ enum ast_flash_style style;
+ int rc = 0;
+
+ if (ast_lpc_fw_ipmi_hiomap()) {
+ style = ipmi_hiomap;
+ rc = ipmi_hiomap_init(&bl);
+ }
+
+ if (!ast_lpc_fw_ipmi_hiomap() || rc) {
+ if (!ast_sio_is_enabled())
+ return -ENODEV;
+
+ style = ast_flash_get_fallback_style();
+ if (style == mbox_hiomap)
+ rc = mbox_flash_init(&bl);
+ else if (style == raw_flash)
+ rc = ast_sf_open(AST_SF_TYPE_PNOR, &pnor_ctrl);
+ else if (style == raw_mem)
+ rc = ast_sf_open(AST_SF_TYPE_MEM, &pnor_ctrl);
+ else {
+ prerror("Unhandled flash mode: %d\n", style);
+ return -ENODEV;
+ }
+ }
+
+ if (rc) {
+ prerror("PLAT: Failed to init PNOR driver\n");
+ goto fail;
+ }
+
+ if (style == raw_flash || style == raw_mem) {
+ rc = flash_init(pnor_ctrl, &bl, NULL);
+ if (rc)
+ goto fail;
+ }
+
+ rc = flash_register(bl);
+ if (!rc)
+ return 0;
+
+fail:
+ if (bl) {
+ switch (style) {
+ case raw_flash:
+ case raw_mem:
+ flash_exit(bl);
+ break;
+ case ipmi_hiomap:
+ ipmi_hiomap_exit(bl);
+ break;
+ case mbox_hiomap:
+ mbox_flash_exit(bl);
+ break;
+ }
+ }
+ if (pnor_ctrl)
+ ast_sf_close(pnor_ctrl);
+
+ return rc;
+}
diff --git a/roms/skiboot/platforms/astbmc/rainier.c b/roms/skiboot/platforms/astbmc/rainier.c
new file mode 100644
index 000000000..17d9fe2bf
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/rainier.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright (c) 2020 IBM
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <ipmi.h>
+#include <chip.h>
+#include <i2c.h>
+#include <timebase.h>
+
+#include "astbmc.h"
+
+/*
+ * puti2c pu 2 1 C6 00 6 1 -quiet
+ * puti2c pu 2 1 C6 54 7 1 -quiet
+ * puti2c pu 2 1 C6 05 8 1 -quiet
+ * puti2c pu 2 1 C6 00 9 1 -quiet
+ *
+ * sleep 4
+ *
+ * puti2c pu 2 1 C6 55 6 1 -quiet
+ * puti2c pu 2 1 C6 55 7 1 -quiet
+ * 2 - engine
+ * 1 - port
+ * C6 - slave addr
+ * 55 - data
+ * 7 - register
+ * 1 - register length?
+ */
+
+static int64_t smbus_write8(struct i2c_bus *bus, uint8_t reg, uint8_t data)
+{
+ struct i2c_request req;
+
+ memset(&req, 0, sizeof(req));
+
+ req.bus = bus;
+ req.dev_addr = 0xC6 >> 1; /* Docs use 8bit addresses */
+
+ req.op = SMBUS_WRITE;
+ req.offset = reg;
+ req.offset_bytes = 1;
+ req.rw_buf = &data;
+ req.rw_len = 1;
+ req.timeout = 100;
+
+ return i2c_request_sync(&req);
+}
+
+static int64_t slot_power_enable(struct i2c_bus *bus)
+{
+ /* FIXME: we could do this in one transaction using auto-increment */
+ if (smbus_write8(bus, 0x6, 0x00))
+ return -1;
+ if (smbus_write8(bus, 0x7, 0x54))
+ return -1;
+ if (smbus_write8(bus, 0x8, 0x05))
+ return -1;
+ if (smbus_write8(bus, 0x9, 0x00))
+ return -1;
+
+ /* FIXME: Poll for PGOOD going high */
+
+ if (smbus_write8(bus, 0x6, 0x55))
+ return -1;
+ if (smbus_write8(bus, 0x7, 0x55))
+ return -1;
+
+ return 0;
+}
+
+static void rainier_init_slot_power(void)
+{
+ struct proc_chip *chip;
+ struct i2c_bus *bus;
+
+ /*
+ * Controller on P0 is for slots C7 -> C11
+ * on P2 is for slots C0 -> C4
+ * Both chips use engine 2 port 1
+ *
+ * Rainier with only one socket is officially supported, so
+ * we may not have slots C0 -> C4
+ */
+ for_each_chip(chip) {
+ if (chip->id % 4)
+ continue;
+ bus = p8_i2c_add_bus(chip->id, 2, 1, 400000);
+ if (!bus) {
+ prerror("Unable to find PCIe power controller I2C bus!\n");
+ return;
+ }
+ if (slot_power_enable(bus)) {
+ prerror("Error enabling PCIe slot power on chip %d\n",
+ chip->id);
+ }
+ }
+}
+
+static void rainier_init(void)
+{
+ astbmc_init();
+ rainier_init_slot_power();
+}
+
+static bool rainier_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "ibm,rainier") &&
+ !dt_node_is_compatible(dt_root, "ibm,rainier-2s2u") &&
+ !dt_node_is_compatible(dt_root, "ibm,rainier-2s4u"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Setup UART for use by OPAL (Linux hvc) */
+ uart_set_console_policy(UART_CONSOLE_OPAL);
+
+ return true;
+}
+
+DECLARE_PLATFORM(rainier) = {
+ .name = "Rainier",
+ .probe = rainier_probe,
+ .init = rainier_init,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .bmc = &bmc_plat_ast2600_openbmc,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .exit = astbmc_exit,
+ .terminate = ipmi_terminate,
+};
diff --git a/roms/skiboot/platforms/astbmc/romulus.c b/roms/skiboot/platforms/astbmc/romulus.c
new file mode 100644
index 000000000..9afb7d3de
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/romulus.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2017-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+#include <psi.h>
+#include <npu-regs.h>
+
+#include "astbmc.h"
+
+ST_PLUGGABLE(romulus_cpu1_slot1, "CPU1 Slot1 (8x)");
+ST_PLUGGABLE(romulus_cpu1_slot2, "CPU1 Slot2 (16x)");
+
+ST_PLUGGABLE(romulus_cpu2_slot1, "CPU2 Slot1 (16x)");
+ST_PLUGGABLE(romulus_cpu2_slot2, "CPU2 Slot2 (16x)");
+ST_PLUGGABLE(romulus_cpu2_slot3, "CPU2 Slot3 (8x)");
+
+ST_BUILTIN_DEV(romulus_builtin_raid, "Builtin RAID");
+ST_BUILTIN_DEV(romulus_builtin_usb, "Builtin USB");
+ST_BUILTIN_DEV(romulus_builtin_ethernet, "Builtin Ethernet");
+ST_BUILTIN_DEV(romulus_builtin_bmc, "BMC");
+
+static const struct slot_table_entry romulus_phb_table[] = {
+ ST_PHB_ENTRY(0, 0, romulus_cpu1_slot2),
+ ST_PHB_ENTRY(0, 1, romulus_cpu1_slot1),
+
+ ST_PHB_ENTRY(0, 2, romulus_builtin_raid),
+ ST_PHB_ENTRY(0, 3, romulus_builtin_usb),
+ ST_PHB_ENTRY(0, 4, romulus_builtin_ethernet),
+ ST_PHB_ENTRY(0, 5, romulus_builtin_bmc),
+
+ ST_PHB_ENTRY(8, 0, romulus_cpu2_slot2), // might be swapped with 3
+ ST_PHB_ENTRY(8, 1, romulus_cpu2_slot3), // might be PHB1 or 2
+ ST_PHB_ENTRY(8, 3, romulus_cpu2_slot1),
+
+ { .etype = st_end },
+};
+
+static bool romulus_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "ibm,romulus"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Setup UART for use by OPAL (Linux hvc) */
+ uart_set_console_policy(UART_CONSOLE_OPAL);
+
+ slot_table_init(romulus_phb_table);
+
+ return true;
+}
+
+DECLARE_PLATFORM(romulus) = {
+ .name = "Romulus",
+ .probe = romulus_probe,
+ .init = astbmc_init,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .bmc = &bmc_plat_ast2500_openbmc,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .exit = astbmc_exit,
+ .terminate = ipmi_terminate,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/astbmc/slots.c b/roms/skiboot/platforms/astbmc/slots.c
new file mode 100644
index 000000000..622483a15
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/slots.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015-2018 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <pci-cfg.h>
+#include <pci.h>
+#include <pci-slot.h>
+
+#include "astbmc.h"
+
+static const struct slot_table_entry *slot_top_table;
+
+void slot_table_init(const struct slot_table_entry *top_table)
+{
+ slot_top_table = top_table;
+}
+
+static const struct slot_table_entry *match_slot_phb_entry(struct phb *phb)
+{
+ uint32_t chip_id = dt_get_chip_id(phb->dt_node);
+ uint32_t phb_idx = dt_prop_get_u32_def(phb->dt_node,
+ "ibm,phb-index", 0);
+ const struct slot_table_entry *ent;
+
+ if (!slot_top_table)
+ return NULL;
+
+ for (ent = slot_top_table; ent->etype != st_end; ent++) {
+ if (ent->etype != st_phb) {
+ prerror("SLOT: Bad DEV entry type in table !\n");
+ continue;
+ }
+ if (ent->location == ST_LOC_PHB(chip_id, phb_idx))
+ return ent;
+ }
+ return NULL;
+}
+
+static const struct slot_table_entry *match_slot_dev_entry(struct phb *phb,
+ struct pci_device *pd)
+{
+ const struct slot_table_entry *parent, *ent;
+ uint32_t bdfn;
+
+ /* Find a parent recursively */
+ if (pd->parent)
+ parent = match_slot_dev_entry(phb, pd->parent);
+ else {
+ /* No parent, this is a root complex, find the PHB */
+ parent = match_slot_phb_entry(phb);
+ }
+ /* No parent ? Oops ... */
+ if (!parent || !parent->children)
+ return NULL;
+ for (ent = parent->children; ent->etype != st_end; ent++) {
+ if (ent->etype == st_phb) {
+ prerror("SLOT: Bad PHB entry type in table !\n");
+ continue;
+ }
+
+ /* NPU slots match on device, not function */
+ if (ent->etype == st_npu_slot)
+ bdfn = pd->bdfn & 0xf8;
+ else
+ bdfn = pd->bdfn & 0xff;
+
+ if (ent->location == bdfn)
+ return ent;
+ }
+ return NULL;
+}
+
+static void slot_table_add_properties(struct pci_slot *slot,
+ struct dt_node *np)
+{
+ struct slot_table_entry *ent = slot->data;
+
+ if (ent)
+ pci_slot_add_loc(slot, np, ent->name);
+ else
+ pci_slot_add_loc(slot, np, NULL);
+}
+
+void slot_table_add_slot_info(struct pci_device *pd,
+ const struct slot_table_entry *ent)
+{
+ struct pci_slot *slot;
+
+ if (!ent || !ent->name) {
+ slot = pcie_slot_create_dynamic(pd->phb, pd);
+ if (slot) {
+ slot->ops.add_properties = slot_table_add_properties;
+ slot->pluggable = true;
+ }
+
+ return;
+ }
+
+ slot = pcie_slot_create(pd->phb, pd);
+ assert(slot);
+
+ slot->pluggable = !!(ent->etype == st_pluggable_slot);
+ slot->ops.add_properties = slot_table_add_properties;
+ slot->power_limit = ent->power_limit;
+ slot->data = (void *)ent;
+}
+
+void slot_table_get_slot_info(struct phb *phb, struct pci_device *pd)
+{
+ const struct slot_table_entry *ent;
+
+ if (!pd || pd->slot)
+ return;
+
+ ent = match_slot_dev_entry(phb, pd);
+ slot_table_add_slot_info(pd, ent);
+}
+
+static void dt_slot_add_properties(struct pci_slot *slot,
+ struct dt_node *np)
+{
+ struct dt_node *slot_np = slot->data;
+ const char *label = NULL;
+
+ if (slot_np)
+ label = dt_prop_get_def(slot_np, "ibm,slot-label", NULL);
+
+ pci_slot_add_loc(slot, np, label);
+}
+
+void dt_slot_get_slot_info(struct phb *phb, struct pci_device *pd)
+{
+ struct dt_node *slot_np;
+ struct pci_slot *slot;
+ const char *name = NULL;
+ uint32_t power_limit = 0;
+ bool pluggable = false;
+
+ if (!pd || pd->slot)
+ return;
+
+ slot_np = map_pci_dev_to_slot(phb, pd);
+ if (slot_np) {
+ pluggable = dt_has_node_property(slot_np,
+ "ibm,pluggable", NULL);
+ power_limit = dt_prop_get_u32_def(slot_np,
+ "ibm,power-limit", 0);
+ name = dt_prop_get_def(slot_np, "ibm,slot-label", NULL);
+ }
+
+ if (!slot_np || !name) {
+ slot = pcie_slot_create_dynamic(phb, pd);
+ if (slot) {
+ slot->ops.add_properties = dt_slot_add_properties;
+ slot->pluggable = true;
+ slot->data = (void *)slot_np;
+ }
+
+ return;
+ }
+
+ slot = pcie_slot_create(phb, pd);
+ assert(slot);
+
+ slot->ops.add_properties = dt_slot_add_properties;
+ slot->pluggable = pluggable;
+ slot->power_limit = power_limit;
+ slot->data = (void *)slot_np;
+}
+
+static int __pci_find_dev_by_location(struct phb *phb,
+ struct pci_device *pd, void *userdata)
+{
+ uint16_t location = *((uint16_t *)userdata);
+
+ if (!phb || !pd)
+ return 0;
+
+ if ((pd->bdfn & 0xff) == location)
+ return 1;
+
+ return 0;
+}
+
+static struct pci_device *pci_find_dev_by_location(struct phb *phb, uint16_t location)
+{
+ return pci_walk_dev(phb, NULL, __pci_find_dev_by_location, &location);
+}
+
+static struct phb* get_phb_by_location(uint32_t location)
+{
+ struct phb *phb = NULL;
+ uint32_t chip_id, phb_idx;
+
+ for_each_phb(phb) {
+ chip_id = dt_get_chip_id(phb->dt_node);
+ phb_idx = dt_prop_get_u32_def(phb->dt_node,
+ "ibm,phb-index", 0);
+ if (location == ST_LOC_PHB(chip_id, phb_idx))
+ break;
+ }
+
+ return phb;
+}
+
+static int check_slot_table(struct phb *phb,
+ const struct slot_table_entry *parent)
+{
+ const struct slot_table_entry *ent;
+ struct pci_device *dev = NULL;
+ int r = 0;
+
+ if (parent == NULL)
+ return 0;
+
+ for (ent = parent; ent->etype != st_end; ent++) {
+ switch (ent->etype) {
+ case st_phb:
+ phb = get_phb_by_location(ent->location);
+ if (!phb) {
+ prlog(PR_ERR, "PCI: PHB %s (%x) not found\n",
+ ent->name, ent->location);
+ r++;
+ }
+ break;
+ case st_pluggable_slot:
+ case st_builtin_dev:
+ if (!phb)
+ break;
+ phb_lock(phb);
+ dev = pci_find_dev_by_location(phb, ent->location);
+ phb_unlock(phb);
+ if (!dev) {
+ prlog(PR_ERR, "PCI: built-in device not found: %s (loc: %x)\n",
+ ent->name, ent->location);
+ r++;
+ }
+ break;
+ case st_end:
+ case st_npu_slot:
+ break;
+ }
+ if (ent->children)
+ r+= check_slot_table(phb, ent->children);
+ }
+ return r;
+}
+
+void check_all_slot_table(void)
+{
+ if (!slot_top_table)
+ return;
+
+ prlog(PR_DEBUG, "PCI: Checking slot table against detected devices\n");
+ check_slot_table(NULL, slot_top_table);
+}
diff --git a/roms/skiboot/platforms/astbmc/swift.c b/roms/skiboot/platforms/astbmc/swift.c
new file mode 100644
index 000000000..991a79d42
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/swift.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Copyright 2019 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <ipmi.h>
+#include <npu3.h>
+#include "astbmc.h"
+
+/* nvidia,link-speed uses a magic driver value */
+#define NVIDIA_LINK_SPEED_20000000000_BPS 3
+#define NVIDIA_LINK_SPEED_25781250000_BPS 8
+#define NVIDIA_LINK_SPEED_25000000000_BPS 9
+
+static void swift_npu3_device_detect(struct npu3 *npu)
+{
+ struct npu3_dev *dev;
+ uint32_t node, gpu_index;
+ char slot[6];
+
+ node = P9_GCID2NODEID(npu->chip_id);
+
+ switch (npu->index) {
+ case 0:
+ gpu_index = node * 2 + 1;
+ break;
+ case 2:
+ gpu_index = node * 2;
+ break;
+ default:
+ return;
+ }
+
+ snprintf(slot, sizeof(slot), "GPU%d", gpu_index);
+
+ npu3_for_each_dev(dev, npu) {
+ dev->type = NPU3_DEV_TYPE_NVLINK;
+ dt_add_property_string(dev->dn, "ibm,slot-label", slot);
+ dt_add_property_u64(dev->dn, "ibm,link-speed", 25000000000ull);
+ dt_add_property_cells(dev->dn, "nvidia,link-speed",
+ NVIDIA_LINK_SPEED_25000000000_BPS);
+ }
+}
+
+#define SWIFT_POSSIBLE_GPUS 4
+
+#define G(g) (devs[g] ? devs[g]->nvlink.gpu->dn->phandle : 0)
+#define N(g) (devs[g] ? devs[g]->npu->nvlink.phb.dt_node->phandle : 0)
+
+#define add_peers_prop(g, p...) \
+ if (devs[g]) \
+ dt_add_property_cells(devs[g]->nvlink.gpu->dn, \
+ "ibm,nvlink-peers", ##p)
+
+static void swift_finalise_dt(bool is_reboot)
+{
+ struct npu3 *npu;
+ struct npu3_dev *dev;
+ struct npu3_dev *devs[SWIFT_POSSIBLE_GPUS] = {};
+ int32_t index;
+
+ if (is_reboot)
+ return;
+
+ /* Collect the first link we find for each GPU */
+ npu3_for_each_nvlink_npu(npu) {
+ npu3_for_each_nvlink_dev(dev, npu) {
+ index = npu3_dev_gpu_index(dev);
+ if (index == -1 || index >= ARRAY_SIZE(devs))
+ continue;
+
+ if (dev->nvlink.gpu && !devs[index])
+ devs[index] = dev;
+ }
+ }
+
+ /* Add GPU interconnect properties */
+ add_peers_prop(0, G(3), G(2), G(2), G(2),
+ G(3), G(1), G(1), G(1),
+ N(0), N(0), N(0), N(0));
+
+ add_peers_prop(1, G(2), G(3), G(3), G(3),
+ G(0), G(0), G(0), G(2),
+ N(1), N(1), N(1), N(1));
+
+ add_peers_prop(2, G(1), G(3), G(3), G(3),
+ G(0), G(0), G(0), G(1),
+ N(2), N(2), N(2), N(2));
+
+ add_peers_prop(3, G(2), G(2), G(2), G(0),
+ G(1), G(1), G(0), G(1),
+ N(3), N(3), N(3), N(3));
+}
+
+static bool swift_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "ibm,swift"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Setup UART for use by OPAL (Linux hvc) */
+ uart_set_console_policy(UART_CONSOLE_OPAL);
+
+ return true;
+}
+
+DECLARE_PLATFORM(swift) = {
+ .bmc = &bmc_plat_ast2500_openbmc,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .exit = astbmc_exit,
+ .finalise_dt = swift_finalise_dt,
+ .init = astbmc_init,
+ .name = "Swift",
+ .npu3_device_detect = swift_npu3_device_detect,
+ .pci_get_slot_info = dt_slot_get_slot_info,
+ .probe = swift_probe,
+ .resource_loaded = flash_resource_loaded,
+ .start_preload_resource = flash_start_preload_resource,
+ .terminate = ipmi_terminate,
+};
diff --git a/roms/skiboot/platforms/astbmc/talos.c b/roms/skiboot/platforms/astbmc/talos.c
new file mode 100644
index 000000000..a5a37d342
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/talos.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Copyright 2017-2019 IBM Corp.
+ * Copyright 2018-2019 Raptor Engineering, LLC
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+#include <psi.h>
+#include <npu-regs.h>
+
+#include "astbmc.h"
+
+ST_PLUGGABLE(talos_cpu1_slot1, "CPU1 Slot1 (8x)");
+ST_PLUGGABLE(talos_cpu1_slot2, "CPU1 Slot2 (16x)");
+
+ST_PLUGGABLE(talos_cpu2_slot1, "CPU2 Slot1 (16x)");
+ST_PLUGGABLE(talos_cpu2_slot2, "CPU2 Slot2 (16x)");
+ST_PLUGGABLE(talos_cpu2_slot3, "CPU2 Slot3 (8x)");
+
+ST_BUILTIN_DEV(talos_builtin_raid, "Builtin SAS");
+ST_BUILTIN_DEV(talos_builtin_usb, "Builtin USB");
+ST_BUILTIN_DEV(talos_builtin_ethernet, "Builtin Ethernet");
+ST_BUILTIN_DEV(talos_builtin_bmc, "BMC");
+
+static const struct slot_table_entry talos_phb_table[] = {
+ ST_PHB_ENTRY(0, 0, talos_cpu1_slot2),
+ ST_PHB_ENTRY(0, 1, talos_cpu1_slot1),
+
+ ST_PHB_ENTRY(0, 2, talos_builtin_raid),
+ ST_PHB_ENTRY(0, 3, talos_builtin_usb),
+ ST_PHB_ENTRY(0, 4, talos_builtin_ethernet),
+ ST_PHB_ENTRY(0, 5, talos_builtin_bmc),
+
+ ST_PHB_ENTRY(8, 0, talos_cpu2_slot2), // might be swapped with 3
+ ST_PHB_ENTRY(8, 1, talos_cpu2_slot3), // might be PHB1 or 2
+ ST_PHB_ENTRY(8, 3, talos_cpu2_slot1),
+
+ { .etype = st_end },
+};
+
+static bool talos_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "rcs,talos"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Setup UART for use by OPAL (Linux hvc) */
+ uart_set_console_policy(UART_CONSOLE_OPAL);
+
+ slot_table_init(talos_phb_table);
+
+ return true;
+}
+
+DECLARE_PLATFORM(talos) = {
+ .name = "Talos",
+ .probe = talos_probe,
+ .init = astbmc_init,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .bmc = &bmc_plat_ast2500_openbmc,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .exit = astbmc_exit,
+ .terminate = ipmi_terminate,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/astbmc/vesnin.c b/roms/skiboot/platforms/astbmc/vesnin.c
new file mode 100644
index 000000000..f7e1844fc
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/vesnin.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Copyright (c) 2018 YADRO
+ * Copyright 2018-2019 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+#include <pci.h>
+#include <pci-cfg.h>
+
+#include "astbmc.h"
+
+#define CHIP_ID_CPU0 0x00
+#define CHIP_ID_CPU1 0x08
+#define CHIP_ID_CPU2 0x10
+#define CHIP_ID_CPU3 0x18
+
+/* IPMI message code for PCI inventory (OEM). */
+#define PCIINV_IPMI_CODE IPMI_CODE(0x2e, 0x2a)
+/* IANA number used to identify IPMI OEM command group. */
+#define PCIINV_OEM_IANA 49769 /* YADRO */
+
+/**
+ * struct pciinv_device - PCI device inventory description.
+ * @domain_num: Domain number.
+ * @bus_num: Bus number.
+ * @device_num: Device number.
+ * @func_num: Function number.
+ * @vendor_id: Vendor Id.
+ * @device_id: Device Id.
+ * @class_code: Device class code.
+ * @revision: Revision number.
+ *
+ * All fields have Big Endian byte order.
+ */
+struct pciinv_device {
+ beint16_t domain_num;
+ uint8_t bus_num;
+ uint8_t device_num;
+ uint8_t func_num;
+ beint16_t vendor_id;
+ beint16_t device_id;
+ beint32_t class_code;
+ uint8_t revision;
+} __packed;
+
+/**
+ * struct pciinv_message - IPMI message packet data.
+ * @iana: IANA id for OEM message, must be set to PCIINV_OEM_IANA.
+ * @reset: Reset flag.
+ * @device: PCI device description.
+ */
+struct pciinv_message {
+ uint8_t iana[3];
+ uint8_t reset;
+ struct pciinv_device device;
+} __packed;
+
+
+static const struct slot_table_entry vesnin_phb0_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "AUX connector00",
+ },
+ { .etype = st_end }
+};
+
+
+static const struct slot_table_entry vesnin_plx_slots[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0x01,0),
+ .name = "Backplane SSD0",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0x02,0),
+ .name = "Backplane SSD1",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0x03,0),
+ .name = "Backplane LAN",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0x04,0),
+ .name = "Backplane BMC",
+ },
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0x05,0),
+ .name = "Backplane USB",
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry vesnin_plx_up[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .children = vesnin_plx_slots,
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry vesnin_phb0_1_slot[] = {
+ {
+ .etype = st_builtin_dev,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "Backplane PLX",
+ .children = vesnin_plx_up,
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry vesnin_phb0_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "PCIE0_x8_CPU0",
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry vesnin_phb8_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "PCIE1_x16_CPU1",
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry vesnin_phb8_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "AUX connector10",
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry vesnin_phb9_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "AUX connector30",
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry vesnin_phb9_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "PCIE3_x8_CPU2",
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry vesnin_phb9_2_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "PCIE2_x8_CPU2",
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry vesnin_phbA_0_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "PCIE4_x16_CPU3",
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry vesnin_phbA_1_slot[] = {
+ {
+ .etype = st_pluggable_slot,
+ .location = ST_LOC_DEVFN(0,0),
+ .name = "AUX connector40",
+ },
+ { .etype = st_end }
+};
+
+static const struct slot_table_entry vesnin_phb_table[] = {
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(CHIP_ID_CPU0,0),
+ .children = vesnin_phb0_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(CHIP_ID_CPU0,1),
+ .children = vesnin_phb0_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(CHIP_ID_CPU0,2),
+ .children = vesnin_phb0_2_slot,
+ },
+
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(CHIP_ID_CPU1,0),
+ .children = vesnin_phb8_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(CHIP_ID_CPU1,1),
+ .children = vesnin_phb8_1_slot,
+ },
+
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(CHIP_ID_CPU2,0),
+ .children = vesnin_phb9_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(CHIP_ID_CPU2,1),
+ .children = vesnin_phb9_1_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(CHIP_ID_CPU2,2),
+ .children = vesnin_phb9_2_slot,
+ },
+
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(CHIP_ID_CPU3,0),
+ .children = vesnin_phbA_0_slot,
+ },
+ {
+ .etype = st_phb,
+ .location = ST_LOC_PHB(CHIP_ID_CPU3,1),
+ .children = vesnin_phbA_1_slot,
+ },
+ { .etype = st_end }
+};
+
+/**
+ * pciinv_walk() - Callback from PCI enumerator, see :c:func:`pci_walk_dev`.
+ * User data parameter is interpreted as a pointer to pciinv_message structure.
+ */
+static int pciinv_walk(struct phb *phb, struct pci_device *pd, void *data)
+{
+ struct ipmi_msg *msg;
+ struct pciinv_message* pack = (struct pciinv_message*)data;
+
+ /* PCI device filter: Skip non-EP devices */
+ if (pci_has_cap(pd, PCI_CFG_CAP_ID_EXP, false)) {
+ if (pd->dev_type != PCIE_TYPE_ENDPOINT)
+ return OPAL_SUCCESS;
+ }
+ else if (pd->is_bridge)
+ return OPAL_SUCCESS;
+
+ /* Fill the PCI device inventory description */
+ pack->device.domain_num = cpu_to_be16(phb->opal_id & 0xffff);
+ pack->device.bus_num = PCI_BUS_NUM(pd->bdfn);
+ pack->device.device_num = PCI_DEV(pd->bdfn);
+ pack->device.func_num = PCI_FUNC(pd->bdfn);
+ pack->device.vendor_id = cpu_to_be16(PCI_VENDOR_ID(pd->vdid));
+ pack->device.device_id = cpu_to_be16(PCI_DEVICE_ID(pd->vdid));
+ pack->device.class_code = cpu_to_be32(pd->class & 0xffffff);
+ pci_cfg_read8(phb, pd->bdfn, PCI_CFG_REV_ID, &pack->device.revision);
+
+ msg = ipmi_mkmsg_simple(PCIINV_IPMI_CODE, pack, sizeof(*pack));
+ if (!msg)
+ return OPAL_HARDWARE;
+
+ ipmi_queue_msg(msg);
+
+ /* Disable reset flag for further messages in the current session. */
+ pack->reset = 0;
+
+ return OPAL_SUCCESS;
+}
+
+static void vesnin_pci_probe_complete(void)
+{
+ struct phb *phb;
+
+ /* IPMI message packet instance.
+ * PCI device description will be filled in the PCI enumerator, see
+ * `pciinv_walk()` function.
+ * For each first message in a session, the Reset flag is turned on,
+ * this indicates that the list of existing PCI devices must be
+ * cleaned. */
+ struct pciinv_message pack = {
+ .iana = {
+ PCIINV_OEM_IANA & 0xff,
+ (PCIINV_OEM_IANA >> 8) & 0xff,
+ (PCIINV_OEM_IANA >> 16) & 0xff
+ },
+ .reset = 1
+ };
+
+ check_all_slot_table();
+
+ /* Send PCI device list to the BMC */
+ prlog(PR_INFO, "Send PCI device list\n");
+ for_each_phb(phb) {
+ pci_walk_dev(phb, NULL, &pciinv_walk, &pack);
+ }
+}
+
+static bool vesnin_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "YADRO,vesnin"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+ slot_table_init(vesnin_phb_table);
+
+ return true;
+}
+
+DECLARE_PLATFORM(vesnin) = {
+ .name = "vesnin",
+ .bmc = &bmc_plat_ast2400_ami,
+ .probe = vesnin_probe,
+ .init = astbmc_init,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .pci_probe_complete = vesnin_pci_probe_complete,
+ .external_irq = astbmc_ext_irq_serirq_cpld,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/astbmc/witherspoon.c b/roms/skiboot/platforms/astbmc/witherspoon.c
new file mode 100644
index 000000000..67c24b532
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/witherspoon.c
@@ -0,0 +1,604 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2017-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+#include <psi.h>
+#include <npu-regs.h>
+#include <xscom.h>
+#include <xscom-p9-regs.h>
+#include <timebase.h>
+#include <pci.h>
+#include <pci-slot.h>
+#include <phb4.h>
+#include <npu2.h>
+#include <occ.h>
+#include <i2c.h>
+#include <secvar.h>
+
+#include "astbmc.h"
+#include "ast.h"
+
+static enum {
+ WITHERSPOON_TYPE_UNKNOWN,
+ WITHERSPOON_TYPE_SEQUOIA,
+ WITHERSPOON_TYPE_REDBUD
+} witherspoon_type;
+
+/*
+ * HACK: Hostboot doesn't export the correct data for the system VPD EEPROM
+ * for this system. So we need to work around it here.
+ */
+static void vpd_dt_fixup(void)
+{
+ struct dt_node *n = dt_find_by_path(dt_root,
+ "/xscom@603fc00000000/i2cm@a2000/i2c-bus@0/eeprom@50");
+
+ if (n) {
+ dt_check_del_prop(n, "compatible");
+ dt_add_property_string(n, "compatible", "atmel,24c512");
+
+ dt_check_del_prop(n, "label");
+ dt_add_property_string(n, "label", "system-vpd");
+ }
+}
+
+static void witherspoon_create_ocapi_i2c_bus(void)
+{
+ struct dt_node *xscom, *i2cm, *i2c_bus;
+ prlog(PR_DEBUG, "OCAPI: Adding I2C bus device node for OCAPI reset\n");
+ dt_for_each_compatible(dt_root, xscom, "ibm,xscom") {
+ i2cm = dt_find_by_name(xscom, "i2cm@a1000");
+ if (!i2cm) {
+ prlog(PR_ERR, "OCAPI: Failed to add I2C bus device node\n");
+ continue;
+ }
+
+ if (dt_find_by_name(i2cm, "i2c-bus@4"))
+ continue;
+
+ i2c_bus = dt_new_addr(i2cm, "i2c-bus", 4);
+ dt_add_property_cells(i2c_bus, "reg", 4);
+ dt_add_property_cells(i2c_bus, "bus-frequency", 0x61a80);
+ dt_add_property_strings(i2c_bus, "compatible",
+ "ibm,opal-i2c", "ibm,power8-i2c-port",
+ "ibm,power9-i2c-port");
+ }
+}
+
+static bool witherspoon_probe(void)
+{
+ struct dt_node *np;
+ int highest_gpu_group_id = 0;
+ int gpu_group_id;
+
+ if (!dt_node_is_compatible(dt_root, "ibm,witherspoon"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Setup UART for use by OPAL (Linux hvc) */
+ uart_set_console_policy(UART_CONSOLE_OPAL);
+
+ vpd_dt_fixup();
+
+ witherspoon_create_ocapi_i2c_bus();
+
+ dt_for_each_compatible(dt_root, np, "ibm,npu-link") {
+ gpu_group_id = dt_prop_get_u32(np, "ibm,npu-group-id");
+ if (gpu_group_id > highest_gpu_group_id)
+ highest_gpu_group_id = gpu_group_id;
+ };
+
+ switch (highest_gpu_group_id) {
+ case 1:
+ witherspoon_type = WITHERSPOON_TYPE_REDBUD;
+ break;
+ case 2:
+ witherspoon_type = WITHERSPOON_TYPE_SEQUOIA;
+ break;
+ default:
+ witherspoon_type = WITHERSPOON_TYPE_UNKNOWN;
+ prlog(PR_NOTICE, "PLAT: Unknown Witherspoon variant detected\n");
+ }
+
+ return true;
+}
+
+static void phb4_activate_shared_slot_witherspoon(struct proc_chip *chip)
+{
+ uint64_t val;
+
+ /*
+ * Shared slot activation is done by raising a GPIO line on the
+ * chip with the secondary slot. It will somehow activate the
+ * sideband signals between the slots.
+ * Need to wait 100us for stability.
+ */
+ xscom_read(chip->id, P9_GPIO_DATA_OUT_ENABLE, &val);
+ val |= PPC_BIT(2);
+ xscom_write(chip->id, P9_GPIO_DATA_OUT_ENABLE, val);
+
+ xscom_read(chip->id, P9_GPIO_DATA_OUT, &val);
+ val |= PPC_BIT(2);
+ xscom_write(chip->id, P9_GPIO_DATA_OUT, val);
+ time_wait_us(100);
+ prlog(PR_INFO, "Shared PCI slot activated\n");
+}
+
+static void witherspoon_shared_slot_fixup(void)
+{
+ struct pci_slot *slot0, *slot1;
+ struct proc_chip *chip0, *chip1;
+ uint8_t p0 = 0, p1 = 0;
+
+ /*
+ * Detect if a x16 card is present on the shared slot and
+ * do some extra configuration if it is.
+ *
+ * The shared slot, a.k.a "Slot 2" in the documentation, is
+ * connected to PEC2 phb index 3 on both chips. From skiboot,
+ * it looks like two x8 slots, each with its own presence bit.
+ *
+ * Here is the matrix of possibilities for the presence bits:
+ *
+ * slot0 presence slot1 presence
+ * 0 0 => no card
+ * 1 0 => x8 or less card detected
+ * 1 1 => x16 card detected
+ * 0 1 => invalid combination
+ *
+ * We only act if a x16 card is detected ('1 1' combination above).
+ *
+ * One issue is that we don't really know if it is a
+ * shared-slot-compatible card (such as Mellanox CX5) or
+ * a 'normal' x16 PCI card. We activate the shared slot in both cases,
+ * as it doesn't seem to hurt.
+ *
+ * If the card is a normal x16 PCI card, the link won't train on the
+ * second slot (nothing to do with the shared slot activation), the
+ * procedure will timeout, thus adding some delay to the boot time.
+ * Therefore the recommendation is that we shouldn't use a normal
+ * x16 card on the shared slot of a witherspoon.
+ *
+ * Plugging a x8 or less adapter on the shared slot should work
+ * like any other physical slot.
+ */
+ chip0 = next_chip(NULL);
+ chip1 = next_chip(chip0);
+ if (!chip1 || next_chip(chip1)) {
+ prlog(PR_WARNING,
+ "PLAT: Can't find second chip, "
+ "skipping PCIe shared slot detection\n");
+ return;
+ }
+
+ /* the shared slot is connected to PHB3 on both chips */
+ slot0 = pci_slot_find(phb4_get_opal_id(chip0->id, 3));
+ slot1 = pci_slot_find(phb4_get_opal_id(chip1->id, 3));
+ if (slot0 && slot1) {
+ if (slot0->ops.get_presence_state)
+ slot0->ops.get_presence_state(slot0, &p0);
+ if (slot1->ops.get_presence_state)
+ slot1->ops.get_presence_state(slot1, &p1);
+ if (p0 == 1 && p1 == 1) {
+ phb4_activate_shared_slot_witherspoon(chip1);
+ slot0->peer_slot = slot1;
+ slot1->peer_slot = slot0;
+ }
+ }
+}
+
+static int check_mlx_cards(struct phb *phb __unused, struct pci_device *dev,
+ void *userdata __unused)
+{
+ uint16_t mlx_cards[] = {
+ 0x1017, /* ConnectX-5 */
+ 0x1019, /* ConnectX-5 Ex */
+ 0x101b, /* ConnectX-6 */
+ 0x101d, /* ConnectX-6 Dx */
+ 0x101f, /* ConnectX-6 Lx */
+ 0x1021, /* ConnectX-7 */
+ };
+
+ if (PCI_VENDOR_ID(dev->vdid) == 0x15b3) { /* Mellanox */
+ for (int i = 0; i < ARRAY_SIZE(mlx_cards); i++) {
+ if (mlx_cards[i] == PCI_DEVICE_ID(dev->vdid))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void witherspoon_pci_probe_complete(void)
+{
+ struct pci_device *dev;
+ struct phb *phb;
+ struct phb4 *p;
+
+ /*
+ * Reallocate dma engines between stacks in PEC2 if a Mellanox
+ * card is found on the shared slot, as it is required to get
+ * good GPU direct performance.
+ */
+ for_each_phb(phb) {
+ /* skip the virtual PHBs */
+ if (phb->phb_type != phb_type_pcie_v4)
+ continue;
+ p = phb_to_phb4(phb);
+ /* Keep only the first PHB on PEC2 */
+ if (p->index != 3)
+ continue;
+ dev = pci_walk_dev(phb, NULL, check_mlx_cards, NULL);
+ if (dev)
+ phb4_pec2_dma_engine_realloc(p);
+ }
+}
+
+static void set_link_details(struct npu2 *npu, uint32_t link_index,
+ uint32_t brick_index, enum npu2_dev_type type)
+{
+ struct npu2_dev *dev = NULL;
+ for (int i = 0; i < npu->total_devices; i++) {
+ if (npu->devices[i].link_index == link_index) {
+ dev = &npu->devices[i];
+ break;
+ }
+ }
+ if (!dev) {
+ prlog(PR_ERR, "PLAT: Could not find NPU link index %d\n",
+ link_index);
+ return;
+ }
+ dev->brick_index = brick_index;
+ dev->type = type;
+}
+
+static void witherspoon_npu2_device_detect(struct npu2 *npu)
+{
+ struct proc_chip *chip;
+ uint8_t state;
+ uint64_t i2c_port_id = 0;
+ char port_name[17];
+ struct dt_node *dn;
+ int rc;
+
+ bool gpu0_present, gpu1_present;
+
+ if (witherspoon_type != WITHERSPOON_TYPE_REDBUD) {
+ prlog(PR_DEBUG, "PLAT: Setting all NPU links to NVLink, OpenCAPI only supported on Redbud\n");
+ for (int i = 0; i < npu->total_devices; i++) {
+ npu->devices[i].type = NPU2_DEV_TYPE_NVLINK;
+ }
+ return;
+ }
+ assert(npu->total_devices == 6);
+
+ chip = get_chip(npu->chip_id);
+
+ /* Find I2C port */
+ snprintf(port_name, sizeof(port_name), "p8_%08x_e%dp%d",
+ chip->id, platform.ocapi->i2c_engine,
+ platform.ocapi->i2c_port);
+ dt_for_each_compatible(dt_root, dn, "ibm,power9-i2c-port") {
+ if (streq(port_name, dt_prop_get(dn, "ibm,port-name"))) {
+ i2c_port_id = dt_prop_get_u32(dn, "ibm,opal-id");
+ break;
+ }
+ }
+
+ if (!i2c_port_id) {
+ prlog(PR_ERR, "PLAT: Could not find NPU presence I2C port\n");
+ return;
+ }
+
+ gpu0_present = occ_get_gpu_presence(chip, 0);
+ if (gpu0_present) {
+ prlog(PR_DEBUG, "PLAT: Chip %d GPU#0 slot present\n", chip->id);
+ }
+
+ gpu1_present = occ_get_gpu_presence(chip, 1);
+ if (gpu1_present) {
+ prlog(PR_DEBUG, "PLAT: Chip %d GPU#1 slot present\n", chip->id);
+ }
+
+ /*
+ * The following I2C ops generate errors if no device is
+ * present on any SXM2 slot. Since it's useless, let's skip it
+ */
+ if (!gpu0_present && !gpu1_present)
+ return;
+
+ /* Set pins to input */
+ state = 0xff;
+ rc = i2c_request_send(i2c_port_id,
+ platform.ocapi->i2c_presence_addr, SMBUS_WRITE, 3,
+ 1, &state, 1, 120);
+ if (rc)
+ goto i2c_failed;
+
+ /* Read the presence value */
+ state = 0x00;
+ rc = i2c_request_send(i2c_port_id,
+ platform.ocapi->i2c_presence_addr, SMBUS_READ, 0,
+ 1, &state, 1, 120);
+ if (rc)
+ goto i2c_failed;
+
+ if (gpu0_present) {
+ if (state & (1 << 0)) {
+ prlog(PR_DEBUG, "PLAT: Chip %d GPU#0 is OpenCAPI\n",
+ chip->id);
+ /*
+ * On witherspoon, bricks 2 and 3 are connected to
+ * the lanes matching links 0 and 1 in OpenCAPI mode.
+ */
+ set_link_details(npu, 1, 3, NPU2_DEV_TYPE_OPENCAPI);
+ /* We current don't support using the second link */
+ set_link_details(npu, 0, 2, NPU2_DEV_TYPE_UNKNOWN);
+ } else {
+ prlog(PR_DEBUG, "PLAT: Chip %d GPU#0 is NVLink\n",
+ chip->id);
+ set_link_details(npu, 0, 0, NPU2_DEV_TYPE_NVLINK);
+ set_link_details(npu, 1, 1, NPU2_DEV_TYPE_NVLINK);
+ set_link_details(npu, 2, 2, NPU2_DEV_TYPE_NVLINK);
+ }
+ }
+
+ if (gpu1_present) {
+ if (state & (1 << 1)) {
+ prlog(PR_DEBUG, "PLAT: Chip %d GPU#1 is OpenCAPI\n",
+ chip->id);
+ set_link_details(npu, 4, 4, NPU2_DEV_TYPE_OPENCAPI);
+ /* We current don't support using the second link */
+ set_link_details(npu, 5, 5, NPU2_DEV_TYPE_UNKNOWN);
+ } else {
+ prlog(PR_DEBUG, "PLAT: Chip %d GPU#1 is NVLink\n",
+ chip->id);
+ set_link_details(npu, 3, 3, NPU2_DEV_TYPE_NVLINK);
+ set_link_details(npu, 4, 4, NPU2_DEV_TYPE_NVLINK);
+ set_link_details(npu, 5, 5, NPU2_DEV_TYPE_NVLINK);
+ }
+ }
+
+ return;
+
+i2c_failed:
+ prlog(PR_ERR, "PLAT: NPU device type detection failed, rc=%d\n", rc);
+ return;
+}
+
+static const char *witherspoon_ocapi_slot_label(uint32_t chip_id,
+ uint32_t brick_index)
+{
+ const char *name = NULL;
+
+ if (chip_id == 0) {
+ if (brick_index == 3)
+ name = "OPENCAPI-GPU0";
+ else if (brick_index == 4)
+ name = "OPENCAPI-GPU1";
+ } else {
+ if (brick_index == 3)
+ name = "OPENCAPI-GPU3";
+ else if (brick_index == 4)
+ name = "OPENCAPI-GPU4";
+ }
+ return name;
+}
+
+static const struct platform_ocapi witherspoon_ocapi = {
+ .i2c_engine = 1,
+ .i2c_port = 4,
+ .odl_phy_swap = false,
+ .i2c_reset_addr = 0x20,
+ /*
+ * Witherspoon uses SXM2 connectors, carrying 2 OCAPI links
+ * over a single connector - hence each pair of bricks shares
+ * the same pin for resets. We currently only support using
+ * bricks 3 and 4, among other reasons because we can't handle
+ * a reset on one link causing the other link to reset as
+ * well.
+ */
+ .i2c_reset_brick2 = 1 << 0,
+ .i2c_reset_brick3 = 1 << 0,
+ .i2c_reset_brick4 = 1 << 1,
+ .i2c_reset_brick5 = 1 << 1,
+ .i2c_presence_addr = 0x20,
+ /* unused, we do this in custom presence detect */
+ .i2c_presence_brick2 = 0,
+ .i2c_presence_brick3 = 0,
+ .i2c_presence_brick4 = 0,
+ .i2c_presence_brick5 = 0,
+ .ocapi_slot_label = witherspoon_ocapi_slot_label,
+};
+
+static int gpu_slot_to_num(const char *slot)
+{
+ char *p = NULL;
+ int ret;
+
+ if (!slot)
+ return -1;
+
+ if (memcmp(slot, "GPU", 3))
+ return -1;
+
+ ret = strtol(slot + 3, &p, 10);
+ if (*p || p == slot + 3)
+ return -1;
+
+ return ret;
+}
+
+static void npu2_phb_nvlink_dt(struct phb *npuphb)
+{
+ struct dt_node *g[3] = { NULL }; /* Current maximum 3 GPUs per 1 NPU */
+ struct dt_node *n[6] = { NULL };
+ int max_gpus, i, gpuid, first, last;
+ struct npu2 *npu2_phb = phb_to_npu2_nvlink(npuphb);
+ struct pci_device *npd;
+
+ switch (witherspoon_type) {
+ case WITHERSPOON_TYPE_REDBUD:
+ max_gpus = 4;
+ break;
+ case WITHERSPOON_TYPE_SEQUOIA:
+ max_gpus = 6;
+ break;
+ default:
+ /* witherspoon_probe() already reported missing support */
+ return;
+ }
+
+ /* Find the indexes of GPUs connected to this NPU */
+ for (i = 0, first = max_gpus, last = 0; i < npu2_phb->total_devices;
+ ++i) {
+ gpuid = gpu_slot_to_num(npu2_phb->devices[i].nvlink.slot_label);
+ if (gpuid < 0)
+ continue;
+ if (gpuid > last)
+ last = gpuid;
+ if (gpuid < first)
+ first = gpuid;
+ }
+
+ /* Either no "GPUx" slots found or they are not consecutive, abort */
+ if (!last || last + 1 - first > max_gpus)
+ return;
+
+ /* Collect GPU device nodes, sorted by an index from "GPUn" */
+ for (i = 0; i < npu2_phb->total_devices; ++i) {
+ gpuid = gpu_slot_to_num(npu2_phb->devices[i].nvlink.slot_label);
+ g[gpuid - first] = npu2_phb->devices[i].nvlink.pd->dn;
+
+ /* Collect NVLink bridge nodes too, for their phandles */
+ list_for_each(&npuphb->devices, npd, link) {
+ if (npd->bdfn == npu2_phb->devices[i].bdfn) {
+ assert(npu2_phb->devices[i].brick_index <
+ ARRAY_SIZE(n));
+ n[npu2_phb->devices[i].brick_index] = npd->dn;
+ }
+ }
+ }
+
+ /*
+ * Store interconnect phandles in the device tree.
+ * The mapping is from Witherspoon_Design_Workbook_v1.7_19June2018.pdf,
+ * pages 39 (Sequoia), 40 (Redbud):
+ * Figure 16: NVLink wiring diagram for planar with 6 GPUs
+ * Figure 17: NVLink wiring diagram for planar with 4 GPUs
+ */
+#define PEERPH(g) ((g)?(g)->phandle:0)
+ switch (witherspoon_type) {
+ case WITHERSPOON_TYPE_REDBUD:
+ if (g[0])
+ dt_add_property_cells(g[0], "ibm,nvlink-peers",
+ PEERPH(g[1]), PEERPH(n[0]),
+ PEERPH(g[1]), PEERPH(n[1]),
+ PEERPH(g[1]), PEERPH(n[2]));
+ if (g[1])
+ dt_add_property_cells(g[1], "ibm,nvlink-peers",
+ PEERPH(g[0]), PEERPH(n[3]),
+ PEERPH(g[0]), PEERPH(n[4]),
+ PEERPH(g[0]), PEERPH(n[5]));
+ break;
+ case WITHERSPOON_TYPE_SEQUOIA:
+ if (g[0])
+ dt_add_property_cells(g[0], "ibm,nvlink-peers",
+ PEERPH(g[1]), PEERPH(n[0]),
+ PEERPH(g[2]), PEERPH(g[2]),
+ PEERPH(g[1]), PEERPH(n[1]));
+ if (g[1])
+ dt_add_property_cells(g[1], "ibm,nvlink-peers",
+ PEERPH(g[0]), PEERPH(n[2]),
+ PEERPH(g[2]), PEERPH(g[2]),
+ PEERPH(g[0]), PEERPH(n[3]));
+ if (g[2])
+ dt_add_property_cells(g[2], "ibm,nvlink-peers",
+ PEERPH(g[1]), PEERPH(g[0]),
+ PEERPH(g[1]), PEERPH(n[4]),
+ PEERPH(g[0]), PEERPH(n[5]));
+ break;
+ default:
+ break;
+ }
+}
+
+static void witherspoon_finalise_dt(bool is_reboot)
+{
+ struct dt_node *np;
+ struct proc_chip *c;
+
+ if (is_reboot)
+ return;
+
+ dt_for_each_compatible(dt_root, np, "ibm,power9-npu-pciex") {
+ u32 opal_id = dt_prop_get_cell(np, "ibm,opal-phbid", 1);
+ struct phb *npphb = pci_get_phb(opal_id);
+
+ if (!npphb)
+ continue;
+ if (npphb->phb_type != phb_type_npu_v2)
+ continue;
+ npu2_phb_nvlink_dt(npphb);
+ }
+
+ /*
+ * The I2C bus on used to talk to the GPUs has a 750K pullup
+ * which is way too big. If there's no GPUs connected to the
+ * chip all I2C transactions fail with an Arb loss error since
+ * SCL/SDA don't return to the idle state fast enough. Disable
+ * the port to squash the errors.
+ */
+ for (c = next_chip(NULL); c; c = next_chip(c)) {
+ bool detected = false;
+ int i;
+
+ np = dt_find_by_path(c->devnode, "i2cm@a1000/i2c-bus@4");
+ if (!np)
+ continue;
+
+ for (i = 0; i < 3; i++)
+ detected |= occ_get_gpu_presence(c, i);
+
+ if (!detected) {
+ dt_check_del_prop(np, "status");
+ dt_add_property_string(np, "status", "disabled");
+ }
+ }
+}
+
+static int witherspoon_secvar_init(void)
+{
+ return secvar_main(secboot_tpm_driver, edk2_compatible_v1);
+}
+
+/* The only difference between these is the PCI slot handling */
+
+DECLARE_PLATFORM(witherspoon) = {
+ .name = "Witherspoon",
+ .probe = witherspoon_probe,
+ .init = astbmc_init,
+ .pre_pci_fixup = witherspoon_shared_slot_fixup,
+ .pci_probe_complete = witherspoon_pci_probe_complete,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .bmc = &bmc_plat_ast2500_openbmc,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .finalise_dt = witherspoon_finalise_dt,
+ .exit = astbmc_exit,
+ .terminate = ipmi_terminate,
+
+ .pci_get_slot_info = dt_slot_get_slot_info,
+ .ocapi = &witherspoon_ocapi,
+ .npu2_device_detect = witherspoon_npu2_device_detect,
+ .op_display = op_display_lpc,
+ .secvar_init = witherspoon_secvar_init,
+};
diff --git a/roms/skiboot/platforms/astbmc/zaius.c b/roms/skiboot/platforms/astbmc/zaius.c
new file mode 100644
index 000000000..f3807a007
--- /dev/null
+++ b/roms/skiboot/platforms/astbmc/zaius.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2017-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <ipmi.h>
+#include <psi.h>
+#include <npu-regs.h>
+#include <npu2.h>
+#include <pci.h>
+#include <pci-cfg.h>
+
+#include "astbmc.h"
+
+/* backplane slots */
+static const struct slot_table_entry hdd_bay_slots[] = {
+ SW_PLUGGABLE("hdd0", 0xe),
+ SW_PLUGGABLE("hdd1", 0x4),
+ SW_PLUGGABLE("hdd2", 0x5),
+ SW_PLUGGABLE("hdd3", 0x6),
+ SW_PLUGGABLE("hdd4", 0x7),
+ SW_PLUGGABLE("hdd5", 0xf),
+ SW_PLUGGABLE("hdd6", 0xc),
+ SW_PLUGGABLE("hdd7", 0xd),
+ SW_PLUGGABLE("hdd8", 0x14),
+ SW_PLUGGABLE("hdd9", 0x17),
+ SW_PLUGGABLE("hdd10", 0x8),
+ SW_PLUGGABLE("hdd11", 0xb),
+ SW_PLUGGABLE("hdd12", 0x10),
+ SW_PLUGGABLE("hdd13", 0x13),
+ SW_PLUGGABLE("hdd14", 0x16),
+ SW_PLUGGABLE("hdd15", 0x09),
+ SW_PLUGGABLE("hdd16", 0xa),
+ SW_PLUGGABLE("hdd17", 0x11),
+ SW_PLUGGABLE("hdd18", 0x12),
+ SW_PLUGGABLE("hdd19", 0x15),
+
+ { .etype = st_end },
+};
+
+static void zaius_get_slot_info(struct phb *phb, struct pci_device *pd)
+{
+ const struct slot_table_entry *ent = NULL;
+
+ if (!pd || pd->slot)
+ return;
+
+ /*
+ * If we find a 9797 switch then assume it's the HDD Rack. This might
+ * break if we have another 9797 in the system for some reason. This is
+ * a really dumb hack, but until we get query the BMC about whether we
+ * have a HDD rack or not we don't have much of a choice.
+ */
+ if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT && pd->vdid == 0x979710b5)
+ for (ent = hdd_bay_slots; ent->etype != st_end; ent++)
+ if (ent->location == (pd->bdfn & 0xff))
+ break;
+ if (ent)
+ slot_table_add_slot_info(pd, ent);
+ else
+ slot_table_get_slot_info(phb, pd);
+}
+
+static const struct platform_ocapi zaius_ocapi = {
+ .i2c_engine = 1,
+ .i2c_port = 4,
+ .i2c_reset_addr = 0x20,
+ .i2c_reset_brick2 = (1 << 1),
+ .i2c_reset_brick3 = (1 << 6),
+ .i2c_reset_brick4 = 0, /* unused */
+ .i2c_reset_brick5 = 0, /* unused */
+ .i2c_presence_addr = 0x20,
+ .i2c_presence_brick2 = (1 << 2), /* bottom connector */
+ .i2c_presence_brick3 = (1 << 7), /* top connector */
+ .i2c_presence_brick4 = 0, /* unused */
+ .i2c_presence_brick5 = 0, /* unused */
+ .odl_phy_swap = true,
+};
+
+ST_PLUGGABLE(pe0_slot, "PE0");
+ST_PLUGGABLE(pe1_slot, "PE1");
+ST_PLUGGABLE(pe2_slot, "PE2");
+ST_PLUGGABLE(pe3_slot, "PE3");
+ST_PLUGGABLE(pe4_slot, "PE4");
+ST_PLUGGABLE(mezz_slot_a, "MEZZ A");
+ST_PLUGGABLE(mezz_slot_b, "MEZZ B");
+
+static const struct slot_table_entry zaius_phb_table[] = {
+ ST_PHB_ENTRY(0, 0, pe1_slot), /* PE1 is on PHB0 */
+ ST_PHB_ENTRY(0, 1, pe0_slot), /* PE0 is on PHB1 */
+/* ST_PHB_ENTRY(0, 2, builtin_sata), */
+ ST_PHB_ENTRY(0, 3, pe2_slot), /* un-bifurcated 16x */
+
+ ST_PHB_ENTRY(8, 0, pe3_slot),
+ ST_PHB_ENTRY(8, 1, pe4_slot),
+/* ST_PHB_ENTRY(8, 2, builtin_usb), */
+
+ /*
+ * The MEZZ slot is kind of weird. Conceptually it's a 16x slot, but
+ * physically it's two separate 8x slots (MEZZ A and B) which can be
+ * used as a 16x slot if the PHB is un-bifurcated. The BMC detects what
+ * to do based on the the presence detect bits of the MEZZ slots to
+ * configure the correct bifurcation at IPL time.
+ *
+ * There's some additional weirdness too since MEZZ B can be used to
+ * access the built-in BCM5719 and the BMC PCIe interface via a special
+ * module that bridges MEZZ B to an adjacent connector.
+ *
+ * We should probably detect the bifurcation setting and set the slot
+ * names appropriately, but this will do for now.
+ */
+ ST_PHB_ENTRY(8, 3, mezz_slot_a),
+ ST_PHB_ENTRY(8, 4, mezz_slot_b),
+/* ST_PHB_ENTRY(8, 5, builtin_bmc), */
+
+ { .etype = st_end },
+};
+
+#define NPU_BASE 0x5011000
+#define NPU_SIZE 0x2c
+#define NPU_INDIRECT0 0x8000000009010c3fUL /* OB0 - no OB3 on Zaius */
+
+/* OpenCAPI only */
+static void create_link(struct dt_node *npu, int group, int index)
+{
+ struct dt_node *link;
+ uint32_t lane_mask;
+ char namebuf[32];
+
+ snprintf(namebuf, sizeof(namebuf), "link@%x", index);
+ link = dt_new(npu, namebuf);
+
+ dt_add_property_string(link, "compatible", "ibm,npu-link");
+ dt_add_property_cells(link, "ibm,npu-link-index", index);
+
+ switch (index) {
+ case 2:
+ lane_mask = 0xf1e000; /* 0-3, 7-10 */
+ break;
+ case 3:
+ lane_mask = 0x00078f; /* 13-16, 20-23 */
+ break;
+ default:
+ assert(0);
+ }
+
+ dt_add_property_u64s(link, "ibm,npu-phy", NPU_INDIRECT0);
+ dt_add_property_cells(link, "ibm,npu-lane-mask", lane_mask);
+ dt_add_property_cells(link, "ibm,npu-group-id", group);
+ dt_add_property_u64s(link, "ibm,link-speed", 25000000000ul);
+}
+
+/* FIXME: Get rid of this after we get NPU information properly via HDAT/MRW */
+static void zaius_create_npu(void)
+{
+ struct dt_node *xscom, *npu;
+ int npu_index = 0;
+ char namebuf[32];
+
+ /* Abort if there's already an NPU in the device tree */
+ if (dt_find_compatible_node(dt_root, NULL, "ibm,power9-npu"))
+ return;
+
+ prlog(PR_DEBUG, "OCAPI: Adding NPU device nodes\n");
+ dt_for_each_compatible(dt_root, xscom, "ibm,xscom") {
+ snprintf(namebuf, sizeof(namebuf), "npu@%x", NPU_BASE);
+ npu = dt_new(xscom, namebuf);
+ dt_add_property_cells(npu, "reg", NPU_BASE, NPU_SIZE);
+ dt_add_property_strings(npu, "compatible", "ibm,power9-npu");
+ dt_add_property_cells(npu, "ibm,npu-index", npu_index++);
+ dt_add_property_cells(npu, "ibm,npu-links", 2);
+ create_link(npu, 1, 2);
+ create_link(npu, 2, 3);
+ }
+}
+
+/* FIXME: Get rid of this after we get NPU information properly via HDAT/MRW */
+static void zaius_create_ocapi_i2c_bus(void)
+{
+ struct dt_node *xscom, *i2cm, *i2c_bus;
+ prlog(PR_DEBUG, "OCAPI: Adding I2C bus device node for OCAPI reset\n");
+ dt_for_each_compatible(dt_root, xscom, "ibm,xscom") {
+ i2cm = dt_find_by_name(xscom, "i2cm@a1000");
+ if (!i2cm) {
+ prlog(PR_ERR, "OCAPI: Failed to add I2C bus device node\n");
+ continue;
+ }
+
+ if (dt_find_by_name(i2cm, "i2c-bus@4"))
+ continue;
+
+ i2c_bus = dt_new_addr(i2cm, "i2c-bus", 4);
+ dt_add_property_cells(i2c_bus, "reg", 4);
+ dt_add_property_cells(i2c_bus, "bus-frequency", 0x61a80);
+ dt_add_property_strings(i2c_bus, "compatible",
+ "ibm,opal-i2c", "ibm,power8-i2c-port",
+ "ibm,power9-i2c-port");
+ }
+}
+
+static bool zaius_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "ingrasys,zaius"))
+ return false;
+
+ /* Lot of common early inits here */
+ astbmc_early_init();
+
+ /* Setup UART for direct use by Linux */
+ uart_set_console_policy(UART_CONSOLE_OS);
+
+ zaius_create_npu();
+ zaius_create_ocapi_i2c_bus();
+
+ slot_table_init(zaius_phb_table);
+
+ return true;
+}
+
+/* Extracted from zaius1-bmc */
+static const struct bmc_hw_config bmc_hw_zaius = {
+ .scu_revision_id = 0x04030303,
+ .mcr_configuration = 0x11000FD7,
+ .mcr_scu_mpll = 0x000071C1,
+ .mcr_scu_strap = 0x00000000,
+};
+
+static const struct bmc_sw_config bmc_sw_openbmc = {
+ .ipmi_oem_partial_add_esel = IPMI_CODE(0x3a, 0xf0),
+ .ipmi_oem_hiomap_cmd = IPMI_CODE(0x3a, 0x5a),
+};
+
+static const struct bmc_platform bmc_zaius_openbmc = {
+ .name = "zaius:openbmc",
+ .hw = &bmc_hw_zaius,
+ .sw = &bmc_sw_openbmc,
+};
+
+DECLARE_PLATFORM(zaius) = {
+ .name = "Zaius",
+ .probe = zaius_probe,
+ .init = astbmc_init,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .bmc = &bmc_zaius_openbmc,
+ .pci_get_slot_info = zaius_get_slot_info,
+ .pci_probe_complete = check_all_slot_table,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
+ .exit = ipmi_wdt_final_reset,
+ .terminate = ipmi_terminate,
+ .ocapi = &zaius_ocapi,
+ .npu2_device_detect = npu2_i2c_presence_detect,
+ .op_display = op_display_lpc,
+};
diff --git a/roms/skiboot/platforms/ibm-fsp/Makefile.inc b/roms/skiboot/platforms/ibm-fsp/Makefile.inc
new file mode 100644
index 000000000..8883f09c1
--- /dev/null
+++ b/roms/skiboot/platforms/ibm-fsp/Makefile.inc
@@ -0,0 +1,9 @@
+SUBDIRS += $(PLATDIR)/ibm-fsp
+
+IBM_FSP_OBJS = common.o lxvpd.o hostservices.o fsp-vpd.o \
+ firenze.o firenze-pci.o zz.o
+IBM_FSP = $(PLATDIR)/ibm-fsp/built-in.a
+
+ifeq ($(CONFIG_FSP),1)
+$(IBM_FSP): $(IBM_FSP_OBJS:%=$(PLATDIR)/ibm-fsp/%)
+endif
diff --git a/roms/skiboot/platforms/ibm-fsp/common.c b/roms/skiboot/platforms/ibm-fsp/common.c
new file mode 100644
index 000000000..4a723b25b
--- /dev/null
+++ b/roms/skiboot/platforms/ibm-fsp/common.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+
+#include <skiboot.h>
+#include <fsp.h>
+#include <fsp-sysparam.h>
+#include <opal.h>
+#include <console.h>
+#include <hostservices.h>
+#include <ipmi.h>
+#include <debug_descriptor.h>
+#include <occ.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 = cpu_to_be32(PSI_DMA_MEMCONS);
+ t = be64_to_cpu(memcons.obuf_phys) - INMEM_CON_START + PSI_DMA_LOG_BUF;
+ debug_descriptor.memcons_obuf_tce = cpu_to_be32(t);
+ t = be64_to_cpu(memcons.ibuf_phys) - INMEM_CON_START + PSI_DMA_LOG_BUF;
+ debug_descriptor.memcons_ibuf_tce = cpu_to_be32(t);
+
+ t = PSI_DMA_TRACE_BASE;
+ for (i = 0; i < be32_to_cpu(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;
+ uint64_t trace_phys = be64_to_cpu(debug_descriptor.trace_phys[i]);
+ uint32_t trace_size = be32_to_cpu(debug_descriptor.trace_size[i]);
+
+ tstart = ALIGN_DOWN(trace_phys, 0x1000);
+ tend = ALIGN_UP(trace_phys + trace_size, 0x1000);
+ toff = trace_phys - tstart;
+ tsize = tend - tstart;
+
+ fsp_tce_map(t, (void *)tstart, tsize);
+ debug_descriptor.trace_tce[i] = cpu_to_be32(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();
+
+ /* Preload hostservices lids */
+ hservices_lid_preload();
+
+ /* Initialize SP attention area */
+ fsp_attn_init();
+
+ /* Initialize monitoring of TOD topology change event notification */
+ fsp_chiptod_init();
+
+ /* 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();
+
+ /* IPMI */
+ fsp_ipmi_init();
+ ipmi_opal_init();
+
+ /* 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();
+
+ /* EPOW */
+ op_display(OP_LOG, OP_MOD_INIT, 0x000A);
+ fsp_epow_init();
+
+ /* EPOW */
+ op_display(OP_LOG, OP_MOD_INIT, 0x000B);
+ fsp_dpo_init();
+
+ /* Setup console */
+ if (fsp_present())
+ fsp_console_add_nodes();
+
+ if (proc_gen >= proc_gen_p9)
+ prd_init();
+
+ preload_io_vpd();
+}
+
+void ibm_fsp_finalise_dt(bool is_reboot)
+{
+ if (is_reboot)
+ return;
+
+ /*
+ * LED related SPCN commands might take a while to
+ * complete. Call this as late as possible to
+ * ensure we have all the LED information.
+ */
+ create_led_device_nodes();
+
+ /*
+ * OCC takes few secs to boot. Call this as late as
+ * as possible to avoid delay.
+ */
+ occ_pstates_init();
+
+ /* Wait for FW VPD data read to complete */
+ fsp_code_update_wait_vpd(true);
+
+ fsp_console_select_stdout();
+}
+
+void ibm_fsp_exit(void)
+{
+ op_panel_disable_src_echo();
+
+ /* Clear SRCs on the op-panel when Linux starts */
+ op_panel_clear_src();
+}
+
+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;
+
+ /* Clear flash hook */
+ fsp_flash_term_hook = NULL;
+
+ 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_BUSY_EVENT;
+
+ 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();
+
+ /* Clear flash hook */
+ fsp_flash_term_hook = NULL;
+
+ printf("FSP: Sending shutdown command to FSP...\n");
+
+ if (fsp_sync_msg(fsp_mkmsg(FSP_CMD_POWERDOWN_NORM, 1, request), true))
+ return OPAL_BUSY_EVENT;
+
+ fsp_reset_links();
+ return OPAL_SUCCESS;
+}
+
+int64_t ibm_fsp_sensor_read(uint32_t sensor_hndl, int token,
+ __be64 *sensor_data)
+{
+ return fsp_opal_read_sensor(sensor_hndl, token, sensor_data);
+}
+
+int __attrconst fsp_heartbeat_time(void)
+{
+ /* Same as core/timer.c HEARTBEAT_DEFAULT_MS * 10 */
+ return 200 * 10;
+}
+
+static void fsp_psihb_interrupt(void)
+{
+ /* Poll the console buffers on any interrupt since we don't
+ * get send notifications
+ */
+ fsp_console_poll(NULL);
+}
+
+struct platform_psi fsp_platform_psi = {
+ .psihb_interrupt = fsp_psihb_interrupt,
+ .link_established = fsp_reinit_fsp,
+ .fsp_interrupt = fsp_interrupt,
+};
+
+struct platform_prd fsp_platform_prd = {
+ .msg_response = hservice_hbrt_msg_response,
+ .send_error_log = hservice_send_error_log,
+ .send_hbrt_msg = hservice_send_hbrt_msg,
+ .wakeup = hservice_wakeup,
+ .fsp_occ_load_start_status = fsp_occ_load_start_status,
+ .fsp_occ_reset_status = fsp_occ_reset_status,
+};
diff --git a/roms/skiboot/platforms/ibm-fsp/firenze-pci.c b/roms/skiboot/platforms/ibm-fsp/firenze-pci.c
new file mode 100644
index 000000000..5fbbc2ff2
--- /dev/null
+++ b/roms/skiboot/platforms/ibm-fsp/firenze-pci.c
@@ -0,0 +1,1044 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#define pr_fmt(fmt) "FIRENZE-PCI: " fmt
+#include <skiboot.h>
+#include <device.h>
+#include <fsp.h>
+#include <lock.h>
+#include <timer.h>
+#include <xscom.h>
+#include <pci-cfg.h>
+#include <pci.h>
+#include <pci-slot.h>
+#include <phb3.h>
+#include <chip.h>
+#include <i2c.h>
+
+#include "ibm-fsp.h"
+#include "lxvpd.h"
+
+/* Dump PCI slots before sending to FSP */
+#define FIRENZE_PCI_INVENTORY_DUMP
+
+/*
+ * Firenze PCI slot states to override the default set.
+ * Refer to pci-slot.h for the default PCI state set
+ * when you're going to change below values.
+ */
+#define FIRENZE_PCI_SLOT_NORMAL PCI_SLOT_STATE_NORMAL
+#define FIRENZE_PCI_SLOT_LINK PCI_SLOT_STATE_LINK
+#define FIRENZE_PCI_SLOT_LINK_START (FIRENZE_PCI_SLOT_LINK + 1)
+#define FIRENZE_PCI_SLOT_HRESET PCI_SLOT_STATE_HRESET
+#define FIRENZE_PCI_SLOT_HRESET_START (FIRENZE_PCI_SLOT_HRESET + 1)
+#define FIRENZE_PCI_SLOT_FRESET PCI_SLOT_STATE_FRESET
+#define FIRENZE_PCI_SLOT_FRESET_START (FIRENZE_PCI_SLOT_FRESET + 1)
+#define FIRENZE_PCI_SLOT_FRESET_WAIT_RSP (FIRENZE_PCI_SLOT_FRESET + 2)
+#define FIRENZE_PCI_SLOT_FRESET_DELAY (FIRENZE_PCI_SLOT_FRESET + 3)
+#define FIRENZE_PCI_SLOT_FRESET_POWER_STATE (FIRENZE_PCI_SLOT_FRESET + 4)
+#define FIRENZE_PCI_SLOT_FRESET_POWER_OFF (FIRENZE_PCI_SLOT_FRESET + 5)
+#define FIRENZE_PCI_SLOT_FRESET_POWER_ON (FIRENZE_PCI_SLOT_FRESET + 6)
+#define FIRENZE_PCI_SLOT_PERST_DEASSERT (FIRENZE_PCI_SLOT_FRESET + 7)
+#define FIRENZE_PCI_SLOT_PERST_DELAY (FIRENZE_PCI_SLOT_FRESET + 8)
+#define FIRENZE_PCI_SLOT_GPOWER PCI_SLOT_STATE_GPOWER
+#define FIRENZE_PCI_SLOT_GPOWER_START (FIRENZE_PCI_SLOT_GPOWER + 1)
+#define FIRENZE_PCI_SLOT_SPOWER PCI_SLOT_STATE_SPOWER
+#define FIRENZE_PCI_SLOT_SPOWER_START (FIRENZE_PCI_SLOT_SPOWER + 1)
+#define FIRENZE_PCI_SLOT_SPOWER_DONE (FIRENZE_PCI_SLOT_SPOWER + 2)
+
+/* Timeout for power status */
+#define FIRENZE_PCI_SLOT_RETRIES 500
+#define FIRENZE_PCI_SLOT_DELAY 10 /* ms */
+#define FIRENZE_PCI_I2C_TIMEOUT 500 /* ms */
+
+/*
+ * Need figure out more stuff later: LED and presence
+ * detection sensors are accessed from PSI/FSP.
+ */
+struct firenze_pci_slot {
+ struct lxvpd_pci_slot lxvpd_slot; /* LXVPD slot data */
+
+ /* Next slot state */
+ uint32_t next_state;
+
+ /* Power management */
+ struct i2c_bus *i2c_bus; /* Where MAX5961 seats */
+ struct i2c_request *req; /* I2C request message */
+ uint8_t i2c_rw_buf[8]; /* I2C read/write buffer */
+ uint8_t power_mask; /* Bits for power status */
+ uint8_t power_on; /* Bits for power on */
+ uint8_t power_off; /* Bits for power off */
+ uint8_t *power_status; /* Last power status */
+ uint16_t perst_reg; /* PERST config register */
+ uint16_t perst_bit; /* PERST bit */
+};
+
+struct firenze_pci_slot_info {
+ uint8_t index;
+ const char *label;
+ uint8_t external_power_mgt;
+ uint8_t inband_perst;
+ uint8_t chip_id;
+ uint8_t master_id;
+ uint8_t port_id;
+ uint8_t slave_addr;
+ uint8_t channel;
+ uint8_t power_status;
+ uint8_t buddy;
+};
+
+struct firenze_pci_slot_fixup_info {
+ const char *label;
+ uint8_t reg;
+ uint8_t val;
+};
+
+struct firenze_pci_inv {
+ __be32 hw_proc_id;
+ __be16 slot_idx;
+ __be16 reserved;
+ __be16 vendor_id;
+ __be16 device_id;
+ __be16 subsys_vendor_id;
+ __be16 subsys_device_id;
+} __packed;
+
+struct firenze_pci_inv_data {
+ __be32 version; /* currently 1 */
+ __be32 num_entries;
+ __be32 entry_size;
+ __be32 entry_offset;
+ struct firenze_pci_inv entries[];
+} __packed;
+
+/*
+ * Note: According to Tuleta system workbook, I didn't figure
+ * out the I2C mapping info for slot C14/C15.
+ */
+static struct firenze_pci_inv_data *firenze_inv_data;
+static uint32_t firenze_inv_cnt;
+static struct firenze_pci_slot_info firenze_pci_slots[] = {
+ { 0x0B, "C7", 1, 1, 0, 1, 0, 0x35, 1, 0xAA, 0 },
+ { 0x11, "C14", 0, 1, 0, 0, 0, 0x00, 0, 0xAA, 1 },
+ { 0x0F, "C11", 1, 1, 0, 1, 0, 0x32, 1, 0xAA, 2 },
+ { 0x10, "C12", 1, 1, 0, 1, 0, 0x39, 0, 0xAA, 3 },
+ { 0x0A, "C6", 1, 1, 0, 1, 0, 0x35, 0, 0xAA, 0 },
+ { 0x12, "C15", 0, 1, 0, 0, 0, 0x00, 0, 0xAA, 5 },
+ { 0x01, "USB", 0, 0, 0, 0, 0, 0x00, 0, 0xAA, 6 },
+ { 0x0C, "C8", 1, 1, 0, 1, 0, 0x36, 0, 0xAA, 7 },
+ { 0x0D, "C9", 1, 1, 0, 1, 0, 0x36, 1, 0xAA, 7 },
+ { 0x0E, "C10", 1, 1, 0, 1, 0, 0x32, 0, 0xAA, 2 },
+ { 0x09, "C5", 1, 1, 0x10, 1, 0, 0x39, 1, 0xAA, 10 },
+ { 0x08, "C4", 1, 1, 0x10, 1, 0, 0x39, 0, 0xAA, 10 },
+ { 0x07, "C3", 1, 1, 0x10, 1, 0, 0x3A, 1, 0xAA, 12 },
+ { 0x06, "C2", 1, 1, 0x10, 1, 0, 0x3A, 0, 0xAA, 12 }
+};
+
+/*
+ * I2C power controller register fix up table. Not sure what they do, but
+ * they seem to relate to the fast-trip setpoint.
+ */
+static struct firenze_pci_slot_fixup_info firenze_pci_slot_fixup_tbl[] = {
+ { "C3", 0x5e, 0xfb },
+ { "C3", 0x5b, 0xff },
+ { "C5", 0x5e, 0xfb },
+ { "C5", 0x5b, 0xff },
+ { "C6", 0x5e, 0xfa },
+ { "C6", 0x5a, 0xff },
+ { "C6", 0x5b, 0xff },
+ { "C7", 0x5e, 0xfa },
+ { "C7", 0x5a, 0xff },
+ { "C7", 0x5b, 0xff }
+};
+
+static void firenze_pci_add_inventory(struct phb *phb,
+ struct pci_device *pd)
+{
+ struct lxvpd_pci_slot *lxvpd_slot;
+ struct firenze_pci_inv *entry;
+ struct proc_chip *chip;
+ size_t size;
+ bool need_init = false;
+ u32 num_entries;
+ u16 tmp16;
+
+ /*
+ * 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 && pd->slot->data)
+ return;
+ if (!pd->parent->slot ||
+ !pd->parent->slot->data)
+ return;
+ lxvpd_slot = pd->parent->slot->data;
+ if (!lxvpd_slot->pluggable)
+ return;
+
+ /* Check if we need to do some (Re)allocation */
+ if (!firenze_inv_data ||
+ be32_to_cpu(firenze_inv_data->num_entries) == firenze_inv_cnt) {
+ need_init = !firenze_inv_data;
+
+ /* (Re)allocate the block to the new size */
+ firenze_inv_cnt += 4;
+ size = sizeof(struct firenze_pci_inv_data) +
+ sizeof(struct firenze_pci_inv) * firenze_inv_cnt;
+ firenze_inv_data = realloc(firenze_inv_data, size);
+ }
+
+ /* Initialize the header for a new inventory */
+ if (need_init) {
+ firenze_inv_data->version = cpu_to_be32(1);
+ firenze_inv_data->num_entries = 0;
+ firenze_inv_data->entry_size =
+ cpu_to_be32(sizeof(struct firenze_pci_inv));
+ firenze_inv_data->entry_offset =
+ cpu_to_be32(offsetof(struct firenze_pci_inv_data, entries));
+ }
+
+ /* Append slot entry */
+ num_entries = be32_to_cpu(firenze_inv_data->num_entries);
+ firenze_inv_data->num_entries = cpu_to_be32(num_entries + 1);
+ entry = &firenze_inv_data->entries[num_entries];
+ chip = get_chip(dt_get_chip_id(phb->dt_node));
+ if (!chip) {
+ /**
+ * @fwts-label FirenzePCIInventory
+ * @fwts-advice Device tree didn't contain enough information
+ * to correctly report back PCI inventory. Service processor
+ * is likely to be missing information about what hardware
+ * is physically present in the machine.
+ */
+ prlog(PR_ERR, "No chip device node for PHB%04x\n",
+ phb->opal_id);
+ return;
+ }
+
+ entry->hw_proc_id = cpu_to_be32(chip->pcid);
+ entry->reserved = 0;
+ if (pd->parent &&
+ pd->parent->slot &&
+ pd->parent->slot->data) {
+ lxvpd_slot = pd->parent->slot->data;
+ entry->slot_idx = cpu_to_be16(lxvpd_slot->slot_index);
+ }
+
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_VENDOR_ID, &tmp16);
+ entry->vendor_id = cpu_to_be16(tmp16);
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_DEVICE_ID, &tmp16);
+ entry->device_id = cpu_to_be16(tmp16);
+ 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 = cpu_to_be16(0xffff);
+ entry->subsys_device_id = cpu_to_be16(0xffff);
+ } else {
+ pci_cfg_read16(phb, pd->bdfn,
+ ssvc + PCICAP_SUBSYS_VID_VENDOR, &tmp16);
+ entry->subsys_vendor_id = cpu_to_be16(tmp16);
+ pci_cfg_read16(phb, pd->bdfn,
+ ssvc + PCICAP_SUBSYS_VID_DEVICE, &tmp16);
+ entry->subsys_device_id = cpu_to_be16(tmp16);
+ }
+ } else {
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_VENDOR_ID, &tmp16);
+ entry->subsys_vendor_id = cpu_to_be16(tmp16);
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_ID, &tmp16);
+ entry->subsys_device_id = cpu_to_be16(tmp16);
+ }
+}
+
+static void firenze_dump_pci_inventory(void)
+{
+#ifdef FIRENZE_PCI_INVENTORY_DUMP
+ struct firenze_pci_inv *e;
+ uint32_t i;
+
+ if (!firenze_inv_data)
+ return;
+
+ prlog(PR_INFO, "Dumping Firenze PCI inventory\n");
+ prlog(PR_INFO, "HWP SLT VDID DVID SVID SDID\n");
+ prlog(PR_INFO, "---------------------------\n");
+ for (i = 0; i < be32_to_cpu(firenze_inv_data->num_entries); i++) {
+ e = &firenze_inv_data->entries[i];
+
+ prlog(PR_INFO, "%03d %03d %04x %04x %04x %04x\n",
+ be32_to_cpu(e->hw_proc_id),
+ be16_to_cpu(e->slot_idx),
+ be16_to_cpu(e->vendor_id),
+ be16_to_cpu(e->device_id),
+ be16_to_cpu(e->subsys_vendor_id),
+ be16_to_cpu(e->subsys_device_id));
+ }
+#endif /* FIRENZE_PCI_INVENTORY_DUMP */
+}
+
+void firenze_pci_send_inventory(void)
+{
+ uint64_t base, abase, end, aend, offset;
+ int64_t rc;
+
+ if (!firenze_inv_data)
+ return;
+
+ /* Dump the inventory */
+ prlog(PR_INFO, "Sending %d inventory to FSP\n",
+ be32_to_cpu(firenze_inv_data->num_entries));
+ firenze_dump_pci_inventory();
+
+ /* Memory location for inventory */
+ base = (uint64_t)firenze_inv_data;
+ end = base + sizeof(struct firenze_pci_inv_data) +
+ be32_to_cpu(firenze_inv_data->num_entries) *
+ be32_to_cpu(firenze_inv_data->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) {
+ /**
+ * @fwts-label FirenzePCIInventoryTooLarge
+ * @fwts-advice More PCI inventory than we can send to service
+ * processor. The service processor will have an incomplete
+ * view of the world.
+ */
+ prlog(PR_ERR, "Inventory (%lld bytes) too large\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)
+ {
+ /**
+ * @fwts-label FirenzePCIInventoryError
+ * @fwts-advice Error communicating with service processor
+ * when sending PCI Inventory.
+ */
+ prlog(PR_ERR, "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(firenze_inv_data);
+ firenze_inv_data = NULL;
+ firenze_inv_cnt = 0;
+}
+
+/* The function is called when the I2C request is completed
+ * successfully, or with errors.
+ */
+static void firenze_i2c_req_done(int rc, struct i2c_request *req)
+{
+ struct pci_slot *slot = req->user_data;
+ uint32_t state;
+
+ /* Check if there are errors for the completion */
+ if (rc) {
+ /**
+ * @fwts-label FirenzePCII2CError
+ * @fwts-advice On Firenze platforms, I2C is used to control
+ * power to PCI slots. Errors here mean we may be in trouble
+ * in regards to PCI slot power on/off.
+ */
+ prlog(PR_ERR, "Error %d from I2C request on slot %016llx\n",
+ rc, slot->id);
+ return;
+ }
+
+ /* Check the request type */
+ if (req->op != SMBUS_READ && req->op != SMBUS_WRITE) {
+ /**
+ * @fwts-label FirenzePCII2CInvalid
+ * @fwts-advice Likely a coding error: invalid I2C request.
+ */
+ prlog(PR_ERR, "Invalid I2C request %d on slot %016llx\n",
+ req->op, slot->id);
+ return;
+ }
+
+ /* After writting power status to I2C slave, we need at least
+ * 5ms delay for the slave to settle down. We also have the
+ * delay after reading the power status as well.
+ */
+ switch (slot->state) {
+ case FIRENZE_PCI_SLOT_FRESET_WAIT_RSP:
+ prlog(PR_DEBUG, "%016llx FRESET: I2C request completed\n",
+ slot->id);
+ state = FIRENZE_PCI_SLOT_FRESET_DELAY;
+ break;
+ case FIRENZE_PCI_SLOT_SPOWER_START:
+ prlog(PR_DEBUG, "%016llx SPOWER: I2C request completed\n",
+ slot->id);
+ state = FIRENZE_PCI_SLOT_SPOWER_DONE;
+ break;
+ default:
+ /**
+ * @fwts-label FirenzePCISlotI2CStateError
+ * @fwts-advice The Firenze platform uses I2C to control
+ * power to PCI slots. Something went wrong in the state
+ * machine controlling that. Slots may/may not have power.
+ */
+ prlog(PR_ERR, "Wrong state %08x on slot %016llx\n",
+ slot->state, slot->id);
+ return;
+ }
+
+ /* Switch to net state */
+ pci_slot_set_state(slot, state);
+}
+
+/* This function is called to setup normal PCI device or PHB slot.
+ * For the later case, the slot doesn't have the associated PCI
+ * device. Besides, the I2C response timeout is set to 5s. We might
+ * improve I2C in future to support priorized requests so that the
+ * timeout can be shortened.
+ */
+static int64_t firenze_pci_slot_freset(struct pci_slot *slot)
+{
+ struct firenze_pci_slot *plat_slot = slot->data;
+ uint8_t *pval, presence = 1;
+ uint32_t timeout;
+
+ switch (slot->state) {
+ case FIRENZE_PCI_SLOT_NORMAL:
+ case FIRENZE_PCI_SLOT_FRESET_START:
+ prlog(PR_DEBUG, "%016llx FRESET: Starts\n",
+ slot->id);
+
+ /* Bail if nothing is connected */
+ if (slot->ops.get_presence_state)
+ slot->ops.get_presence_state(slot, &presence);
+ if (!presence) {
+ prlog(PR_DEBUG, "%016llx FRESET: No device\n",
+ slot->id);
+ return OPAL_SUCCESS;
+ }
+
+ /* Prepare link down */
+ if (slot->ops.prepare_link_change) {
+ prlog(PR_DEBUG, "%016llx FRESET: Prepares link down\n",
+ slot->id);
+ slot->ops.prepare_link_change(slot, false);
+ }
+
+ /* Send I2C request */
+ prlog(PR_DEBUG, "%016llx FRESET: Check power state\n",
+ slot->id);
+ plat_slot->next_state =
+ FIRENZE_PCI_SLOT_FRESET_POWER_STATE;
+ plat_slot->req->op = SMBUS_READ;
+ slot->retries = FIRENZE_PCI_SLOT_RETRIES;
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_FRESET_WAIT_RSP);
+ if (pci_slot_has_flags(slot, PCI_SLOT_FLAG_BOOTUP))
+ plat_slot->req->timeout = FIRENZE_PCI_I2C_TIMEOUT;
+ else
+ plat_slot->req->timeout = 0ul;
+ i2c_queue_req(plat_slot->req);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_FRESET_WAIT_RSP:
+ if (slot->retries-- == 0) {
+ prlog(PR_DEBUG, "%016llx FRESET: Timeout waiting for %08x\n",
+ slot->id, plat_slot->next_state);
+ goto out;
+ }
+
+ check_timers(false);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_FRESET_DELAY:
+ prlog(PR_DEBUG, "%016llx FRESET: Delay %dms on I2C completion\n",
+ slot->id, FIRENZE_PCI_SLOT_DELAY);
+ pci_slot_set_state(slot, plat_slot->next_state);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_FRESET_POWER_STATE:
+ /* Update last power status */
+ pval = (uint8_t *)(plat_slot->req->rw_buf);
+ *plat_slot->power_status = *pval;
+
+ /* Power is on, turn it off */
+ if (((*pval) & plat_slot->power_mask) == plat_slot->power_on) {
+ prlog(PR_DEBUG, "%016llx FRESET: Power (%02x) on, turn off\n",
+ slot->id, *pval);
+ (*pval) &= ~plat_slot->power_mask;
+ (*pval) |= plat_slot->power_off;
+ plat_slot->req->op = SMBUS_WRITE;
+ slot->retries = FIRENZE_PCI_SLOT_RETRIES;
+ plat_slot->next_state =
+ FIRENZE_PCI_SLOT_FRESET_POWER_OFF;
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_FRESET_WAIT_RSP);
+
+ if (pci_slot_has_flags(slot, PCI_SLOT_FLAG_BOOTUP))
+ timeout = FIRENZE_PCI_I2C_TIMEOUT;
+ else
+ timeout = 0ul;
+ plat_slot->req->timeout = timeout;
+
+ i2c_queue_req(plat_slot->req);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ }
+
+ /* Power is off, turn it on */
+ /* Fallthrough */
+ case FIRENZE_PCI_SLOT_FRESET_POWER_OFF:
+ /* Update last power status */
+ pval = (uint8_t *)(plat_slot->req->rw_buf);
+ *plat_slot->power_status = *pval;
+
+ prlog(PR_DEBUG, "%016llx FRESET: Power (%02x) off, turn on\n",
+ slot->id, *pval);
+ (*pval) &= ~plat_slot->power_mask;
+ (*pval) |= plat_slot->power_on;
+ plat_slot->req->op = SMBUS_WRITE;
+ plat_slot->next_state =
+ FIRENZE_PCI_SLOT_FRESET_POWER_ON;
+ slot->retries = FIRENZE_PCI_SLOT_RETRIES;
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_FRESET_WAIT_RSP);
+
+ if (pci_slot_has_flags(slot, PCI_SLOT_FLAG_BOOTUP))
+ plat_slot->req->timeout = FIRENZE_PCI_I2C_TIMEOUT;
+ else
+ plat_slot->req->timeout = 0ul;
+ i2c_queue_req(plat_slot->req);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_FRESET_POWER_ON:
+ /* Update last power status */
+ pval = (uint8_t *)(plat_slot->req->rw_buf);
+ *plat_slot->power_status = *pval;
+
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_LINK_START);
+ return slot->ops.poll_link(slot);
+ default:
+ prlog(PR_DEBUG, "%016llx FRESET: Unexpected state %08x\n",
+ slot->id, slot->state);
+ }
+
+out:
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_NORMAL);
+ return OPAL_HARDWARE;
+}
+
+static int64_t firenze_pci_slot_perst(struct pci_slot *slot)
+{
+ struct firenze_pci_slot *plat_slot = slot->data;
+ uint8_t presence = 1;
+ uint16_t ctrl;
+
+ switch (slot->state) {
+ case FIRENZE_PCI_SLOT_NORMAL:
+ case FIRENZE_PCI_SLOT_FRESET_START:
+ prlog(PR_DEBUG, "%016llx PERST: Starts\n",
+ slot->id);
+
+ /* Bail if nothing is connected */
+ if (slot->ops.get_presence_state)
+ slot->ops.get_presence_state(slot, &presence);
+ if (!presence) {
+ prlog(PR_DEBUG, "%016llx PERST: No device\n",
+ slot->id);
+ return OPAL_SUCCESS;
+ }
+
+ /* Prepare link down */
+ if (slot->ops.prepare_link_change) {
+ prlog(PR_DEBUG, "%016llx PERST: Prepare link down\n",
+ slot->id);
+ slot->ops.prepare_link_change(slot, false);
+ }
+
+ /* Assert PERST */
+ prlog(PR_DEBUG, "%016llx PERST: Assert\n",
+ slot->id);
+ pci_cfg_read16(slot->phb, slot->pd->bdfn,
+ plat_slot->perst_reg, &ctrl);
+ ctrl |= plat_slot->perst_bit;
+ pci_cfg_write16(slot->phb, slot->pd->bdfn,
+ plat_slot->perst_reg, ctrl);
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_PERST_DEASSERT);
+ return pci_slot_set_sm_timeout(slot, msecs_to_tb(250));
+ case FIRENZE_PCI_SLOT_PERST_DEASSERT:
+ /* Deassert PERST */
+ pci_cfg_read16(slot->phb, slot->pd->bdfn,
+ plat_slot->perst_reg, &ctrl);
+ ctrl &= ~plat_slot->perst_bit;
+ pci_cfg_write16(slot->phb, slot->pd->bdfn,
+ plat_slot->perst_reg, ctrl);
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_PERST_DELAY);
+ return pci_slot_set_sm_timeout(slot, msecs_to_tb(1500));
+ case FIRENZE_PCI_SLOT_PERST_DELAY:
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_LINK_START);
+ return slot->ops.poll_link(slot);
+ default:
+ prlog(PR_DEBUG, "%016llx PERST: Unexpected state %08x\n",
+ slot->id, slot->state);
+ }
+
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_NORMAL);
+ return OPAL_HARDWARE;
+}
+
+static int64_t firenze_pci_slot_get_power_state(struct pci_slot *slot,
+ uint8_t *val)
+{
+ if (slot->state != FIRENZE_PCI_SLOT_NORMAL)
+ {
+ /**
+ * @fwts-label FirenzePCISlotGPowerState
+ * @fwts-advice Unexpected state in the FIRENZE PCI Slot
+ * state machine. This could mean PCI is not functioning
+ * correctly.
+ */
+ prlog(PR_ERR, "%016llx GPOWER: Unexpected state %08x\n",
+ slot->id, slot->state);
+ }
+
+ *val = slot->power_state;
+ return OPAL_SUCCESS;
+}
+
+static int64_t firenze_pci_slot_set_power_state(struct pci_slot *slot,
+ uint8_t val)
+{
+ struct firenze_pci_slot *plat_slot = slot->data;
+ uint8_t *pval;
+
+ if (slot->state != FIRENZE_PCI_SLOT_NORMAL)
+ {
+ /**
+ * @fwts-label FirenzePCISlotSPowerState
+ * @fwts-advice Unexpected state in the FIRENZE PCI Slot
+ * state machine. This could mean PCI is not functioning
+ * correctly.
+ */
+ prlog(PR_ERR, "%016llx SPOWER: Unexpected state %08x\n",
+ slot->id, slot->state);
+ }
+
+ if (val != PCI_SLOT_POWER_OFF && val != PCI_SLOT_POWER_ON)
+ return OPAL_PARAMETER;
+
+ if (!pci_slot_has_flags(slot, PCI_SLOT_FLAG_ENFORCE) &&
+ slot->power_state == val)
+ return OPAL_SUCCESS;
+
+ /* Update with the requested power state and bail immediately when
+ * surprise hotplug is supported on the slot. It keeps the power
+ * supply to the slot on and it guarentees the link state change
+ * events will be raised properly during surprise hot add/remove.
+ */
+ if (!pci_slot_has_flags(slot, PCI_SLOT_FLAG_ENFORCE) &&
+ slot->surprise_pluggable) {
+ slot->power_state = val;
+ return OPAL_SUCCESS;
+ }
+
+ slot->power_state = val;
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_SPOWER_START);
+
+ plat_slot->req->op = SMBUS_WRITE;
+ pval = (uint8_t *)plat_slot->req->rw_buf;
+ if (val == PCI_SLOT_POWER_ON) {
+ *pval = *plat_slot->power_status;
+ (*pval) &= ~plat_slot->power_mask;
+ (*pval) |= plat_slot->power_on;
+ } else {
+ *pval = *plat_slot->power_status;
+ (*pval) &= ~plat_slot->power_mask;
+ (*pval) |= plat_slot->power_off;
+ }
+
+ if (pci_slot_has_flags(slot, PCI_SLOT_FLAG_BOOTUP))
+ plat_slot->req->timeout = FIRENZE_PCI_I2C_TIMEOUT;
+ else
+ plat_slot->req->timeout = 0ul;
+ i2c_queue_req(plat_slot->req);
+
+ return OPAL_ASYNC_COMPLETION;
+}
+
+static struct i2c_bus *firenze_pci_find_i2c_bus(uint8_t chip,
+ uint8_t eng,
+ uint8_t port)
+{
+ struct dt_node *np, *child;
+ uint32_t reg;
+
+ /* Iterate I2C masters */
+ dt_for_each_compatible(dt_root, np, "ibm,power8-i2cm") {
+ if (!np->parent ||
+ !dt_node_is_compatible(np->parent, "ibm,power8-xscom"))
+ continue;
+
+ /* Check chip index */
+ reg = dt_prop_get_u32(np->parent, "ibm,chip-id");
+ if (reg != chip)
+ continue;
+
+ /* Check I2C master index */
+ reg = dt_prop_get_u32(np, "chip-engine#");
+ if (reg != eng)
+ continue;
+
+ /* Iterate I2C buses */
+ dt_for_each_child(np, child) {
+ if (!dt_node_is_compatible(child, "ibm,power8-i2c-port"))
+ continue;
+
+ /* Check I2C port index */
+ reg = dt_prop_get_u32(child, "reg");
+ if (reg != port)
+ continue;
+
+ reg = dt_prop_get_u32(child, "ibm,opal-id");
+ return i2c_find_bus_by_id(reg);
+ }
+ }
+
+ return NULL;
+}
+
+static int64_t firenze_pci_slot_fixup_one_reg(struct pci_slot *slot,
+ struct firenze_pci_slot_fixup_info *fixup)
+{
+ struct firenze_pci_slot *plat_slot = slot->data;
+ struct i2c_request req;
+ uint8_t buf;
+ int64_t rc;
+
+ /*
+ * Fill out our own request structure since we don't want to invoke the
+ * normal completion handler.
+ */
+ memset(&req, 0, sizeof(req));
+ req.dev_addr = plat_slot->req->dev_addr;
+ req.bus = plat_slot->req->bus;
+ req.offset = fixup->reg;
+ req.offset_bytes = 1;
+ req.rw_buf = &buf;
+ req.rw_len = 1;
+ req.timeout = FIRENZE_PCI_I2C_TIMEOUT;
+
+ req.op = SMBUS_WRITE;
+ buf = fixup->val;
+ rc = i2c_request_sync(&req);
+ if (rc < 0)
+ return rc;
+
+ /*
+ * Check the register fixup has been applied. It's not the end of the
+ * world we don't, but eh...
+ */
+ req.op = SMBUS_READ;
+ rc = i2c_request_sync(&req);
+ if (rc == OPAL_SUCCESS && buf != fixup->val) {
+ prlog(PR_ERR, "Error verifying fixup [%s] - (%02x, %02x, %02x)\n",
+ fixup->label, fixup->reg, fixup->val, buf);
+ }
+
+ return rc;
+}
+
+static void firenze_pci_slot_fixup(struct pci_slot *slot,
+ struct firenze_pci_slot_info *info)
+{
+ int64_t rc, i, applied = 0;
+ const uint32_t *p;
+ uint64_t id;
+
+ p = dt_prop_get_def(dt_root, "ibm,vpd-lx-info", NULL);
+ id = p ? (((uint64_t)p[1] << 32) | p[2]) : 0ul;
+ if (id != LX_VPD_2S4U_BACKPLANE &&
+ id != LX_VPD_1S4U_BACKPLANE)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(firenze_pci_slot_fixup_tbl); i++) {
+ struct firenze_pci_slot_fixup_info *fixup =
+ &firenze_pci_slot_fixup_tbl[i];
+
+ if (strcmp(info->label, fixup->label))
+ continue;
+
+ rc = firenze_pci_slot_fixup_one_reg(slot, fixup);
+ if (rc) {
+ prlog(PR_ERR, "I2C error (%lld) applying fixup [%s] - (%02x, %02x)\n",
+ rc, fixup->label, fixup->reg, fixup->val);
+ return;
+ }
+
+ applied++;
+ }
+
+ if (applied)
+ prlog(PR_INFO, "Applied %lld fixups for [%s]\n",
+ applied, info->label);
+}
+
+static void firenze_pci_setup_power_mgt(struct pci_slot *slot,
+ struct firenze_pci_slot *plat_slot,
+ struct firenze_pci_slot_info *info)
+{
+ plat_slot->i2c_bus = firenze_pci_find_i2c_bus(info->chip_id,
+ info->master_id,
+ info->port_id);
+ if (!plat_slot->i2c_bus)
+ return;
+
+ plat_slot->req = zalloc(sizeof(*plat_slot->req));
+ if (!plat_slot->req)
+ return;
+
+ plat_slot->req->dev_addr = info->slave_addr;
+ plat_slot->req->offset_bytes = 1;
+ plat_slot->req->rw_buf = plat_slot->i2c_rw_buf;
+ plat_slot->req->rw_len = 1;
+ plat_slot->req->completion = firenze_i2c_req_done;
+ plat_slot->req->user_data = slot;
+ plat_slot->req->bus = plat_slot->i2c_bus;
+
+ firenze_pci_slot_fixup(slot, info);
+
+ /*
+ * For all slots, the register used to change the power state is
+ * always 0x69. It could have been set to something else in the
+ * above fixup. Lets fix it to 0x69 here.
+ *
+ * The power states of two slots are controlled by one register.
+ * This means two slots have to share data buffer for power states,
+ * which are tracked by struct firenze_pci_slot_info::power_status.
+ * With it, we can avoid affecting slot#B's power state when trying
+ * to adjust that on slot#A. Also, the initial power states for all
+ * slots are assumed to be PCI_SLOT_POWER_ON.
+ */
+ plat_slot->req->offset = 0x69;
+ plat_slot->power_status = &firenze_pci_slots[info->buddy].power_status;
+ switch (info->channel) {
+ case 0:
+ plat_slot->power_mask = 0x33;
+ plat_slot->power_on = 0x22;
+ plat_slot->power_off = 0;
+ break;
+ case 1:
+ plat_slot->power_status = &firenze_pci_slots[info->buddy].power_status;
+ plat_slot->power_mask = 0xcc;
+ plat_slot->power_on = 0x88;
+ plat_slot->power_off = 0;
+ break;
+ default:
+ prlog(PR_ERR, "%016llx: Invalid channel %d\n",
+ slot->id, info->channel);
+ }
+}
+
+static void firenze_pci_slot_init(struct pci_slot *slot)
+{
+ struct lxvpd_pci_slot *s = slot->data;
+ struct firenze_pci_slot *plat_slot = slot->data;
+ struct firenze_pci_slot_info *info = NULL;
+ uint32_t vdid;
+ int i;
+
+ /* Init the slot info from the LXVPD */
+ slot->ops.add_properties = lxvpd_add_slot_properties;
+
+ /* Search for power control information in the per-system table */
+ for (i = 0; i < ARRAY_SIZE(firenze_pci_slots); i++) {
+ if (firenze_pci_slots[i].index == s->slot_index &&
+ !strcmp(firenze_pci_slots[i].label, s->label)) {
+ info = &firenze_pci_slots[i];
+ break;
+ }
+ }
+ if (!info)
+ return;
+
+ /* Search I2C bus for external power mgt */
+ if (slot->power_ctl)
+ firenze_pci_setup_power_mgt(slot, plat_slot, info);
+
+ /*
+ * If the slot has external power logic, to override the
+ * default power management methods. Because of the bad
+ * I2C design, the API supplied by I2C is really hard to
+ * be utilized. To figure out power status retrival or
+ * configuration after we have a blocking API for that.
+ */
+ if (plat_slot->req) {
+ slot->ops.freset = firenze_pci_slot_freset;
+ slot->ops.get_power_state = firenze_pci_slot_get_power_state;
+ slot->ops.set_power_state = firenze_pci_slot_set_power_state;
+ prlog(PR_DEBUG, "%016llx: External power mgt initialized\n",
+ slot->id);
+ } else if (info->inband_perst) {
+ /*
+ * For PLX downstream ports, PCI config register can be
+ * leveraged to do PERST. If the slot doesn't have external
+ * power management stuff, lets try to stick to the PERST
+ * logic if applicable
+ */
+ if (slot->pd->dev_type == PCIE_TYPE_SWITCH_DNPORT) {
+ pci_cfg_read32(slot->phb, slot->pd->bdfn,
+ PCI_CFG_VENDOR_ID, &vdid);
+ switch (vdid) {
+ case 0x873210b5: /* PLX8732 */
+ case 0x874810b5: /* PLX8748 */
+ plat_slot->perst_reg = 0x80;
+ plat_slot->perst_bit = 0x0400;
+ slot->ops.freset = firenze_pci_slot_perst;
+ break;
+ }
+ }
+ }
+}
+
+void firenze_pci_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, sizeof(struct firenze_pci_slot));
+}
+
+void firenze_pci_get_slot_info(struct phb *phb, struct pci_device *pd)
+{
+ struct pci_slot *slot;
+ struct lxvpd_pci_slot *s;
+
+ /* Prepare the PCI inventory */
+ firenze_pci_add_inventory(phb, pd);
+
+ if (pd->dev_type != PCIE_TYPE_ROOT_PORT &&
+ pd->dev_type != PCIE_TYPE_SWITCH_UPPORT &&
+ pd->dev_type != PCIE_TYPE_SWITCH_DNPORT &&
+ pd->dev_type != PCIE_TYPE_PCIE_TO_PCIX)
+ return;
+
+ /* Create PCIe slot */
+ slot = pcie_slot_create(phb, pd);
+ if (!slot)
+ return;
+
+ /* Root complex inherits methods from PHB slot */
+ if (!pd->parent && phb->slot)
+ memcpy(&slot->ops, &phb->slot->ops, sizeof(struct pci_slot_ops));
+
+ /* Patch PCIe slot */
+ s = lxvpd_get_slot(slot);
+ if (s) {
+ lxvpd_extract_info(slot, s);
+ firenze_pci_slot_init(slot);
+ }
+}
+
+void firenze_pci_add_loc_code(struct dt_node *np, struct pci_device *pd)
+{
+ struct dt_node *p;
+ const char *blcode = NULL;
+ char *lcode;
+ uint32_t class_code;
+ uint8_t class,sub;
+ uint8_t pos, len;
+
+
+ /*
+ * prefer fully-qualified slot-location-code, walk-up parent tree
+ * to find one
+ */
+ for (p = np->parent; p; p = p->parent) {
+ blcode = dt_prop_get_def(p, "ibm,slot-location-code", NULL);
+ if (blcode)
+ break;
+ }
+
+ /* try the node itself if none is found */
+ if (!blcode)
+ blcode = dt_prop_get_def(np, "ibm,slot-location-code", NULL);
+
+ if (!blcode) {
+ /* still not found, fall back to ibm,loc-code */
+
+ for (p = np->parent; p; p = p->parent) {
+ blcode = dt_prop_get_def(p, "ibm,loc-code", NULL);
+ if (blcode)
+ break;
+ }
+ }
+
+ if (!blcode) {
+ prlog(PR_ERR,
+ "No suitable location code to add for device PHB#%04x:%02x:%02x.%x\n",
+ pd->phb->opal_id, PCI_BUS_NUM(pd->bdfn),
+ PCI_DEV(pd->bdfn), PCI_FUNC(pd->bdfn));
+ return;
+ }
+
+ /* ethernet devices get port codes */
+ class_code = dt_prop_get_u32(np, "class-code");
+ class = class_code >> 16;
+ sub = (class_code >> 8) & 0xff;
+
+ if (class == 0x02 && sub == 0x00) {
+ /* There's usually several spaces at the end of the property.
+ Test for, but don't rely on, that being the case */
+ len = strlen(blcode);
+ for (pos = 0; pos < len; pos++)
+ if (blcode[pos] == ' ') break;
+ if (pos + 3 < len)
+ lcode = strdup(blcode);
+ else {
+ lcode = malloc(pos + 3);
+ memcpy(lcode, blcode, len);
+ }
+ lcode[pos++] = '-';
+ lcode[pos++] = 'T';
+ lcode[pos++] = (char)PCI_FUNC(pd->bdfn) + '1';
+ lcode[pos++] = '\0';
+ dt_add_property_string(np, "ibm,loc-code", lcode);
+ free(lcode);
+ } else {
+ dt_add_property_string(np, "ibm,loc-code", blcode);
+ }
+}
diff --git a/roms/skiboot/platforms/ibm-fsp/firenze.c b/roms/skiboot/platforms/ibm-fsp/firenze.c
new file mode 100644
index 000000000..887a9c0ac
--- /dev/null
+++ b/roms/skiboot/platforms/ibm-fsp/firenze.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <fsp.h>
+#include <pci.h>
+#include <pci-cfg.h>
+#include <chip.h>
+#include <i2c.h>
+#include <timebase.h>
+#include <hostservices.h>
+
+#include "ibm-fsp.h"
+#include "lxvpd.h"
+
+static struct dt_node *dt_create_i2c_master(struct dt_node *n, uint32_t eng_id)
+{
+ struct dt_node *i2cm;
+ uint64_t freq;
+ uint32_t clock;
+
+ /* Each master registers set is of length 0x20 */
+ i2cm = dt_new_addr(n, "i2cm", 0xa0000 + eng_id * 0x20);
+ if (!i2cm)
+ return NULL;
+
+ dt_add_property_string(i2cm, "compatible",
+ "ibm,power8-i2cm");
+ dt_add_property_cells(i2cm, "reg", 0xa0000 + eng_id * 0x20,
+ 0x20);
+ dt_add_property_cells(i2cm, "chip-engine#", eng_id);
+ dt_add_property_cells(i2cm, "#address-cells", 1);
+ dt_add_property_cells(i2cm, "#size-cells", 0);
+
+ /* Derive the clock source frequency */
+ freq = dt_prop_get_u64_def(n, "bus-frequency", 0);
+ clock = (u32)(freq / 4);
+ if (clock)
+ dt_add_property_cells(i2cm, "clock-frequency", clock);
+ else
+ dt_add_property_cells(i2cm, "clock-frequency", 125000000);
+ return i2cm;
+}
+
+static struct dt_node *dt_create_i2c_bus(struct dt_node *i2cm,
+ const char *port_name, uint32_t port_id)
+{
+ static struct dt_node *port;
+
+ port = dt_new_addr(i2cm, "i2c-bus", port_id);
+ if (!port)
+ return NULL;
+
+ dt_add_property_strings(port, "compatible", "ibm,power8-i2c-port",
+ "ibm,opal-i2c");
+ dt_add_property_string(port, "ibm,port-name", port_name);
+ dt_add_property_cells(port, "reg", port_id);
+ dt_add_property_cells(port, "bus-frequency", 400000);
+ dt_add_property_cells(port, "#address-cells", 1);
+ dt_add_property_cells(port, "#size-cells", 0);
+
+ return port;
+}
+
+static struct dt_node *dt_create_i2c_device(struct dt_node *bus, uint8_t addr,
+ const char *name, const char *compat,
+ const char *label)
+{
+ struct dt_node *dev;
+
+ dev = dt_new_addr(bus, name, addr);
+ if (!dev)
+ return NULL;
+
+ dt_add_property_string(dev, "compatible", compat);
+ dt_add_property_string(dev, "label", label);
+ dt_add_property_cells(dev, "reg", addr);
+ dt_add_property_string(dev, "status", "reserved");
+
+ return dev;
+}
+
+static void firenze_dt_fixup_i2cm(void)
+{
+ struct dt_node *master, *bus, *dev;
+ struct proc_chip *c;
+ const uint32_t *p;
+ char name[32];
+ uint64_t lx;
+
+ if (dt_find_compatible_node(dt_root, NULL, "ibm,power8-i2cm"))
+ return;
+
+ p = dt_prop_get_def(dt_root, "ibm,vpd-lx-info", NULL);
+ if (!p)
+ return;
+
+ lx = ((uint64_t)p[1] << 32) | p[2];
+
+ switch (lx) {
+ case LX_VPD_2S4U_BACKPLANE:
+ case LX_VPD_2S2U_BACKPLANE:
+ case LX_VPD_SHARK_BACKPLANE: /* XXX confirm ? */
+ /* i2c nodes on chip 0x10 */
+ c = get_chip(0x10);
+ if (c) {
+ /* Engine 1 */
+ master = dt_create_i2c_master(c->devnode, 1);
+ assert(master);
+ snprintf(name, sizeof(name), "p8_%08x_e%dp%d", c->id, 1, 0);
+ bus = dt_create_i2c_bus(master, name, 0);
+ assert(bus);
+ dev = dt_create_i2c_device(bus, 0x39, "power-control",
+ "maxim,5961", "pcie-hotplug");
+ assert(dev);
+ dt_add_property_strings(dev, "target-list", "slot-C4",
+ "slot-C5");
+
+ dev = dt_create_i2c_device(bus, 0x3a, "power-control",
+ "maxim,5961", "pcie-hotplug");
+ assert(dev);
+ dt_add_property_strings(dev, "target-list", "slot-C2",
+ "slot-C3");
+ } else {
+ prlog(PR_INFO, "PLAT: Chip not found for the id 0x10\n");
+ }
+
+ /* Fall through */
+ case LX_VPD_1S4U_BACKPLANE:
+ case LX_VPD_1S2U_BACKPLANE:
+ /* i2c nodes on chip 0 */
+ c = get_chip(0);
+ if (!c) {
+ prlog(PR_INFO, "PLAT: Chip not found for the id 0x0\n");
+ break;
+ }
+
+ /* Engine 1*/
+ master = dt_create_i2c_master(c->devnode, 1);
+ assert(master);
+ snprintf(name, sizeof(name), "p8_%08x_e%dp%d", c->id, 1, 0);
+ bus = dt_create_i2c_bus(master, name, 0);
+ assert(bus);
+ dev = dt_create_i2c_device(bus, 0x32, "power-control",
+ "maxim,5961", "pcie-hotplug");
+ assert(dev);
+ dt_add_property_strings(dev, "target-list", "slot-C10", "slot-C11");
+
+ dev = dt_create_i2c_device(bus, 0x35, "power-control",
+ "maxim,5961", "pcie-hotplug");
+ assert(dev);
+ dt_add_property_strings(dev, "target-list", "slot-C6", "slot-C7");
+
+ dev = dt_create_i2c_device(bus, 0x36, "power-control",
+ "maxim,5961", "pcie-hotplug");
+ assert(dev);
+ dt_add_property_strings(dev, "target-list", "slot-C8", "slot-C9");
+
+ dev = dt_create_i2c_device(bus, 0x39, "power-control", "maxim,5961",
+ "pcie-hotplug");
+ assert(dev);
+ dt_add_property_strings(dev, "target-list", "slot-C12");
+ break;
+ default:
+ break;
+ }
+}
+
+static bool firenze_probe(void)
+{
+ if (!dt_node_is_compatible(dt_root, "ibm,firenze"))
+ return false;
+
+ firenze_dt_fixup_i2cm();
+
+ return true;
+}
+
+static uint32_t ibm_fsp_occ_timeout(void)
+{
+ /* Use a fixed 60s value for now */
+ return 60;
+}
+
+static void firenze_init(void)
+{
+ /* We call hservices_init to relocate the hbrt image now, as the FSP
+ * may request an OCC load any time after ibm_fsp_init.
+ */
+ hservices_init();
+
+ ibm_fsp_init();
+}
+
+DECLARE_PLATFORM(firenze) = {
+ .name = "Firenze",
+ .psi = &fsp_platform_psi,
+ .prd = &fsp_platform_prd,
+ .probe = firenze_probe,
+ .init = firenze_init,
+ .fast_reboot_init = fsp_console_reset,
+ .finalise_dt = ibm_fsp_finalise_dt,
+ .exit = ibm_fsp_exit,
+ .cec_power_down = ibm_fsp_cec_power_down,
+ .cec_reboot = ibm_fsp_cec_reboot,
+ .pci_setup_phb = firenze_pci_setup_phb,
+ .pci_get_slot_info = firenze_pci_get_slot_info,
+ .pci_add_loc_code = firenze_pci_add_loc_code,
+ .pci_probe_complete = firenze_pci_send_inventory,
+ .nvram_info = fsp_nvram_info,
+ .nvram_start_read = fsp_nvram_start_read,
+ .nvram_write = fsp_nvram_write,
+ .occ_timeout = ibm_fsp_occ_timeout,
+ .elog_commit = elog_fsp_commit,
+ .start_preload_resource = fsp_start_preload_resource,
+ .resource_loaded = fsp_resource_loaded,
+ .sensor_read = ibm_fsp_sensor_read,
+ .terminate = ibm_fsp_terminate,
+ .op_display = fsp_op_display,
+ .vpd_iohub_load = vpd_iohub_load,
+ .heartbeat_time = fsp_heartbeat_time,
+};
diff --git a/roms/skiboot/platforms/ibm-fsp/fsp-vpd.c b/roms/skiboot/platforms/ibm-fsp/fsp-vpd.c
new file mode 100644
index 000000000..52d2c8255
--- /dev/null
+++ b/roms/skiboot/platforms/ibm-fsp/fsp-vpd.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <vpd.h>
+#include <string.h>
+#include <fsp.h>
+#include <device.h>
+#include "ibm-fsp.h"
+
+static void *vpd_lid;
+static size_t vpd_lid_size;
+static uint32_t vpd_lid_no;
+
+
+void vpd_iohub_load(struct dt_node *hub_node)
+{
+ uint8_t record[4] = { 'L','X','R','0' }; /* not null terminated */
+ const void *valid_lx;
+ uint8_t lx_size;
+ int r;
+ const uint32_t *p;
+ const uint8_t *lx;
+ unsigned int lxrn;
+
+ if (!fsp_present())
+ return;
+
+ p = dt_prop_get_def(hub_node, "ibm,vpd-lx-info", NULL);
+ if (!p)
+ return;
+
+ lxrn = p[0];
+ lx = (const char *)&p[1];
+
+ /* verify the lid preload has started */
+ if (!vpd_lid || !vpd_lid_no) {
+ prlog(PR_WARNING, "VPD: WARNING: Unable to load VPD lid");
+ return;
+ }
+
+ r = fsp_wait_lid_loaded(vpd_lid_no);
+
+ if (r)
+ goto fail;
+
+ /* Validate it */
+ if (lxrn < 9)
+ record[3] = '0' + lxrn;
+ else
+ memcpy(record, "VINI", 4);
+
+ valid_lx = vpd_find(vpd_lid, vpd_lid_size, record, "LX", &lx_size);
+ if (!valid_lx || lx_size != 8) {
+ prerror("VPD: Cannot find validation LX record\n");
+ goto fail;
+ }
+ if (memcmp(valid_lx, lx, 8) != 0) {
+ prerror("VPD: LX record mismatch !\n");
+ goto fail;
+ }
+
+ printf("VPD: Loaded %zu bytes\n", vpd_lid_size);
+
+ dt_add_property(hub_node, "ibm,io-vpd", vpd_lid, vpd_lid_size);
+ free(vpd_lid);
+ return;
+
+fail:
+ free(vpd_lid);
+ vpd_lid = NULL;
+ prerror("VPD: Failed to load VPD LID\n");
+ return;
+}
+
+/* Helper to load a VPD LID. Pass a ptr to the corresponding LX keyword */
+static void *vpd_lid_preload(const uint8_t *lx)
+{
+ int rc;
+
+ if (!fsp_present())
+ return NULL;
+
+ /* Now this is a guess game as we don't have the info from the
+ * pHyp folks. But basically, it seems to boil down to loading
+ * a LID whose name is 0x80e000yy where yy is the last 2 digits
+ * of the LX record in hex.
+ *
+ * [ Correction: After a chat with some folks, it looks like it's
+ * actually 4 digits, though the lid number is limited to fff
+ * so we weren't far off. ]
+ *
+ * For safety, we look for a matching LX record in an LXRn
+ * (n = lxrn argument) or in VINI if lxrn=0xff
+ */
+ vpd_lid_no = 0x80e00000 | ((lx[6] & 0xf) << 8) | lx[7];
+
+ /* We don't quite know how to get to the LID directory so
+ * we don't know the size. Let's allocate 16K. All the VPD LIDs
+ * I've seen so far are much smaller.
+ */
+#define VPD_LID_MAX_SIZE 0x4000
+ vpd_lid = malloc(VPD_LID_MAX_SIZE);
+
+ if (!vpd_lid) {
+ prerror("VPD: Failed to allocate memory for LID\n");
+ return NULL;
+ }
+
+ /* Adjust LID number for flash side */
+ vpd_lid_no = fsp_adjust_lid_side(vpd_lid_no);
+ printf("VPD: Trying to load VPD LID 0x%08x...\n", vpd_lid_no);
+
+ vpd_lid_size = VPD_LID_MAX_SIZE;
+
+ /* Load it from the FSP */
+ rc = fsp_preload_lid(vpd_lid_no, vpd_lid, &vpd_lid_size);
+ if (rc) {
+ prerror("VPD: Error %d loading VPD LID\n", rc);
+ goto fail;
+ }
+
+ return vpd_lid;
+ fail:
+ free(vpd_lid);
+ return NULL;
+}
+
+void vpd_preload(struct dt_node *hub_node)
+{
+ const uint32_t *p;
+ const char *lxr;
+
+ p = dt_prop_get_def(hub_node, "ibm,vpd-lx-info", NULL);
+ if (!p)
+ return;
+
+ lxr = (const char *)&p[1];
+
+ vpd_lid = vpd_lid_preload(lxr);
+}
+
+void preload_io_vpd(void)
+{
+ const struct dt_property *prop;
+
+ prop = dt_find_property(dt_root, "ibm,io-vpd");
+ if (!prop) {
+ /* LX VPD Lid not already loaded */
+ vpd_preload(dt_root);
+ }
+}
diff --git a/roms/skiboot/platforms/ibm-fsp/hostservices.c b/roms/skiboot/platforms/ibm-fsp/hostservices.c
new file mode 100644
index 000000000..accc0989a
--- /dev/null
+++ b/roms/skiboot/platforms/ibm-fsp/hostservices.c
@@ -0,0 +1,912 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <lock.h>
+#include <device.h>
+#include <compiler.h>
+#include <hostservices.h>
+#include <mem_region.h>
+#include <xscom.h>
+#include <fsp.h>
+#include <chip.h>
+#include <console.h>
+#include <mem-map.h>
+#include <timebase.h>
+#include <occ.h>
+
+#define HOSTBOOT_RUNTIME_INTERFACE_VERSION 1
+
+struct host_interfaces {
+ /** Interface version. */
+ uint64_t interface_version;
+
+ /** Put a string to the console. */
+ void (*puts)(const char*);
+ /** Critical failure in runtime execution. */
+ void (*assert)(void);
+
+ /** OPTIONAL. Hint to environment that the page may be executed. */
+ int (*set_page_execute)(void*);
+
+ /** malloc */
+ void *(*malloc)(size_t);
+ /** free */
+ void (*free)(void*);
+ /** realloc */
+ void *(*realloc)(void*, size_t);
+
+ /** sendErrorLog
+ * @param[in] plid Platform Log identifier
+ * @param[in] data size in bytes
+ * @param[in] pointer to data
+ * @return 0 on success else error code
+ */
+ int (*send_error_log)(uint32_t,uint32_t,void *);
+
+ /** Scan communication read
+ * @param[in] chip_id (based on devtree defn)
+ * @param[in] address
+ * @param[in] pointer to 8-byte data buffer
+ * @return 0 on success else return code
+ */
+ int (*scom_read)(uint64_t, uint64_t, void*);
+
+ /** Scan communication write
+ * @param[in] chip_id (based on devtree defn)
+ * @param[in] address
+ * @param[in] pointer to 8-byte data buffer
+ * @return 0 on success else return code
+ */
+ int (*scom_write)(uint64_t, uint64_t, const void *);
+
+ /** lid_load
+ * Load a LID from PNOR, FSP, etc.
+ *
+ * @param[in] LID number.
+ * @param[out] Allocated buffer for LID.
+ * @param[out] Size of LID (in bytes).
+ *
+ * @return 0 on success, else RC.
+ */
+ int (*lid_load)(uint32_t lid, void **buf, size_t *len);
+
+ /** lid_unload
+ * Release memory from previously loaded LID.
+ *
+ * @param[in] Allocated buffer for LID to release.
+ *
+ * @return 0 on success, else RC.
+ */
+ int (*lid_unload)(void *buf);
+
+ /** Get the address of a reserved memory region by its devtree name.
+ *
+ * @param[in] Devtree name (ex. "ibm,hbrt-vpd-image")
+ * @return physical address of region (or NULL).
+ **/
+ uint64_t (*get_reserved_mem)(const char*);
+
+ /**
+ * @brief Force a core to be awake, or clear the force
+ * @param[in] i_core Core to wake up (pid)
+ * @param[in] i_mode 0=force awake
+ * 1=clear force
+ * 2=clear all previous forces
+ * @return rc non-zero on error
+ */
+ int (*wakeup)( uint32_t i_core, uint32_t i_mode );
+
+ /**
+ * @brief Delay/sleep for at least the time given
+ * @param[in] seconds
+ * @param[in] nano seconds
+ */
+ void (*nanosleep)(uint64_t i_seconds, uint64_t i_nano_seconds);
+
+ // Reserve some space for future growth.
+ void (*reserved[32])(void);
+};
+
+struct runtime_interfaces {
+ /** Interface version. */
+ uint64_t interface_version;
+
+ /** Execute CxxTests that may be contained in the image.
+ *
+ * @param[in] - Pointer to CxxTestStats structure for results reporting.
+ */
+ void (*cxxtestExecute)(void *);
+ /** Get a list of lids numbers of the lids known to HostBoot
+ *
+ * @param[out] o_num - the number of lids in the list
+ * @return a pointer to the list
+ */
+ const uint32_t * (*get_lid_list)(size_t * o_num);
+
+ /** Load OCC Image and common data into mainstore, also setup OCC BARSs
+ *
+ * @param[in] i_homer_addr_phys - The physical mainstore address of the
+ * start of the HOMER image
+ * @param[in] i_homer_addr_va - Virtual memory address of the HOMER image
+ * @param[in] i_common_addr_phys - The physical mainstore address of the
+ * OCC common area.
+ * @param[in] i_common_addr_va - Virtual memory address of the common area
+ * @param[in] i_chip - The HW chip id (XSCOM chip ID)
+ * @return 0 on success else return code
+ */
+ int(*loadOCC)(uint64_t i_homer_addr_phys,
+ uint64_t i_homer_addr_va,
+ uint64_t i_common_addr_phys,
+ uint64_t i_common_addr_va,
+ uint64_t i_chip);
+
+ /** Start OCC on all chips, by module
+ *
+ * @param[in] i_chip - Array of functional HW chip ids
+ * @Note The caller must include a complete modules worth of chips
+ * @param[in] i_num_chips - Number of chips in the array
+ * @return 0 on success else return code
+ */
+ int (*startOCCs)(uint64_t* i_chip,
+ size_t i_num_chips);
+
+ /** Stop OCC hold OCCs in reset
+ *
+ * @param[in] i_chip - Array of functional HW chip ids
+ * @Note The caller must include a complete modules worth of chips
+ * @param[in] i_num_chips - Number of chips in the array
+ * @return 0 on success else return code
+ */
+ int (*stopOCCs)(uint64_t* i_chip,
+ size_t i_num_chips);
+
+ /* Reserve some space for future growth. */
+ void (*reserved[32])(void);
+};
+
+static struct runtime_interfaces *hservice_runtime;
+
+static char *hbrt_con_buf = (char *)HBRT_CON_START;
+static size_t hbrt_con_pos;
+static bool hbrt_con_wrapped;
+
+#define HBRT_CON_IN_LEN 0
+#define HBRT_CON_OUT_LEN (HBRT_CON_LEN - HBRT_CON_IN_LEN)
+
+static struct memcons hbrt_memcons __section(".data.memcons") = {
+ .magic = CPU_TO_BE64(MEMCONS_MAGIC),
+ .obuf_phys = CPU_TO_BE64(HBRT_CON_START),
+ .ibuf_phys = CPU_TO_BE64(HBRT_CON_START + HBRT_CON_OUT_LEN),
+ .obuf_size = CPU_TO_BE32(HBRT_CON_OUT_LEN),
+ .ibuf_size = CPU_TO_BE32(HBRT_CON_IN_LEN),
+};
+
+static void hservice_putc(char c)
+{
+ uint32_t opos;
+
+ hbrt_con_buf[hbrt_con_pos++] = c;
+ if (hbrt_con_pos >= HBRT_CON_OUT_LEN) {
+ hbrt_con_pos = 0;
+ hbrt_con_wrapped = true;
+ }
+
+ /*
+ * We must always re-generate memcons.out_pos because
+ * under some circumstances, the console script will
+ * use a broken putmemproc that does RMW on the full
+ * 8 bytes containing out_pos and in_prod, thus corrupting
+ * out_pos
+ */
+ opos = hbrt_con_pos;
+ if (hbrt_con_wrapped)
+ opos |= MEMCONS_OUT_POS_WRAP;
+ lwsync();
+ hbrt_memcons.out_pos = cpu_to_be32(opos);
+}
+
+static void hservice_puts(const char *str)
+{
+ char c;
+
+ while((c = *(str++)) != 0)
+ hservice_putc(c);
+ hservice_putc(10);
+}
+
+static void hservice_mark(void)
+{
+ hservice_puts("--------------------------------------------------"
+ "--------------------------------------------------\n");
+}
+
+static void hservice_assert(void)
+{
+ /**
+ * @fwts-label HBRTassert
+ * @fwts-advice HBRT triggered assert: you need to debug HBRT
+ */
+ prlog(PR_EMERG, "HBRT: Assertion from hostservices\n");
+ abort();
+}
+
+static void *hservice_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+static void hservice_free(void *ptr)
+{
+ free(ptr);
+}
+
+
+static void *hservice_realloc(void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+struct hbrt_elog_ent {
+ void *buf;
+ unsigned int size;
+ unsigned int plid;
+ struct list_node link;
+};
+static LIST_HEAD(hbrt_elogs);
+static struct lock hbrt_elog_lock = LOCK_UNLOCKED;
+static bool hbrt_elog_sending;
+static void hservice_start_elog_send(void);
+
+static void hservice_elog_write_complete(struct fsp_msg *msg)
+{
+ struct hbrt_elog_ent *ent = msg->user_data;
+
+ lock(&hbrt_elog_lock);
+ prlog(PR_DEBUG, "HBRT: Completed send of PLID 0x%08x\n", ent->plid);
+ hbrt_elog_sending = false;
+ fsp_tce_unmap(PSI_DMA_HBRT_LOG_WRITE_BUF,
+ PSI_DMA_HBRT_LOG_WRITE_BUF_SZ);
+ free(ent->buf);
+ free(ent);
+ fsp_freemsg(msg);
+ hservice_start_elog_send();
+ unlock(&hbrt_elog_lock);
+}
+
+static void hservice_start_elog_send(void)
+{
+ struct fsp_msg *msg;
+ struct hbrt_elog_ent *ent;
+
+ again:
+ if (list_empty(&hbrt_elogs))
+ return;
+ ent = list_pop(&hbrt_elogs, struct hbrt_elog_ent, link);
+
+ hbrt_elog_sending = true;
+
+ prlog(PR_DEBUG, "HBRT: Starting send of PLID 0x%08x\n", ent->plid);
+
+ fsp_tce_map(PSI_DMA_HBRT_LOG_WRITE_BUF, ent->buf,
+ PSI_DMA_HBRT_LOG_WRITE_BUF_SZ);
+
+ msg = fsp_mkmsg(FSP_CMD_WRITE_SP_DATA, 6, FSP_DATASET_HBRT_BLOB,
+ 0, 0, 0, PSI_DMA_HBRT_LOG_WRITE_BUF,
+ ent->size);
+
+ if (!msg) {
+ prerror("HBRT: Failed to create error msg log to FSP\n");
+ goto error;
+ }
+ msg->user_data = ent;
+ if (!fsp_queue_msg(msg, hservice_elog_write_complete))
+ return;
+ prerror("FSP: Error queueing elog update\n");
+ error:
+ if (msg)
+ fsp_freemsg(msg);
+ fsp_tce_unmap(PSI_DMA_HBRT_LOG_WRITE_BUF,
+ PSI_DMA_HBRT_LOG_WRITE_BUF_SZ);
+ free(ent->buf);
+ free(ent);
+ hbrt_elog_sending = false;
+ goto again;
+}
+
+int hservice_send_error_log(uint32_t plid, uint32_t dsize, void *data)
+{
+ struct hbrt_elog_ent *ent;
+ void *abuf;
+
+ prlog(PR_ERR, "HBRT: Error log generated with plid 0x%08x\n", plid);
+
+ /* We only know how to send error logs to FSP */
+ if (!fsp_present()) {
+ prerror("HBRT: Warning, error log from HBRT discarded !\n");
+ return OPAL_UNSUPPORTED;
+ }
+ if (dsize > PSI_DMA_HBRT_LOG_WRITE_BUF_SZ) {
+ prerror("HBRT: Warning, error log from HBRT too big (%d) !\n",
+ dsize);
+ dsize = PSI_DMA_HBRT_LOG_WRITE_BUF_SZ;
+ }
+
+ lock(&hbrt_elog_lock);
+
+ /* Create and populate a tracking structure */
+ ent = zalloc(sizeof(struct hbrt_elog_ent));
+ if (!ent) {
+ unlock(&hbrt_elog_lock);
+ return OPAL_NO_MEM;
+ }
+
+ /* Grab a 4k aligned page */
+ abuf = memalign(0x1000, PSI_DMA_HBRT_LOG_WRITE_BUF_SZ);
+ if (!abuf) {
+ free(ent);
+ unlock(&hbrt_elog_lock);
+ return OPAL_NO_MEM;
+ }
+ memset(abuf, 0, PSI_DMA_HBRT_LOG_WRITE_BUF_SZ);
+ memcpy(abuf, data, dsize);
+ ent->buf = abuf;
+ ent->size = dsize;
+ ent->plid = plid;
+ list_add_tail(&hbrt_elogs, &ent->link);
+ if (!hbrt_elog_sending)
+ hservice_start_elog_send();
+ unlock(&hbrt_elog_lock);
+
+ return 0;
+}
+
+static int hservice_scom_read(uint64_t chip_id, uint64_t addr, void *buf)
+{
+ return xscom_read(chip_id, addr, buf);
+}
+
+static int hservice_scom_write(uint64_t chip_id, uint64_t addr,
+ const void *buf)
+{
+ uint64_t val;
+
+ memcpy(&val, buf, sizeof(val));
+ return xscom_write(chip_id, addr, val);
+}
+
+struct hbrt_lid {
+ void *load_addr;
+ size_t len;
+ uint32_t id;
+ struct list_node link;
+};
+static LIST_HEAD(hbrt_lid_list);
+
+static bool hbrt_lid_preload_complete = false;
+
+bool hservices_lid_preload_complete(void)
+{
+ return hbrt_lid_preload_complete;
+}
+
+/* TODO: Few of the following routines can be generalized */
+static int __hservice_lid_load(uint32_t lid, void **buf, size_t *len)
+{
+ int rc;
+
+ /* Adjust LID side first or we get a cache mismatch */
+ lid = fsp_adjust_lid_side(lid);
+
+ /*
+ * Allocate a new buffer and load the LID into it
+ * XXX: We currently use the same size for each HBRT lid.
+ */
+ *buf = malloc(HBRT_LOAD_LID_SIZE);
+ *len = HBRT_LOAD_LID_SIZE;
+ rc = fsp_preload_lid(lid, *buf, len);
+ rc = fsp_wait_lid_loaded(lid);
+ if (rc != 0)
+ /* Take advantage of realloc corner case here. */
+ *len = 0;
+ *buf = realloc(*buf, *len);
+
+ prlog(PR_DEBUG, "HBRT: LID 0x%08x successfully loaded, len=0x%lx\n",
+ lid, (unsigned long)len);
+
+ return rc;
+}
+
+static int __hservice_lid_preload(const uint32_t lid)
+{
+ struct hbrt_lid *hlid;
+ void *buf;
+ size_t len;
+ int rc;
+
+ hlid = zalloc(sizeof(struct hbrt_lid));
+ if (!hlid) {
+ prerror("HBRT: Could not allocate struct hbrt_lid\n");
+ return OPAL_NO_MEM;
+ }
+
+ rc = __hservice_lid_load(lid, &buf, &len);
+ if (rc) {
+ free(hlid);
+ return rc;
+ }
+
+ hlid->load_addr = buf;
+ hlid->len = len;
+ hlid->id = lid;
+ list_add_tail(&hbrt_lid_list, &hlid->link);
+
+ return 0;
+}
+
+/* Find and preload all lids needed by hostservices */
+void hservices_lid_preload(void)
+{
+ const uint32_t *lid_list = NULL;
+ size_t num_lids;
+ int i;
+
+ if (!hservice_runtime)
+ return;
+
+ lid_list = (const uint32_t *)hservice_runtime->get_lid_list(&num_lids);
+ if (!lid_list) {
+ prerror("HBRT: get_lid_list() returned NULL\n");
+ return;
+ }
+
+ prlog(PR_INFO, "HBRT: %d lids to load\n", (int)num_lids);
+
+ /* Currently HBRT needs only one (OCC) lid */
+ for (i = 0; i < num_lids; i++)
+ __hservice_lid_preload(lid_list[i]);
+
+ hbrt_lid_preload_complete = true;
+ occ_poke_load_queue();
+}
+
+static int hservice_lid_load(uint32_t lid, void **buf, size_t *len)
+{
+ struct hbrt_lid *hlid;
+
+ prlog(PR_INFO, "HBRT: Lid load request for 0x%08x\n", lid);
+
+ if (list_empty(&hbrt_lid_list)) { /* Should not happen */
+ /**
+ * @fwts-label HBRTlidLoadFail
+ * @fwts-advice Firmware should have aborted boot
+ */
+ prlog(PR_CRIT, "HBRT: LID Load failed\n");
+ abort();
+ }
+
+ list_for_each(&hbrt_lid_list, hlid, link) {
+ if (hlid->id == lid) {
+ *buf = hlid->load_addr;
+ *len = hlid->len;
+ prlog(PR_DEBUG, "HBRT: LID Serviced from cache,"
+ " %x, len=0x%lx\n", hlid->id, hlid->len);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+static int hservice_lid_unload(void *buf __unused)
+{
+ /* We do nothing as the LID is held in cache */
+ return 0;
+}
+
+static uint64_t hservice_get_reserved_mem(const char *name)
+{
+ struct mem_region *region;
+ uint64_t ret;
+
+ /* We assume it doesn't change after we've unlocked it, but
+ * lock ensures list is safe to walk. */
+ lock(&mem_region_lock);
+ region = find_mem_region(name);
+ ret = region ? region->start : 0;
+ unlock(&mem_region_lock);
+
+ if (!ret)
+ prlog(PR_WARNING, "HBRT: Mem region '%s' not found !\n", name);
+
+ return ret;
+}
+
+static void hservice_nanosleep(uint64_t i_seconds, uint64_t i_nano_seconds)
+{
+ struct timespec ts;
+
+ ts.tv_sec = i_seconds;
+ ts.tv_nsec = i_nano_seconds;
+ nanosleep_nopoll(&ts, NULL);
+}
+
+int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
+{
+ struct cpu_thread *cpu;
+ int rc = OPAL_SUCCESS;
+
+ switch (proc_gen) {
+ case proc_gen_p8:
+ /*
+ * Mask out the top nibble of i_core since it may contain
+ * 0x4 (which we use for XSCOM targeting)
+ */
+ i_core &= 0x0fffffff;
+ i_core <<= 3;
+ break;
+ case proc_gen_p9:
+ i_core &= SPR_PIR_P9_MASK;
+ i_core <<= 2;
+ break;
+ case proc_gen_p10:
+ i_core &= SPR_PIR_P10_MASK;
+ i_core <<= 2;
+ break;
+ default:
+ return OPAL_UNSUPPORTED;
+ }
+
+ /* What do we need to do ? */
+ switch(i_mode) {
+ case 0: /* Assert special wakeup */
+ cpu = find_cpu_by_pir(i_core);
+ if (!cpu)
+ return OPAL_PARAMETER;
+ prlog(PR_TRACE, "HBRT: Special wakeup assert for core 0x%x,"
+ " count=%d\n", i_core, cpu->hbrt_spec_wakeup);
+ if (cpu->hbrt_spec_wakeup == 0)
+ rc = dctl_set_special_wakeup(cpu);
+ if (rc == 0)
+ cpu->hbrt_spec_wakeup++;
+ return rc;
+ case 1: /* Deassert special wakeup */
+ cpu = find_cpu_by_pir(i_core);
+ if (!cpu)
+ return OPAL_PARAMETER;
+ prlog(PR_TRACE, "HBRT: Special wakeup release for core"
+ " 0x%x, count=%d\n", i_core, cpu->hbrt_spec_wakeup);
+ if (cpu->hbrt_spec_wakeup == 0) {
+ prerror("HBRT: Special wakeup clear"
+ " on core 0x%x with count=0\n",
+ i_core);
+ return OPAL_WRONG_STATE;
+ }
+ /* What to do with count on errors ? */
+ cpu->hbrt_spec_wakeup--;
+ if (cpu->hbrt_spec_wakeup == 0)
+ rc = dctl_clear_special_wakeup(cpu);
+ return rc;
+ case 2: /* Clear all special wakeups */
+ prlog(PR_DEBUG, "HBRT: Special wakeup release for all cores\n");
+ for_each_cpu(cpu) {
+ if (cpu->hbrt_spec_wakeup) {
+ cpu->hbrt_spec_wakeup = 0;
+ /* What to do on errors ? */
+ dctl_clear_special_wakeup(cpu);
+ }
+ }
+ return OPAL_SUCCESS;
+ default:
+ return OPAL_PARAMETER;
+ }
+}
+
+static struct host_interfaces hinterface = {
+ .interface_version = HOSTBOOT_RUNTIME_INTERFACE_VERSION,
+ .puts = hservice_puts,
+ .assert = hservice_assert,
+ .malloc = hservice_malloc,
+ .free = hservice_free,
+ .realloc = hservice_realloc,
+ .send_error_log = hservice_send_error_log,
+ .scom_read = hservice_scom_read,
+ .scom_write = hservice_scom_write,
+ .lid_load = hservice_lid_load,
+ .lid_unload = hservice_lid_unload,
+ .get_reserved_mem = hservice_get_reserved_mem,
+ .wakeup = hservice_wakeup,
+ .nanosleep = hservice_nanosleep,
+};
+
+int host_services_occ_load(void)
+{
+ struct proc_chip *chip;
+ int rc = 0;
+
+ prlog(PR_DEBUG, "HBRT: OCC Load requested\n");
+
+ if (!(hservice_runtime && hservice_runtime->loadOCC)) {
+ prerror("HBRT: No hservice_runtime->loadOCC\n");
+ return -ENOENT;
+ }
+
+ for_each_chip(chip) {
+
+ prlog(PR_DEBUG, "HBRT: Calling loadOCC() homer"
+ " %016llx, occ_common_area %016llx, chip %04x\n",
+ chip->homer_base,
+ chip->occ_common_base,
+ chip->id);
+
+ rc = hservice_runtime->loadOCC(chip->homer_base,
+ chip->homer_base,
+ chip->occ_common_base,
+ chip->occ_common_base,
+ chip->id);
+
+ hservice_mark();
+ prlog(PR_DEBUG, "HBRT: -> rc = %d\n", rc);
+ }
+ return rc;
+}
+
+int host_services_occ_start(void)
+{
+ struct proc_chip *chip;
+ int i, rc = 0, nr_chips=0;
+ uint64_t chipids[MAX_CHIPS];
+
+ prlog(PR_INFO, "HBRT: OCC Start requested\n");
+
+ if (!(hservice_runtime && hservice_runtime->startOCCs)) {
+ prerror("HBRT: No hservice_runtime->startOCCs\n");
+ return -ENOENT;
+ }
+
+ for_each_chip(chip) {
+ chipids[nr_chips++] = chip->id;
+ }
+
+ for (i = 0; i < nr_chips; i++)
+ prlog(PR_TRACE, "HBRT: Calling startOCC() for %04llx\n",
+ chipids[i]);
+
+ /* Lets start all OCC */
+ rc = hservice_runtime->startOCCs(chipids, nr_chips);
+ hservice_mark();
+ prlog(PR_DEBUG, "HBRT: startOCCs() rc = %d\n", rc);
+ return rc;
+}
+
+int host_services_occ_stop(void)
+{
+ int i, rc = 0, nr_slaves = 0, nr_masters = 0;
+ uint64_t *master_chipids = NULL, *slave_chipids = NULL;
+
+ prlog(PR_INFO, "HBRT: OCC Stop requested\n");
+
+ if (!(hservice_runtime && hservice_runtime->stopOCCs)) {
+ prerror("HBRT: No hservice_runtime->stopOCCs\n");
+ return -ENOENT;
+ }
+
+ rc = find_master_and_slave_occ(&master_chipids, &slave_chipids,
+ &nr_masters, &nr_slaves);
+ if (rc)
+ goto out;
+
+ for (i = 0; i < nr_slaves; i++)
+ prlog(PR_TRACE, "HBRT: Calling stopOCC() for %04llx ",
+ slave_chipids[i]);
+
+ if (!nr_slaves)
+ goto master;
+
+ /* Lets STOP all the slave OCC */
+ rc = hservice_runtime->stopOCCs(slave_chipids, nr_slaves);
+ prlog(PR_DEBUG, "HBRT: stopOCCs() slave rc = %d\n", rc);
+
+master:
+ for (i = 0; i < nr_masters; i++)
+ prlog(PR_TRACE, "HBRT: Calling stopOCC() for %04llx ",
+ master_chipids[i]);
+
+ /* Lets STOP all the master OCC */
+ rc = hservice_runtime->stopOCCs(master_chipids, nr_masters);
+
+ hservice_mark();
+ prlog(PR_DEBUG, "HBRT: stopOCCs() master rc = %d\n", rc);
+
+out:
+ free(master_chipids);
+ free(slave_chipids);
+ return rc;
+}
+
+bool hservices_init(void)
+{
+ void *code = NULL;
+ struct runtime_interfaces *(*hbrt_init)(struct host_interfaces *);
+
+ struct function_descriptor {
+ void *addr;
+ void *toc;
+ } fdesc;
+
+ code = (void *)hservice_get_reserved_mem("ibm,hbrt-code-image");
+ if (!code) {
+ prerror("HBRT: No ibm,hbrt-code-image found.\n");
+ return false;
+ }
+
+ if (memcmp(code, "HBRTVERS", 8) != 0) {
+ prerror("HBRT: Bad eyecatcher for ibm,hbrt-code-image!\n");
+ return false;
+ }
+
+ prlog(PR_INFO, "HBRT: Found HostBoot Runtime version %llu\n",
+ ((u64 *)code)[1]);
+
+ /* We enter at 0x100 into the image. */
+ fdesc.addr = code + 0x100;
+ /* It doesn't care about TOC */
+ fdesc.toc = NULL;
+
+ hbrt_init = (void *)&fdesc;
+
+ hservice_runtime = hbrt_init(&hinterface);
+ hservice_mark();
+ if (!hservice_runtime) {
+ prerror("HBRT: Host services init failed\n");
+ return false;
+ }
+
+ prlog(PR_INFO, "HBRT: Interface version %llu\n",
+ hservice_runtime->interface_version);
+
+ return true;
+}
+
+static void hservice_send_hbrt_msg_resp(struct fsp_msg *msg)
+{
+ int status = (msg->resp->word1 >> 8) & 0xff;
+
+ fsp_freemsg(msg);
+ if (status) {
+ prlog(PR_NOTICE, "HBRT: HBRT to FSP MBOX command failed "
+ "[rc=0x%x]\n", status);
+ }
+
+ fsp_tce_unmap(PSI_DMA_HBRT_FSP_MSG, PSI_DMA_HBRT_FSP_MSG_SIZE);
+ /* Send response data to HBRT */
+ prd_fw_resp_fsp_response(status);
+}
+
+#define FSP_STATUS_RR (-8193)
+/* Caller takes care of serializing MBOX message */
+int hservice_send_hbrt_msg(void *data, u64 dsize)
+{
+ uint32_t tce_len, offset;
+ int rc;
+ uint64_t addr;
+ struct fsp_msg *msg;
+
+ prlog(PR_NOTICE, "HBRT: HBRT - FSP message generated\n");
+
+ /* We only support FSP based system */
+ if (!fsp_present()) {
+ prlog(PR_DEBUG,
+ "HBRT: Warning, HBRT - FSP message discarded!\n");
+ return OPAL_UNSUPPORTED;
+ }
+
+ /*
+ * If FSP is in R/R then send specific return code to HBRT (inside
+ * HBRT message) and return success to caller (opal_prd_msg()).
+ */
+ if (fsp_in_rr()) {
+ prlog(PR_DEBUG,
+ "HBRT: FSP is in R/R. Dropping HBRT - FSP message\n");
+ prd_fw_resp_fsp_response(FSP_STATUS_RR);
+ return OPAL_SUCCESS;
+ }
+
+ /* Adjust address, size for TCE mapping */
+ addr = (u64)data & ~TCE_MASK;
+ offset = (u64)data & TCE_MASK;
+ tce_len = ALIGN_UP((dsize + offset), TCE_PSIZE);
+
+ if (tce_len > PSI_DMA_HBRT_FSP_MSG_SIZE) {
+ prlog(PR_DEBUG,
+ "HBRT: HBRT - FSP message is too big, discarded\n");
+ return OPAL_PARAMETER;
+ }
+ fsp_tce_map(PSI_DMA_HBRT_FSP_MSG, (void *)addr, tce_len);
+
+ msg = fsp_mkmsg(FSP_CMD_HBRT_TO_FSP, 3, 0,
+ (PSI_DMA_HBRT_FSP_MSG + offset), dsize);
+ if (!msg) {
+ prlog(PR_DEBUG,
+ "HBRT: Failed to create HBRT - FSP message to FSP\n");
+ rc = OPAL_NO_MEM;
+ goto out_tce_unmap;
+ }
+
+ rc = fsp_queue_msg(msg, hservice_send_hbrt_msg_resp);
+ if (rc == 0)
+ return rc;
+
+ prlog(PR_DEBUG, "HBRT: Failed to queue HBRT message to FSP\n");
+ fsp_freemsg(msg);
+
+out_tce_unmap:
+ fsp_tce_unmap(PSI_DMA_HBRT_FSP_MSG, PSI_DMA_HBRT_FSP_MSG_SIZE);
+ return rc;
+}
+
+void hservice_hbrt_msg_response(uint32_t rc)
+{
+ struct fsp_msg *resp;
+
+ resp = fsp_mkmsg(FSP_RSP_FSP_TO_HBRT | (uint8_t)rc, 0);
+ if (!resp) {
+ prlog(PR_DEBUG,
+ "HBRT: Failed to allocate FSP - HBRT response message\n");
+ return;
+ }
+
+ if (fsp_queue_msg(resp, fsp_freemsg)) {
+ prlog(PR_DEBUG,
+ "HBRT: Failed to send FSP - HBRT response message\n");
+ fsp_freemsg(resp);
+ return;
+ }
+}
+
+/* FSP sends HBRT notification message. Pass this message to HBRT */
+static bool hservice_hbrt_msg_notify(uint32_t cmd_sub_mod, struct fsp_msg *msg)
+{
+ u32 tce_token, len;
+ void *buf;
+
+ if (cmd_sub_mod != FSP_CMD_FSP_TO_HBRT)
+ return false;
+
+ prlog(PR_TRACE, "HBRT: FSP - HBRT message generated\n");
+
+ tce_token = fsp_msg_get_data_word(msg, 1);
+ len = fsp_msg_get_data_word(msg, 2);
+ buf = fsp_inbound_buf_from_tce(tce_token);
+ if (!buf) {
+ prlog(PR_DEBUG, "HBRT: Invalid inbound data\n");
+ hservice_hbrt_msg_response(FSP_STATUS_INVALID_DATA);
+ return true;
+ }
+
+ if (prd_hbrt_fsp_msg_notify(buf, len)) {
+ hservice_hbrt_msg_response(FSP_STATUS_GENERIC_ERROR);
+ prlog(PR_NOTICE, "Unable to send FSP - HBRT message\n");
+ }
+
+ return true;
+}
+
+static struct fsp_client fsp_hbrt_msg_client = {
+ .message = hservice_hbrt_msg_notify,
+};
+
+/* Register for FSP 0xF2 class messages */
+void hservice_fsp_init(void)
+{
+ if (proc_gen < proc_gen_p9)
+ return;
+
+ if (!fsp_present())
+ return;
+
+ /* Register for Class F2 */
+ fsp_register_client(&fsp_hbrt_msg_client, FSP_MCLASS_HBRT);
+}
diff --git a/roms/skiboot/platforms/ibm-fsp/ibm-fsp.h b/roms/skiboot/platforms/ibm-fsp/ibm-fsp.h
new file mode 100644
index 000000000..bb191645c
--- /dev/null
+++ b/roms/skiboot/platforms/ibm-fsp/ibm-fsp.h
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#ifndef __IBM_FSP_COMMON_H
+#define __IBM_FSP_COMMON_H
+
+extern void ibm_fsp_init(void);
+extern void ibm_fsp_exit(void);
+void ibm_fsp_finalise_dt(bool is_reboot);
+
+extern int64_t ibm_fsp_cec_power_down(uint64_t request);
+extern int64_t ibm_fsp_cec_reboot(void);
+
+struct errorlog;
+extern int elog_fsp_commit(struct errorlog *buf);
+
+extern int64_t ibm_fsp_sensor_read(uint32_t sensor_hndl, int token,
+ __be64 *sensor_data);
+
+/* Apollo PCI support */
+extern void apollo_pci_setup_phb(struct phb *phb,
+ unsigned int index);
+extern void apollo_pci_get_slot_info(struct phb *phb,
+ struct pci_device *pd);
+
+/* Firenze PCI support */
+extern void firenze_pci_send_inventory(void);
+extern void firenze_pci_setup_phb(struct phb *phb,
+ unsigned int index);
+extern void firenze_pci_get_slot_info(struct phb *phb,
+ struct pci_device *pd);
+extern void firenze_pci_add_loc_code(struct dt_node *np,
+ struct pci_device *pd);
+
+/* VPD support */
+void vpd_iohub_load(struct dt_node *hub_node);
+void vpd_preload(struct dt_node *hub_node);
+
+/* Platform heartbeat time */
+int __attrconst fsp_heartbeat_time(void);
+
+extern struct platform_psi fsp_platform_psi;
+extern struct platform_prd fsp_platform_prd;
+
+#endif /* __IBM_FSP_COMMON_H */
diff --git a/roms/skiboot/platforms/ibm-fsp/lxvpd.c b/roms/skiboot/platforms/ibm-fsp/lxvpd.c
new file mode 100644
index 000000000..9b34a23a1
--- /dev/null
+++ b/roms/skiboot/platforms/ibm-fsp/lxvpd.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#define pr_fmt(fmt) "LXVPD: " fmt
+
+#include <skiboot.h>
+#include <device.h>
+#include <vpd.h>
+#include <pci.h>
+#include <pci-cfg.h>
+#include <pci-slot.h>
+
+#include "lxvpd.h"
+
+/*
+ * Currently, the lxvpd PCI slot struct is shared by multiple
+ * platforms (Apollo and Firenze), but each slot still has
+ * platform specific features. In order for unified data structs,
+ * "struct lxvpd_slot" is expected to be embedded in platform
+ * PCI slot struct. "entry_size" indicates the size of platform
+ * specific PCI slot instance.
+ */
+struct lxvpd_pci_slot_data {
+ uint8_t num_slots;
+ int32_t entry_size; /* Size of platform PCI slot */
+ void *slots; /* Data of platform PCI slots */
+};
+
+static bool lxvpd_supported_slot(struct phb *phb, struct pci_device *pd)
+{
+ /* PHB should always be valid */
+ if (!phb)
+ return false;
+
+ /* We expect platform slot for root complex */
+ if (!pd)
+ return true;
+
+ /* 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(struct pci_slot *slot)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ struct lxvpd_pci_slot_data *sdata = phb->platform_data;
+ struct lxvpd_pci_slot *s = NULL;
+ uint8_t slot_num = pd ? PCI_DEV(pd->bdfn) : 0xff;
+ bool is_phb = (pd && pd->parent) ? false : true;
+ uint8_t index;
+
+ /* Check if we have slot info */
+ if (!sdata) {
+ prlog(PR_DEBUG, "PHB%04x not have VPD data\n",
+ phb->opal_id);
+ return NULL;
+ }
+
+ /* Platform slot attached ? */
+ s = slot->data;
+ if (s) {
+ prlog(PR_DEBUG, "Slot %016llx had platform data [%s]\n",
+ slot->id, s->label);
+ return s;
+ }
+
+ /*
+ * 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. Generally, we have
+ * more strict rules to support slot than PCI core.
+ */
+ if (!lxvpd_supported_slot(phb, pd)) {
+ prlog(PR_DEBUG, "Slot %016llx not supported\n",
+ slot->id);
+ return NULL;
+ }
+
+ /* Iterate the platform slot array */
+ for (index = 0; index < sdata->num_slots; index++) {
+ s = sdata->slots + (index * sdata->entry_size);
+
+ /* Match PHB with switch_id == 0 */
+ if (is_phb && s->switch_id == 0) {
+ slot->data = s;
+ s->pci_slot = slot;
+ prlog(PR_DEBUG, "Found [%s] for PHB slot %016llx\n",
+ s->label, slot->id);
+
+ return s;
+ }
+
+ /* Match downstream switch port with switch_id != 0 */
+ if (!is_phb && s->switch_id != 0 && !s->upstream_port &&
+ s->dev_id == slot_num) {
+ slot->data = s;
+ s->pci_slot = slot;
+ prlog(PR_DEBUG, "Found [%s] for slot %016llx\n",
+ s->label, slot->id);
+
+ return s;
+ }
+ }
+
+ prlog(PR_DEBUG, "No data found for %sslot %016llx\n",
+ is_phb ? "PHB " : " ", slot->id);
+ return NULL;
+}
+
+void lxvpd_extract_info(struct pci_slot *slot, struct lxvpd_pci_slot *s)
+{
+ slot->pluggable = s->pluggable ? 1 : 0;
+ slot->power_ctl = s->power_ctl ? 1 : 0;
+ slot->power_led_ctl = s->pwr_led_ctl;
+ slot->attn_led_ctl = s->attn_led_ctl;
+ slot->connector_type = s->connector_type;
+ slot->card_desc = s->card_desc;
+ slot->card_mech = s->card_mech;
+ slot->wired_lanes = s->wired_lanes;
+
+ prlog(PR_DEBUG, "[%s]: pluggable: %d power_ctrl: %d\n",
+ s->label, (int) s->pluggable, (int) s->power_ctl);
+}
+
+static struct lxvpd_pci_slot_data *lxvpd_alloc_slots(struct phb *phb,
+ uint8_t count,
+ uint32_t slot_size)
+{
+ struct lxvpd_pci_slot_data *sdata;
+
+ sdata = zalloc(sizeof(struct lxvpd_pci_slot_data) + count * slot_size);
+ assert(sdata);
+ sdata->num_slots = count;
+ sdata->entry_size = slot_size;
+ sdata->slots = sdata + 1;
+ phb->platform_data = sdata;
+
+ return sdata;
+}
+
+static void lxvpd_format_label(char *dst, const char *src, size_t len)
+{
+ int i;
+
+ memcpy(dst, src, len);
+
+ /* Remove blank suffix */
+ for (i = strlen(dst) - 1; i >= 0; i--) {
+ if (dst[i] != ' ')
+ break;
+
+ dst[i] = 0;
+ }
+}
+
+static void lxvpd_parse_1004_map(struct phb *phb,
+ const uint8_t *sm,
+ uint8_t size,
+ uint32_t slot_size)
+{
+ struct lxvpd_pci_slot_data *sdata;
+ struct lxvpd_pci_slot *s;
+ const struct pci_slot_entry_1004 *entry;
+ uint8_t num_slots, slot;
+
+ num_slots = (size / sizeof(struct pci_slot_entry_1004));
+ sdata = lxvpd_alloc_slots(phb, num_slots, slot_size);
+
+ /* Iterate through the entries in the keyword */
+ entry = (const struct pci_slot_entry_1004 *)sm;
+ for (slot = 0; slot < num_slots; slot++, entry++) {
+ s = sdata->slots + slot * sdata->entry_size;
+
+ /* Figure out PCI slot info */
+ lxvpd_format_label(s->label, entry->label, 3);
+ s->slot_index = entry->slot_index;
+ s->switch_id = entry->pba >> 4;
+ s->vswitch_id = entry->pba & 0xf;
+ s->dev_id = entry->sba;
+ s->pluggable = ((entry->p0.byte & 0x20) == 0);
+ s->power_ctl = !!(entry->p0.byte & 0x40);
+ s->bus_clock = entry->p2.bus_clock - 4;
+ s->connector_type = entry->p2.connector_type - 5;
+ s->card_desc = entry->p3.byte >> 6;
+ if (entry->p3.byte < 0xc0)
+ s->card_desc -= 4;
+ s->card_mech = (entry->p3.byte >> 4) & 0x3;
+ s->pwr_led_ctl = (entry->p3.byte & 0xf) >> 2;
+ s->attn_led_ctl = entry->p3.byte & 0x3;
+
+ switch(entry->p1.wired_lanes) {
+ case 1: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_32; break;
+ case 2: /* fall through */
+ case 3: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_64; break;
+ case 4: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X1; break;
+ case 5: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X4; break;
+ case 6: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X8; break;
+ case 7: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X16; break;
+ default:
+ s->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN;
+ }
+
+ prlog(PR_DEBUG, "1004 Platform data [%s] %02x %02x on PHB%04x\n",
+ s->label, s->switch_id, s->dev_id, phb->opal_id);
+ }
+}
+
+static void lxvpd_parse_1005_map(struct phb *phb,
+ const uint8_t *sm,
+ uint8_t size,
+ uint32_t slot_size)
+{
+ struct lxvpd_pci_slot_data *sdata;
+ struct lxvpd_pci_slot *s;
+ const struct pci_slot_entry_1005 *entry;
+ uint8_t num_slots, slot;
+
+ num_slots = (size / sizeof(struct pci_slot_entry_1005));
+ sdata = lxvpd_alloc_slots(phb, num_slots, slot_size);
+
+ /* Iterate through the entries in the keyword */
+ entry = (const struct pci_slot_entry_1005 *)sm;
+ for (slot = 0; slot < num_slots; slot++, entry++) {
+ s = sdata->slots + slot * sdata->entry_size;
+
+ /* Put slot info into pci device structure */
+ lxvpd_format_label(s->label, entry->label, 8);
+ s->slot_index = entry->slot_index;
+ s->switch_id = entry->pba >> 4;
+ s->vswitch_id = entry->pba & 0xf;
+ s->dev_id = entry->switch_device_id;
+ s->pluggable = (entry->p0.pluggable == 0);
+ s->power_ctl = entry->p0.power_ctl;
+ s->upstream_port = entry->p0.upstream_port;
+ s->bus_clock = entry->p2.bus_clock;
+ s->connector_type = entry->p2.connector_type;
+ s->card_desc = entry->p3.byte >> 6;
+ s->card_mech = (entry->p3.byte >> 4) & 0x3;
+ s->pwr_led_ctl = (entry->p3.byte & 0xf) >> 2;
+ s->attn_led_ctl = entry->p3.byte & 0x3;
+ s->wired_lanes = entry->p1.wired_lanes;
+ if (s->wired_lanes > PCI_SLOT_WIRED_LANES_PCIE_X32)
+ s->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN;
+
+ prlog(PR_DEBUG, "1005 Platform data [%s] %02x %02x %s on PHB%04x\n",
+ s->label, s->switch_id, s->dev_id,
+ s->upstream_port ? "upstream" : "downstream",
+ phb->opal_id);
+ }
+}
+
+void lxvpd_process_slot_entries(struct phb *phb,
+ struct dt_node *node,
+ uint8_t chip_id,
+ uint8_t index,
+ uint32_t slot_size)
+{
+ const void *lxvpd;
+ const uint8_t *pr_rec, *pr_end, *sm;
+ size_t lxvpd_size, pr_size;
+ const beint16_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) {
+ prlog(PR_WARNING, "No data found for PHB%04x %s\n",
+ phb->opal_id, record);
+ return;
+ }
+
+ pr_rec = vpd_find_record(lxvpd, lxvpd_size, record, &pr_size);
+ if (!pr_rec) {
+ prlog(PR_WARNING, "Record %s not found on PHB%04x\n",
+ record, phb->opal_id);
+ return;
+ }
+
+ /* As long as there's still something in the PRxy record */
+ prlog(PR_DEBUG, "PHB%04x record %s has %ld bytes\n",
+ phb->opal_id, record, pr_size);
+ pr_end = pr_rec + pr_size;
+ 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)
+ prlog(PR_WARNING, "Slot Map keyword %s not found\n",
+ record);
+ return;
+ }
+
+ prlog(PR_DEBUG, "Found 0x%04x map...\n", be16_to_cpu(*mf));
+ switch (be16_to_cpu(*mf)) {
+ case 0x1004:
+ lxvpd_parse_1004_map(phb, sm + 1, sm_sz - 1, slot_size);
+ found = true;
+ break;
+ case 0x1005:
+ lxvpd_parse_1005_map(phb, sm + 1, sm_sz - 1, slot_size);
+ found = true;
+ break;
+ /* Add support for 0x1006 maps ... */
+ }
+
+ pr_rec = sm + sm_sz;
+ }
+}
+
+void lxvpd_add_slot_properties(struct pci_slot *slot,
+ struct dt_node *np)
+{
+ struct phb *phb = slot->phb;
+ struct lxvpd_pci_slot *s = slot->data;
+ char loc_code[LOC_CODE_SIZE];
+ size_t base_loc_code_len, slot_label_len;
+
+ /* Check if we have platform specific slot */
+ if (!s || !np)
+ return;
+
+ /* Check PHB base location code */
+ if (!phb->base_loc_code)
+ return;
+
+ /* Check location length is valid */
+ base_loc_code_len = strlen(phb->base_loc_code);
+ slot_label_len = strlen(s->label);
+ if ((base_loc_code_len + slot_label_len + 1) >= LOC_CODE_SIZE)
+ return;
+
+ /* Location code */
+ strcpy(loc_code, phb->base_loc_code);
+ strcat(loc_code, "-");
+ strcat(loc_code, s->label);
+ dt_add_property(np, "ibm,slot-location-code",
+ loc_code, strlen(loc_code) + 1);
+ dt_add_property_string(np, "ibm,slot-label",
+ s->label);
+}
diff --git a/roms/skiboot/platforms/ibm-fsp/lxvpd.h b/roms/skiboot/platforms/ibm-fsp/lxvpd.h
new file mode 100644
index 000000000..4b771f3f7
--- /dev/null
+++ b/roms/skiboot/platforms/ibm-fsp/lxvpd.h
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2016 IBM Corp. */
+
+#ifndef __LXVPD_H
+#define __LXVPD_H
+
+#define LX_VPD_1S2U_BACKPLANE 0x3100040100300041ull
+#define LX_VPD_2S2U_BACKPLANE 0x3100040100300042ull
+#define LX_VPD_SHARK_BACKPLANE 0x3100040100300942ull
+#define LX_VPD_1S4U_BACKPLANE 0x3100040100300043ull
+#define LX_VPD_2S4U_BACKPLANE 0x3100040100300044ull
+
+struct slot_p0 {
+ union {
+ uint8_t byte;
+ struct {
+#if HAVE_BIG_ENDIAN
+ 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;
+#else
+ uint8_t alt_load_source:1;
+ uint8_t upstream_port:1;
+ uint8_t rsvd_5:1;
+ uint8_t power_ctl:1;
+ uint8_t pluggable_location:3;
+ uint8_t pluggable:1;
+#endif
+ };
+ };
+};
+
+struct slot_p1 {
+#if HAVE_BIG_ENDIAN
+ uint8_t rsvd_0:1;
+ uint8_t wired_lanes:3;
+ uint8_t rsvd_4:4;
+#else
+ uint8_t rsvd_4:4;
+ uint8_t wired_lanes:3;
+ uint8_t rsvd_0:1;
+#endif
+};
+
+struct slot_p2 {
+#if HAVE_BIG_ENDIAN
+ uint8_t rsvd_0:1;
+ uint8_t bus_clock:3;
+ uint8_t connector_type:4;
+#else
+ uint8_t connector_type:4;
+ uint8_t bus_clock:3;
+ uint8_t rsvd_0:1;
+#endif
+};
+
+struct slot_p3 {
+ union {
+ uint8_t byte;
+ struct {
+#if HAVE_BIG_ENDIAN
+ 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;
+#else
+ uint8_t attn_led_fsp:1;
+ uint8_t attn_led_kvm:1;
+ uint8_t pow_led_fsp:1;
+ uint8_t pow_led_kvm:1;
+ uint8_t right_mech:1;
+ uint8_t left_mech:1;
+ uint8_t length:1;
+ uint8_t height:1;
+#endif
+ };
+ };
+};
+
+struct pci_slot_entry_1004 {
+ uint8_t pba;
+ uint8_t sba;
+ uint8_t phb_or_slot_type;
+ char label[3];
+ __be16 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;
+};
+
+/* P8 PCI Slot Entry Definitions -- 1005 */
+struct pci_slot_entry_1005 {
+ union {
+ uint8_t pba;
+ struct {
+#if HAVE_BIG_ENDIAN
+ uint8_t switch_id:4;
+ uint8_t vswitch_id:4;
+#else
+ uint8_t vswitch_id:4;
+ uint8_t switch_id:4;
+#endif
+ };
+ };
+ uint8_t switch_device_id;
+#if HAVE_BIG_ENDIAN
+ uint8_t slot_type:4;
+ uint8_t phb_id:4;
+#else
+ uint8_t phb_id:4;
+ uint8_t slot_type:4;
+#endif
+ 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 lxvpd_pci_slot {
+ struct pci_slot *pci_slot;
+ uint8_t switch_id;
+ uint8_t vswitch_id;
+ uint8_t dev_id;
+ char label[9];
+ bool pluggable;
+ bool power_ctl;
+ bool upstream_port;
+ uint8_t wired_lanes;
+ uint8_t bus_clock;
+ uint8_t connector_type;
+ uint8_t card_desc;
+ uint8_t card_mech;
+ uint8_t pwr_led_ctl;
+ uint8_t attn_led_ctl;
+ uint8_t slot_index;
+};
+
+extern void lxvpd_process_slot_entries(struct phb *phb, struct dt_node *node,
+ uint8_t chip_id, uint8_t index,
+ uint32_t slot_size);
+extern void *lxvpd_get_slot(struct pci_slot *slot);
+extern void lxvpd_extract_info(struct pci_slot *slot,
+ struct lxvpd_pci_slot *s);
+extern void lxvpd_add_slot_properties(struct pci_slot *slot,
+ struct dt_node *np);
+#endif /* __LXVPD_H */
diff --git a/roms/skiboot/platforms/ibm-fsp/zz.c b/roms/skiboot/platforms/ibm-fsp/zz.c
new file mode 100644
index 000000000..493d6030a
--- /dev/null
+++ b/roms/skiboot/platforms/ibm-fsp/zz.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2016-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <fsp.h>
+#include <pci.h>
+#include <pci-cfg.h>
+#include <chip.h>
+#include <i2c.h>
+#include <timebase.h>
+#include <hostservices.h>
+#include <npu2.h>
+
+#include "ibm-fsp.h"
+#include "lxvpd.h"
+
+static const char *zz_ocapi_slot_label(uint32_t chip_id,
+ uint32_t brick_index)
+{
+ const char *name = NULL;
+
+ if (chip_id == 0) {
+ if (brick_index == 2)
+ name = "P1-T5";
+ else
+ name = "P1-T6";
+ } else {
+ if (brick_index == 2)
+ name = "P1-T7";
+ else
+ name = "P1-T8";
+ }
+ return name;
+}
+
+/* We don't yet create NPU device nodes on ZZ, but these values are correct */
+static const struct platform_ocapi zz_ocapi = {
+ .i2c_engine = 1,
+ .i2c_port = 4,
+ .i2c_reset_addr = 0x20,
+ .i2c_reset_brick2 = (1 << 1),
+ .i2c_reset_brick3 = (1 << 6),
+ .i2c_reset_brick4 = 0, /* unused */
+ .i2c_reset_brick5 = 0, /* unused */
+ .i2c_presence_addr = 0x20,
+ .i2c_presence_brick2 = (1 << 2), /* bottom connector */
+ .i2c_presence_brick3 = (1 << 7), /* top connector */
+ .i2c_presence_brick4 = 0, /* unused */
+ .i2c_presence_brick5 = 0, /* unused */
+ .odl_phy_swap = true,
+ .ocapi_slot_label = zz_ocapi_slot_label,
+};
+
+#define NPU_BASE 0x5011000
+#define NPU_SIZE 0x2c
+#define NPU_INDIRECT0 0x8000000009010c3f /* OB0 - no OB3 on ZZ */
+
+static void create_link(struct dt_node *npu, int group, int index)
+{
+ struct dt_node *link;
+ uint32_t lane_mask;
+
+ switch (index) {
+ case 2:
+ lane_mask = 0xf1e000; /* 0-3, 7-10 */
+ break;
+ case 3:
+ lane_mask = 0x00078f; /* 13-16, 20-23 */
+ break;
+ default:
+ assert(0);
+ }
+
+ link = dt_new_addr(npu, "link", index);
+ dt_add_property_string(link, "compatible", "ibm,npu-link");
+ dt_add_property_cells(link, "ibm,npu-link-index", index);
+ dt_add_property_u64s(link, "ibm,npu-phy", NPU_INDIRECT0);
+ dt_add_property_cells(link, "ibm,npu-lane-mask", lane_mask);
+ dt_add_property_cells(link, "ibm,npu-group-id", group);
+ dt_add_property_u64s(link, "ibm,link-speed", 25000000000ul);
+}
+
+static void add_opencapi_dt_nodes(void)
+{
+ struct dt_node *npu, *xscom;
+ int npu_index = 0;
+
+ /*
+ * In an ideal world, we should get all the NPU links
+ * information from HDAT. But after some effort, HDAT is still
+ * giving incorrect information for opencapi. As of this
+ * writing:
+ * 1. link usage is wrong for most FPGA cards (0xFFFF vs. 2)
+ * 2. the 24-bit lane mask is aligned differently than on
+ * other platforms (witherspoon)
+ * 3. connecting a link entry in HDAT to the real physical
+ * link will need extra work:
+ * - HDAT does presence detection and only lists links with
+ * an adapter, so we cannot use default ordering like on
+ * witherspoon
+ * - best option is probably the brick ID field (offset 8).
+ * It's coming straight from the MRW, but seems to match
+ * what we expect (2 or 3). Would need to be checked.
+ *
+ * To make things more fun, any change in the HDAT data needs
+ * to be coordinated with PHYP, which is using (some of) those
+ * fields.
+ *
+ * As a consequence:
+ * 1. the hdat parsing code in skiboot remains disabled (for
+ * opencapi)
+ * 2. we hard-code the NPU and links entries in the device
+ * tree.
+ *
+ * Getting the data from HDAT would have the advantage of
+ * providing the real link speed (20.0 vs. 25.78125 gbps),
+ * which is useful as there's one speed-dependent setting we
+ * need to do when initializing the NPU. Our hard coded
+ * definition assumes the higher speed and may need tuning in
+ * debug scenario using a lower link speed.
+ */
+ dt_for_each_compatible(dt_root, xscom, "ibm,xscom") {
+ /*
+ * our hdat parsing code may create NPU nodes with no
+ * links, so let's make sure we start from a clean
+ * state
+ */
+ npu = dt_find_by_name_addr(xscom, "npu", NPU_BASE);
+ if (npu)
+ dt_free(npu);
+
+ npu = dt_new_addr(xscom, "npu", NPU_BASE);
+ dt_add_property_cells(npu, "reg", NPU_BASE, NPU_SIZE);
+ dt_add_property_strings(npu, "compatible", "ibm,power9-npu");
+ dt_add_property_cells(npu, "ibm,npu-index", npu_index++);
+ dt_add_property_cells(npu, "ibm,npu-links", 2);
+
+ create_link(npu, 1, 2);
+ create_link(npu, 2, 3);
+ }
+}
+
+static bool zz_probe(void)
+{
+ /* FIXME: make this neater when the dust settles */
+ if (dt_node_is_compatible(dt_root, "ibm,zz-1s2u") ||
+ dt_node_is_compatible(dt_root, "ibm,zz-1s4u") ||
+ dt_node_is_compatible(dt_root, "ibm,zz-2s2u") ||
+ dt_node_is_compatible(dt_root, "ibm,zz-2s4u") ||
+ dt_node_is_compatible(dt_root, "ibm,zz-1s4u+gen4") ||
+ dt_node_is_compatible(dt_root, "ibm,zz-2s2u+gen4") ||
+ dt_node_is_compatible(dt_root, "ibm,zz-2s4u+gen4")) {
+
+ add_opencapi_dt_nodes();
+ return true;
+ }
+
+ /* Add Fleetwood FSP platform and map it to ZZ */
+ if (dt_node_is_compatible(dt_root, "ibm,fleetwood-m9s")) {
+ return true;
+ }
+
+ /* Add Denali FSP platform and map it to ZZ */
+ if (dt_node_is_compatible(dt_root, "ibm,denali")) {
+ return true;
+ }
+
+ return false;
+}
+
+static uint32_t ibm_fsp_occ_timeout(void)
+{
+ /* Use a fixed 60s value for now */
+ return 60;
+}
+
+static void zz_init(void)
+{
+ ibm_fsp_init();
+ hservice_fsp_init();
+}
+
+DECLARE_PLATFORM(zz) = {
+ .name = "ZZ",
+ .psi = &fsp_platform_psi,
+ .prd = &fsp_platform_prd,
+ .probe = zz_probe,
+ .init = zz_init,
+ .fast_reboot_init = fsp_console_reset,
+ .finalise_dt = ibm_fsp_finalise_dt,
+ .exit = ibm_fsp_exit,
+ .cec_power_down = ibm_fsp_cec_power_down,
+ .cec_reboot = ibm_fsp_cec_reboot,
+ .pci_setup_phb = firenze_pci_setup_phb,
+ .pci_get_slot_info = firenze_pci_get_slot_info,
+ .pci_add_loc_code = firenze_pci_add_loc_code,
+ .pci_probe_complete = firenze_pci_send_inventory,
+ .nvram_info = fsp_nvram_info,
+ .nvram_start_read = fsp_nvram_start_read,
+ .nvram_write = fsp_nvram_write,
+ .occ_timeout = ibm_fsp_occ_timeout,
+ .elog_commit = elog_fsp_commit,
+ .start_preload_resource = fsp_start_preload_resource,
+ .resource_loaded = fsp_resource_loaded,
+ .sensor_read = ibm_fsp_sensor_read,
+ .terminate = ibm_fsp_terminate,
+ .ocapi = &zz_ocapi,
+ .npu2_device_detect = npu2_i2c_presence_detect,
+ .op_display = fsp_op_display,
+ .vpd_iohub_load = vpd_iohub_load,
+ .heartbeat_time = fsp_heartbeat_time,
+};
diff --git a/roms/skiboot/platforms/mambo/Makefile.inc b/roms/skiboot/platforms/mambo/Makefile.inc
new file mode 100644
index 000000000..b0e6b0c46
--- /dev/null
+++ b/roms/skiboot/platforms/mambo/Makefile.inc
@@ -0,0 +1,6 @@
+SUBDIRS += $(PLATDIR)/mambo
+
+MAMBO_OBJS = mambo.o console.o
+MAMBO = $(PLATDIR)/mambo/built-in.a
+$(MAMBO): $(MAMBO_OBJS:%=$(PLATDIR)/mambo/%)
+
diff --git a/roms/skiboot/platforms/mambo/console.c b/roms/skiboot/platforms/mambo/console.c
new file mode 100644
index 000000000..6d5b20b56
--- /dev/null
+++ b/roms/skiboot/platforms/mambo/console.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2016-2017 IBM Corp. */
+
+#include <skiboot.h>
+#include <console.h>
+
+#include "mambo.h"
+
+/*
+ * The SIM_READ_CONSOLE callout will return -1 if there is no character to read.
+ * There's no explicit poll callout so we "poll" by doing a read and stashing
+ * the result until we do an actual read.
+ */
+static int mambo_char = -1;
+
+static bool mambo_console_poll(void)
+{
+ if (mambo_char < 0)
+ mambo_char = callthru0(SIM_READ_CONSOLE_CODE);
+
+ return mambo_char >= 0;
+}
+
+static size_t mambo_console_read(char *buf, size_t len)
+{
+ size_t count = 0;
+
+ while (count < len) {
+ if (!mambo_console_poll())
+ break;
+
+ buf[count++] = mambo_char;
+ mambo_char = -1;
+ }
+
+ return count;
+}
+
+size_t mambo_console_write(const char *buf, size_t len)
+{
+ callthru2(SIM_WRITE_CONSOLE_CODE, (unsigned long)buf, len);
+ return len;
+}
+
+static struct con_ops mambo_con_driver = {
+ .poll_read = mambo_console_poll,
+ .read = mambo_console_read,
+ .write = mambo_console_write,
+};
+
+void enable_mambo_console(void)
+{
+ prlog(PR_NOTICE, "Enabling Mambo console\n");
+ set_console(&mambo_con_driver);
+}
+
+/*
+ * mambo console based printf(), this is useful for debugging the console
+ * since mambo_console_write() can be safely called from anywhere.
+ *
+ * This is a debug hack and you shouldn't use it in real code.
+ */
+void mprintf(const char *fmt, ...)
+{
+ char buf[320];
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ mambo_console_write(buf, i);
+}
diff --git a/roms/skiboot/platforms/mambo/mambo.c b/roms/skiboot/platforms/mambo/mambo.c
new file mode 100644
index 000000000..a1e0488c8
--- /dev/null
+++ b/roms/skiboot/platforms/mambo/mambo.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015-2017 IBM Corp. */
+
+#include <skiboot.h>
+#include <device.h>
+#include <console.h>
+#include <chip.h>
+#include <cpu.h>
+#include <opal-api.h>
+#include <opal-internal.h>
+#include <time-utils.h>
+#include <time.h>
+
+#include "mambo.h"
+
+static bool mambo_probe(void)
+{
+ if (!dt_find_by_path(dt_root, "/mambo"))
+ return false;
+
+ return true;
+}
+
+#define BD_INFO_SYNC 0
+#define BD_INFO_STATUS 1
+#define BD_INFO_BLKSZ 2
+#define BD_INFO_DEVSZ 3
+#define BD_INFO_CHANGE 4
+
+#define BD_SECT_SZ 512
+
+static inline int callthru_disk_read(int id, void *buf, unsigned long sect,
+ unsigned long nrsect)
+{
+ return callthru3(SIM_BOGUS_DISK_READ, (unsigned long)buf, sect,
+ (nrsect << 16) | id);
+}
+
+static inline int callthru_disk_write(int id, void *buf, unsigned long sect,
+ unsigned long nrsect)
+{
+ return callthru3(SIM_BOGUS_DISK_WRITE, (unsigned long)buf, sect,
+ (nrsect << 16) | id);
+}
+
+static inline unsigned long callthru_disk_info(int op, int id)
+{
+ return callthru2(SIM_BOGUS_DISK_INFO, (unsigned long)op,
+ (unsigned long)id);
+}
+
+extern unsigned long callthru_tcl(const char *str, int len);
+
+unsigned long callthru_tcl(const char *str, int len)
+{
+ prlog(PR_DEBUG, "Sending TCL to Mambo, cmd: %s\n", str);
+ return callthru2(SIM_CALL_TCL, (unsigned long)str, (unsigned long)len);
+}
+
+struct bogus_disk_info {
+ unsigned long size;
+ int id;
+};
+
+static int bogus_disk_read(struct blocklevel_device *bl, uint64_t pos, void *buf,
+ uint64_t len)
+{
+ struct bogus_disk_info *bdi = bl->priv;
+ int rc, read_sectors = 0;
+ char b[BD_SECT_SZ];
+
+ if (len >= BD_SECT_SZ) {
+ rc = callthru_disk_read(bdi->id, buf, pos/BD_SECT_SZ,
+ len/BD_SECT_SZ);
+ if (rc)
+ return rc;
+ read_sectors = (len / BD_SECT_SZ);
+ }
+
+ if ((len % BD_SECT_SZ) == 0)
+ return 0;
+
+ /*
+ * Read any unaligned data into a temporaty buffer b, then copy
+ * to buf
+ */
+ rc = callthru_disk_read(bdi->id, b, (pos/BD_SECT_SZ) + read_sectors, 1);
+ if (rc)
+ return rc;
+ memcpy(buf + (read_sectors * BD_SECT_SZ) , &b[pos % BD_SECT_SZ],
+ len - (read_sectors * BD_SECT_SZ));
+ return rc;
+}
+
+static int bogus_disk_write(struct blocklevel_device *bl, uint64_t pos,
+ const void *buf, uint64_t len)
+{
+ struct bogus_disk_info *bdi = bl->priv;
+
+ if ((len % BD_SECT_SZ) != 0)
+ return OPAL_PARAMETER;
+
+ return callthru_disk_write(bdi->id, (void *)buf, pos/BD_SECT_SZ,
+ len/BD_SECT_SZ);
+
+}
+
+static int bogus_disk_erase(struct blocklevel_device *bl __unused,
+ uint64_t pos __unused, uint64_t len __unused)
+{
+ return 0; /* NOP */
+}
+
+static int bogus_disk_get_info(struct blocklevel_device *bl, const char **name,
+ uint64_t *total_size, uint32_t *erase_granule)
+{
+ struct bogus_disk_info *bdi = bl->priv;
+
+ if (total_size)
+ *total_size = bdi->size;
+
+ if (erase_granule)
+ *erase_granule = BD_SECT_SZ;
+
+ if (name)
+ *name = "mambobogus";
+
+ return 0;
+}
+
+static void bogus_disk_flash_init(void)
+{
+ struct blocklevel_device *bl;
+ struct bogus_disk_info *bdi;
+ unsigned long id = 0, size;
+ int rc;
+
+ if (!chip_quirk(QUIRK_MAMBO_CALLOUTS))
+ return;
+
+ while (1) {
+
+ rc = callthru_disk_info(BD_INFO_STATUS, id);
+ if (rc < 0)
+ return;
+
+ size = callthru_disk_info(BD_INFO_DEVSZ, id) * 1024;
+ prlog(PR_NOTICE, "mambo: Found bogus disk size: 0x%lx\n", size);
+
+ bl = zalloc(sizeof(struct blocklevel_device));
+ bdi = zalloc(sizeof(struct bogus_disk_info));
+ if (!bl || !bdi) {
+ free(bl);
+ free(bdi);
+ prerror("mambo: Failed to start bogus disk, ENOMEM\n");
+ return;
+ }
+
+ bl->read = &bogus_disk_read;
+ bl->write = &bogus_disk_write;
+ bl->erase = &bogus_disk_erase;
+ bl->get_info = &bogus_disk_get_info;
+ bdi->id = id;
+ bdi->size = size;
+ bl->priv = bdi;
+ bl->erase_mask = BD_SECT_SZ - 1;
+
+ rc = flash_register(bl);
+ if (rc)
+ prerror("mambo: Failed to register bogus disk: %li\n",
+ id);
+ id++;
+ }
+}
+
+static int64_t time_delta = 0;
+
+static int64_t mambo_rtc_read(__be32 *ymd, __be64 *hmsm)
+{
+ int64_t mambo_time;
+ struct tm t;
+ time_t mt;
+ uint32_t __ymd;
+ uint64_t __hmsm;
+
+ if (!ymd || !hmsm)
+ return OPAL_PARAMETER;
+
+ mambo_time = callthru0(SIM_GET_TIME_CODE);
+ mt = mambo_time >> 32;
+ mt += time_delta;
+ gmtime_r(&mt, &t);
+ tm_to_datetime(&t, &__ymd, &__hmsm);
+
+ *ymd = cpu_to_be32(__ymd);
+ *hmsm = cpu_to_be64(__hmsm);
+
+ return OPAL_SUCCESS;
+}
+
+static int64_t mambo_rtc_write(uint32_t ymd, uint64_t hmsm)
+{
+ int64_t mambo_time;
+ struct tm tm;
+ time_t mt, new_mt;
+
+ mambo_time = callthru0(SIM_GET_TIME_CODE);
+ mt = mambo_time >> 32;
+
+ datetime_to_tm(ymd, hmsm, &tm);
+ new_mt = mktime(&tm);
+
+ time_delta = new_mt - mt;
+
+ return OPAL_SUCCESS;
+}
+
+static void mambo_rtc_init(void)
+{
+ struct dt_node *np = dt_new(opal_node, "rtc");
+ dt_add_property_strings(np, "compatible", "ibm,opal-rtc");
+
+ opal_register(OPAL_RTC_READ, mambo_rtc_read, 2);
+ opal_register(OPAL_RTC_WRITE, mambo_rtc_write, 2);
+}
+
+static void mambo_system_reset_cpu(struct cpu_thread *cpu)
+{
+ uint32_t core_id;
+ uint32_t thread_id;
+ char tcl_cmd[50];
+
+ core_id = pir_to_core_id(cpu->pir);
+ thread_id = pir_to_thread_id(cpu->pir);
+
+ snprintf(tcl_cmd, sizeof(tcl_cmd), "mysim cpu %i:%i interrupt SystemReset", core_id, thread_id);
+ callthru_tcl(tcl_cmd, strlen(tcl_cmd));
+}
+
+#define SYS_RESET_ALL -1
+#define SYS_RESET_ALL_OTHERS -2
+
+static int64_t mambo_signal_system_reset(int32_t cpu_nr)
+{
+ struct cpu_thread *cpu;
+
+ if (cpu_nr < 0) {
+ if (cpu_nr < SYS_RESET_ALL_OTHERS)
+ return OPAL_PARAMETER;
+
+ for_each_cpu(cpu) {
+ if (cpu == this_cpu())
+ continue;
+ mambo_system_reset_cpu(cpu);
+
+ }
+ if (cpu_nr == SYS_RESET_ALL)
+ mambo_system_reset_cpu(this_cpu());
+
+ return OPAL_SUCCESS;
+
+ } else {
+ cpu = find_cpu_by_server(cpu_nr);
+ if (!cpu)
+ return OPAL_PARAMETER;
+
+ mambo_system_reset_cpu(cpu);
+ return OPAL_SUCCESS;
+ }
+}
+
+static void mambo_sreset_init(void)
+{
+ opal_register(OPAL_SIGNAL_SYSTEM_RESET, mambo_signal_system_reset, 1);
+}
+
+static void mambo_platform_init(void)
+{
+ mambo_sreset_init();
+ mambo_rtc_init();
+ bogus_disk_flash_init();
+}
+
+static int64_t mambo_cec_power_down(uint64_t request __unused)
+{
+ if (chip_quirk(QUIRK_MAMBO_CALLOUTS))
+ callthru0(SIM_EXIT_CODE);
+
+ return OPAL_UNSUPPORTED;
+}
+
+static void __attribute__((noreturn)) mambo_terminate(const char *msg __unused)
+{
+ if (chip_quirk(QUIRK_MAMBO_CALLOUTS))
+ callthru0(SIM_EXIT_CODE);
+
+ for (;;) ;
+}
+
+static int mambo_heartbeat_time(void)
+{
+ /*
+ * Mambo is slow and has no console input interrupt, so faster
+ * polling is needed to ensure its responsiveness.
+ */
+ return 100;
+}
+
+DECLARE_PLATFORM(mambo) = {
+ .name = "Mambo",
+ .probe = mambo_probe,
+ .init = mambo_platform_init,
+ .cec_power_down = mambo_cec_power_down,
+ .terminate = mambo_terminate,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .heartbeat_time = mambo_heartbeat_time,
+ .nvram_info = fake_nvram_info,
+ .nvram_start_read = fake_nvram_start_read,
+ .nvram_write = fake_nvram_write,
+};
diff --git a/roms/skiboot/platforms/mambo/mambo.h b/roms/skiboot/platforms/mambo/mambo.h
new file mode 100644
index 000000000..0b3bb60c4
--- /dev/null
+++ b/roms/skiboot/platforms/mambo/mambo.h
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2016 IBM Corp. */
+
+#ifndef __MAMBO_H__
+#define __MAMBO_H__
+
+static inline unsigned long callthru0(int command)
+{
+ register uint64_t c asm("r3") = command;
+ asm volatile (".long 0x000eaeb0":"=r" (c):"r"(c));
+ return c;
+}
+
+static inline unsigned long callthru2(int command, unsigned long arg1,
+ unsigned long arg2)
+{
+ register unsigned long c asm("r3") = command;
+ register unsigned long a1 asm("r4") = arg1;
+ register unsigned long a2 asm("r5") = arg2;
+ asm volatile (".long 0x000eaeb0":"=r" (c):"r"(c), "r"(a1), "r"(a2));
+ return c;
+}
+
+static inline unsigned long callthru3(int command, unsigned long arg1,
+ unsigned long arg2, unsigned long arg3)
+{
+ register unsigned long c asm("r3") = command;
+ register unsigned long a1 asm("r4") = arg1;
+ register unsigned long a2 asm("r5") = arg2;
+ register unsigned long a3 asm("r6") = arg3;
+ asm volatile (".long 0x000eaeb0":"=r" (c):"r"(c), "r"(a1), "r"(a2),
+ "r"(a3));
+ return c;
+}
+
+/* Mambo callthru commands */
+#define SIM_WRITE_CONSOLE_CODE 0
+#define SIM_EXIT_CODE 31
+#define SIM_READ_CONSOLE_CODE 60
+#define SIM_GET_TIME_CODE 70
+#define SIM_CALL_TCL 86
+#define SIM_BOGUS_DISK_READ 116
+#define SIM_BOGUS_DISK_WRITE 117
+#define SIM_BOGUS_DISK_INFO 118
+
+#endif /* __MAMBO_H__ */
diff --git a/roms/skiboot/platforms/qemu/Makefile.inc b/roms/skiboot/platforms/qemu/Makefile.inc
new file mode 100644
index 000000000..d4f988e7c
--- /dev/null
+++ b/roms/skiboot/platforms/qemu/Makefile.inc
@@ -0,0 +1,6 @@
+SUBDIRS += $(PLATDIR)/qemu
+
+QEMU_OBJS = qemu.o
+QEMU = $(PLATDIR)/qemu/built-in.a
+$(QEMU): $(QEMU_OBJS:%=$(PLATDIR)/qemu/%)
+
diff --git a/roms/skiboot/platforms/qemu/qemu.c b/roms/skiboot/platforms/qemu/qemu.c
new file mode 100644
index 000000000..96153e853
--- /dev/null
+++ b/roms/skiboot/platforms/qemu/qemu.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2019 IBM Corp. */
+
+#include <skiboot.h>
+#include <console.h>
+#include <device.h>
+#include <ipmi.h>
+
+#include <platforms/astbmc/astbmc.h>
+
+static bool bt_device_present;
+
+ST_PLUGGABLE(qemu_slot0, "pcie.0");
+ST_PLUGGABLE(qemu_slot1, "pcie.1");
+
+static const struct slot_table_entry qemu_sw_down[] = {
+ SW_PLUGGABLE("sw0_down0", 0x0),
+ SW_BUILTIN ("sw0_down1", 0x1),
+ SW_PLUGGABLE("sw0_down2", 0x2),
+ { .etype = st_end },
+};
+ST_BUILTIN_DEV(qemu_sw_up, "sw0_up", .children = qemu_sw_down);
+ST_BUILTIN_DEV(qemu_sw_root, "pcie.2", .children = qemu_sw_up);
+
+ST_PLUGGABLE(qemu_slot3, "pcie.3");
+ST_PLUGGABLE(qemu_slot4, "pcie.4");
+ST_PLUGGABLE(qemu_slot5, "pcie.5");
+
+static const struct slot_table_entry qemu_phb_table[] = {
+ ST_PHB_ENTRY(0, 0, qemu_slot0),
+ ST_PHB_ENTRY(0, 1, qemu_slot1),
+ ST_PHB_ENTRY(0, 2, qemu_sw_root),
+ ST_PHB_ENTRY(0, 3, qemu_slot3),
+ ST_PHB_ENTRY(0, 4, qemu_slot4),
+ ST_PHB_ENTRY(0, 5, qemu_slot5),
+ { .etype = st_end },
+};
+
+static bool qemu_probe_common(const char *compat)
+{
+ struct dt_node *n;
+
+ if (!dt_node_is_compatible(dt_root, compat))
+ return false;
+
+ astbmc_early_init();
+
+ /* check if the BT device was defined by QEMU */
+ dt_for_each_compatible(dt_root, n, "bt") {
+ bt_device_present = true;
+ }
+
+ slot_table_init(qemu_phb_table);
+
+ return true;
+}
+
+static bool qemu_probe(void)
+{
+ return qemu_probe_common("qemu,powernv");
+}
+
+static bool qemu_probe_powernv8(void)
+{
+ return qemu_probe_common("qemu,powernv8");
+}
+
+static bool qemu_probe_powernv9(void)
+{
+ return qemu_probe_common("qemu,powernv9");
+}
+
+static bool qemu_probe_powernv10(void)
+{
+ return qemu_probe_common("qemu,powernv10");
+}
+
+static void qemu_init(void)
+{
+ if (!bt_device_present) {
+ set_opal_console(&uart_opal_con);
+ } else {
+ astbmc_init();
+ }
+}
+
+DECLARE_PLATFORM(qemu) = {
+ .name = "QEMU",
+ .probe = qemu_probe,
+ .init = qemu_init,
+ .external_irq = astbmc_ext_irq_serirq_cpld,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .terminate = ipmi_terminate,
+};
+
+/*
+ * For a QEMU PowerNV machine using POWER8 CPUs (Palmetto)
+ */
+DECLARE_PLATFORM(qemu_powernv8) = {
+ .name = "QEMU POWER8",
+ .probe = qemu_probe_powernv8,
+ .bmc = &bmc_plat_ast2400_ami,
+ .init = qemu_init,
+ .external_irq = astbmc_ext_irq_serirq_cpld,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = astbmc_exit,
+ .terminate = ipmi_terminate,
+};
+
+/*
+ * For a QEMU PowerNV machine using POWER9 CPUs (Witherspoon)
+ */
+DECLARE_PLATFORM(qemu_powernv9) = {
+ .name = "QEMU POWER9",
+ .probe = qemu_probe_powernv9,
+ .bmc = &bmc_plat_ast2500_openbmc,
+ .init = qemu_init,
+ .external_irq = astbmc_ext_irq_serirq_cpld,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = astbmc_exit,
+ .terminate = ipmi_terminate,
+};
+
+/*
+ * For a QEMU PowerNV machine using POWER10 CPUs (Rainier)
+ */
+DECLARE_PLATFORM(qemu_powernv10) = {
+ .name = "QEMU POWER10",
+ .probe = qemu_probe_powernv10,
+ .bmc = &bmc_plat_ast2500_openbmc,
+ .init = qemu_init,
+ .external_irq = astbmc_ext_irq_serirq_cpld,
+ .cec_power_down = astbmc_ipmi_power_down,
+ .cec_reboot = astbmc_ipmi_reboot,
+ .pci_get_slot_info = slot_table_get_slot_info,
+ .start_preload_resource = flash_start_preload_resource,
+ .resource_loaded = flash_resource_loaded,
+ .exit = astbmc_exit,
+ .terminate = ipmi_terminate,
+};
diff --git a/roms/skiboot/platforms/rhesus/Makefile.inc b/roms/skiboot/platforms/rhesus/Makefile.inc
new file mode 100644
index 000000000..aa66f6aea
--- /dev/null
+++ b/roms/skiboot/platforms/rhesus/Makefile.inc
@@ -0,0 +1,6 @@
+SUBDIRS += $(PLATDIR)/rhesus
+
+RHESUS_OBJS = rhesus.o gpio.o
+RHESUS = $(PLATDIR)/rhesus/built-in.a
+$(RHESUS): $(RHESUS_OBJS:%=$(PLATDIR)/rhesus/%)
+
diff --git a/roms/skiboot/platforms/rhesus/ec/config.h b/roms/skiboot/platforms/rhesus/ec/config.h
new file mode 100644
index 000000000..df2ad5d46
--- /dev/null
+++ b/roms/skiboot/platforms/rhesus/ec/config.h
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: Apache-2.0
+/* Copyright 2013-2014 IBM Corp. */
+
+/**
+ * @file config.H
+ *
+ * @brief Definitions for EC configuration values.
+ *
+ */
+
+#ifndef __EC_CONFIG_H_
+#define __EC_CONFIG_H_
+
+#include <stdint.h>
+
+#define EC_RTC_PORT_BASE (0x70) // RTC/CMOS LPC base address
+#define EC_RTC_BLOCK_SIZE (512) // Size of addressable data in RTC
+#define EC_RTC_CENTURY (1) // 1 if century format is enabled
+#if EC_RTC_CENTURY
+#define EC_RTC_BBRAM_OFFSET (0x33) // Offset of NV data (= size of calendar)
+#else
+#define EC_RTC_BBRAM_OFFSET (0x0E) // Offset of NV data (= size of calendar)
+#endif // #if EC_RTC_CENTURY
+
+#define EC_RTCDD_READ_TRIES (2) // Times to try the RTC if updating
+#define EC_RTCDD_RETRY_DELAY (300000) // Delay between RTC read retries in ns
+ // based on update time of 244 + 30.5 µs
+
+#define EC_GPIO_INDEX 0x200
+#define EC_GPIO_DATA 0x201
+#define EC_GPIO_NUM_PORTS 17
+#define EC_GPIO_PORT_SKIP 4
+
+#define EC_GPIO_DATA_OFFSET 0x0
+#define EC_GPIO_DDR_OFFSET 0x1
+#define EC_GPIO_PIN_OFFSET 0x2
+#define EC_GPIO_PUP_OFFSET 0x3
+
+typedef enum EcGpioPort {
+ EC_GPIO_PORT_A = 0,
+ EC_GPIO_PORT_B = 1,
+ EC_GPIO_PORT_C = 2,
+ EC_GPIO_PORT_D = 3,
+ EC_GPIO_PORT_E = 4,
+ EC_GPIO_PORT_F = 5,
+ EC_GPIO_PORT_G = 6,
+ EC_GPIO_PORT_H = 7,
+ // skip port I
+ EC_GPIO_PORT_J = 8,
+ EC_GPIO_PORT_K = 9,
+ EC_GPIO_PORT_L = 10,
+ EC_GPIO_PORT_M = 11,
+ EC_GPIO_PORT_N = 12,
+ // skip port O
+ EC_GPIO_PORT_P = 13,
+ EC_GPIO_PORT_Q = 14,
+ EC_GPIO_PORT_R = 15,
+ EC_GPIO_PORT_S = 16,
+} EcGpioPort;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void ec_outb(uint16_t, uint8_t);
+uint8_t ec_inb(uint16_t);
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __EC_CONFIG_H_
diff --git a/roms/skiboot/platforms/rhesus/ec/gpio.h b/roms/skiboot/platforms/rhesus/ec/gpio.h
new file mode 100644
index 000000000..65d7d878a
--- /dev/null
+++ b/roms/skiboot/platforms/rhesus/ec/gpio.h
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: Apache-2.0
+/* Copyright 2013-2014 Google Corp. */
+
+/**
+ * @file gpio.h
+ *
+ * @brief Public interface of the EC GPIO device driver
+ *
+ */
+
+#ifndef __EC_GPIO_H_
+#define __EC_GPIO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EC_GPIO_INPUT 0
+#define EC_GPIO_OUTPUT 1
+#define EC_GPIO_PULLUP_DISABLE 0
+#define EC_GPIO_PULLUP_ENABLE 1
+
+// Sets up a GPIO as output or input.
+// Returns: <0 on error
+int ec_gpio_setup(EcGpioPort port, uint8_t pin,
+ int is_output, int pullup_enable);
+
+// Reads the current value of an input GPIO.
+// Returns: GPIO value (0,1) or <0 on error.
+int ec_gpio_read(EcGpioPort port, uint8_t pin);
+
+// Sets the driving value of an output GPIO. Port should already be set
+// to output mode.
+// Returns: <0 on error
+int ec_gpio_set(EcGpioPort port, uint8_t pin, int val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __EC_GPIO_H_
diff --git a/roms/skiboot/platforms/rhesus/gpio.c b/roms/skiboot/platforms/rhesus/gpio.c
new file mode 100644
index 000000000..2ac36303e
--- /dev/null
+++ b/roms/skiboot/platforms/rhesus/gpio.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: Apache-2.0
+/* Copyright 2013-2014 Google Corp. */
+
+#include <stdint.h>
+#include "ec/config.h"
+#include "ec/gpio.h"
+
+int ec_gpio_setup(EcGpioPort port, uint8_t pin,
+ int is_output, int pullup_enable)
+{
+ uint8_t ddr_reg;
+ if (pin > 7) {
+ return -1;
+ }
+
+ /* Set data direction */
+ ec_outb(EC_GPIO_INDEX,
+ port * EC_GPIO_PORT_SKIP + EC_GPIO_DDR_OFFSET);
+ ddr_reg = ec_inb(EC_GPIO_DATA);
+ if (is_output) {
+ ddr_reg |= (1 << pin);
+ } else {
+ ddr_reg &= ~(1 << pin);
+ }
+ ec_outb(EC_GPIO_DATA, ddr_reg);
+
+ /* Set pullup enable for output GPOs */
+ if (is_output)
+ {
+ uint8_t pup_reg;
+ ec_outb(EC_GPIO_INDEX,
+ port * EC_GPIO_PORT_SKIP + EC_GPIO_PUP_OFFSET);
+ pup_reg = ec_inb(EC_GPIO_DATA);
+ if (pullup_enable) {
+ pup_reg |= (1 << pin);
+ } else {
+ pup_reg &= ~(1 << pin);
+ }
+ ec_outb(EC_GPIO_DATA, pup_reg);
+ }
+
+ return 0;
+}
+
+int ec_gpio_read(EcGpioPort port, uint8_t pin)
+{
+ uint8_t pin_reg;
+ if (pin > 7) {
+ return -1;
+ }
+
+ ec_outb(EC_GPIO_INDEX,
+ port * EC_GPIO_PORT_SKIP + EC_GPIO_PIN_OFFSET);
+ pin_reg = ec_inb(EC_GPIO_DATA);
+ return !!(pin_reg & (1 << pin));
+}
+
+int ec_gpio_set(EcGpioPort port, uint8_t pin, int val)
+{
+ uint8_t data_reg;
+ if (pin > 7) {
+ return -1;
+ }
+
+ ec_outb(EC_GPIO_INDEX,
+ port * EC_GPIO_PORT_SKIP + EC_GPIO_DATA_OFFSET);
+ data_reg = ec_inb(EC_GPIO_DATA);
+ if (val) {
+ data_reg |= (1 << pin);
+ } else {
+ data_reg &= ~(1 << pin);
+ }
+ ec_outb(EC_GPIO_DATA, data_reg);
+ return 0;
+}
diff --git a/roms/skiboot/platforms/rhesus/rhesus.c b/roms/skiboot/platforms/rhesus/rhesus.c
new file mode 100644
index 000000000..125f2c5ff
--- /dev/null
+++ b/roms/skiboot/platforms/rhesus/rhesus.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright 2013-2017 IBM Corp.
+ * Copyright 2014 Google Corp.
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <lpc.h>
+#include <console.h>
+#include <opal.h>
+#include <interrupts.h>
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include <libflash/blocklevel.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
+
+
+/*
+ * 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)
+{
+ struct spi_flash_ctrl *pnor_ctrl;
+ struct blocklevel_device *bl = NULL;
+ 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, &bl, NULL);
+ if (rc) {
+ prerror("PLAT: Failed to open init PNOR driver\n");
+ goto fail;
+ }
+
+ rc = flash_register(bl);
+ if (!rc)
+ return 0;
+
+ fail:
+ if (bl)
+ flash_exit(bl);
+ if (pnor_ctrl)
+ sfc_close(pnor_ctrl);
+
+ return rc;
+}
+
+static void rhesus_init(void)
+{
+ /* Initialize PNOR/NVRAM */
+ rhesus_pnor_init();
+
+ /* Setup UART for direct use by Linux */
+ uart_set_console_policy(UART_CONSOLE_OS);
+}
+
+static void rhesus_dt_fixup_uart(struct dt_node *lpc, bool has_irq)
+{
+ /*
+ * 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
+
+ snprintf(namebuf, sizeof(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");
+
+ /* Expose the external interrupt if supported
+ */
+ if (has_irq) {
+ uint32_t chip_id = dt_get_chip_id(lpc);
+ uint32_t irq = get_psi_interrupt(chip_id) + P8_IRQ_PSI_EXTERNAL;
+ dt_add_property_cells(uart, "interrupts", irq, 1);
+ dt_add_property_cells(uart, "interrupt-parent",
+ get_ics_phandle());
+ }
+}
+
+/*
+ * 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);
+ assert(rtc);
+ 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(bool has_uart_irq)
+{
+ 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, has_uart_irq);
+}
+
+static bool rhesus_probe(void)
+{
+ const char *model;
+ int rev;
+ bool has_uart_irq = false;
+
+ 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(has_uart_irq);
+
+ /* Setup UART and use it as console */
+ uart_init();
+
+ return true;
+}
+
+DECLARE_PLATFORM(rhesus) = {
+ .name = "Rhesus",
+ .probe = rhesus_probe,
+ .init = rhesus_init,
+ .cec_power_down = rhesus_power_down,
+ .cec_reboot = rhesus_reboot,
+};