[Skiboot] [PATCH v2 1/3] core/pci: Set slot power limit when supported
Oliver O'Halloran
oohall at gmail.com
Tue Apr 10 17:29:19 AEST 2018
The PCIe slot capability can be implemented in a root or switch
downstream port to set the maximum power a card is allowed to draw
from the system. This patch adds support for setting the power limit
when the platform has defined one.
Signed-off-by: Oliver O'Halloran <oohall at gmail.com>
---
v2: The limit was not being applied to empty slots since it was
done while scanning the downstream port. If a port has a training
failure then we skip trying to probe downstream of the device so
the limit would never be applied to non-populated slots.
---
core/pci-slot.c | 1 +
core/pci.c | 37 +++++++++++++++++++++++++++++++++++++
include/pci-slot.h | 1 +
3 files changed, 39 insertions(+)
diff --git a/core/pci-slot.c b/core/pci-slot.c
index 8bddc147e9d2..71d2769e710b 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);
diff --git a/core/pci.c b/core/pci.c
index 3fc4854a3ed8..00f474d45ce6 100644
--- a/core/pci.c
+++ b/core/pci.c
@@ -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 bb66d7c7f888..cd757535a470 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.
--
2.9.5
More information about the Skiboot
mailing list