[RFC,05/12] core/pci-dt-slot: Represent PCIe slots in the devicetree

Message ID 20170801130007.8990-6-oohall@gmail.com
State RFC
Headers show

Commit Message

Oliver Aug. 1, 2017, 1 p.m.
In P9 we get information about the physical PCIe slot topology
through the HDAT. As a rule we never directly consume the HDAT
inside of Skiboot and we always parse and incorporate the data
from HDAT into the Skiboot device tree.

Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
 core/Makefile.inc  |   2 +-
 core/pci-dt-slot.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/pci-slot.h |   7 ++
 3 files changed, 219 insertions(+), 1 deletion(-)
 create mode 100644 core/pci-dt-slot.c

Patch

diff --git a/core/Makefile.inc b/core/Makefile.inc
index c343386cf995..619ab398065d 100644
--- a/core/Makefile.inc
+++ b/core/Makefile.inc
@@ -9,7 +9,7 @@  CORE_OBJS += vpd.o hostservices.o platform.o nvram.o nvram-format.o hmi.o
 CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o
 CORE_OBJS += timer.o i2c.o rtc.o flash.o sensor.o ipmi-opal.o
 CORE_OBJS += flash-subpartition.o bitmap.o buddy.o pci-quirk.o powercap.o psr.o
-CORE_OBJS += vas.o
+CORE_OBJS += vas.o pci-dt-slot.o
 
 ifeq ($(SKIBOOT_GCOV),1)
 CORE_OBJS += gcov-profiling.o
diff --git a/core/pci-dt-slot.c b/core/pci-dt-slot.c
new file mode 100644
index 000000000000..e394d76c0b4a
--- /dev/null
+++ b/core/pci-dt-slot.c
@@ -0,0 +1,211 @@ 
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <skiboot.h>
+#include <device.h>
+
+#include <pci.h>
+#include <pci-cfg.h>
+#include <pci-slot.h>
+#include <ccan/list/list.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt) "DT-SLOT: " fmt
+
+#define PCIDBG(_p, _bdfn, fmt, a...) \
+        prlog(PR_DEBUG, "PHB#%04x:%02x:%02x.%x " fmt,   \
+              (_p)->opal_id,                            \
+              ((_bdfn) >> 8) & 0xff,                    \
+              ((_bdfn) >> 3) & 0x1f, (_bdfn) & 0x7, ## a)
+
+struct dt_node *dt_slots;
+
+static struct dt_node *map_phb_to_slot(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);
+	struct dt_node *slot_node;
+
+	dt_for_each_child(dt_slots, slot_node) {
+		u32 reg[2];
+
+		if (!dt_has_node_property(slot_node, "compatible",
+					"ibm,pcie-root-port"))
+			continue;
+
+		reg[0] = dt_prop_get_cell(slot_node, "reg", 0);
+		reg[1] = dt_prop_get_cell(slot_node, "reg", 1);
+
+		if (reg[0] == chip_id && reg[1] == phb_idx)
+			return slot_node;
+	}
+
+	return NULL;
+}
+
+static struct dt_node *map_downport_to_slot(struct phb *phb,
+					    struct pci_device *pd)
+{
+	struct dt_node *bus_node, *child;
+	struct pci_device *cursor;
+	uint32_t port_dev_id;
+
+	/*
+	 * Downports are a little bit special since we need to figure
+	 * out which PCI device corresponds to which down port in the
+	 * slot map.
+	 *
+	 * XXX: I'm assuming the ordering of port IDs and probed
+	 * PCIe switch downstream devices is the same. We should
+	 * check what we actually get in the HDAT.
+	 */
+
+	list_for_each(&pd->parent->children, cursor, link)
+		if (cursor == pd)
+			break;
+
+	/* the child should always be on the parent's child list */
+	assert(cursor);
+	port_dev_id = (cursor->bdfn & 0xff) >> 3;
+
+	bus_node = map_pci_dev_to_slot(phb, pd->parent);
+	if (bus_node) {
+		dt_for_each_child(bus_node, child)
+			if (dt_prop_get_u32(child, "reg") == port_dev_id)
+				return child;
+	}
+
+	/* unused downport */
+	return NULL;
+}
+
+static struct dt_node *__map_pci_dev_to_slot(struct phb *phb,
+					   struct pci_device *pd)
+{
+	struct dt_node *child, *bus_node, *wildcard= NULL;
+
+	if (!pd || !pd->parent || pd->dev_type == PCIE_TYPE_ROOT_PORT)
+		return map_phb_to_slot(phb);
+
+	if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT)
+		return map_downport_to_slot(phb, pd);
+
+	/*
+	 * For matching against devices always use the 0th function.
+	 * This is necessary since some functions may have a different
+	 * VDID to the base device. e.g. The DMA engines in PLX switches
+	 */
+	if (pd->bdfn & 0x7) {
+		struct pci_device *cursor;
+
+		PCIDBG(phb, pd->bdfn, "mapping fn %x to 0th fn (%x)\n",
+			pd->bdfn, pd->bdfn & (~0x7));
+
+		list_for_each(&pd->parent->children, cursor, link)
+			if ((pd->bdfn & ~0x7) == cursor->bdfn)
+				return map_pci_dev_to_slot(phb, cursor);
+
+		return NULL;
+	}
+
+	/* No slot information for this device. Might be a firmware bug */
+	bus_node = map_pci_dev_to_slot(phb, pd->parent);
+	if (!bus_node)
+		return NULL;
+
+	/*
+	 * If this PCI device is mounted on a card the parent "bus"
+	 * may actually be a slot or builtin.
+	 */
+	if (list_empty(&bus_node->children))
+		return bus_node;
+
+	/* find the device in the parent bus node */
+	dt_for_each_child(bus_node, child) {
+		u32 vdid;
+
+		/* "pluggable" and "builtin" without unit addrs are wildcards */
+		if (!dt_has_node_property(child, "reg", NULL)) {
+			if (wildcard) {
+				prerror("Duplicate wildcard entry! Already have %s, found %s",
+					wildcard->name, child->name);
+				assert(0);
+			}
+
+			wildcard = child;
+			continue;
+		}
+
+		/* NB: the pci_device vdid is did,vid rather than vid,did */
+		vdid = dt_prop_get_cell(child, "reg", 1) << 16 |
+			dt_prop_get_cell(child, "reg", 0);
+
+		if (vdid == pd->vdid)
+			return child;
+	}
+
+	if (!wildcard)
+		PCIDBG(phb, pd->bdfn,
+			"Unable to find a slot for device %.4x:%.4x\n",
+			(pd->vdid & 0xffff0000) >> 16, pd->vdid & 0xffff);
+
+	return wildcard;
+}
+
+struct dt_node *map_pci_dev_to_slot(struct phb *phb, struct pci_device *pd)
+{
+	uint32_t bdfn = pd ? pd->bdfn : 0;
+	struct dt_node *n;
+	char *path;
+
+	if (pd && pd->slot && pd->slot->data)
+		return pd->slot->data;
+
+	PCIDBG(phb, bdfn, "Finding slot\n");
+
+	n = __map_pci_dev_to_slot(phb, pd);
+	if (!n) {
+		PCIDBG(phb, bdfn, "No slot found!\n");
+	} else {
+		path = dt_get_path(n);
+		PCIDBG(phb, bdfn, "Slot found %s\n", path);
+		free(path);
+	}
+
+	return n;
+}
+
+int __print_slot(struct phb *phb, struct pci_device *pd, void *userdata);
+int __print_slot(struct phb *phb, struct pci_device *pd,
+			void __unused *userdata)
+{
+	struct dt_node *node;
+	struct dt_node *pnode;
+	char *c = NULL;
+	u32 phandle = 0;
+
+	if (!pd)
+		return 0;
+
+	node = map_pci_dev_to_slot(phb, pd);
+
+	/* at this point all node associations should be done */
+	if (pd->dn && dt_has_node_property(pd->dn, "ibm,pcie-slot", NULL)) {
+		phandle = dt_prop_get_u32(pd->dn, "ibm,pcie-slot");
+		pnode = dt_find_by_phandle(dt_root, phandle);
+
+		assert(node == pnode);
+	}
+
+	if (node)
+		c = dt_get_path(node);
+
+	PCIDBG(phb, pd->bdfn, "Mapped to slot %s (%x)\n",
+		c ? c : "<null>", phandle);
+
+	free(c);
+
+	return 0;
+}
diff --git a/include/pci-slot.h b/include/pci-slot.h
index 5cc54f837155..c86a21d7f1a0 100644
--- a/include/pci-slot.h
+++ b/include/pci-slot.h
@@ -256,4 +256,11 @@  extern struct pci_slot *pcie_slot_create(struct phb *phb,
 extern void pci_slot_add_dt_properties(struct pci_slot *slot,
 				       struct dt_node *np);
 extern struct pci_slot *pci_slot_find(uint64_t id);
+
+/* DT based slot map */
+
+extern struct dt_node *dt_slots;
+extern struct dt_node *map_pci_dev_to_slot(struct phb *phb,
+		struct pci_device *pd);
+
 #endif /* __PCI_SLOT_H */