aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Neuling <mikey@neuling.org>2018-02-22 10:52:18 +1100
committerStewart Smith <stewart@linux.vnet.ibm.com>2018-02-22 00:05:20 -0600
commit737c0ba3d72b8aab05a765a9fc111a48faac0f75 (patch)
treec441f1f220b96a49e7ebeb1e7974a6ab91d5b8c7
parent4769f3932c0b93d9e5f929ac8589cef2e4a7d5d6 (diff)
downloadskiboot-737c0ba3d72b8aab05a765a9fc111a48faac0f75.zip
skiboot-737c0ba3d72b8aab05a765a9fc111a48faac0f75.tar.gz
skiboot-737c0ba3d72b8aab05a765a9fc111a48faac0f75.tar.bz2
phb4: Disable lane eq when retrying some nvidia GEN3 devices
This fixes these nvidia cards training at only GEN2 spends rather than GEN3 by disabling PCIe lane equalisation. Firstly we check if the card is in a whitelist. If it is and the link has not trained optimally, retry with lane equalisation off. We do this on all POWER9 chip revisions since this is a device issue, not a POWER9 chip issue. Signed-off-by: Michael Neuling <mikey@neuling.org> Reviewed-by: Russell Currey <ruscur@russell.cc> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
-rw-r--r--hw/phb4.c54
-rw-r--r--include/phb4-regs.h4
-rw-r--r--include/phb4.h1
3 files changed, 50 insertions, 9 deletions
diff --git a/hw/phb4.c b/hw/phb4.c
index eedddcd..c53898d 100644
--- a/hw/phb4.c
+++ b/hw/phb4.c
@@ -2494,12 +2494,33 @@ static bool phb4_adapter_in_whitelist(uint32_t vdid)
return false;
}
+static struct pci_card_id lane_eq_disable[] = {
+ { 0x10de, 0x17fd }, /* Nvidia GM200GL [Tesla M40] */
+ { 0x10de, 0x1db4 }, /* Nvidia GV100 */
+};
+
+static bool phb4_lane_eq_retry_whitelist(uint32_t vdid)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lane_eq_disable); i++)
+ if ((lane_eq_disable[i].vendor == VENDOR(vdid)) &&
+ (lane_eq_disable[i].device == DEVICE(vdid)))
+ return true;
+ return false;
+}
+
+static void phb4_lane_eq_change(struct phb4 *p, uint32_t vdid)
+{
+ p->lane_eq_en = !phb4_lane_eq_retry_whitelist(vdid);
+}
+
#define min(x,y) ((x) < (y) ? x : y)
-static bool phb4_link_optimal(struct pci_slot *slot)
+static bool phb4_link_optimal(struct pci_slot *slot, uint32_t *vdid)
{
struct phb4 *p = phb_to_phb4(slot->phb);
- uint32_t vdid;
+ uint32_t id;
uint16_t bdfn;
uint8_t trained_speed, phb_speed, dev_speed, target_speed;
uint8_t trained_width, phb_width, dev_width, target_width;
@@ -2516,7 +2537,7 @@ static bool phb4_link_optimal(struct pci_slot *slot)
/* Get device capability */
bdfn = 0x0100; /* bus=1 dev=0 device=0 */
/* Since this is the first access, we need to wait for CRS */
- if (!pci_wait_crs(slot->phb, bdfn , &vdid))
+ if (!pci_wait_crs(slot->phb, bdfn , &id))
return true;
phb4_get_info(slot->phb, bdfn, &dev_speed, &dev_width);
@@ -2526,17 +2547,21 @@ static bool phb4_link_optimal(struct pci_slot *slot)
target_width = min(phb_width, dev_width);
optimal_width = (trained_width >= target_width);
optimal = optimal_width && optimal_speed;
- retry_enabled = phb4_chip_retry_workaround() &&
- phb4_adapter_in_whitelist(vdid);
+ retry_enabled = (phb4_chip_retry_workaround() &&
+ phb4_adapter_in_whitelist(id)) ||
+ phb4_lane_eq_retry_whitelist(id);
- PHBDBG(p, "LINK: Card [%04x:%04x] %s Retry:%s\n", VENDOR(vdid),
- DEVICE(vdid), optimal ? "Optimal" : "Degraded",
+ PHBDBG(p, "LINK: Card [%04x:%04x] %s Retry:%s\n", VENDOR(id),
+ DEVICE(id), optimal ? "Optimal" : "Degraded",
retry_enabled ? "enabled" : "disabled");
PHBDBG(p, "LINK: Speed Train:GEN%i PHB:GEN%i DEV:GEN%i%s\n",
trained_speed, phb_speed, dev_speed, optimal_speed ? "" : " *");
PHBDBG(p, "LINK: Width Train:x%02i PHB:x%02i DEV:x%02i%s\n",
trained_width, phb_width, dev_width, optimal_width ? "" : " *");
+ if (vdid)
+ *vdid = id;
+
if (!retry_enabled)
return true;
@@ -2582,6 +2607,7 @@ static int64_t phb4_poll_link(struct pci_slot *slot)
{
struct phb4 *p = phb_to_phb4(slot->phb);
uint64_t reg;
+ uint32_t vdid;
switch (slot->state) {
case PHB4_SLOT_NORMAL:
@@ -2657,10 +2683,12 @@ static int64_t phb4_poll_link(struct pci_slot *slot)
}
if (reg & PHB_PCIE_DLP_TL_LINKACT) {
PHBDBG(p, "LINK: Link is stable\n");
- if (!phb4_link_optimal(slot)) {
+ if (!phb4_link_optimal(slot, &vdid)) {
PHBDBG(p, "LINK: Link degraded\n");
- if (slot->link_retries)
+ if (slot->link_retries) {
+ phb4_lane_eq_change(p, vdid);
return phb4_retry_state(slot);
+ }
/*
* Link is degraded but no more retries, so
* settle for what we have :-(
@@ -4463,6 +4491,13 @@ static void phb4_init_hw(struct phb4 *p, bool first_init)
be64_to_cpu(p->lane_eq[7]));
}
}
+ if (!p->lane_eq_en) {
+ /* Read modify write and set to 2 bits */
+ PHBDBG(p, "LINK: Disabling Lane EQ\n");
+ val = in_be64(p->regs + PHB_PCIE_DLP_CTL);
+ val |= PHB_PCIE_DLP_CTL_BYPASS_PH2 | PHB_PCIE_DLP_CTL_BYPASS_PH2;
+ out_be64(p->regs + PHB_PCIE_DLP_CTL, val);
+ }
/* Init_14 - Clear link training */
phb4_pcicfg_write32(&p->phb, 0, 0x78,
@@ -5021,6 +5056,7 @@ static void phb4_create(struct dt_node *np)
PHBINF(p, "Max link speed: GEN%i\n", p->max_link_speed);
/* Check for lane equalization values from HB or HDAT */
+ p->lane_eq_en = true;
p->lane_eq = dt_prop_get_def_size(np, "ibm,lane-eq", NULL, &lane_eq_len);
if (p->rev == PHB4_REV_NIMBUS_DD10)
lane_eq_len_req = 8 * 8;
diff --git a/include/phb4-regs.h b/include/phb4-regs.h
index 2dc64fe..fa585d0 100644
--- a/include/phb4-regs.h
+++ b/include/phb4-regs.h
@@ -301,6 +301,10 @@
#define PHB_PCIE_DLP_TRAINING PPC_BIT(20)
#define PHB_PCIE_DLP_INBAND_PRESENCE PPC_BIT(19)
+#define PHB_PCIE_DLP_CTL 0x1A78
+#define PHB_PCIE_DLP_CTL_BYPASS_PH2 PPC_BIT(4)
+#define PHB_PCIE_DLP_CTL_BYPASS_PH3 PPC_BIT(5)
+
#define PHB_PCIE_DLP_TRWCTL 0x1A80
#define PHB_PCIE_DLP_TRWCTL_EN PPC_BIT(0)
diff --git a/include/phb4.h b/include/phb4.h
index 6f865b6..757a3fe 100644
--- a/include/phb4.h
+++ b/include/phb4.h
@@ -264,6 +264,7 @@ struct phb4 {
int64_t ecap; /* cached PCI-E cap offset */
int64_t aercap; /* cached AER ecap offset */
const __be64 *lane_eq;
+ bool lane_eq_en;
unsigned int max_link_speed;
uint64_t mrt_size;