diff options
-rw-r--r-- | core/pci-slot.c | 1 | ||||
-rw-r--r-- | core/pci.c | 37 | ||||
-rw-r--r-- | include/pci-slot.h | 1 |
3 files changed, 39 insertions, 0 deletions
diff --git a/core/pci-slot.c b/core/pci-slot.c index 8bddc147..71d2769e 100644 --- a/core/pci-slot.c +++ b/core/pci-slot.c @@ -133,6 +133,7 @@ void pci_slot_add_dt_properties(struct pci_slot *slot, dt_add_property_cells(np, "ibm,slot-card-desc", slot->card_desc); dt_add_property_cells(np, "ibm,slot-card-mech", slot->card_mech); dt_add_property_cells(np, "ibm,slot-wired-lanes", slot->wired_lanes); + dt_add_property_cells(np, "ibm,power-limit", slot->power_limit); if (slot->ops.add_properties) slot->ops.add_properties(slot, np); @@ -713,6 +713,37 @@ void pci_remove_bus(struct phb *phb, struct list_head *list) } } +static void pci_set_power_limit(struct pci_device *pd) +{ + uint32_t offset, val; + uint16_t caps; + + offset = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + if (!offset) + return; /* legacy dev */ + + pci_cfg_read16(pd->phb, pd->bdfn, + offset + PCICAP_EXP_CAPABILITY_REG, &caps); + + if (!(caps & PCICAP_EXP_CAP_SLOT)) + return; /* bridge has no slot capabilities */ + if (!pd->slot || !pd->slot->power_limit) + return; + + pci_cfg_read32(pd->phb, pd->bdfn, offset + PCICAP_EXP_SLOTCAP, &val); + + val = SETFIELD(PCICAP_EXP_SLOTCAP_SPLSC, val, 0); /* 1W scale */ + val = SETFIELD(PCICAP_EXP_SLOTCAP_SPLVA, val, pd->slot->power_limit); + + pci_cfg_write32(pd->phb, pd->bdfn, offset + PCICAP_EXP_SLOTCAP, val); + + /* update the cached copy in the slot */ + pd->slot->slot_cap = val; + + PCIDBG(pd->phb, pd->bdfn, "Slot power limit set to %dW\n", + pd->slot->power_limit); +} + /* Perform a recursive scan of the bus at bus_number populating * the list passed as an argument. This also performs the bus * numbering, so it returns the largest bus number that was @@ -777,6 +808,12 @@ uint8_t pci_scan_bus(struct phb *phb, uint8_t bus, uint8_t max_bus, rc->subordinate_bus); } + /* set the power limit for any downstream slots while we're here */ + list_for_each(list, pd, link) { + if (pd->is_bridge) + pci_set_power_limit(pd); + } + /* * We only scan downstream if instructed to do so by the * caller. Typically we avoid the scan when we know the diff --git a/include/pci-slot.h b/include/pci-slot.h index bb66d7c7..cd757535 100644 --- a/include/pci-slot.h +++ b/include/pci-slot.h @@ -169,6 +169,7 @@ struct pci_slot { uint8_t card_desc; uint8_t card_mech; uint8_t wired_lanes; + uint8_t power_limit; /* * PCI slot is driven by state machine with polling function. |