aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/platforms/astbmc
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/platforms/astbmc
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/platforms/astbmc')
-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
24 files changed, 5742 insertions, 0 deletions
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,
+};