aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/npu2-opencapi.c50
-rw-r--r--include/npu2-regs.h2
2 files changed, 33 insertions, 19 deletions
diff --git a/hw/npu2-opencapi.c b/hw/npu2-opencapi.c
index c771fae..da86b46 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 165e0b7..c5096dd 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