diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-01-21 23:55:25 -0800 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-01-21 23:55:25 -0800 |
commit | 7755726fe90a8b253659756e6de68c1a55aa427f (patch) | |
tree | a3523fa77e07854db3b8089e3066a55ea997060c /drivers/net/sfc | |
parent | 3bf127637e22ddf95e67e10a23c339cee3d52429 (diff) | |
parent | 92dcffb916d309aa01778bf8963a6932e4014d07 (diff) |
Merge commit 'v2.6.33-rc5' into next
Diffstat (limited to 'drivers/net/sfc')
-rw-r--r-- | drivers/net/sfc/efx.c | 6 | ||||
-rw-r--r-- | drivers/net/sfc/falcon.c | 1 | ||||
-rw-r--r-- | drivers/net/sfc/falcon_xmac.c | 38 | ||||
-rw-r--r-- | drivers/net/sfc/mcdi.c | 5 | ||||
-rw-r--r-- | drivers/net/sfc/mcdi_phy.c | 93 | ||||
-rw-r--r-- | drivers/net/sfc/net_driver.h | 1 | ||||
-rw-r--r-- | drivers/net/sfc/nic.c | 2 | ||||
-rw-r--r-- | drivers/net/sfc/qt202x_phy.c | 238 | ||||
-rw-r--r-- | drivers/net/sfc/selftest.c | 10 | ||||
-rw-r--r-- | drivers/net/sfc/siena.c | 1 | ||||
-rw-r--r-- | drivers/net/sfc/tenxpress.c | 138 | ||||
-rw-r--r-- | drivers/net/sfc/tx.c | 4 |
12 files changed, 379 insertions, 158 deletions
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index f983e3b507c..103e8b0e2a0 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -741,14 +741,14 @@ static int efx_probe_port(struct efx_nic *efx) EFX_LOG(efx, "create port\n"); + if (phy_flash_cfg) + efx->phy_mode = PHY_MODE_SPECIAL; + /* Connect up MAC/PHY operations table */ rc = efx->type->probe_port(efx); if (rc) goto err; - if (phy_flash_cfg) - efx->phy_mode = PHY_MODE_SPECIAL; - /* Sanity check MAC address */ if (is_valid_ether_addr(efx->mac_address)) { memcpy(efx->net_dev->dev_addr, efx->mac_address, ETH_ALEN); diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index 17afcd26e87..9d009c46e96 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -925,6 +925,7 @@ static int falcon_probe_port(struct efx_nic *efx) static void falcon_remove_port(struct efx_nic *efx) { + efx->phy_op->remove(efx); efx_nic_free_buffer(efx, &efx->stats_buffer); } diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c index 3da933f8f07..8ccab2c67a2 100644 --- a/drivers/net/sfc/falcon_xmac.c +++ b/drivers/net/sfc/falcon_xmac.c @@ -111,16 +111,12 @@ static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) efx_writeo(efx, ®, FR_AB_XM_MGT_INT_MASK); } -/* Get status of XAUI link */ -static bool falcon_xaui_link_ok(struct efx_nic *efx) +static bool falcon_xgxs_link_ok(struct efx_nic *efx) { efx_oword_t reg; bool align_done, link_ok = false; int sync_status; - if (LOOPBACK_INTERNAL(efx)) - return true; - /* Read link status */ efx_reado(efx, ®, FR_AB_XX_CORE_STAT); @@ -135,14 +131,24 @@ static bool falcon_xaui_link_ok(struct efx_nic *efx) EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_DISPERR, FFE_AB_XX_STAT_ALL_LANES); efx_writeo(efx, ®, FR_AB_XX_CORE_STAT); - /* If the link is up, then check the phy side of the xaui link */ - if (efx->link_state.up && link_ok) - if (efx->mdio.mmds & (1 << MDIO_MMD_PHYXS)) - link_ok = efx_mdio_phyxgxs_lane_sync(efx); - return link_ok; } +static bool falcon_xmac_link_ok(struct efx_nic *efx) +{ + /* + * Check MAC's XGXS link status except when using XGMII loopback + * which bypasses the XGXS block. + * If possible, check PHY's XGXS link status except when using + * MAC loopback. + */ + return (efx->loopback_mode == LOOPBACK_XGMII || + falcon_xgxs_link_ok(efx)) && + (!(efx->mdio.mmds & (1 << MDIO_MMD_PHYXS)) || + LOOPBACK_INTERNAL(efx) || + efx_mdio_phyxgxs_lane_sync(efx)); +} + void falcon_reconfigure_xmac_core(struct efx_nic *efx) { unsigned int max_frame_len; @@ -245,9 +251,9 @@ static void falcon_reconfigure_xgxs_core(struct efx_nic *efx) /* Try to bring up the Falcon side of the Falcon-Phy XAUI link */ -static bool falcon_check_xaui_link_up(struct efx_nic *efx, int tries) +static bool falcon_xmac_link_ok_retry(struct efx_nic *efx, int tries) { - bool mac_up = falcon_xaui_link_ok(efx); + bool mac_up = falcon_xmac_link_ok(efx); if (LOOPBACK_MASK(efx) & LOOPBACKS_EXTERNAL(efx) & LOOPBACKS_WS || efx_phy_mode_disabled(efx->phy_mode)) @@ -261,7 +267,7 @@ static bool falcon_check_xaui_link_up(struct efx_nic *efx, int tries) falcon_reset_xaui(efx); udelay(200); - mac_up = falcon_xaui_link_ok(efx); + mac_up = falcon_xmac_link_ok(efx); --tries; } @@ -272,7 +278,7 @@ static bool falcon_check_xaui_link_up(struct efx_nic *efx, int tries) static bool falcon_xmac_check_fault(struct efx_nic *efx) { - return !falcon_check_xaui_link_up(efx, 5); + return !falcon_xmac_link_ok_retry(efx, 5); } static int falcon_reconfigure_xmac(struct efx_nic *efx) @@ -284,7 +290,7 @@ static int falcon_reconfigure_xmac(struct efx_nic *efx) falcon_reconfigure_mac_wrapper(efx); - efx->xmac_poll_required = !falcon_check_xaui_link_up(efx, 5); + efx->xmac_poll_required = !falcon_xmac_link_ok_retry(efx, 5); falcon_mask_status_intr(efx, true); return 0; @@ -357,7 +363,7 @@ void falcon_poll_xmac(struct efx_nic *efx) return; falcon_mask_status_intr(efx, false); - efx->xmac_poll_required = !falcon_check_xaui_link_up(efx, 1); + efx->xmac_poll_required = !falcon_xmac_link_ok_retry(efx, 1); falcon_mask_status_intr(efx, true); } diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/sfc/mcdi.c index 683353b904c..0d4eba7266e 100644 --- a/drivers/net/sfc/mcdi.c +++ b/drivers/net/sfc/mcdi.c @@ -142,8 +142,9 @@ static int efx_mcdi_poll(struct efx_nic *efx) if (spins != 0) { --spins; udelay(1); - } else - schedule(); + } else { + schedule_timeout_uninterruptible(1); + } time = get_seconds(); diff --git a/drivers/net/sfc/mcdi_phy.c b/drivers/net/sfc/mcdi_phy.c index 0e1bcc5a0d5..eb694af7a47 100644 --- a/drivers/net/sfc/mcdi_phy.c +++ b/drivers/net/sfc/mcdi_phy.c @@ -304,31 +304,47 @@ static u32 mcdi_to_ethtool_media(u32 media) static int efx_mcdi_phy_probe(struct efx_nic *efx) { - struct efx_mcdi_phy_cfg *phy_cfg; + struct efx_mcdi_phy_cfg *phy_data; + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + u32 caps; int rc; - /* TODO: Move phy_data initialisation to - * phy_op->probe/remove, rather than init/fini */ - phy_cfg = kzalloc(sizeof(*phy_cfg), GFP_KERNEL); - if (phy_cfg == NULL) { - rc = -ENOMEM; - goto fail_alloc; - } - rc = efx_mcdi_get_phy_cfg(efx, phy_cfg); + /* Initialise and populate phy_data */ + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + if (phy_data == NULL) + return -ENOMEM; + + rc = efx_mcdi_get_phy_cfg(efx, phy_data); if (rc != 0) goto fail; - efx->phy_type = phy_cfg->type; + /* Read initial link advertisement */ + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), NULL); + if (rc) + goto fail; + + /* Fill out nic state */ + efx->phy_data = phy_data; + efx->phy_type = phy_data->type; - efx->mdio_bus = phy_cfg->channel; - efx->mdio.prtad = phy_cfg->port; - efx->mdio.mmds = phy_cfg->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22); + efx->mdio_bus = phy_data->channel; + efx->mdio.prtad = phy_data->port; + efx->mdio.mmds = phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22); efx->mdio.mode_support = 0; - if (phy_cfg->mmd_mask & (1 << MC_CMD_MMD_CLAUSE22)) + if (phy_data->mmd_mask & (1 << MC_CMD_MMD_CLAUSE22)) efx->mdio.mode_support |= MDIO_SUPPORTS_C22; - if (phy_cfg->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22)) + if (phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22)) efx->mdio.mode_support |= MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + caps = MCDI_DWORD(outbuf, GET_LINK_OUT_CAP); + if (caps & (1 << MC_CMD_PHY_CAP_AN_LBN)) + efx->link_advertising = + mcdi_to_ethtool_cap(phy_data->media, caps); + else + phy_data->forced_cap = caps; + /* Assert that we can map efx -> mcdi loopback modes */ BUILD_BUG_ON(LOOPBACK_NONE != MC_CMD_LOOPBACK_NONE); BUILD_BUG_ON(LOOPBACK_DATA != MC_CMD_LOOPBACK_DATA); @@ -365,46 +381,6 @@ static int efx_mcdi_phy_probe(struct efx_nic *efx) * but by convention we don't */ efx->loopback_modes &= ~(1 << LOOPBACK_NONE); - kfree(phy_cfg); - - return 0; - -fail: - kfree(phy_cfg); -fail_alloc: - return rc; -} - -static int efx_mcdi_phy_init(struct efx_nic *efx) -{ - struct efx_mcdi_phy_cfg *phy_data; - u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; - u32 caps; - int rc; - - phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); - if (phy_data == NULL) - return -ENOMEM; - - rc = efx_mcdi_get_phy_cfg(efx, phy_data); - if (rc != 0) - goto fail; - - efx->phy_data = phy_data; - - BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); - rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, - outbuf, sizeof(outbuf), NULL); - if (rc) - goto fail; - - caps = MCDI_DWORD(outbuf, GET_LINK_OUT_CAP); - if (caps & (1 << MC_CMD_PHY_CAP_AN_LBN)) - efx->link_advertising = - mcdi_to_ethtool_cap(phy_data->media, caps); - else - phy_data->forced_cap = caps; - return 0; fail: @@ -504,7 +480,7 @@ static bool efx_mcdi_phy_poll(struct efx_nic *efx) return !efx_link_state_equal(&efx->link_state, &old_state); } -static void efx_mcdi_phy_fini(struct efx_nic *efx) +static void efx_mcdi_phy_remove(struct efx_nic *efx) { struct efx_mcdi_phy_data *phy_data = efx->phy_data; @@ -586,10 +562,11 @@ static int efx_mcdi_phy_set_settings(struct efx_nic *efx, struct ethtool_cmd *ec struct efx_phy_operations efx_mcdi_phy_ops = { .probe = efx_mcdi_phy_probe, - .init = efx_mcdi_phy_init, + .init = efx_port_dummy_op_int, .reconfigure = efx_mcdi_phy_reconfigure, .poll = efx_mcdi_phy_poll, - .fini = efx_mcdi_phy_fini, + .fini = efx_port_dummy_op_void, + .remove = efx_mcdi_phy_remove, .get_settings = efx_mcdi_phy_get_settings, .set_settings = efx_mcdi_phy_set_settings, .run_tests = NULL, diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 34c381f009b..d5aab5b3fa0 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -524,6 +524,7 @@ struct efx_phy_operations { int (*probe) (struct efx_nic *efx); int (*init) (struct efx_nic *efx); void (*fini) (struct efx_nic *efx); + void (*remove) (struct efx_nic *efx); int (*reconfigure) (struct efx_nic *efx); bool (*poll) (struct efx_nic *efx); void (*get_settings) (struct efx_nic *efx, diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c index a577be22786..db44224ed2c 100644 --- a/drivers/net/sfc/nic.c +++ b/drivers/net/sfc/nic.c @@ -1576,6 +1576,8 @@ void efx_nic_init_common(struct efx_nic *efx) EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_SOFT_EVT_EN, 1); /* Prefetch threshold 2 => fetch when descriptor cache half empty */ EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PREF_THRESHOLD, 2); + /* Disable hardware watchdog which can misfire */ + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PREF_WD_TMR, 0x3fffff); /* Squash TX of packets of 16 bytes or less */ if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1); diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c index 3800fc791b2..ff8f0a417fa 100644 --- a/drivers/net/sfc/qt202x_phy.c +++ b/drivers/net/sfc/qt202x_phy.c @@ -33,6 +33,9 @@ #define PCS_FW_HEARTBEAT_REG 0xd7ee #define PCS_FW_HEARTB_LBN 0 #define PCS_FW_HEARTB_WIDTH 8 +#define PCS_FW_PRODUCT_CODE_1 0xd7f0 +#define PCS_FW_VERSION_1 0xd7f3 +#define PCS_FW_BUILD_1 0xd7f6 #define PCS_UC8051_STATUS_REG 0xd7fd #define PCS_UC_STATUS_LBN 0 #define PCS_UC_STATUS_WIDTH 8 @@ -52,14 +55,24 @@ void falcon_qt202x_set_led(struct efx_nic *p, int led, int mode) struct qt202x_phy_data { enum efx_phy_mode phy_mode; + bool bug17190_in_bad_state; + unsigned long bug17190_timer; + u32 firmware_ver; }; #define QT2022C2_MAX_RESET_TIME 500 #define QT2022C2_RESET_WAIT 10 -static int qt2025c_wait_reset(struct efx_nic *efx) +#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_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 */ @@ -74,11 +87,25 @@ static int qt2025c_wait_reset(struct efx_nic *efx) old_counter = counter; else if (counter != old_counter) break; - if (time_after(jiffies, timeout)) + if (time_after(jiffies, timeout)) { + /* Some cables have EEPROMs that conflict with the + * PHY's on-board EEPROM so it cannot load firmware */ + EFX_ERR(efx, "If an SFP+ direct attach cable is" + " connected, please check that it complies" + " with the SFP+ specification\n"); 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); @@ -90,7 +117,178 @@ static int qt2025c_wait_reset(struct efx_nic *efx) break; if (time_after(jiffies, timeout)) return -ETIMEDOUT; + 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; + u8 firmware_id[9]; + size_t i; + + for (i = 0; i < sizeof(firmware_id); i++) + firmware_id[i] = efx_mdio_read(efx, MDIO_MMD_PCS, + PCS_FW_PRODUCT_CODE_1 + i); + EFX_INFO(efx, "QT2025C firmware %xr%d v%d.%d.%d.%d [20%02d-%02d-%02d]\n", + (firmware_id[0] << 8) | firmware_id[1], firmware_id[2], + firmware_id[3] >> 4, firmware_id[3] & 0xf, + firmware_id[4], firmware_id[5], + firmware_id[6], firmware_id[7], firmware_id[8]); + phy_data->firmware_ver = ((firmware_id[3] & 0xf0) << 20) | + ((firmware_id[3] & 0x0f) << 16) | + (firmware_id[4] << 8) | firmware_id[5]; +} + +static void qt2025c_bug17190_workaround(struct efx_nic *efx) +{ + struct qt202x_phy_data *phy_data = efx->phy_data; + + /* The PHY can get stuck in a state where it reports PHY_XS and PMA/PMD + * layers up, but PCS down (no block_lock). If we notice this state + * persisting for a couple of seconds, we switch PMA/PMD loopback + * briefly on and then off again, which is normally sufficient to + * recover it. + */ + if (efx->link_state.up || + !efx_mdio_links_ok(efx, MDIO_DEVS_PMAPMD | MDIO_DEVS_PHYXS)) { + phy_data->bug17190_in_bad_state = false; + return; + } + + if (!phy_data->bug17190_in_bad_state) { + phy_data->bug17190_in_bad_state = true; + phy_data->bug17190_timer = jiffies + BUG17190_INTERVAL; + return; + } + + if (time_after_eq(jiffies, phy_data->bug17190_timer)) { + EFX_LOG(efx, "bashing QT2025C PMA/PMD\n"); + efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1, + MDIO_PMA_CTRL1_LOOPBACK, true); msleep(100); + efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1, + MDIO_PMA_CTRL1_LOOPBACK, false); + phy_data->bug17190_timer = jiffies + BUG17190_INTERVAL; + } +} + +static int qt2025c_select_phy_mode(struct efx_nic *efx) +{ + struct qt202x_phy_data *phy_data = efx->phy_data; + struct falcon_board *board = falcon_board(efx); + int reg, rc, i; + uint16_t phy_op_mode; + + /* Only 2.0.1.0+ PHY firmware supports the more optimal SFP+ + * Self-Configure mode. Don't attempt any switching if we encounter + * older firmware. */ + if (phy_data->firmware_ver < 0x02000100) + return 0; + + /* In general we will get optimal behaviour in "SFP+ Self-Configure" + * mode; however, that powers down most of the PHY when no module is + * present, so we must use a different mode (any fixed mode will do) + * to be sure that loopbacks will work. */ + phy_op_mode = (efx->loopback_mode == LOOPBACK_NONE) ? 0x0038 : 0x0020; + + /* Only change mode if really necessary */ + reg = efx_mdio_read(efx, 1, 0xc319); + if ((reg & 0x0038) == phy_op_mode) + return 0; + EFX_LOG(efx, "Switching PHY to mode 0x%04x\n", phy_op_mode); + + /* This sequence replicates the register writes configured in the boot + * EEPROM (including the differences between board revisions), except + * that the operating mode is changed, and the PHY is prevented from + * unnecessarily reloading the main firmware image again. */ + efx_mdio_write(efx, 1, 0xc300, 0x0000); + /* (Note: this portion of the boot EEPROM sequence, which bit-bashes 9 + * STOPs onto the firmware/module I2C bus to reset it, varies across + * board revisions, as the bus is connected to different GPIO/LED + * outputs on the PHY.) */ + if (board->major == 0 && board->minor < 2) { + efx_mdio_write(efx, 1, 0xc303, 0x4498); + for (i = 0; i < 9; i++) { + efx_mdio_write(efx, 1, 0xc303, 0x4488); + efx_mdio_write(efx, 1, 0xc303, 0x4480); + efx_mdio_write(efx, 1, 0xc303, 0x4490); + efx_mdio_write(efx, 1, 0xc303, 0x4498); + } + } else { + efx_mdio_write(efx, 1, 0xc303, 0x0920); + efx_mdio_write(efx, 1, 0xd008, 0x0004); + for (i = 0; i < 9; i++) { + efx_mdio_write(efx, 1, 0xc303, 0x0900); + efx_mdio_write(efx, 1, 0xd008, 0x0005); + efx_mdio_write(efx, 1, 0xc303, 0x0920); + efx_mdio_write(efx, 1, 0xd008, 0x0004); + } + efx_mdio_write(efx, 1, 0xc303, 0x4900); + } + efx_mdio_write(efx, 1, 0xc303, 0x4900); + efx_mdio_write(efx, 1, 0xc302, 0x0004); + efx_mdio_write(efx, 1, 0xc316, 0x0013); + efx_mdio_write(efx, 1, 0xc318, 0x0054); + efx_mdio_write(efx, 1, 0xc319, phy_op_mode); + efx_mdio_write(efx, 1, 0xc31a, 0x0098); + efx_mdio_write(efx, 3, 0x0026, 0x0e00); + efx_mdio_write(efx, 3, 0x0027, 0x0013); + efx_mdio_write(efx, 3, 0x0028, 0xa528); + efx_mdio_write(efx, 1, 0xd006, 0x000a); + efx_mdio_write(efx, 1, 0xd007, 0x0009); + efx_mdio_write(efx, 1, 0xd008, 0x0004); + /* This additional write is not present in the boot EEPROM. It + * prevents the PHY's internal boot ROM doing another pointless (and + * slow) reload of the firmware image (the microcontroller's code + * memory is not affected by the microcontroller reset). */ + efx_mdio_write(efx, 1, 0xc317, 0x00ff); + efx_mdio_write(efx, 1, 0xc300, 0x0002); + msleep(20); + + /* Restart microcontroller execution of firmware from RAM */ + qt2025c_restart_firmware(efx); + + /* Wait for the microcontroller to be ready again */ + rc = qt2025c_wait_reset(efx); + if (rc < 0) { + EFX_ERR(efx, "PHY microcontroller reset during mode switch " + "timed out\n"); + return rc; } return 0; @@ -137,6 +335,16 @@ static int qt202x_reset_phy(struct efx_nic *efx) static int qt202x_phy_probe(struct efx_nic *efx) { + struct qt202x_phy_data *phy_data; + + phy_data = kzalloc(sizeof(struct qt202x_phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + efx->phy_data = phy_data; + phy_data->phy_mode = efx->phy_mode; + phy_data->bug17190_in_bad_state = false; + phy_data->bug17190_timer = 0; + efx->mdio.mmds = QT202X_REQUIRED_DEVS; efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; efx->loopback_modes = QT202X_LOOPBACKS | FALCON_XMAC_LOOPBACKS; @@ -145,7 +353,6 @@ static int qt202x_phy_probe(struct efx_nic *efx) static int qt202x_phy_init(struct efx_nic *efx) { - struct qt202x_phy_data *phy_data; u32 devid; int rc; @@ -155,17 +362,14 @@ static int qt202x_phy_init(struct efx_nic *efx) return rc; } - phy_data = kzalloc(sizeof(struct qt202x_phy_data), GFP_KERNEL); - if (!phy_data) - return -ENOMEM; - efx->phy_data = phy_data; - devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS); EFX_INFO(efx, "PHY ID reg %x (OUI %06x model %02x revision %x)\n", devid, efx_mdio_id_oui(devid), efx_mdio_id_model(devid), efx_mdio_id_rev(devid)); - phy_data->phy_mode = efx->phy_mode; + if (efx->phy_type == PHY_TYPE_QT2025C) + qt2025c_firmware_id(efx); + return 0; } @@ -183,6 +387,9 @@ static bool qt202x_phy_poll(struct efx_nic *efx) efx->link_state.fd = true; efx->link_state.fc = efx->wanted_fc; + if (efx->phy_type == PHY_TYPE_QT2025C) + qt2025c_bug17190_workaround(efx); + return efx->link_state.up != was_up; } @@ -191,6 +398,10 @@ static int qt202x_phy_reconfigure(struct efx_nic *efx) struct qt202x_phy_data *phy_data = efx->phy_data; if (efx->phy_type == PHY_TYPE_QT2025C) { + int rc = qt2025c_select_phy_mode(efx); + if (rc) + return rc; + /* There are several different register bits which can * disable TX (and save power) on direct-attach cables * or optical transceivers, varying somewhat between @@ -224,7 +435,7 @@ static void qt202x_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecm mdio45_ethtool_gset(&efx->mdio, ecmd); } -static void qt202x_phy_fini(struct efx_nic *efx) +static void qt202x_phy_remove(struct efx_nic *efx) { /* Free the context block */ kfree(efx->phy_data); @@ -236,7 +447,8 @@ struct efx_phy_operations falcon_qt202x_phy_ops = { .init = qt202x_phy_init, .reconfigure = qt202x_phy_reconfigure, .poll = qt202x_phy_poll, - .fini = qt202x_phy_fini, + .fini = efx_port_dummy_op_void, + .remove = qt202x_phy_remove, .get_settings = qt202x_phy_get_settings, .set_settings = efx_mdio_set_settings, }; diff --git a/drivers/net/sfc/selftest.c b/drivers/net/sfc/selftest.c index 14949bb303a..250c8827b84 100644 --- a/drivers/net/sfc/selftest.c +++ b/drivers/net/sfc/selftest.c @@ -47,7 +47,7 @@ static const unsigned char payload_source[ETH_ALEN] = { 0x00, 0x0f, 0x53, 0x1b, 0x1b, 0x1b, }; -static const char *payload_msg = +static const char payload_msg[] = "Hello world! This is an Efx loopback test in progress!"; /** @@ -79,10 +79,14 @@ struct efx_loopback_state { static int efx_test_mdio(struct efx_nic *efx, struct efx_self_tests *tests) { int rc = 0; - int devad = __ffs(efx->mdio.mmds); + int devad; u16 physid1, physid2; - if (efx->phy_type == PHY_TYPE_NONE) + if (efx->mdio.mode_support & MDIO_SUPPORTS_C45) + devad = __ffs(efx->mdio.mmds); + else if (efx->mdio.mode_support & MDIO_SUPPORTS_C22) + devad = MDIO_DEVAD_NONE; + else return 0; mutex_lock(&efx->mac_lock); diff --git a/drivers/net/sfc/siena.c b/drivers/net/sfc/siena.c index de07a4f031b..f8c6771e66d 100644 --- a/drivers/net/sfc/siena.c +++ b/drivers/net/sfc/siena.c @@ -133,6 +133,7 @@ static int siena_probe_port(struct efx_nic *efx) void siena_remove_port(struct efx_nic *efx) { + efx->phy_op->remove(efx); efx_nic_free_buffer(efx, &efx->stats_buffer); } diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index ca11572a49a..3009c297c13 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -202,10 +202,14 @@ static ssize_t set_phy_short_reach(struct device *dev, int rc; rtnl_lock(); - efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TXPWR, - MDIO_PMA_10GBT_TXPWR_SHORT, - count != 0 && *buf != '0'); - rc = efx_reconfigure_port(efx); + if (efx->state != STATE_RUNNING) { + rc = -EBUSY; + } else { + efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TXPWR, + MDIO_PMA_10GBT_TXPWR_SHORT, + count != 0 && *buf != '0'); + rc = efx_reconfigure_port(efx); + } rtnl_unlock(); return rc < 0 ? rc : (ssize_t)count; @@ -298,36 +302,62 @@ static int tenxpress_init(struct efx_nic *efx) return 0; } -static int sfx7101_phy_probe(struct efx_nic *efx) +static int tenxpress_phy_probe(struct efx_nic *efx) { - efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; - efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; - efx->loopback_modes = SFX7101_LOOPBACKS | FALCON_XMAC_LOOPBACKS; - return 0; -} + struct tenxpress_phy_data *phy_data; + int rc; + + /* Allocate phy private storage */ + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + efx->phy_data = phy_data; + phy_data->phy_mode = efx->phy_mode; + + /* Create any special files */ + if (efx->phy_type == PHY_TYPE_SFT9001B) { + rc = device_create_file(&efx->pci_dev->dev, + &dev_attr_phy_short_reach); + if (rc) + goto fail; + } + + if (efx->phy_type == PHY_TYPE_SFX7101) { + efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45; + + efx->loopback_modes = SFX7101_LOOPBACKS | FALCON_XMAC_LOOPBACKS; + + efx->link_advertising = (ADVERTISED_TP | ADVERTISED_Autoneg | + ADVERTISED_10000baseT_Full); + } else { + efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + + efx->loopback_modes = (SFT9001_LOOPBACKS | + FALCON_XMAC_LOOPBACKS | + FALCON_GMAC_LOOPBACKS); + + efx->link_advertising = (ADVERTISED_TP | ADVERTISED_Autoneg | + ADVERTISED_10000baseT_Full | + ADVERTISED_1000baseT_Full | + ADVERTISED_100baseT_Full); + } -static int sft9001_phy_probe(struct efx_nic *efx) -{ - efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; - efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; - efx->loopback_modes = (SFT9001_LOOPBACKS | FALCON_XMAC_LOOPBACKS | - FALCON_GMAC_LOOPBACKS); return 0; + +fail: + kfree(efx->phy_data); + efx->phy_data = NULL; + return rc; } static int tenxpress_phy_init(struct efx_nic *efx) { - struct tenxpress_phy_data *phy_data; - int rc = 0; + int rc; falcon_board(efx)->type->init_phy(efx); - phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); - if (!phy_data) - return -ENOMEM; - efx->phy_data = phy_data; - phy_data->phy_mode = efx->phy_mode; - if (!(efx->phy_mode & PHY_MODE_SPECIAL)) { if (efx->phy_type == PHY_TYPE_SFT9001A) { int reg; @@ -341,44 +371,27 @@ static int tenxpress_phy_init(struct efx_nic *efx) rc = efx_mdio_wait_reset_mmds(efx, TENXPRESS_REQUIRED_DEVS); if (rc < 0) - goto fail; + return rc; rc = efx_mdio_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0); if (rc < 0) - goto fail; + return rc; } rc = tenxpress_init(efx); if (rc < 0) - goto fail; + return rc; - /* Initialise advertising flags */ - efx->link_advertising = (ADVERTISED_TP | ADVERTISED_Autoneg | - ADVERTISED_10000baseT_Full); - if (efx->phy_type != PHY_TYPE_SFX7101) - efx->link_advertising |= (ADVERTISED_1000baseT_Full | - ADVERTISED_100baseT_Full); + /* Reinitialise flow control settings */ efx_link_set_wanted_fc(efx, efx->wanted_fc); efx_mdio_an_reconfigure(efx); - if (efx->phy_type == PHY_TYPE_SFT9001B) { - rc = device_create_file(&efx->pci_dev->dev, - &dev_attr_phy_short_reach); - if (rc) - goto fail; - } - schedule_timeout_uninterruptible(HZ / 5); /* 200ms */ /* Let XGXS and SerDes out of reset */ falcon_reset_xaui(efx); return 0; - - fail: - kfree(efx->phy_data); - efx->phy_data = NULL; - return rc; } /* Perform a "special software reset" on the PHY. The caller is @@ -589,25 +602,26 @@ static bool tenxpress_phy_poll(struct efx_nic *efx) return !efx_link_state_equal(&efx->link_state, &old_state); } -static void tenxpress_phy_fini(struct efx_nic *efx) +static void sfx7101_phy_fini(struct efx_nic *efx) { int reg; + /* Power down the LNPGA */ + reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN); + efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, reg); + + /* Waiting here ensures that the board fini, which can turn + * off the power to the PHY, won't get run until the LNPGA + * powerdown has been given long enough to complete. */ + schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */ +} + +static void tenxpress_phy_remove(struct efx_nic *efx) +{ if (efx->phy_type == PHY_TYPE_SFT9001B) device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_short_reach); - if (efx->phy_type == PHY_TYPE_SFX7101) { - /* Power down the LNPGA */ - reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN); - efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, reg); - - /* Waiting here ensures that the board fini, which can turn - * off the power to the PHY, won't get run until the LNPGA - * powerdown has been given long enough to complete. */ - schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */ - } - kfree(efx->phy_data); efx->phy_data = NULL; } @@ -819,11 +833,12 @@ static void sft9001_set_npage_adv(struct efx_nic *efx, u32 advertising) } struct efx_phy_operations falcon_sfx7101_phy_ops = { - .probe = sfx7101_phy_probe, + .probe = tenxpress_phy_probe, .init = tenxpress_phy_init, .reconfigure = tenxpress_phy_reconfigure, .poll = tenxpress_phy_poll, - .fini = tenxpress_phy_fini, + .fini = sfx7101_phy_fini, + .remove = tenxpress_phy_remove, .get_settings = tenxpress_get_settings, .set_settings = tenxpress_set_settings, .set_npage_adv = sfx7101_set_npage_adv, @@ -832,11 +847,12 @@ struct efx_phy_operations falcon_sfx7101_phy_ops = { }; struct efx_phy_operations falcon_sft9001_phy_ops = { - .probe = sft9001_phy_probe, + .probe = tenxpress_phy_probe, .init = tenxpress_phy_init, .reconfigure = tenxpress_phy_reconfigure, .poll = tenxpress_phy_poll, - .fini = tenxpress_phy_fini, + .fini = efx_port_dummy_op_void, + .remove = tenxpress_phy_remove, .get_settings = tenxpress_get_settings, .set_settings = tenxpress_set_settings, .set_npage_adv = sft9001_set_npage_adv, diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c index e669f94e821..a8b70ef6d81 100644 --- a/drivers/net/sfc/tx.c +++ b/drivers/net/sfc/tx.c @@ -821,8 +821,6 @@ static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue) EFX_TXQ_MASK]; efx_tsoh_free(tx_queue, buffer); EFX_BUG_ON_PARANOID(buffer->skb); - buffer->len = 0; - buffer->continuation = true; if (buffer->unmap_len) { unmap_addr = (buffer->dma_addr + buffer->len - buffer->unmap_len); @@ -836,6 +834,8 @@ static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue) PCI_DMA_TODEVICE); buffer->unmap_len = 0; } + buffer->len = 0; + buffer->continuation = true; } } |