[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