aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/hdata/fsp.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/hdata/fsp.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/hdata/fsp.c')
-rw-r--r--roms/skiboot/hdata/fsp.c560
1 files changed, 560 insertions, 0 deletions
diff --git a/roms/skiboot/hdata/fsp.c b/roms/skiboot/hdata/fsp.c
new file mode 100644
index 000000000..30cda53f6
--- /dev/null
+++ b/roms/skiboot/hdata/fsp.c
@@ -0,0 +1,560 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2017 IBM Corp. */
+
+#include <device.h>
+#include "spira.h"
+#include <cpu.h>
+#include <vpd.h>
+#include <ccan/str/str.h>
+#include <interrupts.h>
+#include <inttypes.h>
+#include <phys-map.h>
+#include <chip.h>
+#include <ipmi.h>
+
+#include "hdata.h"
+
+enum sp_type {
+ SP_BAD = 0,
+ SP_UNKNOWN,
+ SP_FSP,
+ SP_BMC,
+};
+
+static const char * const sp_names[] = {
+ "Broken", "Unknown", "FSP", "BMC",
+};
+
+static enum sp_type find_service_proc_type(const struct HDIF_common_hdr *spss,
+ int index)
+{
+ const struct spss_sp_impl *sp_impl;
+ int hw_ver, sw_ver, flags;
+ enum sp_type sp_type;
+ bool functional, installed;
+
+ /* Find an check the SP Implementation structure */
+ sp_impl = HDIF_get_idata(spss, SPSS_IDATA_SP_IMPL, NULL);
+ if (!CHECK_SPPTR(sp_impl)) {
+ prerror("SP #%d: SPSS/SP_Implementation not found !\n", index);
+ return SP_BAD;
+ }
+
+ hw_ver = be16_to_cpu(sp_impl->hw_version);
+ sw_ver = be16_to_cpu(sp_impl->sw_version);
+ flags = be16_to_cpu(sp_impl->func_flags);
+
+ switch (hw_ver) {
+ case 0x1:
+ case 0x2: /* We only support FSP2 */
+ sp_type = SP_FSP;
+ break;
+ case 0x3:
+ sp_type = SP_BMC;
+ break;
+ default:
+ sp_type = SP_UNKNOWN;
+ }
+
+ if (sp_type == SP_UNKNOWN)
+ return SP_UNKNOWN;
+
+ installed = !!(flags & SPSS_SP_IMPL_FLAGS_INSTALLED);
+ functional = !!(flags & SPSS_SP_IMPL_FLAGS_FUNCTIONAL);
+
+ if (!installed || !functional) {
+ prerror("%s #%d not usable: %sinstalled, %sfunctional\n",
+ sp_names[sp_type], index,
+ installed ? "" : "not ",
+ functional ? "" : "not ");
+
+ return SP_BAD;
+ }
+
+ prlog(PR_INFO, "%s #%d: HW version %d, SW version %d, chip DD%d.%d\n",
+ sp_names[sp_type], index, hw_ver, sw_ver,
+ sp_impl->chip_version >> 4,
+ sp_impl->chip_version & 0xf);
+
+ return sp_type;
+}
+
+/*
+ * Note on DT representation of the PSI links and FSPs:
+ *
+ * We create a XSCOM node for each PSI host bridge(one per chip),
+ *
+ * This is done in spira.c
+ *
+ * We do not create the /psi MMIO variant at this stage, it will
+ * be added by the psi driver in skiboot.
+ *
+ * We do not put the FSP(s) as children of these. Instead, we create
+ * a top-level /fsps node with the FSPs as children.
+ *
+ * Each FSP then has a "links" property which is an array of chip IDs
+ */
+
+static struct dt_node *fsp_create_node(const void *spss, int i,
+ struct dt_node *parent)
+{
+ const struct spss_sp_impl *sp_impl;
+ struct dt_node *node;
+
+ sp_impl = HDIF_get_idata(spss, SPSS_IDATA_SP_IMPL, NULL);
+
+ node = dt_new_addr(parent, "fsp", i);
+ assert(node);
+
+ dt_add_property_cells(node, "reg", i);
+
+ if (be16_to_cpu(sp_impl->hw_version) == 1) {
+ dt_add_property_strings(node, "compatible", "ibm,fsp",
+ "ibm,fsp1");
+ /* Offset into the FSP MMIO space where the mailbox
+ * registers are */
+ /* seen in the FSP1 spec */
+ dt_add_property_cells(node, "reg-offset", 0xb0016000);
+ } else if (be16_to_cpu(sp_impl->hw_version) == 2) {
+ dt_add_property_strings(node, "compatible", "ibm,fsp",
+ "ibm,fsp2");
+ dt_add_property_cells(node, "reg-offset", 0xb0011000);
+ }
+ dt_add_property_cells(node, "hw-version", be16_to_cpu(sp_impl->hw_version));
+ dt_add_property_cells(node, "sw-version", be16_to_cpu(sp_impl->sw_version));
+
+ if (be16_to_cpu(sp_impl->func_flags) & SPSS_SP_IMPL_FLAGS_PRIMARY)
+ dt_add_property(node, "primary", NULL, 0);
+
+ return node;
+}
+
+static uint32_t fsp_create_link(const struct spss_iopath *iopath, int index,
+ int fsp_index)
+{
+ struct dt_node *node;
+ const char *ststr;
+ bool current = false;
+ bool working = false;
+ uint32_t chip_id;
+
+ switch(be16_to_cpu(iopath->psi.link_status)) {
+ case SPSS_IO_PATH_PSI_LINK_BAD_FRU:
+ ststr = "Broken";
+ break;
+ case SPSS_IO_PATH_PSI_LINK_CURRENT:
+ ststr = "Active";
+ current = working = true;
+ break;
+ case SPSS_IO_PATH_PSI_LINK_BACKUP:
+ ststr = "Backup";
+ working = true;
+ break;
+ default:
+ ststr = "Unknown";
+ }
+ prlog(PR_DEBUG, "FSP #%d: IO PATH %d is %s PSI Link, GXHB at %" PRIx64 "\n",
+ fsp_index, index, ststr, be64_to_cpu(iopath->psi.gxhb_base));
+
+ chip_id = pcid_to_chip_id(be32_to_cpu(iopath->psi.proc_chip_id));
+ node = dt_find_compatible_node_on_chip(dt_root, NULL, "ibm,psihb-x",
+ chip_id);
+ if (!node) {
+ prerror("FSP #%d: Can't find psihb node for link %d\n",
+ fsp_index, index);
+ } else {
+ if (current)
+ dt_add_property(node, "boot-link", NULL, 0);
+ dt_add_property_strings(node, "status", working ? "ok" : "bad");
+ }
+
+ return chip_id;
+}
+
+static void fsp_create_links(const void *spss, int index,
+ struct dt_node *fsp_node)
+{
+ __be32 *links = NULL;
+ unsigned int i, lp, lcount = 0;
+ int count;
+
+ count = HDIF_get_iarray_size(spss, SPSS_IDATA_SP_IOPATH);
+ if (count < 0) {
+ prerror("FSP #%d: Can't find IO PATH array size !\n", index);
+ return;
+ }
+ prlog(PR_DEBUG, "FSP #%d: Found %d IO PATH\n", index, count);
+
+ /* Iterate all links */
+ for (i = 0; i < count; i++) {
+ const struct spss_iopath *iopath;
+ unsigned int iopath_sz;
+ uint32_t chip;
+
+ iopath = HDIF_get_iarray_item(spss, SPSS_IDATA_SP_IOPATH,
+ i, &iopath_sz);
+ if (!CHECK_SPPTR(iopath)) {
+ prerror("FSP #%d: Can't find IO PATH %d\n", index, i);
+ break;
+ }
+ if (be16_to_cpu(iopath->iopath_type) != SPSS_IOPATH_TYPE_PSI) {
+ prerror("FSP #%d: Unsupported IO PATH %d type 0x%04x\n",
+ index, i, iopath->iopath_type);
+ continue;
+ }
+
+ chip = fsp_create_link(iopath, i, index);
+ lp = lcount++;
+ links = realloc(links, 4 * lcount);
+ links[lp] = cpu_to_be32(chip);
+ }
+ if (links)
+ dt_add_property(fsp_node, "ibm,psi-links", links, lcount * 4);
+
+ free(links);
+}
+
+static struct dt_node *add_lpc_io_node(struct dt_node *parent,
+ const char *name, u32 offset, u32 size)
+{
+ struct dt_node *n;
+ char buffer[32];
+
+ /*
+ * LPC bus addresses have strange DT names, they have the
+ * Bus address space embedded into the unit address e.g.
+ * serial@i3f8 - refers to offset 0x3f8 in the IO space
+ */
+
+ snprintf(buffer, sizeof(buffer), "%s@i%x", name, offset);
+ n = dt_new(parent, buffer);
+ assert(n);
+
+ /* first address cell of 1 indicates the LPC IO space */
+ dt_add_property_cells(n, "reg", 1, offset, size);
+
+ return n;
+}
+
+static void add_uart(const struct spss_iopath *iopath, struct dt_node *lpc)
+{
+ struct dt_node *serial;
+ u64 base;
+
+ /* XXX: The spec says this is supposed to be a MMIO address.
+ * However, in practice we get an LPC IO Space offset.
+ */
+ base = be64_to_cpu(iopath->lpc.uart_base);
+
+ serial = add_lpc_io_node(lpc, "serial", base,
+ be32_to_cpu(iopath->lpc.uart_size));
+
+ dt_add_property_string(serial, "compatible", "ns16550");
+
+ dt_add_property_cells(serial, "current-speed",
+ be32_to_cpu(iopath->lpc.uart_baud));
+ dt_add_property_cells(serial, "clock-frequency",
+ be32_to_cpu(iopath->lpc.uart_clk));
+ dt_add_property_cells(serial, "interrupts",
+ iopath->lpc.uart_int_number);
+ dt_add_property_string(serial, "device_type", "serial");
+
+
+ prlog(PR_DEBUG, "LPC UART: base addr = %#" PRIx64" (%#" PRIx64 ") size = %#x clk = %u, baud = %u\n",
+ be64_to_cpu(iopath->lpc.uart_base),
+ base,
+ be32_to_cpu(iopath->lpc.uart_size),
+ be32_to_cpu(iopath->lpc.uart_clk),
+ be32_to_cpu(iopath->lpc.uart_baud));
+}
+
+static void add_chip_id_to_sensors(struct dt_node *sensor_node, uint32_t slca_index)
+{
+ unsigned int i;
+ const void *hdif;
+ const struct slca_entry *slca;
+ const struct spira_fru_id *fru_id;
+ const struct sppcrd_chip_info *cinfo;
+
+ slca = slca_get_entry(slca_index);
+ if (slca == NULL) {
+ prlog(PR_WARNING, "SENSORS: Invalid slca index\n");
+ return;
+ }
+
+ for_each_ntuple_idx(&spira.ntuples.proc_chip, hdif, i, SPPCRD_HDIF_SIG) {
+ fru_id = HDIF_get_idata(hdif, SPPCRD_IDATA_FRU_ID, NULL);
+ if (!fru_id)
+ return;
+
+ if (fru_id->rsrc_id != slca->rsrc_id)
+ continue;
+
+ cinfo = HDIF_get_idata(hdif, SPPCRD_IDATA_CHIP_INFO, NULL);
+ if (!CHECK_SPPTR(cinfo)) {
+ prlog(PR_ERR, "SENSORS: Bad ChipID data %d\n", i);
+ return;
+ }
+
+ dt_add_property_cells(sensor_node,
+ "ibm,chip-id", get_xscom_id(cinfo));
+ return;
+ }
+}
+
+static void add_ipmi_sensors(struct dt_node *bmc_node)
+{
+ int i;
+ const struct HDIF_common_hdr *hdif_sensor;
+ const struct ipmi_sensors *ipmi_sensors;
+ struct dt_node *sensors_node, *sensor_node;
+
+ hdif_sensor = get_hdif(&spira.ntuples.ipmi_sensor, IPMI_SENSORS_HDIF_SIG);
+ if (!hdif_sensor) {
+ prlog(PR_DEBUG, "SENSORS: Missing IPMI sensors mappings tuple\n");
+ return;
+ }
+
+ ipmi_sensors = HDIF_get_idata(hdif_sensor, IPMI_SENSORS_IDATA_SENSORS, NULL);
+ if (!ipmi_sensors) {
+ prlog(PR_DEBUG, "SENSORS: bad data\n");
+ return;
+ }
+
+ sensors_node = dt_new(bmc_node, "sensors");
+ assert(sensors_node);
+
+ dt_add_property_cells(sensors_node, "#address-cells", 1);
+ dt_add_property_cells(sensors_node, "#size-cells", 0);
+
+ for (i = 0; i < be32_to_cpu(ipmi_sensors->count); i++) {
+ if(dt_find_by_name_addr(sensors_node, "sensor",
+ ipmi_sensors->data[i].id)) {
+ prlog(PR_WARNING, "SENSORS: Duplicate sensor ID : %x\n",
+ ipmi_sensors->data[i].id);
+ continue;
+ }
+
+ /* We support only < MAX_IPMI_SENSORS sensors */
+ if (!(ipmi_sensors->data[i].type < MAX_IPMI_SENSORS))
+ continue;
+
+ sensor_node = dt_new_addr(sensors_node, "sensor",
+ ipmi_sensors->data[i].id);
+ assert(sensor_node);
+ dt_add_property_string(sensor_node, "compatible", "ibm,ipmi-sensor");
+ dt_add_property_cells(sensor_node, "reg", ipmi_sensors->data[i].id);
+ dt_add_property_cells(sensor_node, "ipmi-sensor-type",
+ ipmi_sensors->data[i].type);
+
+ add_chip_id_to_sensors(sensor_node,
+ be32_to_cpu(ipmi_sensors->data[i].slca_index));
+ }
+}
+
+static void bmc_create_node(const struct HDIF_common_hdr *sp)
+{
+ struct dt_node *bmc_node;
+ u32 fw_bar, io_bar, mem_bar, internal_bar, mctp_base;
+ const struct spss_iopath *iopath;
+ const struct spss_sp_impl *sp_impl;
+ struct dt_node *lpcm, *lpc, *n;
+ u64 lpcm_base, lpcm_end;
+ uint32_t chip_id;
+ uint32_t topology_idx;
+ int size;
+
+ bmc_node = dt_new(dt_root, "bmc");
+ assert(bmc_node);
+
+ dt_add_property_cells(bmc_node, "#address-cells", 1);
+ dt_add_property_cells(bmc_node, "#size-cells", 0);
+
+ /* Add sensor info under /bmc */
+ if (proc_gen < proc_gen_p10)
+ add_ipmi_sensors(bmc_node);
+
+ sp_impl = HDIF_get_idata(sp, SPSS_IDATA_SP_IMPL, &size);
+ if (CHECK_SPPTR(sp_impl) && (size > 8)) {
+ dt_add_property_strings(bmc_node, "compatible", sp_impl->sp_family);
+ prlog(PR_INFO, "SP Family is %s\n", sp_impl->sp_family);
+ }
+
+ iopath = HDIF_get_iarray_item(sp, SPSS_IDATA_SP_IOPATH, 0, NULL);
+
+ if (be16_to_cpu(iopath->iopath_type) != SPSS_IOPATH_TYPE_LPC) {
+ prerror("BMC: Non-LPC IOPATH, this is probably broken\n");
+ return;
+ }
+
+ /*
+ * For now we only instantiate the LPC node for the LPC that is used
+ * for Host <-> BMC comms. The secondary LPCs can be skipped.
+ */
+ if (be16_to_cpu(iopath->lpc.link_status) != LPC_STATUS_ACTIVE)
+ return;
+
+#define GB (1024ul * 1024ul * 1024ul)
+ /*
+ * convert the hdat chip ID the HW chip id so we get the right
+ * phys map offset
+ */
+ chip_id = pcid_to_chip_id(be32_to_cpu(iopath->lpc.chip_id));
+ topology_idx = pcid_to_topology_idx(be32_to_cpu(iopath->lpc.chip_id));
+
+ __phys_map_get(topology_idx, chip_id, LPC_BUS, 0, &lpcm_base, NULL);
+ lpcm = dt_new_addr(dt_root, "lpcm-opb", lpcm_base);
+ assert(lpcm);
+
+ dt_add_property_cells(lpcm, "#address-cells", 1);
+ dt_add_property_cells(lpcm, "#size-cells", 1);
+ dt_add_property_strings(lpcm, "compatible",
+ "ibm,power9-lpcm-opb", "simple-bus");
+ dt_add_property_u64s(lpcm, "reg", lpcm_base, 0x100000000ul);
+
+ dt_add_property_cells(lpcm, "ibm,chip-id", chip_id);
+
+ /* Setup the ranges for the MMIO LPC */
+ lpcm_end = lpcm_base + 2 * GB;
+ dt_add_property_cells(lpcm, "ranges",
+ 0x00000000, hi32(lpcm_base), lo32(lpcm_base), 2 * GB,
+ 0x80000000, hi32(lpcm_end), lo32(lpcm_end), 2 * GB);
+
+ /*
+ * Despite the name the "BAR" values provided through the HDAT are
+ * the base addresses themselves rather than the BARs
+ */
+ fw_bar = be32_to_cpu(iopath->lpc.firmware_bar);
+ mem_bar = be32_to_cpu(iopath->lpc.memory_bar);
+ io_bar = be32_to_cpu(iopath->lpc.io_bar);
+ internal_bar = be32_to_cpu(iopath->lpc.internal_bar);
+ mctp_base = be32_to_cpu(iopath->lpc.mctp_base);
+
+ prlog(PR_DEBUG, "LPC: IOPATH chip id = %x\n", chip_id);
+ prlog(PR_DEBUG, "LPC: FW BAR = %#x\n", fw_bar);
+ prlog(PR_DEBUG, "LPC: MEM BAR = %#x\n", mem_bar);
+ prlog(PR_DEBUG, "LPC: IO BAR = %#x\n", io_bar);
+ prlog(PR_DEBUG, "LPC: Internal BAR = %#x\n", internal_bar);
+ if (proc_gen >= proc_gen_p10) {
+ /* MCTP is part of FW BAR */
+ prlog(PR_DEBUG, "LPC: MCTP base = %#x\n", mctp_base);
+ }
+
+ /*
+ * The internal address space BAR actually points to the LPC master
+ * registers. So we "fix" it by masking off the low bits.
+ *
+ * XXX: we probably need separate base addresses for all these things
+ */
+ internal_bar &= 0xf0000000;
+
+ /* Add the various internal bus devices */
+ n = dt_new_addr(lpcm, "opb-master", internal_bar + 0x10000);
+ dt_add_property_string(n, "compatible", "ibm,power9-lpcm-opb-master");
+ dt_add_property_cells(n, "reg", internal_bar + 0x10000, 0x60);
+
+ n = dt_new_addr(lpcm, "opb-arbiter", internal_bar + 0x11000);
+ dt_add_property_string(n, "compatible", "ibm,power9-lpcm-opb-arbiter");
+ dt_add_property_cells(n, "reg", internal_bar + 0x11000, 0x8);
+
+ n = dt_new_addr(lpcm, "lpc-controller", internal_bar + 0x12000);
+ dt_add_property_string(n, "compatible", "ibm,power9-lpc-controller");
+ dt_add_property_cells(n, "reg", internal_bar + 0x12000, 0x100);
+
+ /*
+ * FIXME: lpc@0 might not be accurate, but i'm pretty sure
+ * lpc@f0000000 isn't right either.
+ */
+ lpc = dt_new_addr(lpcm, "lpc", 0x0);
+ dt_add_property_cells(lpc, "#address-cells", 2);
+ dt_add_property_cells(lpc, "#size-cells", 1);
+ dt_add_property_strings(lpc, "compatible",
+ "ibm,power9-lpc", "ibm,power8-lpc");
+
+ dt_add_property_cells(lpc, "ranges",
+ 0, 0, mem_bar, 0x10000000, /* MEM space */
+ 1, 0, io_bar, 0x00010000, /* IO space */
+ /* we don't expose the internal space */
+ 3, 0, fw_bar, 0x10000000 /* FW space */
+ );
+
+ add_uart(iopath, lpc);
+
+ /* BT device info isn't currently populated */
+ prlog(PR_DEBUG, "LPC: BT [%#"PRIx64", %#x] sms_int: %u, bmc_int: %u\n",
+ iopath->lpc.bt_base, iopath->lpc.bt_size,
+ iopath->lpc.bt_sms_int_num, iopath->lpc.bt_bmc_response_int_num
+ );
+}
+
+/*
+ * Search for and instanciate BMC nodes. This is mostly the same as fsp_parse()
+ * below, but it can be called earlier since BMCs don't depend on the psihb
+ * nodes being added.
+ */
+void bmc_parse(void)
+{
+ bool found = false;
+ const void *sp;
+ int i;
+
+ sp = get_hdif(&spira.ntuples.sp_subsys, SPSS_HDIF_SIG);
+ if (!sp)
+ return;
+
+ for_each_ntuple_idx(&spira.ntuples.sp_subsys, sp, i, SPSS_HDIF_SIG) {
+ if (find_service_proc_type(sp, i) == SP_BMC) {
+ bmc_create_node(sp);
+ found = true;
+ }
+ }
+
+ if (found)
+ early_uart_init();
+}
+
+void fsp_parse(void)
+{
+ struct dt_node *fsp_root = NULL, *fsp_node;
+ const void *sp;
+ int index;
+
+ /* Find SPSS tuple in SPIRA */
+ sp = get_hdif(&spira.ntuples.sp_subsys, SPSS_HDIF_SIG);
+ if (!sp) {
+ prlog(PR_WARNING, "HDAT: No FSP/BMC found!\n");
+ return;
+ }
+
+ for_each_ntuple_idx(&spira.ntuples.sp_subsys, sp, index, SPSS_HDIF_SIG) {
+ switch (find_service_proc_type(sp, index)) {
+ case SP_FSP:
+ if (!fsp_root) {
+ fsp_root = dt_new(dt_root, "fsps");
+ assert(fsp_root);
+
+ dt_add_property_cells(fsp_root,
+ "#address-cells", 1);
+ dt_add_property_cells(fsp_root,
+ "#size-cells", 0);
+ }
+
+ fsp_node = fsp_create_node(sp, index, fsp_root);
+ if (fsp_node)
+ fsp_create_links(sp, index, fsp_node);
+
+ break;
+
+ case SP_BMC:
+ /* Handled above */
+ break;
+
+ case SP_BAD:
+ break;
+
+ default:
+ prerror("SP #%d: This service processor is not supported\n", index);
+ break;
+ }
+ }
+}