diff mbox series

[12/14] hdata: Parse IOSLOT information

Message ID 20170915054059.32109-12-oohall@gmail.com
State Accepted
Headers show
Series [01/14] core/pci-dt-slot: Represent PCIe slots in the devicetree | expand

Commit Message

Oliver O'Halloran Sept. 15, 2017, 5:40 a.m. UTC
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@gmail.com>
---
 hdata/iohub.c | 382 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hdata/spira.h |  88 ++++++++++++++
 2 files changed, 470 insertions(+)
diff mbox series

Patch

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
  */