diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/hdata/fsp.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/hdata/fsp.c')
-rw-r--r-- | roms/skiboot/hdata/fsp.c | 560 |
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; + } + } +} |