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_xmac.c b/drivers/net/sfc/falcon_xmac.c
index 4a54d09..2206ead 100644
--- a/drivers/net/sfc/falcon_xmac.c
+++ b/drivers/net/sfc/falcon_xmac.c
@@ -25,24 +25,6 @@
  * MAC operations
  *
  *************************************************************************/
-static int falcon_reset_xmac(struct efx_nic *efx)
-{
-	efx_oword_t reg;
-	int count;
-
-	EFX_POPULATE_OWORD_1(reg, XM_CORE_RST, 1);
-	falcon_write(efx, &reg, XM_GLB_CFG_REG);
-
-	for (count = 0; count < 10000; count++) {	/* wait upto 100ms */
-		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;
-}
 
 /* Configure the XAUI driver that is an output from Falcon */
 static void falcon_setup_xaui(struct efx_nic *efx)
@@ -98,31 +80,20 @@
 	return -ETIMEDOUT;
 }
 
-static bool falcon_xgmii_status(struct efx_nic *efx)
-{
-	efx_oword_t reg;
-
-	if (falcon_rev(efx) < FALCON_REV_B0)
-		return true;
-
-	/* The ISR latches, so clear it and re-read */
-	falcon_read(efx, &reg, XM_MGT_INT_REG_B0);
-	falcon_read(efx, &reg, XM_MGT_INT_REG_B0);
-
-	if (EFX_OWORD_FIELD(reg, XM_LCLFLT) ||
-	    EFX_OWORD_FIELD(reg, XM_RMTFLT)) {
-		EFX_INFO(efx, "MGT_INT: "EFX_DWORD_FMT"\n", EFX_DWORD_VAL(reg));
-		return false;
-	}
-
-	return true;
-}
-
 static void falcon_mask_status_intr(struct efx_nic *efx, bool enable)
 {
 	efx_oword_t reg;
 
-	if ((falcon_rev(efx) < FALCON_REV_B0) || LOOPBACK_INTERNAL(efx))
+	if ((falcon_rev(efx) != FALCON_REV_B0) || LOOPBACK_INTERNAL(efx))
+		return;
+
+	/* We expect xgmii faults if the wireside link is up */
+	if (!EFX_WORKAROUND_5147(efx) || !efx->link_up)
+		return;
+
+	/* We can only use this interrupt to signal the negative edge of
+	 * xaui_align [we have to poll the positive edge]. */
+	if (!efx->mac_up)
 		return;
 
 	/* Flush the ISR */
@@ -135,35 +106,7 @@
 	falcon_write(efx, &reg, XM_MGT_INT_MSK_REG_B0);
 }
 
-int falcon_init_xmac(struct efx_nic *efx)
-{
-	int rc;
-
-	/* Initialize the PHY first so the clock is around */
-	rc = efx->phy_op->init(efx);
-	if (rc)
-		goto fail1;
-
-	rc = falcon_reset_xaui(efx);
-	if (rc)
-		goto fail2;
-
-	/* Wait again. Give the PHY and MAC time to come back */
-	schedule_timeout_uninterruptible(HZ / 10);
-
-	rc = falcon_reset_xmac(efx);
-	if (rc)
-		goto fail2;
-
-	falcon_mask_status_intr(efx, true);
-	return 0;
-
- fail2:
-	efx->phy_op->fini(efx);
- fail1:
-	return rc;
-}
-
+/* Get status of XAUI link */
 bool falcon_xaui_link_ok(struct efx_nic *efx)
 {
 	efx_oword_t reg;
@@ -187,18 +130,10 @@
 	EFX_SET_OWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET);
 	falcon_write(efx, &reg, XX_CORE_STAT_REG);
 
-	/* If the link is up, then check the phy side of the xaui link
-	 * (error conditions from the wire side propoagate back through
-	 * the phy to the xaui side). */
-	if (efx->link_up && link_ok) {
+	/* If the link is up, then check the phy side of the xaui link */
+	if (efx->link_up && link_ok)
 		if (efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS))
 			link_ok = mdio_clause45_phyxgxs_lane_sync(efx);
-	}
-
-	/* If the PHY and XAUI links are up, then check the mac's xgmii
-	 * fault state */
-	if (efx->link_up && link_ok)
-		link_ok = falcon_xgmii_status(efx);
 
 	return link_ok;
 }
@@ -310,70 +245,39 @@
 
 /* Try and bring the Falcon side of the Falcon-Phy XAUI link fails
  * to come back up. Bash it until it comes back up */
-static bool falcon_check_xaui_link_up(struct efx_nic *efx)
+static void falcon_check_xaui_link_up(struct efx_nic *efx, int tries)
 {
-	int max_tries, tries;
-	tries = EFX_WORKAROUND_5147(efx) ? 5 : 1;
-	max_tries = tries;
+	efx->mac_up = falcon_xaui_link_ok(efx);
 
 	if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
-	    (efx->phy_type == PHY_TYPE_NONE) ||
 	    efx_phy_mode_disabled(efx->phy_mode))
-		return false;
+		/* XAUI link is expected to be down */
+		return;
 
-	while (tries) {
-		if (falcon_xaui_link_ok(efx))
-			return true;
-
-		EFX_LOG(efx, "%s Clobbering XAUI (%d tries left).\n",
-			__func__, tries);
+	while (!efx->mac_up && tries) {
+		EFX_LOG(efx, "bashing xaui\n");
 		falcon_reset_xaui(efx);
 		udelay(200);
-		tries--;
-	}
 
-	EFX_LOG(efx, "Failed to bring XAUI link back up in %d tries!\n",
-		max_tries);
-	return false;
+		efx->mac_up = falcon_xaui_link_ok(efx);
+		--tries;
+	}
 }
 
-void falcon_reconfigure_xmac(struct efx_nic *efx)
+static void falcon_reconfigure_xmac(struct efx_nic *efx)
 {
-	bool xaui_link_ok;
-
 	falcon_mask_status_intr(efx, false);
 
-	falcon_deconfigure_mac_wrapper(efx);
-
-	/* Reconfigure the PHY, disabling transmit in mac level loopback. */
-	if (LOOPBACK_INTERNAL(efx))
-		efx->phy_mode |= PHY_MODE_TX_DISABLED;
-	else
-		efx->phy_mode &= ~PHY_MODE_TX_DISABLED;
-	efx->phy_op->reconfigure(efx);
-
 	falcon_reconfigure_xgxs_core(efx);
 	falcon_reconfigure_xmac_core(efx);
 
 	falcon_reconfigure_mac_wrapper(efx);
 
-	/* Ensure XAUI link is up */
-	xaui_link_ok = falcon_check_xaui_link_up(efx);
-
-	if (xaui_link_ok && efx->link_up)
-		falcon_mask_status_intr(efx, true);
+	falcon_check_xaui_link_up(efx, 5);
+	falcon_mask_status_intr(efx, true);
 }
 
-void falcon_fini_xmac(struct efx_nic *efx)
-{
-	/* Isolate the MAC - PHY */
-	falcon_deconfigure_mac_wrapper(efx);
-
-	/* Potentially power down the PHY */
-	efx->phy_op->fini(efx);
-}
-
-void falcon_update_stats_xmac(struct efx_nic *efx)
+static void falcon_update_stats_xmac(struct efx_nic *efx)
 {
 	struct efx_mac_stats *mac_stats = &efx->mac_stats;
 	int rc;
@@ -438,7 +342,7 @@
 		 mac_stats->rx_control * 64);
 }
 
-int falcon_check_xmac(struct efx_nic *efx)
+static int falcon_check_xmac(struct efx_nic *efx)
 {
 	bool xaui_link_ok;
 	int rc;
@@ -463,72 +367,8 @@
 	return rc;
 }
 
-/* Simulate a PHY event */
-void falcon_xmac_sim_phy_event(struct efx_nic *efx)
-{
-	efx_qword_t phy_event;
-
-	EFX_POPULATE_QWORD_2(phy_event,
-			     EV_CODE, GLOBAL_EV_DECODE,
-			     XG_PHY_INTR, 1);
-	falcon_generate_event(&efx->channel[0], &phy_event);
-}
-
-int falcon_xmac_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
-{
-	mdio_clause45_get_settings(efx, ecmd);
-	ecmd->transceiver = XCVR_INTERNAL;
-	ecmd->phy_address = efx->mii.phy_id;
-	ecmd->autoneg = AUTONEG_DISABLE;
-	ecmd->duplex = DUPLEX_FULL;
-	return 0;
-}
-
-int falcon_xmac_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
-{
-	if (ecmd->transceiver != XCVR_INTERNAL)
-		return -EINVAL;
-	if (ecmd->autoneg != AUTONEG_DISABLE)
-		return -EINVAL;
-	if (ecmd->duplex != DUPLEX_FULL)
-		return -EINVAL;
-
-	return mdio_clause45_set_settings(efx, ecmd);
-}
-
-
-int falcon_xmac_set_pause(struct efx_nic *efx, enum efx_fc_type flow_control)
-{
-	bool reset;
-
-	if (flow_control & EFX_FC_AUTO) {
-		EFX_LOG(efx, "10G does not support flow control "
-			"autonegotiation\n");
-		return -EINVAL;
-	}
-
-	if ((flow_control & EFX_FC_TX) && !(flow_control & EFX_FC_RX))
-		return -EINVAL;
-
-	/* TX flow control may automatically turn itself off if the
-	 * link partner (intermittently) stops responding to pause
-	 * frames. There isn't any indication that this has happened,
-	 * so the best we do is leave it up to the user to spot this
-	 * and fix it be cycling transmit flow control on this end. */
-	reset = ((flow_control & EFX_FC_TX) &&
-		 !(efx->flow_control & EFX_FC_TX));
-	if (EFX_WORKAROUND_11482(efx) && reset) {
-		if (falcon_rev(efx) >= FALCON_REV_B0) {
-			/* Recover by resetting the EM block */
-			if (efx->link_up)
-				falcon_drain_tx_fifo(efx);
-		} else {
-			/* Schedule a reset to recover */
-			efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
-		}
-	}
-
-	efx->flow_control = flow_control;
-
-	return 0;
-}
+struct efx_mac_operations falcon_xmac_operations = {
+	.reconfigure	= falcon_reconfigure_xmac,
+	.update_stats	= falcon_update_stats_xmac,
+	.check_hw	= falcon_check_xmac,
+};