aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/pci-dt-slot.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/core/pci-dt-slot.c')
-rw-r--r--roms/skiboot/core/pci-dt-slot.c212
1 files changed, 212 insertions, 0 deletions
diff --git a/roms/skiboot/core/pci-dt-slot.c b/roms/skiboot/core/pci-dt-slot.c
new file mode 100644
index 000000000..2441bf940
--- /dev/null
+++ b/roms/skiboot/core/pci-dt-slot.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * PCI slots in the device tree.
+ *
+ * Copyright 2017-2018 IBM Corp.
+ */
+
+#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
+
+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;
+
+ if (!dt_slots)
+ dt_slots = dt_find_by_path(dt_root, "/ibm,pcie-slots");
+
+ if (!dt_slots)
+ return NULL;
+
+ dt_for_each_child(dt_slots, slot_node) {
+ u32 reg[2];
+
+ if (!dt_node_is_compatible(slot_node, "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 *find_devfn(struct dt_node *bus, uint32_t bdfn)
+{
+ uint32_t port_dev_id = PCI_DEV(bdfn);
+ struct dt_node *child;
+
+ dt_for_each_child(bus, child)
+ if (dt_prop_get_u32_def(child, "reg", ~0u) == port_dev_id)
+ return child;
+
+ return NULL;
+}
+
+/* Looks for a device device under this slot. */
+static struct dt_node *find_dev_under_slot(struct dt_node *slot,
+ struct pci_device *pd)
+{
+ struct dt_node *child, *wildcard = NULL;
+
+ /* find the device in the parent bus node */
+ dt_for_each_child(slot, 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);
+
+ 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(pd->phb, pd->bdfn,
+ "Unable to find a slot for device %.4x:%.4x\n",
+ (pd->vdid & 0xffff0000) >> 16, pd->vdid & 0xffff);
+
+ return wildcard;
+}
+
+/*
+ * If the `pd` is a bridge this returns a node with a compatible of
+ * ibm,pcie-port to indicate it's a "slot node".
+ */
+static struct dt_node *find_node_for_dev(struct phb *phb,
+ struct pci_device *pd)
+{
+ struct dt_node *sw_slot, *sw_up;
+
+ assert(pd);
+
+ if (pd->slot && pd->slot->data)
+ return pd->slot->data;
+
+ /*
+ * Example DT:
+ * /root-complex@8,5/switch-up@10b5,8725/down-port@4
+ */
+ switch (pd->dev_type) {
+ case PCIE_TYPE_ROOT_PORT: // find the root-complex@<chip>,<phb> node
+ return map_phb_to_slot(phb);
+
+ case PCIE_TYPE_SWITCH_DNPORT: // grab the down-port@<devfn>
+ /*
+ * Walk up the topology to find the slot that contains
+ * the switch upstream port is connected to. In the example
+ * this would be the root-complex@8,5 node.
+ */
+ sw_slot = find_node_for_dev(phb, pd->parent->parent);
+ if (!sw_slot)
+ return NULL;
+
+ /* find the per-device node for this switch */
+ sw_up = find_dev_under_slot(sw_slot, pd->parent);
+ if (!sw_up)
+ return NULL;
+
+ /* find this down port */
+ return find_devfn(sw_up, pd->bdfn);
+
+ default:
+ PCIDBG(phb, pd->bdfn,
+ "Trying to find a slot for non-pcie bridge type %d\n",
+ pd->dev_type);
+ assert(0);
+ }
+
+ return NULL;
+}
+
+struct dt_node *map_pci_dev_to_slot(struct phb *phb, struct pci_device *pd)
+{
+ struct dt_node *n;
+ char *path;
+
+ assert(pd);
+
+ /*
+ * Having a slot only makes sense for root and switch downstream ports.
+ * We don't care about PCI-X.
+ */
+ if (pd->dev_type != PCIE_TYPE_SWITCH_DNPORT &&
+ pd->dev_type != PCIE_TYPE_ROOT_PORT)
+ return NULL;
+
+ PCIDBG(phb, pd->bdfn, "Finding slot\n");
+
+ n = find_node_for_dev(phb, pd);
+ if (!n) {
+ PCIDBG(phb, pd->bdfn, "No slot found!\n");
+ } else {
+ path = dt_get_path(n);
+ PCIDBG(phb, pd->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;
+}