[Skiboot] [PATCH 1/3] core/pci: Set slot power limit when supported
Oliver O'Halloran
oohall at gmail.com
Thu Apr 5 16:53:20 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>
---
core/pci-slot.c | 1 +
core/pci.c | 40 ++++++++++++++++++++++++++++++++++++++++
include/pci-slot.h | 1 +
3 files changed, 42 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..6d627a1e4db0 100644
--- a/core/pci.c
+++ b/core/pci.c
@@ -713,6 +713,43 @@ 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) {
+ PCIERR(pd->phb, pd->bdfn, "Legacy dev?\n");
+ return; /* legacy dev */
+ }
+
+ pci_cfg_read16(pd->phb, pd->bdfn,
+ offset + PCICAP_EXP_CAPABILITY_REG, &caps);
+ if (!(caps & PCICAP_EXP_CAP_SLOT)) {
+ PCIERR(pd->phb, pd->bdfn, "slot cap missing?\n");
+ return; /* bridge has no slot capabilities */
+ }
+
+ if (!pd->slot || !pd->slot->power_limit) {
+ PCIERR(pd->phb, pd->bdfn, "slot information missing!!!\n");
+ 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;
+
+ PCIERR(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
@@ -847,6 +884,9 @@ uint8_t pci_scan_bus(struct phb *phb, uint8_t bus, uint8_t max_bus,
/* Clear up bridge resources */
pci_cleanup_bridge(phb, pd);
+ /* set a slot power limit (if we have one) */
+ pci_set_power_limit(pd);
+
/* Configure the bridge. This will enable power to the slot
* if it's currently disabled, lift reset, etc...
*
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