summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver O'Halloran <oohall@gmail.com>2018-04-10 17:29:19 +1000
committerStewart Smith <stewart@linux.ibm.com>2018-04-11 17:59:57 -0500
commit778d86bf9e5b9cf61986f6d316707771eed1d409 (patch)
tree16b18a6d01526d99f20a8dc5c083bb872c99d9c2
parentee7bb4b391d564bd56060790c3a6abf9082e39e6 (diff)
downloadtalos-skiboot-778d86bf9e5b9cf61986f6d316707771eed1d409.tar.gz
talos-skiboot-778d86bf9e5b9cf61986f6d316707771eed1d409.zip
core/pci: Set slot power limit when supported
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@gmail.com> Signed-off-by: Stewart Smith <stewart@linux.ibm.com>
-rw-r--r--core/pci-slot.c1
-rw-r--r--core/pci.c37
-rw-r--r--include/pci-slot.h1
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);
diff --git a/core/pci.c b/core/pci.c
index b5a1c62d..7b5edd9a 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 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.
OpenPOWER on IntegriCloud