From 54b1af6bf9caede6a629fe9ca5071b4d48437b26 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 7 Jan 2010 19:21:53 +0000 Subject: [PATCH] sfc: Apply fixes from 2.6.33-rc3 svn path=/dists/trunk/linux-2.6/; revision=14897 --- debian/changelog | 7 + ...able-TX-descriptor-prefetch-watchdog.patch | 29 + ...-DMA-mapping-cleanup-on-error-in-TSO.patch | 40 ++ ...clude-XGXS-in-XMAC-link-status-check.patch | 121 +++++ ...ve-PHY-software-state-initialisation.patch | 510 ++++++++++++++++++ ...-message-for-suspected-bad-SFP-cable.patch | 37 ++ ...025C-Switch-into-self-configure-mode.patch | 185 +++++++ .../all/sfc-QT2025C-Work-around-PHY-bug.patch | 97 ++++ ...k-around-PHY-firmware-initialisation.patch | 119 ++++ debian/patches/series/5 | 8 + 10 files changed, 1153 insertions(+) create mode 100644 debian/patches/bugfix/all/sfc-Disable-TX-descriptor-prefetch-watchdog.patch create mode 100644 debian/patches/bugfix/all/sfc-Fix-DMA-mapping-cleanup-on-error-in-TSO.patch create mode 100644 debian/patches/bugfix/all/sfc-Include-XGXS-in-XMAC-link-status-check.patch create mode 100644 debian/patches/bugfix/all/sfc-Move-PHY-software-state-initialisation.patch create mode 100644 debian/patches/bugfix/all/sfc-QT2025C-Add-error-message-for-suspected-bad-SFP-cable.patch create mode 100644 debian/patches/bugfix/all/sfc-QT2025C-Switch-into-self-configure-mode.patch create mode 100644 debian/patches/bugfix/all/sfc-QT2025C-Work-around-PHY-bug.patch create mode 100644 debian/patches/bugfix/all/sfc-QT2025C-Work-around-PHY-firmware-initialisation.patch create mode 100644 debian/patches/series/5 diff --git a/debian/changelog b/debian/changelog index 5bbe668a9..16585d769 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +linux-2.6 (2.6.32-5) UNRELEASED; urgency=low + + [ Ben Hutchings ] + * sfc: Apply fixes from 2.6.33-rc3 + + -- Ben Hutchings Thu, 07 Jan 2010 19:19:32 +0000 + linux-2.6 (2.6.32-4) unstable; urgency=low [ Ben Hutchings ] diff --git a/debian/patches/bugfix/all/sfc-Disable-TX-descriptor-prefetch-watchdog.patch b/debian/patches/bugfix/all/sfc-Disable-TX-descriptor-prefetch-watchdog.patch new file mode 100644 index 000000000..a7590ed33 --- /dev/null +++ b/debian/patches/bugfix/all/sfc-Disable-TX-descriptor-prefetch-watchdog.patch @@ -0,0 +1,29 @@ +From 286d47ba90315a871f77351f7f61b7e4a96476a9 Mon Sep 17 00:00:00 2001 +From: Ben Hutchings +Date: Wed, 23 Dec 2009 13:49:13 +0000 +Subject: [PATCH 8/8] sfc: Disable TX descriptor prefetch watchdog + +This hardware watchdog can misfire, so it does more harm than good. + +Signed-off-by: Ben Hutchings +Signed-off-by: David S. Miller +--- + drivers/net/sfc/nic.c | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c +index a577be2..db44224 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); +-- +1.6.5.7 + diff --git a/debian/patches/bugfix/all/sfc-Fix-DMA-mapping-cleanup-on-error-in-TSO.patch b/debian/patches/bugfix/all/sfc-Fix-DMA-mapping-cleanup-on-error-in-TSO.patch new file mode 100644 index 000000000..b1efadedf --- /dev/null +++ b/debian/patches/bugfix/all/sfc-Fix-DMA-mapping-cleanup-on-error-in-TSO.patch @@ -0,0 +1,40 @@ +From a7ebd27a13757248863cd61e541af7fa9e7727ee Mon Sep 17 00:00:00 2001 +From: Neil Turton +Date: Wed, 23 Dec 2009 13:47:13 +0000 +Subject: [PATCH 3/8] sfc: Fix DMA mapping cleanup in case of an error in TSO + +We need buffer->len to remain valid to work out the correct address to +be unmapped. We therefore need to clear buffer->len after the unmap +operation. + +Signed-off-by: Ben Hutchings +Signed-off-by: David S. Miller +--- + drivers/net/sfc/tx.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c +index e669f94..a8b70ef 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; + } + } + +-- +1.6.5.7 + diff --git a/debian/patches/bugfix/all/sfc-Include-XGXS-in-XMAC-link-status-check.patch b/debian/patches/bugfix/all/sfc-Include-XGXS-in-XMAC-link-status-check.patch new file mode 100644 index 000000000..e3a1e00c6 --- /dev/null +++ b/debian/patches/bugfix/all/sfc-Include-XGXS-in-XMAC-link-status-check.patch @@ -0,0 +1,121 @@ +From a355020af415947c7dee7e00a91360d11b6a9b47 Mon Sep 17 00:00:00 2001 +From: Ben Hutchings +Date: Wed, 23 Dec 2009 13:46:47 +0000 +Subject: [PATCH 2/8] sfc: Include XGXS in XMAC link status check except in XGMII loopback + +The XGXS block may not get a link immediately in XGXS or XAUI loopback +modes, so we still need to check it. Split falcon_xaui_link_ok() into +falcon_xgxs_link_ok(), which checks only the Falcon XGXS block, and +falcon_xmac_link_ok(), which checks one or both sides of the link as +appropriate. Also rename falcon_check_xaui_link() to +falcon_xmac_link_ok_retry(). + +Signed-off-by: Ben Hutchings +Signed-off-by: David S. Miller +--- + drivers/net/sfc/falcon_xmac.c | 38 ++++++++++++++++++++++---------------- + 1 files changed, 22 insertions(+), 16 deletions(-) + +diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c +index 3da933f..8ccab2c 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); + } + +-- +1.6.5.7 + diff --git a/debian/patches/bugfix/all/sfc-Move-PHY-software-state-initialisation.patch b/debian/patches/bugfix/all/sfc-Move-PHY-software-state-initialisation.patch new file mode 100644 index 000000000..86dc127ae --- /dev/null +++ b/debian/patches/bugfix/all/sfc-Move-PHY-software-state-initialisation.patch @@ -0,0 +1,510 @@ +From ff3b00a0fcaab89ff885e9f0f4ad83c4ced788f4 Mon Sep 17 00:00:00 2001 +From: Steve Hodgson +Date: Wed, 23 Dec 2009 13:46:36 +0000 +Subject: [PATCH 1/8] sfc: Move PHY software state initialisation from init() into probe() + +This prevents efx->link_advertising from being blatted during +a reset. + +The phy_short_reach sysfs node is now destroyed later in the +port shutdown process, so check for STATE_RUNNING after +acquiring the rtnl_lock (just like in set_phy_flash_cfg). + +Signed-off-by: Ben Hutchings +Signed-off-by: David S. Miller +--- + drivers/net/sfc/efx.c | 6 +- + drivers/net/sfc/falcon.c | 1 + + drivers/net/sfc/mcdi_phy.c | 93 +++++++++++------------------ + drivers/net/sfc/net_driver.h | 1 + + drivers/net/sfc/qt202x_phy.c | 20 +++--- + drivers/net/sfc/siena.c | 1 + + drivers/net/sfc/tenxpress.c | 138 +++++++++++++++++++++++------------------ + 7 files changed, 129 insertions(+), 131 deletions(-) + +diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c +index f983e3b..103e8b0 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 17afcd2..9d009c4 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/mcdi_phy.c b/drivers/net/sfc/mcdi_phy.c +index 0e1bcc5..eb694af 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 34c381f..d5aab5b 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/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c +index 3800fc7..7450e3a 100644 +--- a/drivers/net/sfc/qt202x_phy.c ++++ b/drivers/net/sfc/qt202x_phy.c +@@ -137,6 +137,14 @@ 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; ++ + 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 +153,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 +162,11 @@ 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; + return 0; + } + +@@ -224,7 +225,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 +237,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/siena.c b/drivers/net/sfc/siena.c +index de07a4f..f8c6771 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 ca11572..3009c29 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, +-- +1.6.5.7 + diff --git a/debian/patches/bugfix/all/sfc-QT2025C-Add-error-message-for-suspected-bad-SFP-cable.patch b/debian/patches/bugfix/all/sfc-QT2025C-Add-error-message-for-suspected-bad-SFP-cable.patch new file mode 100644 index 000000000..47e66d2c1 --- /dev/null +++ b/debian/patches/bugfix/all/sfc-QT2025C-Add-error-message-for-suspected-bad-SFP-cable.patch @@ -0,0 +1,37 @@ +From 50d6ec552bdd4d9227fe9ed2bac819eced3170ac Mon Sep 17 00:00:00 2001 +From: Ben Hutchings +Date: Wed, 23 Dec 2009 13:48:42 +0000 +Subject: [PATCH 7/8] sfc: QT2025C: Add error message for suspected bad SFP+ cables + +Some cables have EEPROMs that conflict with the PHY's on-board EEPROM +so it cannot load firmware. + +Signed-off-by: Ben Hutchings +Signed-off-by: David S. Miller +--- + drivers/net/sfc/qt202x_phy.c | 8 +++++++- + 1 files changed, 7 insertions(+), 1 deletions(-) + +diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c +index 326ffa4..ff8f0a4 100644 +--- a/drivers/net/sfc/qt202x_phy.c ++++ b/drivers/net/sfc/qt202x_phy.c +@@ -87,8 +87,14 @@ static int qt2025c_wait_heartbeat(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(QT2025C_HEARTB_WAIT); + } + +-- +1.6.5.7 + diff --git a/debian/patches/bugfix/all/sfc-QT2025C-Switch-into-self-configure-mode.patch b/debian/patches/bugfix/all/sfc-QT2025C-Switch-into-self-configure-mode.patch new file mode 100644 index 000000000..9ab24a0a9 --- /dev/null +++ b/debian/patches/bugfix/all/sfc-QT2025C-Switch-into-self-configure-mode.patch @@ -0,0 +1,185 @@ +From 0d83b2f64c330ee3892cb3117ac5d56e97185ecf Mon Sep 17 00:00:00 2001 +From: Matthew Slattery +Date: Wed, 23 Dec 2009 13:48:04 +0000 +Subject: [PATCH 5/8] sfc: QT2025C: Switch into self-configure mode when not in loopback + +The PHY boots in a mode which is not necessarily optimal. This change +switches it to self-configure mode (except when in loopback, which +won't work in that mode if an SFP+ module is not present) by rebooting +the PHY's microcontroller, and replicating the sequence of configuration +writes from the boot EEPROM with the appropriate changes. + +Signed-off-by: Ben Hutchings +Signed-off-by: David S. Miller +--- + drivers/net/sfc/qt202x_phy.c | 119 ++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 119 insertions(+), 0 deletions(-) + +diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c +index e4590fb..0cd6eed 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 +@@ -54,6 +57,7 @@ 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 +@@ -100,6 +104,25 @@ static int qt2025c_wait_reset(struct efx_nic *efx) + return 0; + } + ++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; +@@ -133,6 +156,95 @@ static void qt2025c_bug17190_workaround(struct efx_nic *efx) + } + } + ++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 from RAM */ ++ efx_mdio_write(efx, 3, 0xe854, 0x00c0); ++ efx_mdio_write(efx, 3, 0xe854, 0x0040); ++ msleep(50); ++ ++ /* 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; ++} ++ + static int qt202x_reset_phy(struct efx_nic *efx) + { + int rc; +@@ -206,6 +318,9 @@ static int qt202x_phy_init(struct efx_nic *efx) + devid, efx_mdio_id_oui(devid), efx_mdio_id_model(devid), + efx_mdio_id_rev(devid)); + ++ if (efx->phy_type == PHY_TYPE_QT2025C) ++ qt2025c_firmware_id(efx); ++ + return 0; + } + +@@ -234,6 +349,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 +-- +1.6.5.7 + diff --git a/debian/patches/bugfix/all/sfc-QT2025C-Work-around-PHY-bug.patch b/debian/patches/bugfix/all/sfc-QT2025C-Work-around-PHY-bug.patch new file mode 100644 index 000000000..764fae3b7 --- /dev/null +++ b/debian/patches/bugfix/all/sfc-QT2025C-Work-around-PHY-bug.patch @@ -0,0 +1,97 @@ +From 17d6aeafe9d8268612d91edc0102659edb382282 Mon Sep 17 00:00:00 2001 +From: Matthew Slattery +Date: Wed, 23 Dec 2009 13:47:37 +0000 +Subject: [PATCH 4/8] sfc: QT2025C: Work around PHY bug + +If we see the PHY remaining stuck in a link-down state due to PCS being +down while PMA/PMD is up, we briefly switch to PMA/PMD loopback and back, +which usually unsticks it. + +Signed-off-by: Ben Hutchings +Signed-off-by: David S. Miller +--- + drivers/net/sfc/qt202x_phy.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 42 insertions(+), 0 deletions(-) + +diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c +index 7450e3a..e4590fb 100644 +--- a/drivers/net/sfc/qt202x_phy.c ++++ b/drivers/net/sfc/qt202x_phy.c +@@ -52,11 +52,15 @@ 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; + }; + + #define QT2022C2_MAX_RESET_TIME 500 + #define QT2022C2_RESET_WAIT 10 + ++#define BUG17190_INTERVAL (2 * HZ) ++ + static int qt2025c_wait_reset(struct efx_nic *efx) + { + unsigned long timeout = jiffies + 10 * HZ; +@@ -96,6 +100,39 @@ static int qt2025c_wait_reset(struct efx_nic *efx) + return 0; + } + ++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 qt202x_reset_phy(struct efx_nic *efx) + { + int rc; +@@ -144,6 +181,8 @@ static int qt202x_phy_probe(struct efx_nic *efx) + 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; +@@ -184,6 +223,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; + } + +-- +1.6.5.7 + diff --git a/debian/patches/bugfix/all/sfc-QT2025C-Work-around-PHY-firmware-initialisation.patch b/debian/patches/bugfix/all/sfc-QT2025C-Work-around-PHY-firmware-initialisation.patch new file mode 100644 index 000000000..f1e006c08 --- /dev/null +++ b/debian/patches/bugfix/all/sfc-QT2025C-Work-around-PHY-firmware-initialisation.patch @@ -0,0 +1,119 @@ +From 1a1284ef97ca79ba747d211b697e996a248a8555 Mon Sep 17 00:00:00 2001 +From: Matthew Slattery +Date: Wed, 23 Dec 2009 13:48:32 +0000 +Subject: [PATCH 6/8] 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 +Signed-off-by: David S. Miller +--- + drivers/net/sfc/qt202x_phy.c | 59 ++++++++++++++++++++++++++++++++++++----- + 1 files changed, 51 insertions(+), 8 deletions(-) + +diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c +index 0cd6eed..326ffa4 100644 +--- a/drivers/net/sfc/qt202x_phy.c ++++ b/drivers/net/sfc/qt202x_phy.c +@@ -63,11 +63,16 @@ struct qt202x_phy_data { + #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 @@ static int qt2025c_wait_reset(struct efx_nic *efx) + 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 @@ static int qt2025c_wait_reset(struct efx_nic *efx) + 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 @@ static int qt2025c_select_phy_mode(struct efx_nic *efx) + 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); +-- +1.6.5.7 + diff --git a/debian/patches/series/5 b/debian/patches/series/5 new file mode 100644 index 000000000..068dbbb62 --- /dev/null +++ b/debian/patches/series/5 @@ -0,0 +1,8 @@ ++ bugfix/all/sfc-Move-PHY-software-state-initialisation.patch ++ bugfix/all/sfc-Include-XGXS-in-XMAC-link-status-check.patch ++ bugfix/all/sfc-Fix-DMA-mapping-cleanup-on-error-in-TSO.patch ++ bugfix/all/sfc-QT2025C-Work-around-PHY-bug.patch ++ bugfix/all/sfc-QT2025C-Switch-into-self-configure-mode.patch ++ bugfix/all/sfc-QT2025C-Work-around-PHY-firmware-initialisation.patch ++ bugfix/all/sfc-QT2025C-Add-error-message-for-suspected-bad-SFP-cable.patch ++ bugfix/all/sfc-Disable-TX-descriptor-prefetch-watchdog.patch