[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