aboutsummaryrefslogtreecommitdiff
path: root/core/pci-dt-slot.c
diff options
context:
space:
mode:
authorOliver O'Halloran <oohall@gmail.com>2017-09-15 15:40:46 +1000
committerStewart Smith <stewart@linux.vnet.ibm.com>2017-09-15 02:49:09 -0500
commit529d86d02aaf5428ec2c6acd912b6a2b7541457a (patch)
tree70ebb0a395a1aa0cf5a09fd7136a15e35d56672e /core/pci-dt-slot.c
parent0a2710381f34e6b4c03cff1fa76bc1b74f280ecd (diff)
downloadskiboot-529d86d02aaf5428ec2c6acd912b6a2b7541457a.zip
skiboot-529d86d02aaf5428ec2c6acd912b6a2b7541457a.tar.gz
skiboot-529d86d02aaf5428ec2c6acd912b6a2b7541457a.tar.bz2
core/pci-dt-slot: Represent PCIe slots in the devicetree
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> [stewart@linux.vnet.ibm.com: add (C) header] Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'core/pci-dt-slot.c')
-rw-r--r--core/pci-dt-slot.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/core/pci-dt-slot.c b/core/pci-dt-slot.c
new file mode 100644
index 0000000..9a93a74
--- /dev/null
+++ b/core/pci-dt-slot.c
@@ -0,0 +1,230 @@
+/* Copyright 2017 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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;
+
+ if (!dt_slots)
+ dt_slots = dt_find_by_path(dt_root, "/ibm,pcie-slots");
+
+ 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 *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 >> 3) & 0x1f;
+
+ bus_node = map_pci_dev_to_slot(phb, pd->parent);
+ if (!bus_node)
+ return NULL;
+
+ 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;
+}