sfc: Add support for sub-10G speeds

The SFC4000 has a separate MAC for use at sub-10G speeds.  Introduce
an efx_mac_operations structure with implementations for the two MACs.
Switch between the MACs as necessary.

PHY settings are independent of the MAC, so add get_settings() and
set_settings() to efx_phy_operations.  Also add macs field to indicate
which MACs the PHY is connected to.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index 448bba9..f09eded 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -1168,6 +1168,19 @@
 	falcon_generate_event(channel, &test_event);
 }
 
+void falcon_sim_phy_event(struct efx_nic *efx)
+{
+	efx_qword_t phy_event;
+
+	EFX_POPULATE_QWORD_1(phy_event, EV_CODE, GLOBAL_EV_DECODE);
+	if (EFX_IS10G(efx))
+		EFX_SET_OWORD_FIELD(phy_event, XG_PHY_INTR, 1);
+	else
+		EFX_SET_OWORD_FIELD(phy_event, G_PHY0_INTR, 1);
+
+	falcon_generate_event(&efx->channel[0], &phy_event);
+}
+
 /**************************************************************************
  *
  * Flush handling
@@ -1839,40 +1852,61 @@
  *
  **************************************************************************
  */
-void falcon_drain_tx_fifo(struct efx_nic *efx)
+
+static int falcon_reset_macs(struct efx_nic *efx)
 {
-	efx_oword_t temp;
+	efx_oword_t reg;
 	int count;
 
-	if ((falcon_rev(efx) < FALCON_REV_B0) ||
-	    (efx->loopback_mode != LOOPBACK_NONE))
-		return;
+	if (falcon_rev(efx) < FALCON_REV_B0) {
+		/* It's not safe to use GLB_CTL_REG to reset the
+		 * macs, so instead use the internal MAC resets
+		 */
+		if (!EFX_IS10G(efx)) {
+			EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 1);
+			falcon_write(efx, &reg, GM_CFG1_REG);
+			udelay(1000);
 
-	falcon_read(efx, &temp, MAC0_CTRL_REG_KER);
-	/* There is no point in draining more than once */
-	if (EFX_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0))
-		return;
+			EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 0);
+			falcon_write(efx, &reg, GM_CFG1_REG);
+			udelay(1000);
+			return 0;
+		} else {
+			EFX_POPULATE_OWORD_1(reg, XM_CORE_RST, 1);
+			falcon_write(efx, &reg, XM_GLB_CFG_REG);
+
+			for (count = 0; count < 10000; count++) {
+				falcon_read(efx, &reg, XM_GLB_CFG_REG);
+				if (EFX_OWORD_FIELD(reg, XM_CORE_RST) == 0)
+					return 0;
+				udelay(10);
+			}
+
+			EFX_ERR(efx, "timed out waiting for XMAC core reset\n");
+			return -ETIMEDOUT;
+		}
+	}
 
 	/* MAC stats will fail whilst the TX fifo is draining. Serialise
 	 * the drain sequence with the statistics fetch */
 	spin_lock(&efx->stats_lock);
 
-	EFX_SET_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0, 1);
-	falcon_write(efx, &temp, MAC0_CTRL_REG_KER);
+	falcon_read(efx, &reg, MAC0_CTRL_REG_KER);
+	EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0, 1);
+	falcon_write(efx, &reg, MAC0_CTRL_REG_KER);
 
-	/* Reset the MAC and EM block. */
-	falcon_read(efx, &temp, GLB_CTL_REG_KER);
-	EFX_SET_OWORD_FIELD(temp, RST_XGTX, 1);
-	EFX_SET_OWORD_FIELD(temp, RST_XGRX, 1);
-	EFX_SET_OWORD_FIELD(temp, RST_EM, 1);
-	falcon_write(efx, &temp, GLB_CTL_REG_KER);
+	falcon_read(efx, &reg, GLB_CTL_REG_KER);
+	EFX_SET_OWORD_FIELD(reg, RST_XGTX, 1);
+	EFX_SET_OWORD_FIELD(reg, RST_XGRX, 1);
+	EFX_SET_OWORD_FIELD(reg, RST_EM, 1);
+	falcon_write(efx, &reg, GLB_CTL_REG_KER);
 
 	count = 0;
 	while (1) {
-		falcon_read(efx, &temp, GLB_CTL_REG_KER);
-		if (!EFX_OWORD_FIELD(temp, RST_XGTX) &&
-		    !EFX_OWORD_FIELD(temp, RST_XGRX) &&
-		    !EFX_OWORD_FIELD(temp, RST_EM)) {
+		falcon_read(efx, &reg, GLB_CTL_REG_KER);
+		if (!EFX_OWORD_FIELD(reg, RST_XGTX) &&
+		    !EFX_OWORD_FIELD(reg, RST_XGRX) &&
+		    !EFX_OWORD_FIELD(reg, RST_EM)) {
 			EFX_LOG(efx, "Completed MAC reset after %d loops\n",
 				count);
 			break;
@@ -1889,21 +1923,39 @@
 
 	/* If we've reset the EM block and the link is up, then
 	 * we'll have to kick the XAUI link so the PHY can recover */
-	if (efx->link_up && EFX_WORKAROUND_5147(efx))
+	if (efx->link_up && EFX_IS10G(efx) && EFX_WORKAROUND_5147(efx))
 		falcon_reset_xaui(efx);
+
+	return 0;
+}
+
+void falcon_drain_tx_fifo(struct efx_nic *efx)
+{
+	efx_oword_t reg;
+
+	if ((falcon_rev(efx) < FALCON_REV_B0) ||
+	    (efx->loopback_mode != LOOPBACK_NONE))
+		return;
+
+	falcon_read(efx, &reg, MAC0_CTRL_REG_KER);
+	/* There is no point in draining more than once */
+	if (EFX_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0))
+		return;
+
+	falcon_reset_macs(efx);
 }
 
 void falcon_deconfigure_mac_wrapper(struct efx_nic *efx)
 {
-	efx_oword_t temp;
+	efx_oword_t reg;
 
 	if (falcon_rev(efx) < FALCON_REV_B0)
 		return;
 
 	/* Isolate the MAC -> RX */
-	falcon_read(efx, &temp, RX_CFG_REG_KER);
-	EFX_SET_OWORD_FIELD(temp, RX_INGR_EN_B0, 0);
-	falcon_write(efx, &temp, RX_CFG_REG_KER);
+	falcon_read(efx, &reg, RX_CFG_REG_KER);
+	EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 0);
+	falcon_write(efx, &reg, RX_CFG_REG_KER);
 
 	if (!efx->link_up)
 		falcon_drain_tx_fifo(efx);
@@ -2030,7 +2082,8 @@
 	efx_dword_t md_stat;
 	int count;
 
-	for (count = 0; count < 1000; count++) {	/* wait upto 10ms */
+	/* wait upto 50ms - taken max from datasheet */
+	for (count = 0; count < 5000; count++) {
 		falcon_readl(efx, &md_stat, MD_STAT_REG_KER);
 		if (EFX_DWORD_FIELD(md_stat, MD_BSY) == 0) {
 			if (EFX_DWORD_FIELD(md_stat, MD_LNFL) != 0 ||
@@ -2206,10 +2259,59 @@
 		return -1;
 	}
 
-	efx->loopback_modes = LOOPBACKS_10G_INTERNAL | efx->phy_op->loopbacks;
+	if (efx->phy_op->macs & EFX_XMAC)
+		efx->loopback_modes |= ((1 << LOOPBACK_XGMII) |
+					(1 << LOOPBACK_XGXS) |
+					(1 << LOOPBACK_XAUI));
+	if (efx->phy_op->macs & EFX_GMAC)
+		efx->loopback_modes |= (1 << LOOPBACK_GMAC);
+	efx->loopback_modes |= efx->phy_op->loopbacks;
+
 	return 0;
 }
 
+int falcon_switch_mac(struct efx_nic *efx)
+{
+	struct efx_mac_operations *old_mac_op = efx->mac_op;
+	efx_oword_t nic_stat;
+	unsigned strap_val;
+
+	/* Internal loopbacks override the phy speed setting */
+	if (efx->loopback_mode == LOOPBACK_GMAC) {
+		efx->link_speed = 1000;
+		efx->link_fd = true;
+	} else if (LOOPBACK_INTERNAL(efx)) {
+		efx->link_speed = 10000;
+		efx->link_fd = true;
+	}
+
+	efx->mac_op = (EFX_IS10G(efx) ?
+		       &falcon_xmac_operations : &falcon_gmac_operations);
+	if (old_mac_op == efx->mac_op)
+		return 0;
+
+	WARN_ON(!mutex_is_locked(&efx->mac_lock));
+
+	/* Not all macs support a mac-level link state */
+	efx->mac_up = true;
+
+	falcon_read(efx, &nic_stat, NIC_STAT_REG);
+	strap_val = EFX_IS10G(efx) ? 5 : 3;
+	if (falcon_rev(efx) >= FALCON_REV_B0) {
+		EFX_SET_OWORD_FIELD(nic_stat, EE_STRAP_EN, 1);
+		EFX_SET_OWORD_FIELD(nic_stat, EE_STRAP_OVR, strap_val);
+		falcon_write(efx, &nic_stat, NIC_STAT_REG);
+	} else {
+		/* Falcon A1 does not support 1G/10G speed switching
+		 * and must not be used with a PHY that does. */
+		BUG_ON(EFX_OWORD_FIELD(nic_stat, STRAP_PINS) != strap_val);
+	}
+
+
+	EFX_LOG(efx, "selected %cMAC\n", EFX_IS10G(efx) ? 'X' : 'G');
+	return falcon_reset_macs(efx);
+}
+
 /* This call is responsible for hooking in the MAC and PHY operations */
 int falcon_probe_port(struct efx_nic *efx)
 {
@@ -2362,6 +2464,10 @@
 	  EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) },
 	{ DP_CTRL_REG,
 	  EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) },
+	{ GM_CFG2_REG,
+	  EFX_OWORD32(0x00007337, 0x00000000, 0x00000000, 0x00000000) },
+	{ GMF_CFG0_REG,
+	  EFX_OWORD32(0x00001F1F, 0x00000000, 0x00000000, 0x00000000) },
 	{ XM_GLB_CFG_REG,
 	  EFX_OWORD32(0x00000C68, 0x00000000, 0x00000000, 0x00000000) },
 	{ XM_TX_CFG_REG,
@@ -2687,6 +2793,7 @@
 static int falcon_probe_nic_variant(struct efx_nic *efx)
 {
 	efx_oword_t altera_build;
+	efx_oword_t nic_stat;
 
 	falcon_read(efx, &altera_build, ALTERA_BUILD_REG_KER);
 	if (EFX_OWORD_FIELD(altera_build, VER_ALL)) {
@@ -2694,27 +2801,20 @@
 		return -ENODEV;
 	}
 
+	falcon_read(efx, &nic_stat, NIC_STAT_REG);
+
 	switch (falcon_rev(efx)) {
 	case FALCON_REV_A0:
 	case 0xff:
 		EFX_ERR(efx, "Falcon rev A0 not supported\n");
 		return -ENODEV;
 
-	case FALCON_REV_A1:{
-		efx_oword_t nic_stat;
-
-		falcon_read(efx, &nic_stat, NIC_STAT_REG);
-
+	case FALCON_REV_A1:
 		if (EFX_OWORD_FIELD(nic_stat, STRAP_PCIE) == 0) {
 			EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n");
 			return -ENODEV;
 		}
-		if (!EFX_OWORD_FIELD(nic_stat, STRAP_10G)) {
-			EFX_ERR(efx, "1G mode not supported\n");
-			return -ENODEV;
-		}
 		break;
-	}
 
 	case FALCON_REV_B0:
 		break;
@@ -2724,6 +2824,9 @@
 		return -ENODEV;
 	}
 
+	/* Initial assumed speed */
+	efx->link_speed = EFX_OWORD_FIELD(nic_stat, STRAP_10G) ? 10000 : 1000;
+
 	return 0;
 }