[Skiboot] [PATCH v12 21/23] core/pci: Power off empty hotpluggable slot

Gavin Shan gwshan at linux.vnet.ibm.com
Fri Jun 10 15:03:50 AEST 2016


This powers off the empty hotpluggable slot during PCI enumeration
for two purpose: power saving and initialize the slot to starting
(power-off) state.

Signed-off-by: Gavin Shan <gwshan at linux.vnet.ibm.com>
Reviewed-by: Russell Currey <ruscur at russell.cc>
---
 core/pci.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/core/pci.c b/core/pci.c
index d2ae012..11ca1c3 100644
--- a/core/pci.c
+++ b/core/pci.c
@@ -476,6 +476,54 @@ void pci_remove_bus(struct phb *phb, struct list_head *list)
 	}
 }
 
+/*
+ * Turn off slot's power supply if there are nothing connected for
+ * 2 purposes: power saving obviously and initialize the slot to
+ * to initial power-off state for hotplug.
+ */
+static void pci_slot_power_off(struct phb *phb, struct pci_device *pd)
+{
+	struct pci_slot *slot;
+	int32_t wait = 100;
+	int64_t rc;
+
+	if (!pd || !pd->slot)
+		return;
+
+	slot = pd->slot;
+	if (!slot->pluggable || !slot->ops.set_power_state)
+		return;
+
+	/* Bail if there're something connected */
+	if (!list_empty(&pd->children))
+		return;
+
+	pci_slot_add_flags(slot, PCI_SLOT_FLAG_BOOTUP);
+	rc = slot->ops.set_power_state(slot, PCI_SLOT_POWER_OFF);
+	if (rc == OPAL_SUCCESS) {
+		PCIDBG(phb, pd->bdfn, "Power off hotpluggable slot\n");
+		return;
+	} else if (rc != OPAL_ASYNC_COMPLETION) {
+		pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+		PCINOTICE(phb, pd->bdfn, "Error %lld powering off slot\n", rc);
+		return;
+	}
+
+	do {
+		if (slot->state == PCI_SLOT_STATE_SPOWER_DONE)
+			break;
+
+		check_timers(false);
+		time_wait(10);
+	} while (--wait >= 0);
+
+	pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+	if (wait >= 0)
+		PCIDBG(phb, pd->bdfn, "Power off hotpluggable slot\n");
+	else
+		PCINOTICE(phb, pd->bdfn, "Timeout powering off slot\n");
+}
+
 /* 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
@@ -539,8 +587,12 @@ uint8_t pci_scan_bus(struct phb *phb, uint8_t bus, uint8_t max_bus,
 	 * link is down already, which happens for the top level
 	 * root complex, and avoids a long secondary timeout
 	 */
-	if (!scan_downstream)
+	if (!scan_downstream) {
+		list_for_each(list, pd, link)
+			pci_slot_power_off(phb, pd);
+
 		return bus;
+	}
 
 	next_bus = bus + 1;
 	max_sub = bus;
@@ -626,6 +678,8 @@ uint8_t pci_scan_bus(struct phb *phb, uint8_t bus, uint8_t max_bus,
 		pd->subordinate_bus = max_sub;
 		pci_cfg_write8(phb, pd->bdfn, PCI_CFG_SUBORDINATE_BUS, max_sub);
 		next_bus = max_sub + 1;
+
+		pci_slot_power_off(phb, pd);
 	}
 
 	return max_sub;
-- 
2.1.0



More information about the Skiboot mailing list