[Skiboot] [PATCH v11 21/23] core/opal: Use PCI slot and new APIs for slot states

Gavin Shan gwshan at linux.vnet.ibm.com
Fri May 20 16:32:23 AEST 2016


The various reset requests are completed by PHB's callbacks. All
of them (except reset on IODA table or error injection) are covered
by PCI slot. opal_pci_poll() faces similar situation.

This reimplements opal_pci_reset() and opal_pci_poll() based on
the callbacks provided by PCI slot instead of PHB. Also, couple of
new APIs are introduced based on the callbacks in PCI slot as below:

   * opal_pci_get_presence_state(): Check if there is adapter presented
     behind the specified PHB or PCI slot.
   * opal_pci_get_power_state(): Returns power supply state (on or off)
     on the specified PHB or PCI slot.
   * opal_pci_set_power_state(): Sets power supply state (on or off)
     on the specified PHB or PCI slot.

Eventually, the definition of unused PHB's callbacks are removed.

Signed-off-by: Gavin Shan <gwshan at linux.vnet.ibm.com>
---
 core/pci-opal.c                                  | 218 ++++++++++++++++++++---
 doc/opal-api/opal-pci-get-power-state-120.txt    |  17 ++
 doc/opal-api/opal-pci-get-presence-state-119.txt |  20 +++
 doc/opal-api/opal-pci-set-power-state-121.txt    |  26 +++
 include/opal-api.h                               |   5 +-
 include/pci.h                                    |  72 --------
 6 files changed, 259 insertions(+), 99 deletions(-)
 create mode 100644 doc/opal-api/opal-pci-get-power-state-120.txt
 create mode 100644 doc/opal-api/opal-pci-get-presence-state-119.txt
 create mode 100644 doc/opal-api/opal-pci-set-power-state-121.txt

diff --git a/core/pci-opal.c b/core/pci-opal.c
index 40eda39..460e39f 100644
--- a/core/pci-opal.c
+++ b/core/pci-opal.c
@@ -18,7 +18,10 @@
 #include <opal-api.h>
 #include <pci.h>
 #include <pci-cfg.h>
+#include <pci-slot.h>
+#include <opal-msg.h>
 #include <timebase.h>
+#include <timer.h>
 
 #define OPAL_PCICFG_ACCESS(op, cb, type)	\
 static int64_t opal_pci_config_##op(uint64_t phb_id,			\
@@ -461,16 +464,15 @@ static int64_t opal_pci_map_pe_dma_window_real(uint64_t phb_id,
 }
 opal_call(OPAL_PCI_MAP_PE_DMA_WINDOW_REAL, opal_pci_map_pe_dma_window_real, 5);
 
-static int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope,
+static int64_t opal_pci_reset(uint64_t id, uint8_t reset_scope,
                               uint8_t assert_state)
 {
-	struct phb *phb = pci_get_phb(phb_id);
+	struct pci_slot *slot = pci_slot_find(id);
+	struct phb *phb = slot ? slot->phb : NULL;
 	int64_t rc = OPAL_SUCCESS;
 
-	if (!phb)
+	if (!slot || !phb)
 		return OPAL_PARAMETER;
-	if (!phb->ops)
-		return OPAL_UNSUPPORTED;
 	if (assert_state != OPAL_ASSERT_RESET &&
 	    assert_state != OPAL_DEASSERT_RESET)
 		return OPAL_PARAMETER;
@@ -479,18 +481,22 @@ static int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope,
 
 	switch(reset_scope) {
 	case OPAL_RESET_PHB_COMPLETE:
-		if (!phb->ops->complete_reset) {
+		/* Complete reset is applicable to PHB slot only */
+		if (!slot->ops.creset || slot->pd) {
 			rc = OPAL_UNSUPPORTED;
 			break;
 		}
 
-		rc = phb->ops->complete_reset(phb, assert_state);
+		if (assert_state != OPAL_ASSERT_RESET)
+			break;
+
+		rc = slot->ops.creset(slot);
 		if (rc < 0)
-			prerror("PHB#%d: Failure on complete reset, rc=%lld\n",
-				phb->opal_id, rc);
+			prlog(PR_ERR, "SLOT-%016llx: Error %lld on complete reset\n",
+			      slot->id, rc);
 		break;
 	case OPAL_RESET_PCI_FUNDAMENTAL:
-		if (!phb->ops->fundamental_reset) {
+		if (!slot->ops.freset) {
 			rc = OPAL_UNSUPPORTED;
 			break;
 		}
@@ -499,13 +505,13 @@ static int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope,
 		if (assert_state != OPAL_ASSERT_RESET)
 			break;
 
-		rc = phb->ops->fundamental_reset(phb);
+		rc = slot->ops.freset(slot);
 		if (rc < 0)
-			prerror("PHB#%d: Failure on fundamental reset, rc=%lld\n",
-				phb->opal_id, rc);
+			prlog(PR_ERR, "SLOT-%016llx: Error %lld on fundamental reset\n",
+			      slot->id, rc);
 		break;
 	case OPAL_RESET_PCI_HOT:
-		if (!phb->ops->hot_reset) {
+		if (!slot->ops.hreset) {
 			rc = OPAL_UNSUPPORTED;
 			break;
 		}
@@ -514,22 +520,34 @@ static int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope,
 		if (assert_state != OPAL_ASSERT_RESET)
 			break;
 
-		rc = phb->ops->hot_reset(phb);
+		rc = slot->ops.hreset(slot);
 		if (rc < 0)
-			prerror("PHB#%d: Failure on hot reset, rc=%lld\n",
-				phb->opal_id, rc);
+			prlog(PR_ERR, "SLOT-%016llx: Error %lld on hot reset\n",
+			      slot->id, rc);
 		break;
 	case OPAL_RESET_PCI_IODA_TABLE:
+		/* It's allowed on PHB slot only */
+		if (slot->pd || !phb->ops || !phb->ops->ioda_reset) {
+			rc = OPAL_UNSUPPORTED;
+			break;
+		}
+
 		if (assert_state != OPAL_ASSERT_RESET)
 			break;
-		if (phb->ops->ioda_reset)
-			phb->ops->ioda_reset(phb, true);
+
+		rc = phb->ops->ioda_reset(phb, true);
 		break;
 	case OPAL_RESET_PHB_ERROR:
+		/* It's allowed on PHB slot only */
+		if (slot->pd || !phb->ops || !phb->ops->papr_errinjct_reset) {
+			rc = OPAL_UNSUPPORTED;
+			break;
+		}
+
 		if (assert_state != OPAL_ASSERT_RESET)
 			break;
-		if (phb->ops->papr_errinjct_reset)
-			phb->ops->papr_errinjct_reset(phb);
+
+		rc = phb->ops->papr_errinjct_reset(phb);
 		break;
 	default:
 		rc = OPAL_UNSUPPORTED;
@@ -560,18 +578,19 @@ static int64_t opal_pci_reinit(uint64_t phb_id,
 }
 opal_call(OPAL_PCI_REINIT, opal_pci_reinit, 3);
 
-static int64_t opal_pci_poll(uint64_t phb_id)
+static int64_t opal_pci_poll(uint64_t id)
 {
-	struct phb *phb = pci_get_phb(phb_id);
+	struct pci_slot *slot = pci_slot_find(id);
+	struct phb *phb = slot ? slot->phb : NULL;
 	int64_t rc;
 
-	if (!phb)
+	if (!slot || !phb)
 		return OPAL_PARAMETER;
-	if (!phb->ops || !phb->ops->poll)
+	if (!slot->ops.poll)
 		return OPAL_UNSUPPORTED;
 
 	phb_lock(phb);
-	rc = phb->ops->poll(phb);
+	rc = slot->ops.poll(slot);
 	phb_unlock(phb);
 
 	/* Return milliseconds for caller to sleep: round up */
@@ -585,6 +604,153 @@ static int64_t opal_pci_poll(uint64_t phb_id)
 }
 opal_call(OPAL_PCI_POLL, opal_pci_poll, 1);
 
+static int64_t opal_pci_get_presence_state(uint64_t id, uint64_t data)
+{
+	struct pci_slot *slot = pci_slot_find(id);
+	struct phb *phb = slot ? slot->phb : NULL;
+	uint8_t *presence = (uint8_t *)data;
+	int64_t rc;
+
+	if (!slot || !phb)
+		return OPAL_PARAMETER;
+	if (!slot->ops.get_presence_state)
+		return OPAL_UNSUPPORTED;
+
+	phb_lock(phb);
+	rc = slot->ops.get_presence_state(slot, presence);
+	phb_unlock(phb);
+
+	return rc;
+}
+opal_call(OPAL_PCI_GET_PRESENCE_STATE, opal_pci_get_presence_state, 2);
+
+static int64_t opal_pci_get_power_state(uint64_t id, uint64_t data)
+{
+	struct pci_slot *slot = pci_slot_find(id);
+	struct phb *phb = slot ? slot->phb : NULL;
+	uint8_t *power_state = (uint8_t *)data;
+	int64_t rc;
+
+	if (!slot || !phb)
+		return OPAL_PARAMETER;
+	if (!slot->ops.get_power_state)
+		return OPAL_UNSUPPORTED;
+
+	phb_lock(phb);
+	rc = slot->ops.get_power_state(slot, power_state);
+	phb_unlock(phb);
+
+	return rc;
+}
+opal_call(OPAL_PCI_GET_POWER_STATE, opal_pci_get_power_state, 2);
+
+static void set_power_timer(struct timer *t __unused, void *data,
+			    uint64_t now __unused)
+{
+	struct pci_slot *slot = data;
+	struct phb *phb = slot->phb;
+	struct pci_device *pd = slot->pd;
+	struct dt_node *dn = pd->dn;
+	uint8_t link;
+
+	switch (slot->state) {
+	case PCI_SLOT_STATE_SPOWER_START:
+		if (slot->retries-- == 0) {
+			pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+			opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+				       slot->async_token, dn->phandle,
+				       slot->power_state, OPAL_BUSY);
+		} else {
+			schedule_timer(&slot->timer, msecs_to_tb(10));
+		}
+
+		break;
+	case PCI_SLOT_STATE_SPOWER_DONE:
+		if (slot->power_state == OPAL_PCI_SLOT_POWER_OFF) {
+			pci_remove_bus(phb, &pd->children);
+			pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+			opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+				       slot->async_token, dn->phandle,
+				       OPAL_PCI_SLOT_POWER_OFF, OPAL_SUCCESS);
+			break;
+		}
+
+		/* Power on */
+		if (slot->ops.get_link_state(slot, &link) != OPAL_SUCCESS)
+			link = 0;
+		if (link) {
+			slot->ops.prepare_link_change(slot, true);
+			pci_scan_bus(phb, pd->secondary_bus,
+				     pd->subordinate_bus,
+				     &pd->children, pd, true);
+			pci_add_device_nodes(phb, &pd->children, dn,
+					     &phb->lstate, 0);
+			pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+			opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+				       slot->async_token, dn->phandle,
+				       OPAL_PCI_SLOT_POWER_ON, OPAL_SUCCESS);
+		} else if (slot->retries-- == 0) {
+			pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+			opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+				       slot->async_token, dn->phandle,
+				       OPAL_PCI_SLOT_POWER_ON, OPAL_BUSY);
+		} else {
+			schedule_timer(&slot->timer, msecs_to_tb(10));
+		}
+
+		break;
+	default:
+		prlog(PR_ERR, "PCI SLOT %016llx: Unexpected state 0x%08x\n",
+		      slot->id, slot->state);
+	}
+}
+
+static int64_t opal_pci_set_power_state(uint64_t async_token,
+					uint64_t id,
+					uint64_t data)
+{
+	struct pci_slot *slot = pci_slot_find(id);
+	struct phb *phb = slot ? slot->phb : NULL;
+	uint8_t *state = (uint8_t *)data;
+	int64_t rc;
+
+	if (!slot || !phb)
+		return OPAL_PARAMETER;
+
+	phb_lock(phb);
+	switch (*state) {
+	case OPAL_PCI_SLOT_POWER_OFF:
+		if (!slot->ops.prepare_link_change ||
+		    !slot->ops.set_power_state)
+			return OPAL_UNSUPPORTED;
+
+		slot->async_token = async_token;
+		slot->ops.prepare_link_change(slot, false);
+		rc = slot->ops.set_power_state(slot, PCI_SLOT_POWER_OFF);
+		break;
+	case OPAL_PCI_SLOT_POWER_ON:
+		if (!slot->ops.set_power_state ||
+		    !slot->ops.get_link_state)
+			return OPAL_UNSUPPORTED;
+
+		slot->async_token = async_token;
+		rc = slot->ops.set_power_state(slot, PCI_SLOT_POWER_ON);
+		break;
+	default:
+		rc = OPAL_PARAMETER;
+	}
+
+	phb_unlock(phb);
+	if (rc == OPAL_ASYNC_COMPLETION) {
+		slot->retries = 500;
+		init_timer(&slot->timer, set_power_timer, slot);
+		schedule_timer(&slot->timer, msecs_to_tb(10));
+	}
+
+	return rc;
+}
+opal_call(OPAL_PCI_SET_POWER_STATE, opal_pci_set_power_state, 3);
+
 static int64_t opal_pci_set_phb_tce_memory(uint64_t phb_id,
 					   uint64_t tce_mem_addr,
 					   uint64_t tce_mem_size)
diff --git a/doc/opal-api/opal-pci-get-power-state-120.txt b/doc/opal-api/opal-pci-get-power-state-120.txt
new file mode 100644
index 0000000..0ad2b58
--- /dev/null
+++ b/doc/opal-api/opal-pci-get-power-state-120.txt
@@ -0,0 +1,17 @@
+OPAL_PCI_GET_POWER_STATE
+---------------------------
+
+Get PCI slot power state
+
+Parameters:
+	uint64_t id: PCI slot ID
+	uint64_t data: memory buffer pointer for power state
+
+Calling:
+
+Retrieve PCI slot's power state. The retrieved power state is stored
+in buffer pointed by @data.
+
+Return Codes:
+
+OPAL_SUCCESS - PCI slot's power state is retrieved successfully
diff --git a/doc/opal-api/opal-pci-get-presence-state-119.txt b/doc/opal-api/opal-pci-get-presence-state-119.txt
new file mode 100644
index 0000000..3cf5159
--- /dev/null
+++ b/doc/opal-api/opal-pci-get-presence-state-119.txt
@@ -0,0 +1,20 @@
+OPAL_PCI_GET_PRESENCE_STATE
+---------------------------
+
+Get PCI slot presence state
+
+Parameters:
+	uint64_t id: PCI slot ID
+	uint64_t data: memory buffer pointer for presence state
+
+Calling:
+
+Retrieve PCI slot's presence state. The detected presence means there are
+adapters inserted to the PCI slot. Otherwise, the PCI slot is regarded as
+an empty one. The typical use is to ensure there are adapters existing
+before probing the PCI slot in PCI hot add path. The retrieved presence
+state is stored in buffer pointed by @data.
+
+Return Codes:
+
+OPAL_SUCCESS - PCI slot's presence state is retrieved successfully
diff --git a/doc/opal-api/opal-pci-set-power-state-121.txt b/doc/opal-api/opal-pci-set-power-state-121.txt
new file mode 100644
index 0000000..ef19aff
--- /dev/null
+++ b/doc/opal-api/opal-pci-set-power-state-121.txt
@@ -0,0 +1,26 @@
+OPAL_PCI_SET_POWER_STATE
+---------------------------
+
+Set PCI slot power state
+
+Parameters:
+	uint64_t id: PCI slot ID
+	uint64_t data: memory buffer pointer for power state
+
+Calling:
+
+Set PCI slot's power state. The power state is stored in buffer pointed
+by @data. The typical use is to hot add or remove adapters behind the
+indicated PCI slot (by @id) in PCI hotplug path.
+
+User will receive an asychronous message after calling the API. The message
+contains the API completion status: event (Power off or on), device node's
+phandle identifying the PCI slot, errcode (e.g. OPAL_SUCCESS). The API returns
+OPAL_ASYNC_COMPLETION for the case.
+
+Return Codes:
+
+OPAL_SUCCESS - PCI hotplug on the slot is completed successfully
+OPAL_ASYNC_COMPLETION - PCI hotplug needs further message to confirm
+OPAL_UNSUPPORTED - PCI hotplug isn't supported on the slot
+OPAL_PARAMETER - Parameter error
diff --git a/include/opal-api.h b/include/opal-api.h
index a81d870..f06613e 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -164,7 +164,10 @@
 #define OPAL_CEC_REBOOT2			116
 #define OPAL_CONSOLE_FLUSH			117
 #define OPAL_GET_DEVICE_TREE			118
-#define OPAL_LAST				118
+#define OPAL_PCI_GET_PRESENCE_STATE		119
+#define OPAL_PCI_GET_POWER_STATE		120
+#define OPAL_PCI_SET_POWER_STATE		121
+#define OPAL_LAST				121
 
 /* Device tree flags */
 
diff --git a/include/pci.h b/include/pci.h
index d9af5a7..f509790 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -300,78 +300,6 @@ struct phb_ops {
 	 */
 	int64_t (*pci_msi_eoi)(struct phb *phb, uint32_t hwirq);
 
-	/*
-	 * Slot control
-	 */
-
-	/* presence_detect - Check for a present device
-	 *
-	 * Immediate return of:
-	 *
-	 * OPAL_SHPC_DEV_NOT_PRESENT = 0,
-	 * OPAL_SHPC_DEV_PRESENT = 1
-	 *
-	 * or a negative OPAL error code
-	 */
-	int64_t (*presence_detect)(struct phb *phb);
-
-	/* link_state - Check link state
-	 *
-	 * Immediate return of:
-	 *
-	 * OPAL_SHPC_LINK_DOWN = 0,
-	 * OPAL_SHPC_LINK_UP_x1 = 1,
-	 * OPAL_SHPC_LINK_UP_x2 = 2,
-	 * OPAL_SHPC_LINK_UP_x4 = 4,
-	 * OPAL_SHPC_LINK_UP_x8 = 8,
-	 * OPAL_SHPC_LINK_UP_x16 = 16,
-	 * OPAL_SHPC_LINK_UP_x32 = 32
-	 *
-	 * or a negative OPAL error code
-	 */
-	int64_t (*link_state)(struct phb *phb);
-
-	/* power_state - Check slot power state
-	 *
-	 * Immediate return of:
-	 *
-	 * OPAL_SLOT_POWER_OFF = 0,
-	 * OPAL_SLOT_POWER_ON = 1,
-	 *
-	 * or a negative OPAL error code
-	 */
-	int64_t (*power_state)(struct phb *phb);
-
-	/* slot_power_off - Start slot power off sequence
-	 *
-	 * Asynchronous function, returns a positive delay
-	 * or a negative error code
-	 */
-	int64_t (*slot_power_off)(struct phb *phb);
-
-	/* slot_power_on - Start slot power on sequence
-	 *
-	 * Asynchronous function, returns a positive delay
-	 * or a negative error code.
-	 */
-	int64_t (*slot_power_on)(struct phb *phb);
-
-	/* PHB power off and on after complete init */
-	int64_t (*complete_reset)(struct phb *phb, uint8_t assert);
-
-	/* hot_reset - Hot Reset sequence */
-	int64_t (*hot_reset)(struct phb *phb);
-
-	/* Fundamental reset */
-	int64_t (*fundamental_reset)(struct phb *phb);
-
-	/* poll - Poll and advance asynchronous operations
-	 *
-	 * Returns a positive delay, 0 for success or a
-	 * negative OPAL error code
-	 */
-	int64_t (*poll)(struct phb *phb);
-
 	/* Put phb in capi mode or pcie mode */
 	int64_t (*set_capi_mode)(struct phb *phb, uint64_t mode, uint64_t pe_number);
 
-- 
2.1.0



More information about the Skiboot mailing list