[Skiboot] [PATCH 12/14] hdata: Parse IOSLOT information
Oliver O'Halloran
oohall at gmail.com
Fri Sep 15 15:40:57 AEST 2017
Add structure definitions that describe the physical PCIe topology of
a system and parse them into the device-tree based PCIe slot
description.
Signed-off-by: Oliver O'Halloran <oohall at gmail.com>
---
hdata/iohub.c | 382 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hdata/spira.h | 88 ++++++++++++++
2 files changed, 470 insertions(+)
diff --git a/hdata/iohub.c b/hdata/iohub.c
index 79a6b07bf6c9..ecfc6deae20d 100644
--- a/hdata/iohub.c
+++ b/hdata/iohub.c
@@ -25,6 +25,7 @@
#include <p7ioc.h>
#include <vpd.h>
#include <inttypes.h>
+#include <string.h>
#include "hdata.h"
@@ -453,6 +454,378 @@ static void io_add_p8_cec_vpd(const struct HDIF_common_hdr *sp_iohubs)
io_get_lx_info(kwvpd, kwvpd_sz, 0, dt_root);
}
+/*
+ * Assumptions:
+ *
+ * a) the IOSLOT index is the hub ID -CHECK
+ *
+ */
+
+static struct dt_node *dt_slots;
+
+static void add_i2c_link(struct dt_node *node, const char *link_name,
+ u32 i2c_link)
+{
+ /* FIXME: Do something not shit */
+ dt_add_property_cells(node, link_name, i2c_link);
+}
+
+/*
+ * the root of the slots node has #address-cells = 2, <hub-index, phb-index>
+ * can we ditch hub-index?
+ */
+
+
+static const struct slot_map_details *find_slot_details(
+ const struct HDIF_common_hdr *ioslot, int entry)
+{
+ const struct slot_map_details *details = NULL;
+ const struct HDIF_array_hdr *arr;
+ unsigned int i;
+
+ arr = HDIF_get_iarray(ioslot, IOSLOT_IDATA_DETAILS, NULL);
+ HDIF_iarray_for_each(arr, i, details)
+ if (be16_to_cpu(details->entry) == entry)
+ break;
+
+ return details;
+}
+
+static void parse_slot_details(struct dt_node *slot,
+ const struct slot_map_details *details)
+{
+ u32 slot_caps;
+
+ /*
+ * generic slot options
+ */
+
+ dt_add_property_cells(slot, "max-power",
+ be16_to_cpu(details->max_power));
+
+ if (details->perst_ctl_type == SLOT_PERST_PHB_OR_SW)
+ dt_add_property(slot, "pci-perst", NULL, 0);
+ else if (details->perst_ctl_type == SLOT_PERST_SW_GPIO)
+ dt_add_property_cells(slot, "gpio-perst", details->perst_gpio);
+
+ if (details->presence_det_type == SLOT_PRESENCE_PCI)
+ dt_add_property(slot, "pci-presence-detect", NULL, 0);
+
+ /*
+ * specific slot capabilities
+ */
+ slot_caps = be32_to_cpu(details->slot_caps);
+
+ if (slot_caps & SLOT_CAP_LSI)
+ dt_add_property(slot, "lsi", NULL, 0);
+
+ if (slot_caps & SLOT_CAP_CAPI) {
+ /* XXX: should we be more specific here?
+ *
+ * Also we should double check that this slot
+ * is a root connected slot.
+ */
+ dt_add_property(slot, "capi", NULL, 0);
+ }
+
+ if (slot_caps & SLOT_CAP_CCARD) {
+ dt_add_property(slot, "cable-card", NULL, 0);
+
+ if (details->presence_det_type == SLOT_PRESENCE_I2C)
+ add_i2c_link(slot, "i2c-presence-detect",
+ be32_to_cpu(details->i2c_cable_card));
+ }
+
+ if (slot_caps & SLOT_CAP_HOTPLUG) {
+ dt_add_property(slot, "hotplug", NULL, 0);
+
+ /*
+ * Power control should only exist when the slot is hotplug
+ * capable
+ */
+ if (details->power_ctrl_type == SLOT_PWR_I2C)
+ add_i2c_link(slot, "i2c-power-ctrl",
+ be32_to_cpu(details->i2c_power_ctl));
+ }
+
+ /*
+ * NB: Additional NVLink specific info is added to this node
+ * when the SMP Link structures are parsed later on.
+ */
+ if (slot_caps & SLOT_CAP_NVLINK)
+ dt_add_property(slot, "nvlink", NULL, 0);
+}
+
+static struct dt_node *find_slot_entry_node(struct dt_node *root, u32 entry_id)
+{
+ struct dt_node *node;
+
+ for (node = dt_first(root); node; node = dt_next(root, node)) {
+ if (!dt_has_node_property(node, DT_PRIVATE "entry_id", NULL))
+ continue;
+
+ if (dt_prop_get_u32(node, DT_PRIVATE "entry_id") == entry_id)
+ return node;
+ }
+
+ return NULL;
+}
+
+/*
+ * The internal HDAT representation of the various types of slot is kinda
+ * dumb, translate it into something more sensible
+ */
+enum slot_types {
+ st_root,
+ st_slot,
+ st_rc_slot,
+ st_sw_upstream,
+ st_sw_downstream,
+ st_builtin
+};
+
+static const char *st_name(enum slot_types type)
+{
+ switch(type) {
+ case st_root: return "root-complex";
+ case st_slot: return "pluggable";
+ case st_rc_slot: return "pluggable"; /* differentiate? */
+ case st_sw_upstream: return "switch-up";
+ case st_sw_downstream: return "switch-down";
+ case st_builtin: return "builtin";
+ }
+
+ return "(none)";
+}
+
+static enum slot_types xlate_type(uint8_t type, u32 features)
+{
+ bool is_slot = features & SLOT_FEAT_SLOT;
+
+ switch (type) {
+ case SLOT_TYPE_ROOT_COMPLEX:
+ return is_slot ? st_rc_slot : st_root;
+ case SLOT_TYPE_BUILTIN:
+ return st_builtin;
+ case SLOT_TYPE_SWITCH_UP:
+ return st_sw_upstream;
+ case SLOT_TYPE_SWITCH_DOWN:
+ return is_slot ? st_slot : st_sw_downstream;
+ }
+
+ return -1; /* shouldn't happen */
+}
+
+static bool is_port(struct dt_node *n)
+{
+ //return dt_node_is_compatible(n, "compatible", "ibm,pcie-port");
+ return dt_node_is_compatible(n, "ibm,pcie-port");
+}
+
+/* this only works inside parse_one_ioslot() */
+#define SM_LOG(level, fmt, ...) \
+ prlog(level, "SLOTMAP: %x:%d:%d " \
+ fmt, /* user input */ \
+ chip_id, entry->phb_index, eid, \
+ ##__VA_ARGS__ /* user args */)
+
+#define SM_ERR(fmt, ...) SM_LOG(PR_ERR, fmt, ##__VA_ARGS__)
+#define SM_DBG(fmt, ...) SM_LOG(PR_DEBUG, fmt, ##__VA_ARGS__)
+
+static void parse_one_slot(const struct slot_map_entry *entry,
+ const struct slot_map_details *details, int chip_id)
+{
+ struct dt_node *node, *parent = NULL;
+ u16 eid, pid, vid, did;
+ u32 flags;
+ int type;
+
+ flags = be32_to_cpu(entry->features);
+ type = xlate_type(entry->type, flags);
+
+ eid = be16_to_cpu(entry->entry_id);
+ pid = be16_to_cpu(entry->parent_id);
+
+ SM_DBG("%s - eid = %d, pid = %d, name = %8s\n",
+ st_name(type), eid, pid,
+ strnlen(entry->name, 8) ? entry->name : "");
+
+ /* empty slot, ignore it */
+ if (eid == 0x0 && pid == 0x0)
+ return;
+
+ if (type != st_root && type != st_rc_slot) {
+ parent = find_slot_entry_node(dt_slots, pid);
+ if (!parent) {
+ SM_ERR("Unable to find node for parent slot (id = %d)\n",
+ pid);
+ return;
+ }
+ }
+
+ switch (type) {
+ case st_root:
+ case st_rc_slot:
+ node = dt_new_2addr(dt_slots, "root-complex",
+ chip_id, entry->phb_index);
+ dt_add_property_cells(node, "reg", chip_id, entry->phb_index);
+ dt_add_property_cells(node, "#address-cells", 2);
+ dt_add_property_cells(node, "#size-cells", 0);
+ dt_add_property_strings(node, "compatible",
+ "ibm,pcie-port", "ibm,pcie-root-port");
+ dt_add_property_cells(node, "ibm,chip-id", chip_id);
+ parent = node;
+
+ /*
+ * The representation of slots attached directly to the
+ * root complex is a bit wierd. If this is just a root
+ * complex then stop here, otherwise fall through to create
+ * the slot node.
+ */
+ if (type == st_root)
+ break;
+
+ /* fallthrough*/
+ case st_sw_upstream:
+ case st_builtin:
+ case st_slot:
+ if (!is_port(parent)) {
+ SM_ERR("%s connected to %s (%d), should be %s or %s!\n",
+ st_name(type), parent->name, pid,
+ st_name(st_root), st_name(st_sw_downstream));
+ return;
+ }
+
+ vid = (be32_to_cpu(entry->vendor_id) & 0xffff);
+ did = (be32_to_cpu(entry->device_id) & 0xffff);
+
+ prlog(PR_DEBUG, "Found %s slot with %x:%x\n",
+ st_name(type), vid, did);
+
+ /* The VID:DID is only meaningful for builtins and switches */
+ if (vid && did) {
+ node = dt_new_2addr(parent, st_name(type), vid, did);
+ dt_add_property_cells(node, "reg", vid, did);
+ } else {
+ /*
+ * If we get no vdid then create a "wildcard" slot
+ * that matches any device
+ */
+ node = dt_new(parent, st_name(type));
+ }
+
+ if (type == st_sw_upstream) {
+ dt_add_property_cells(node, "#address-cells", 1);
+ dt_add_property_cells(node, "#size-cells", 0);
+ dt_add_property_cells(node, "upstream-port",
+ entry->up_port);
+ }
+ break;
+
+ case st_sw_downstream: /* slot connected to switch output */
+ node = dt_new_addr(parent, "down-port", entry->down_port);
+ dt_add_property_strings(node, "compatible",
+ "ibm,pcie-port");
+ dt_add_property_cells(node, "reg", entry->down_port);
+
+ break;
+
+ default:
+ SM_ERR("Unknown slot map type %x\n", entry->type);
+ return;
+ }
+
+ /*
+ * Now add any generic slot map properties.
+ */
+
+ /* private since we don't want hdat stuff leaking */
+ dt_add_property_cells(node, DT_PRIVATE "entry_id", eid);
+
+ if (entry->mrw_slot_id)
+ dt_add_property_cells(node, "mrw-slot-id",
+ be16_to_cpu(entry->mrw_slot_id));
+
+ if (entry->lane_mask)
+ dt_add_property_cells(node, "lane-mask",
+ be16_to_cpu(entry->lane_mask));
+
+ /* what is the difference between this and the lane reverse? */
+ if (entry->lane_reverse)
+ dt_add_property_cells(node, "lanes-reversed",
+ be16_to_cpu(entry->lane_reverse));
+
+ if (strnlen(entry->name, sizeof(entry->name)))
+ dt_add_property_nstr(node, "slot-name",
+ entry->name, sizeof(entry->name));
+ if (entry->type == st_slot || entry->type == st_rc_slot)
+ dt_add_property(node, "ibm,pluggable", NULL, 0);
+
+ if (details)
+ parse_slot_details(node, details);
+}
+
+/*
+ * Under the IOHUB structure we have and idata array describing
+ * the PHBs under each chip. The IOHUB structure also has a child
+ * array called IOSLOT which describes slot map. The i`th element
+ * of the IOSLOT array corresponds to the slot map of the i`th
+ * element of the iohubs idata array.
+ *
+ * Probably.
+ *
+ * Furthermore, arrayarrayarrayarrayarray.
+ */
+
+static struct dt_node *get_slot_node(void)
+{
+ struct dt_node *slots = dt_find_by_name(dt_root, "ibm,pcie-slots");
+
+ if (!slots) {
+ slots = dt_new(dt_root, "ibm,pcie-slots");
+ dt_add_property_cells(slots, "#address-cells", 2);
+ dt_add_property_cells(slots, "#size-cells", 0);
+ }
+
+ return slots;
+}
+
+static void io_parse_slots(const void *sp_iohubs, int hub_id)
+{
+ const struct HDIF_child_ptr *ioslot_arr;
+ const struct HDIF_array_hdr *entry_arr;
+ const struct HDIF_common_hdr *ioslot;
+ const struct slot_map_entry *entry;
+ unsigned int i, count;
+
+ dt_slots = get_slot_node();
+
+ ioslot_arr = HDIF_child_arr(sp_iohubs, CECHUB_CHILD_IOSLOTS);
+ if (!ioslot_arr)
+ return;
+
+ count = be32_to_cpu(ioslot_arr->count); /* should only be 1 */
+ if (!count)
+ return;
+
+ prlog(PR_DEBUG, "CEC: Found slot map for IOHUB %d\n", hub_id);
+ if (count > 1)
+ prerror("CEC: Multiple IOSLOTs found for IO HUB %d\n", hub_id);
+
+ ioslot = HDIF_child(sp_iohubs, ioslot_arr, 0, "IOSLOT");
+ if (!ioslot)
+ return;
+
+ entry_arr = HDIF_get_iarray(ioslot, IOSLOT_IDATA_SLOTMAP, NULL);
+ HDIF_iarray_for_each(entry_arr, i, entry) {
+ const struct slot_map_details *details;
+
+ details = find_slot_details(ioslot,
+ be16_to_cpu(entry->entry_id));
+ parse_one_slot(entry, details, hub_id);
+ }
+}
+
static void io_parse_fru(const void *sp_iohubs)
{
unsigned int i;
@@ -471,6 +844,10 @@ static void io_parse_fru(const void *sp_iohubs)
for (i = 0; i < count; i++) {
const struct cechub_io_hub *hub;
unsigned int size, hub_id;
+ uint32_t chip_id;
+
+ if(i > 0)
+ break;
hub = HDIF_get_iarray_item(sp_iohubs, CECHUB_FRU_IO_HUBS,
i, &size);
@@ -534,6 +911,11 @@ static void io_parse_fru(const void *sp_iohubs)
hub_id);
hn = NULL;
}
+
+ chip_id = pcid_to_chip_id(be32_to_cpu(hub->proc_chip_id));
+
+ /* parse the slot map if we have one */
+ io_parse_slots(sp_iohubs, chip_id);
}
/* On P8, grab the CEC VPD */
diff --git a/hdata/spira.h b/hdata/spira.h
index 0276d4a5f822..8e8c74ed8369 100644
--- a/hdata/spira.h
+++ b/hdata/spira.h
@@ -564,6 +564,7 @@ struct msvpd_hb_reserved_mem {
*/
#define CECHUB_FRU_HDIF_SIG "IO HUB"
#define IOKID_FRU_HDIF_SIG "IO KID"
+#define IOSLOT_FRU_HDIF_SIG "IOSLOT"
/* Idata index 0: FRU ID data
*
@@ -700,6 +701,93 @@ struct cechub_io_hub {
/* Child index 0: IO Daugther Card */
#define CECHUB_CHILD_IO_KIDS 0
+/* Child index 1: PCIe Slot Mapping Information */
+#define CECHUB_CHILD_IOSLOTS 1
+
+#define IOSLOT_IDATA_SLOTMAP 0
+
+struct slot_map_entry {
+ __be16 entry_id;
+ __be16 parent_id;
+ uint8_t phb_index; /* only valid for ROOT and SWITCH_UP */
+
+ uint8_t type;
+#define SLOT_TYPE_ROOT_COMPLEX 0x0
+#define SLOT_TYPE_SWITCH_UP 0x1
+#define SLOT_TYPE_SWITCH_DOWN 0x2
+#define SLOT_TYPE_BUILTIN 0x3
+
+ uint8_t lane_swapped;
+ uint8_t reserved;
+ __be16 lane_mask;
+ __be16 lane_reverse;
+
+ /* what can I do with this? reference something under/vpd/ ? */
+ __be16 slca_idx;
+
+ __be16 mrw_slot_id;
+
+ __be32 features;
+#define SLOT_FEAT_SLOT 0x1
+
+ uint8_t up_port;
+ uint8_t down_port; /* the switch port this device is attached to */
+
+ __be32 vendor_id;
+ __be32 device_id;
+ __be32 sub_vendor_id;
+ __be32 sub_device_id;
+ char name[8];
+} __packed;
+
+#define IOSLOT_IDATA_DETAILS 1
+
+struct slot_map_details {
+ __be16 entry;
+
+ /* Phyp junk, ignore */
+ uint8_t mgc_load_source;
+ uint8_t hddw_order;
+ __be16 mmio_size_32; /* In MB */
+ __be16 mmio_size_64;
+ __be16 dma_size_32;
+ __be16 dma_size_64;
+
+ uint8_t power_ctrl_type; /* slot power control source */
+#define SLOT_PWR_NONE 0x0
+#define SLOT_PWR_I2C 0x1
+
+ uint8_t presence_det_type; /* slot presence detect source */
+#define SLOT_PRESENCE_NONE 0x0
+#define SLOT_PRESENCE_PCI 0x1
+#define SLOT_PRESENCE_I2C 0x2
+
+ uint8_t perst_ctl_type; /* slot PERST source */
+#define SLOT_PERST_NONE 0x0
+#define SLOT_PERST_PHB_OR_SW 0x1
+#define SLOT_PERST_SW_GPIO 0x2
+ uint8_t perst_gpio;
+
+ __be16 max_power; /* in W? */
+
+ __be32 slot_caps;
+#define SLOT_CAP_LSI 0x01 /* phyp junk? */
+#define SLOT_CAP_CAPI 0x02
+#define SLOT_CAP_CCARD 0x04
+#define SLOT_CAP_HOTPLUG 0x08
+#define SLOT_CAP_SRIOV 0x10 /* phyp junk */
+#define SLOT_CAP_ELLOCO 0x20 /* why is this seperate from the nvlink cap? */
+#define SLOT_CAP_NVLINK 0x30
+
+ __be16 reserved1;
+
+ /* I2C Link IDs */
+ __be32 i2c_power_ctl;
+ __be32 i2c_pgood;
+ __be32 i2c_cable_card; /* opencapi presence detect? */
+ __be32 i2c_mex_fpga;
+};
+
/*
* IO KID is a dauther card structure
*/
--
2.9.5
More information about the Skiboot
mailing list