[Skiboot] [PATCH v12 22/23] core/opal: Support PCI slot and new APIs

Gavin Shan gwshan at linux.vnet.ibm.com
Fri Jun 10 15:03:51 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. Besides, the state can be (offline
     or online) without changing the PCI slot's power state.

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

Signed-off-by: Gavin Shan <gwshan at linux.vnet.ibm.com>
Reviewed-by: Andrew Donnellan <andrew.donnellan at au1.ibm.com>
Reviewed-by: Russell Currey <ruscur at russell.cc>
---
 core/pci-opal.c                                  | 235 ++++++++++++++++++++---
 doc/opal-api/opal-pci-get-power-state-120.txt    |  19 ++
 doc/opal-api/opal-pci-get-presence-state-119.txt |  22 +++
 doc/opal-api/opal-pci-set-power-state-121.txt    |  36 ++++
 include/opal-api.h                               |   9 +-
 include/pci.h                                    |  72 -------
 6 files changed, 293 insertions(+), 100 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..c0f399c 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,170 @@ 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;
+	struct pci_device *pd = slot ? slot->pd : 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;
+	case OPAL_PCI_SLOT_OFFLINE:
+		if (!pd)
+			return OPAL_PARAMETER;
+
+		pci_remove_bus(phb, &pd->children);
+		rc = OPAL_SUCCESS;
+		break;
+	case OPAL_PCI_SLOT_ONLINE:
+		if (!pd)
+			return OPAL_PARAMETER;
+		pci_scan_bus(phb, pd->secondary_bus, pd->subordinate_bus,
+			     &pd->children, pd, true);
+		pci_add_device_nodes(phb, &pd->children, pd->dn,
+				     &phb->lstate, 0);
+		rc = OPAL_SUCCESS;
+		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..420cf8d
--- /dev/null
+++ b/doc/opal-api/opal-pci-get-power-state-120.txt
@@ -0,0 +1,19 @@
+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
+OPAL_PARAMETER - The indicated PCI slot isn't found
+OPAL_UNSUPPORTED - Power state retrieval not supported on the PCI slot
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..f3fbd83
--- /dev/null
+++ b/doc/opal-api/opal-pci-get-presence-state-119.txt
@@ -0,0 +1,22 @@
+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
+OPAL_PARAMETER - The indicated PCI slot isn't found
+OPAL_UNSUPPORTED - Presence retrieval not supported on the PCI slot
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..92da235
--- /dev/null
+++ b/doc/opal-api/opal-pci-set-power-state-121.txt
@@ -0,0 +1,36 @@
+OPAL_PCI_SET_POWER_STATE
+---------------------------
+
+Set PCI slot power state
+
+Parameters:
+	uint64_t async_token: Token of asynchronous message to be sent
+                 on completion of OPAL_PCI_SLOT_POWER_{OFF, ON}. It is
+                 ignored when @data is OPAL_PCI_SLOT_{OFFLINE, ONLINE}.
+	uint64_t id: PCI slot ID
+	uint64_t data: memory buffer pointer for the power state which
+                 can be one of OPAL_PCI_SLOT_POWER_{OFF, ON, OFFLINE, ONLINE}.
+
+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.
+
+The states OPAL_PCI_SLOT_OFFLINE and OPAL_PCI_SLOT_ONLINE are used for removing
+or adding devices behind the slot. The device nodes in the device tree are
+removed or added accordingly, without actually changing the slot's power state.
+The API call will return OPAL_SUCCESS immediately and no further asynchronous
+message will be sent.
+
+Return Codes:
+
+OPAL_SUCCESS - PCI hotplug on the slot is completed successfully
+OPAL_ASYNC_COMPLETION - PCI hotplug needs further message to confirm
+OPAL_PARAMETER - The indicated PCI slot isn't found
+OPAL_UNSUPPORTED - Setting power state not supported on the PCI slot
diff --git a/include/opal-api.h b/include/opal-api.h
index 2b8bf57..08a7a43 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 */
 
@@ -383,7 +386,9 @@ enum OpalPciSlotPresence {
 
 enum OpalPciSlotPower {
 	OPAL_PCI_SLOT_POWER_OFF	= 0,
-	OPAL_PCI_SLOT_POWER_ON	= 1
+	OPAL_PCI_SLOT_POWER_ON	= 1,
+	OPAL_PCI_SLOT_OFFLINE	= 2,
+	OPAL_PCI_SLOT_ONLINE	= 3
 };
 
 enum OpalSlotLedType {
diff --git a/include/pci.h b/include/pci.h
index ce46889..83c9683 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -294,78 +294,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