sfc: QT2025C: Work around PHY firmware initialisation bug

The PHY's firmware very occasionally appears to lock up very early, but
with the heartbeat update still running.  Rebooting the microcontroller
core seems to be sufficient to recover.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c
index 0cd6eed0..326ffa4 100644
--- a/drivers/net/sfc/qt202x_phy.c
+++ b/drivers/net/sfc/qt202x_phy.c
@@ -63,11 +63,16 @@
 #define QT2022C2_MAX_RESET_TIME 500
 #define QT2022C2_RESET_WAIT 10
 
+#define QT2025C_MAX_HEARTB_TIME (5 * HZ)
+#define QT2025C_HEARTB_WAIT 100
+#define QT2025C_MAX_FWSTART_TIME (25 * HZ / 10)
+#define QT2025C_FWSTART_WAIT 100
+
 #define BUG17190_INTERVAL (2 * HZ)
 
-static int qt2025c_wait_reset(struct efx_nic *efx)
+static int qt2025c_wait_heartbeat(struct efx_nic *efx)
 {
-	unsigned long timeout = jiffies + 10 * HZ;
+	unsigned long timeout = jiffies + QT2025C_MAX_HEARTB_TIME;
 	int reg, old_counter = 0;
 
 	/* Wait for firmware heartbeat to start */
@@ -84,9 +89,17 @@
 			break;
 		if (time_after(jiffies, timeout))
 			return -ETIMEDOUT;
-		msleep(10);
+		msleep(QT2025C_HEARTB_WAIT);
 	}
 
+	return 0;
+}
+
+static int qt2025c_wait_fw_status_good(struct efx_nic *efx)
+{
+	unsigned long timeout = jiffies + QT2025C_MAX_FWSTART_TIME;
+	int reg;
+
 	/* Wait for firmware status to look good */
 	for (;;) {
 		reg = efx_mdio_read(efx, MDIO_MMD_PCS, PCS_UC8051_STATUS_REG);
@@ -98,12 +111,44 @@
 			break;
 		if (time_after(jiffies, timeout))
 			return -ETIMEDOUT;
-		msleep(100);
+		msleep(QT2025C_FWSTART_WAIT);
 	}
 
 	return 0;
 }
 
+static void qt2025c_restart_firmware(struct efx_nic *efx)
+{
+	/* Restart microcontroller execution of firmware from RAM */
+	efx_mdio_write(efx, 3, 0xe854, 0x00c0);
+	efx_mdio_write(efx, 3, 0xe854, 0x0040);
+	msleep(50);
+}
+
+static int qt2025c_wait_reset(struct efx_nic *efx)
+{
+	int rc;
+
+	rc = qt2025c_wait_heartbeat(efx);
+	if (rc != 0)
+		return rc;
+
+	rc = qt2025c_wait_fw_status_good(efx);
+	if (rc == -ETIMEDOUT) {
+		/* Bug 17689: occasionally heartbeat starts but firmware status
+		 * code never progresses beyond 0x00.  Try again, once, after
+		 * restarting execution of the firmware image. */
+		EFX_LOG(efx, "bashing QT2025C microcontroller\n");
+		qt2025c_restart_firmware(efx);
+		rc = qt2025c_wait_heartbeat(efx);
+		if (rc != 0)
+			return rc;
+		rc = qt2025c_wait_fw_status_good(efx);
+	}
+
+	return rc;
+}
+
 static void qt2025c_firmware_id(struct efx_nic *efx)
 {
 	struct qt202x_phy_data *phy_data = efx->phy_data;
@@ -229,10 +274,8 @@
 	efx_mdio_write(efx, 1, 0xc300, 0x0002);
 	msleep(20);
 
-	/* Restart microcontroller execution from RAM */
-	efx_mdio_write(efx, 3, 0xe854, 0x00c0);
-	efx_mdio_write(efx, 3, 0xe854, 0x0040);
-	msleep(50);
+	/* Restart microcontroller execution of firmware from RAM */
+	qt2025c_restart_firmware(efx);
 
 	/* Wait for the microcontroller to be ready again */
 	rc = qt2025c_wait_reset(efx);