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>
This commit is contained in:
Ben Hutchings
2008-12-12 21:50:08 -08:00
committed by David S. Miller
parent 356eebb2b3
commit 177dfcd80f
16 changed files with 700 additions and 322 deletions

View File

@@ -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 @@ int falcon_reset_xaui(struct efx_nic *efx)
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 @@ static void falcon_mask_status_intr(struct efx_nic *efx, bool enable)
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 @@ bool falcon_xaui_link_ok(struct efx_nic *efx)
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 @@ static void falcon_reconfigure_xgxs_core(struct efx_nic *efx)
/* 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 @@ void falcon_update_stats_xmac(struct efx_nic *efx)
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 @@ int falcon_check_xmac(struct efx_nic *efx)
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,
};