diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/platforms/astbmc | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/platforms/astbmc')
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, +}; |