[Skiboot] [PATCH 2/3] npu2-opencapi: Detect if link trained in degraded mode

Frederic Barrat fbarrat at linux.ibm.com
Fri Nov 23 19:54:38 AEDT 2018


There's no status readily available to tell the effective link
width. Instead, we have to look at the individual status of each lane,
on the transmit and receive direction. All relevant information is in
the ODL status register.

Signed-off-by: Frederic Barrat <fbarrat at linux.ibm.com>
---
 hw/npu2-opencapi.c  | 50 ++++++++++++++++++++++++++++-----------------
 include/npu2-regs.h |  2 ++
 2 files changed, 33 insertions(+), 19 deletions(-)

diff --git a/hw/npu2-opencapi.c b/hw/npu2-opencapi.c
index c771fae8..da86b46c 100644
--- a/hw/npu2-opencapi.c
+++ b/hw/npu2-opencapi.c
@@ -1013,32 +1013,43 @@ static int64_t npu2_opencapi_get_presence_state(struct pci_slot __unused *slot,
 	return OPAL_SUCCESS;
 }
 
+static enum OpalShpcLinkState get_link_width(uint64_t odl_status)
+{
+	uint64_t tx_lanes, rx_lanes, state;
+
+	/*
+	 * On P9, the 'trained mode' field of the ODL status is
+	 * hard-coded to x8 and is useless for us. We need to look at
+	 * the status of the individual lanes.
+	 * The link trains at x8, x4 or not at all.
+	 */
+	state = GETFIELD(OB_ODL_STATUS_TRAINING_STATE_MACHINE, odl_status);
+	if (state != OCAPI_LINK_STATE_TRAINED)
+		return OPAL_SHPC_LINK_DOWN;
+
+	rx_lanes = GETFIELD(OB_ODL_STATUS_RX_TRAINED_LANES, odl_status);
+	tx_lanes = GETFIELD(OB_ODL_STATUS_TX_TRAINED_LANES, odl_status);
+	if ((rx_lanes != 0xFF) || (tx_lanes != 0xFF))
+		return OPAL_SHPC_LINK_UP_x4;
+	else
+		return OPAL_SHPC_LINK_UP_x8;
+}
+
 static int64_t npu2_opencapi_get_link_state(struct pci_slot *slot, uint8_t *val)
 {
 	struct npu2_dev *dev = phb_to_npu2_dev_ocapi(slot->phb);
 	uint64_t reg;
-	int64_t link_width, training_status, rc = OPAL_SUCCESS;
 
 	reg = get_odl_status(dev->npu->chip_id, dev->brick_index);
-	link_width = GETFIELD(OB_ODL_STATUS_TRAINED_MODE, reg);
-	training_status = GETFIELD(OB_ODL_STATUS_TRAINING_STATE_MACHINE, reg);
-
-	if (training_status != OCAPI_LINK_STATE_TRAINED) {
-		*val = OPAL_SHPC_LINK_DOWN;
-		return OPAL_SUCCESS;
-	}
+	*val = get_link_width(reg);
+	return OPAL_SUCCESS;
+}
 
-	switch (link_width) {
-	case 0b0001:
-		*val = OPAL_SHPC_LINK_UP_x4;
-		break;
-	case 0b0010:
-		*val = OPAL_SHPC_LINK_UP_x8;
-		break;
-	default:
-		rc = OPAL_HARDWARE;
-	}
-	return rc;
+static void check_trained_link(struct npu2_dev *dev, uint64_t odl_status)
+{
+	if (get_link_width(odl_status) != OPAL_SHPC_LINK_UP_x8)
+		OCAPIERR(dev, "Link trained in degraded mode (%016llx)\n",
+			odl_status);
 }
 
 static int64_t npu2_opencapi_retry_state(struct pci_slot *slot,
@@ -1089,6 +1100,7 @@ static int64_t npu2_opencapi_poll_link(struct pci_slot *slot)
 			OCAPI_LINK_STATE_TRAINED) {
 			OCAPIINF(dev, "link trained in %lld ms\n",
 				OCAPI_LINK_TRAINING_TIMEOUT - slot->retries);
+			check_trained_link(dev, reg);
 			pci_slot_set_state(slot, OCAPI_SLOT_LINK_TRAINED);
 			return pci_slot_set_sm_timeout(slot, msecs_to_tb(1));
 		}
diff --git a/include/npu2-regs.h b/include/npu2-regs.h
index 165e0b79..c5096dd1 100644
--- a/include/npu2-regs.h
+++ b/include/npu2-regs.h
@@ -741,6 +741,8 @@ void npu2_scom_write(uint64_t gcid, uint64_t scom_base,
 #define OB3_ODL0_STATUS				0xC01082C
 #define OB3_ODL1_STATUS				0xC01082D
 #define   OB_ODL_STATUS_TRAINED_MODE		PPC_BITMASK(0,3)
+#define   OB_ODL_STATUS_RX_TRAINED_LANES	PPC_BITMASK(16, 23)
+#define   OB_ODL_STATUS_TX_TRAINED_LANES	PPC_BITMASK(24, 31)
 #define   OB_ODL_STATUS_TRAINING_STATE_MACHINE	PPC_BITMASK(49, 51)
 
 #define OB0_ODL0_TRAINING_STATUS		0x901082E
-- 
2.19.1



More information about the Skiboot mailing list