// SPDX-License-Identifier: Apache-2.0 /* Copyright 2015-2018 IBM Corp. */ #include #include #include #include #include #include #include #include "astbmc.h" static const struct slot_table_entry *slot_top_table; void slot_table_init(const struct slot_table_entry *top_table) { slot_top_table = top_table; } static const struct slot_table_entry *match_slot_phb_entry(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); const struct slot_table_entry *ent; if (!slot_top_table) return NULL; for (ent = slot_top_table; ent->etype != st_end; ent++) { if (ent->etype != st_phb) { prerror("SLOT: Bad DEV entry type in table !\n"); continue; } if (ent->location == ST_LOC_PHB(chip_id, phb_idx)) return ent; } return NULL; } static const struct slot_table_entry *match_slot_dev_entry(struct phb *phb, struct pci_device *pd) { const struct slot_table_entry *parent, *ent; uint32_t bdfn; /* Find a parent recursively */ if (pd->parent) parent = match_slot_dev_entry(phb, pd->parent); else { /* No parent, this is a root complex, find the PHB */ parent = match_slot_phb_entry(phb); } /* No parent ? Oops ... */ if (!parent || !parent->children) return NULL; for (ent = parent->children; ent->etype != st_end; ent++) { if (ent->etype == st_phb) { prerror("SLOT: Bad PHB entry type in table !\n"); continue; } /* NPU slots match on device, not function */ if (ent->etype == st_npu_slot) bdfn = pd->bdfn & 0xf8; else bdfn = pd->bdfn & 0xff; if (ent->location == bdfn) return ent; } return NULL; } static void slot_table_add_properties(struct pci_slot *slot, struct dt_node *np) { struct slot_table_entry *ent = slot->data; if (ent) pci_slot_add_loc(slot, np, ent->name); else pci_slot_add_loc(slot, np, NULL); } void slot_table_add_slot_info(struct pci_device *pd, const struct slot_table_entry *ent) { struct pci_slot *slot; if (!ent || !ent->name) { slot = pcie_slot_create_dynamic(pd->phb, pd); if (slot) { slot->ops.add_properties = slot_table_add_properties; slot->pluggable = true; } return; } slot = pcie_slot_create(pd->phb, pd); assert(slot); slot->pluggable = !!(ent->etype == st_pluggable_slot); slot->ops.add_properties = slot_table_add_properties; slot->power_limit = ent->power_limit; slot->data = (void *)ent; } void slot_table_get_slot_info(struct phb *phb, struct pci_device *pd) { const struct slot_table_entry *ent; if (!pd || pd->slot) return; ent = match_slot_dev_entry(phb, pd); slot_table_add_slot_info(pd, ent); } static void dt_slot_add_properties(struct pci_slot *slot, struct dt_node *np) { struct dt_node *slot_np = slot->data; const char *label = NULL; if (slot_np) label = dt_prop_get_def(slot_np, "ibm,slot-label", NULL); pci_slot_add_loc(slot, np, label); } void dt_slot_get_slot_info(struct phb *phb, struct pci_device *pd) { struct dt_node *slot_np; struct pci_slot *slot; const char *name = NULL; uint32_t power_limit = 0; bool pluggable = false; if (!pd || pd->slot) return; slot_np = map_pci_dev_to_slot(phb, pd); if (slot_np) { pluggable = dt_has_node_property(slot_np, "ibm,pluggable", NULL); power_limit = dt_prop_get_u32_def(slot_np, "ibm,power-limit", 0); name = dt_prop_get_def(slot_np, "ibm,slot-label", NULL); } if (!slot_np || !name) { slot = pcie_slot_create_dynamic(phb, pd); if (slot) { slot->ops.add_properties = dt_slot_add_properties; slot->pluggable = true; slot->data = (void *)slot_np; } return; } slot = pcie_slot_create(phb, pd); assert(slot); slot->ops.add_properties = dt_slot_add_properties; slot->pluggable = pluggable; slot->power_limit = power_limit; slot->data = (void *)slot_np; } static int __pci_find_dev_by_location(struct phb *phb, struct pci_device *pd, void *userdata) { uint16_t location = *((uint16_t *)userdata); if (!phb || !pd) return 0; if ((pd->bdfn & 0xff) == location) return 1; return 0; } static struct pci_device *pci_find_dev_by_location(struct phb *phb, uint16_t location) { return pci_walk_dev(phb, NULL, __pci_find_dev_by_location, &location); } static struct phb* get_phb_by_location(uint32_t location) { struct phb *phb = NULL; uint32_t chip_id, phb_idx; for_each_phb(phb) { chip_id = dt_get_chip_id(phb->dt_node); phb_idx = dt_prop_get_u32_def(phb->dt_node, "ibm,phb-index", 0); if (location == ST_LOC_PHB(chip_id, phb_idx)) break; } return phb; } static int check_slot_table(struct phb *phb, const struct slot_table_entry *parent) { const struct slot_table_entry *ent; struct pci_device *dev = NULL; int r = 0; if (parent == NULL) return 0; for (ent = parent; ent->etype != st_end; ent++) { switch (ent->etype) { case st_phb: phb = get_phb_by_location(ent->location); if (!phb) { prlog(PR_ERR, "PCI: PHB %s (%x) not found\n", ent->name, ent->location); r++; } break; case st_pluggable_slot: case st_builtin_dev: if (!phb) break; phb_lock(phb); dev = pci_find_dev_by_location(phb, ent->location); phb_unlock(phb); if (!dev) { prlog(PR_ERR, "PCI: built-in device not found: %s (loc: %x)\n", ent->name, ent->location); r++; } break; case st_end: case st_npu_slot: break; } if (ent->children) r+= check_slot_table(phb, ent->children); } return r; } void check_all_slot_table(void) { if (!slot_top_table) return; prlog(PR_DEBUG, "PCI: Checking slot table against detected devices\n"); check_slot_table(NULL, slot_top_table); }