@@ -175,7 +175,16 @@ const struct platform_ocapi generic_ocapi = {
.i2c_offset = { 0x3, 0x1, 0x1 },
.i2c_odl0_data = { 0xFD, 0xFD, 0xFF },
.i2c_odl1_data = { 0xBF, 0xBF, 0xFF },
- .odl_phy_swap = true,
+ .i2c_presence_addr = 0x20,
+ .i2c_presence_odl0 = (1 << 2), /* bottom connector */
+ .i2c_presence_odl1 = (1 << 7), /* top connector */
+ /*
+ * The ZZs we typically use for BML/generic platform tend to
+ * have old planars and presence detection is broken there, so
+ * force presence.
+ */
+ .force_presence = true,
+ .odl_phy_swap = true,
};
static struct bmc_platform generic_bmc = {
@@ -829,6 +829,44 @@ static void reset_ocapi_device(struct npu2_dev *dev)
}
}
+static bool i2c_presence_detect(struct npu2_dev *dev)
+{
+ uint8_t state, data;
+ int rc;
+
+ /*
+ * Opencapi presence detection is done through i2c
+ *
+ * Lagrange platforms (ZZ, Zaius) use the same default mechanism.
+ * Witherspoon will need a specific implementation, TBD.
+ */
+ rc = i2c_request_send(dev->i2c_port_id_ocapi,
+ platform.ocapi->i2c_presence_addr,
+ SMBUS_READ, 0, 1,
+ &state, 1, 120);
+ if (rc) {
+ prlog(PR_ERR, "OCAPI: error detecting link presence: %d\n",
+ rc);
+ return true; /* assume link exists */
+ }
+
+ prlog(PR_DEBUG, "OCAPI: I2C presence detect: 0x%x\n", state);
+
+ switch (dev->index) {
+ case 2:
+ data = platform.ocapi->i2c_presence_odl0;
+ break;
+ case 3:
+ data = platform.ocapi->i2c_presence_odl1;
+ break;
+ default:
+ prlog(PR_ERR, "OCAPI: presence detection on invalid link\n");
+ return true;
+ }
+ /* Presence detect bits are active low */
+ return !(state & data);
+}
+
static int odl_train(uint32_t gcid, uint32_t index, struct npu2_dev *dev)
{
uint64_t reg, config_xscom;
@@ -896,6 +934,20 @@ static int odl_train(uint32_t gcid, uint32_t index, struct npu2_dev *dev)
return OPAL_HARDWARE;
}
+static int64_t npu2_opencapi_get_presence_state(struct pci_slot *slot,
+ uint8_t *val)
+{
+ bool present;
+ struct npu2_dev *dev = phb_to_npu2_dev_ocapi(slot->phb);
+
+ if (platform.ocapi->force_presence)
+ present = true;
+ else
+ present = i2c_presence_detect(dev);
+ *val = present;
+ return OPAL_SUCCESS;
+}
+
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);
@@ -926,9 +978,9 @@ static struct pci_slot *npu2_opencapi_slot_create(struct phb *phb)
return slot;
/* TODO: Figure out other slot functions */
- slot->ops.get_presence_state = NULL;
- slot->ops.get_link_state = npu2_opencapi_get_link_state;
- slot->ops.get_power_state = NULL;
+ slot->ops.get_presence_state = npu2_opencapi_get_presence_state;
+ slot->ops.get_link_state = npu2_opencapi_get_link_state;
+ slot->ops.get_power_state = NULL;
slot->ops.get_attention_state = NULL;
slot->ops.get_latch_state = NULL;
slot->ops.set_power_state = NULL;
@@ -1264,6 +1316,48 @@ static int setup_irq(struct npu2 *p)
#define LINK_TRAINING_RETRIES 5
+static int train_link(int chip_id, struct npu2_dev *dev)
+{
+ bool train;
+ int rc;
+ int retries = LINK_TRAINING_RETRIES;
+
+ if (platform.ocapi->force_presence)
+ train = true;
+ else
+ train = i2c_presence_detect(dev);
+ if (!train) {
+ /*
+ * FIXME: if there's no card on the link, we should consider
+ * powering off the unused lanes to save energy
+ */
+ prlog(PR_INFO, "OCAPI: no card detected on link %d, chip %d\n",
+ dev->index, chip_id);
+ return -1;
+ }
+
+ do {
+ rc = odl_train(chip_id, dev->index, dev);
+ } while (rc != OPAL_SUCCESS && --retries);
+
+ if (rc != OPAL_SUCCESS && retries == 0) {
+ /**
+ * @fwts-label OCAPILinkTrainingFailed
+ * @fwts-advice The OpenCAPI link training procedure failed.
+ * This indicates a hardware or firmware bug. OpenCAPI
+ * functionality will not be available on this link.
+ */
+ prlog(PR_ERR,
+ "OCAPI: Link %d on chip %u failed to train\n",
+ dev->index, chip_id);
+ prlog(PR_ERR, "OCAPI: Final link status: %016llx\n",
+ get_odl_status(chip_id, dev->index));
+ return -1;
+ }
+
+ return 0;
+}
+
static void npu2_opencapi_setup_device(struct dt_node *dn_link, struct npu2 *n,
struct npu2_dev *dev)
{
@@ -1272,7 +1366,6 @@ static void npu2_opencapi_setup_device(struct dt_node *dn_link, struct npu2 *n,
struct pci_slot *slot;
char port_name[17];
uint64_t mm_win[2];
- int retries = LINK_TRAINING_RETRIES;
int rc;
dev_index = dt_prop_get_u32(dn_link, "ibm,npu-link-index");
@@ -1367,27 +1460,11 @@ static void npu2_opencapi_setup_device(struct dt_node *dn_link, struct npu2 *n,
break;
case NPU2_TRAIN_DEFAULT:
- do {
- rc = odl_train(n->chip_id, dev->index, dev);
- } while (rc != OPAL_SUCCESS && --retries);
-
- if (rc != OPAL_SUCCESS && retries == 0) {
- /**
- * @fwts-label OCAPILinkTrainingFailed
- * @fwts-advice The OpenCAPI link training procedure failed.
- * This indicates a hardware or firmware bug. OpenCAPI
- * functionality will not be available on this link.
- */
- prlog(PR_ERR,
- "OCAPI: Link %d on chip %u failed to train\n",
- dev->index, n->chip_id);
- prlog(PR_ERR, "OCAPI: Final link status: %016llx\n",
- get_odl_status(n->chip_id, dev->index));
+ rc = train_link(n->chip_id, dev);
+ if (rc)
goto failed;
- }
otl_enabletx(n->chip_id, n->xscom_base, dev->index);
-
slot = npu2_opencapi_slot_create(&dev->phb_ocapi);
if (!slot) {
/**
@@ -51,6 +51,10 @@ struct platform_ocapi {
uint32_t i2c_offset[3]; /* Offsets on I2C device */
uint8_t i2c_odl0_data[3]; /* Data to reset ODL0 */
uint8_t i2c_odl1_data[3]; /* Data to reset ODL1 */
+ uint8_t i2c_presence_addr; /* I2C address for presence detection */
+ uint8_t i2c_presence_odl0; /* I2C mask for detection on ODL0 */
+ uint8_t i2c_presence_odl1; /* I2C mask for detection on ODL1 */
+ bool force_presence; /* don't use i2c detection */
bool odl_phy_swap; /* Swap ODL1 to use brick 2 rather than
* brick 1 lanes */
};
@@ -24,13 +24,17 @@
#include "astbmc.h"
+
const struct platform_ocapi zaius_ocapi = {
.i2c_engine = 1,
.i2c_port = 4,
.i2c_offset = { 0x3, 0x1, 0x1 },
.i2c_odl0_data = { 0xFD, 0xFD, 0xFF },
.i2c_odl1_data = { 0xBF, 0xBF, 0xFF },
- .odl_phy_swap = true,
+ .i2c_presence_addr = 0x20,
+ .i2c_presence_odl0 = (1 << 2), /* bottom connector */
+ .i2c_presence_odl1 = (1 << 7), /* top connector */
+ .odl_phy_swap = true,
};
#define NPU_BASE 0x5011000
@@ -34,7 +34,15 @@ const struct platform_ocapi zz_ocapi = {
.i2c_offset = { 0x3, 0x1, 0x1 },
.i2c_odl0_data = { 0xFD, 0xFD, 0xFF },
.i2c_odl1_data = { 0xBF, 0xBF, 0xFF },
- .odl_phy_swap = true,
+ .i2c_presence_addr = 0x20,
+ .i2c_presence_odl0 = (1 << 2), /* bottom connector */
+ .i2c_presence_odl1 = (1 << 7), /* top connector */
+ /*
+ * i2c presence detection is broken on ZZ planar < v4 so we
+ * force the presence until all our systems are upgraded
+ */
+ .force_presence = true,
+ .odl_phy_swap = true,
};
static bool zz_probe(void)