From 3e133c44d24a094118caee182200462d46c55b56 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 4 Nov 2008 20:34:56 +0000 Subject: sfc: Use lm87 and lm90 drivers for board temperature/power monitoring Add board monitoring to periodic work whenever link is down. For SFE4001, report when a fault has caused the PHY to turn off. For SFE4002, switch XFP PHY into low-power state in case of a fault. Signed-off-by: Ben Hutchings Signed-off-by: Jeff Garzik --- drivers/net/sfc/xfp_phy.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/net/sfc/xfp_phy.c') diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/xfp_phy.c index 276151df3a7..91f02466210 100644 --- a/drivers/net/sfc/xfp_phy.c +++ b/drivers/net/sfc/xfp_phy.c @@ -128,6 +128,15 @@ static int xfp_phy_check_hw(struct efx_nic *efx) if (link_up != efx->link_up) falcon_xmac_sim_phy_event(efx); + rc = efx->board_info.monitor(efx); + if (rc) { + struct xfp_phy_data *phy_data = efx->phy_data; + EFX_ERR(efx, "XFP sensor alert; putting PHY into low power\n"); + efx->phy_mode |= PHY_MODE_LOW_POWER; + mdio_clause45_set_mmds_lpower(efx, 1, XFP_REQUIRED_DEVS); + phy_data->phy_mode |= PHY_MODE_LOW_POWER; + } + return rc; } -- cgit v1.2.3-70-g09d2 From f31a45d2f45a7667acd6e85ab6613b0910c55ea9 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 12 Dec 2008 21:43:33 -0800 Subject: sfc: Remove MII extension cruft Replace efx_nic::link_options bitfield with link_speed (speed in Mbit/s) and link_fd (full duplex flag). Remove broken auto-negotiation functions. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/efx.c | 23 +------ drivers/net/sfc/ethtool.c | 1 - drivers/net/sfc/falcon.c | 16 +++-- drivers/net/sfc/falcon_xmac.c | 1 - drivers/net/sfc/gmii.h | 137 +----------------------------------------- drivers/net/sfc/net_driver.h | 6 +- drivers/net/sfc/tenxpress.c | 4 +- drivers/net/sfc/xfp_phy.c | 4 +- 8 files changed, 18 insertions(+), 174 deletions(-) (limited to 'drivers/net/sfc/xfp_phy.c') diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index bba11490ef4..957a6320bbc 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -21,7 +21,6 @@ #include #include #include "net_driver.h" -#include "gmii.h" #include "ethtool.h" #include "tx.h" #include "rx.h" @@ -551,26 +550,8 @@ static void efx_link_status_changed(struct efx_nic *efx) /* Status message for kernel log */ if (efx->link_up) { - struct mii_if_info *gmii = &efx->mii; - unsigned adv, lpa; - /* NONE here means direct XAUI from the controller, with no - * MDIO-attached device we can query. */ - if (efx->phy_type != PHY_TYPE_NONE) { - adv = gmii_advertised(gmii); - lpa = gmii_lpa(gmii); - } else { - lpa = GM_LPA_10000 | LPA_DUPLEX; - adv = lpa; - } - EFX_INFO(efx, "link up at %dMbps %s-duplex " - "(adv %04x lpa %04x) (MTU %d)%s\n", - (efx->link_options & GM_LPA_10000 ? 10000 : - (efx->link_options & GM_LPA_1000 ? 1000 : - (efx->link_options & GM_LPA_100 ? 100 : - 10))), - (efx->link_options & GM_LPA_DUPLEX ? - "full" : "half"), - adv, lpa, + EFX_INFO(efx, "link up at %uMbps %s-duplex (MTU %d)%s\n", + efx->link_speed, efx->link_fd ? "full" : "half", efx->net_dev->mtu, (efx->promiscuous ? " [PROMISC]" : "")); } else { diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 931ce141048..43d6d8b4429 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -16,7 +16,6 @@ #include "efx.h" #include "ethtool.h" #include "falcon.h" -#include "gmii.h" #include "spi.h" #include "mac.h" diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index 97cc037a10c..448bba9eed0 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -15,11 +15,11 @@ #include #include #include +#include #include "net_driver.h" #include "bitfield.h" #include "efx.h" #include "mac.h" -#include "gmii.h" #include "spi.h" #include "falcon.h" #include "falcon_hwdefs.h" @@ -1915,14 +1915,12 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) int link_speed; bool tx_fc; - if (efx->link_options & GM_LPA_10000) - link_speed = 0x3; - else if (efx->link_options & GM_LPA_1000) - link_speed = 0x2; - else if (efx->link_options & GM_LPA_100) - link_speed = 0x1; - else - link_speed = 0x0; + switch (efx->link_speed) { + case 10000: link_speed = 3; break; + case 1000: link_speed = 2; break; + case 100: link_speed = 1; break; + default: link_speed = 0; break; + } /* MAC_LINK_STATUS controls MAC backpressure but doesn't work * as advertised. Disable to ensure packets are not * indefinitely held and TX queue can be flushed at any point diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c index d4012314dd0..4a54d0933e7 100644 --- a/drivers/net/sfc/falcon_xmac.c +++ b/drivers/net/sfc/falcon_xmac.c @@ -15,7 +15,6 @@ #include "falcon_hwdefs.h" #include "falcon_io.h" #include "mac.h" -#include "gmii.h" #include "mdio_10g.h" #include "phy.h" #include "boards.h" diff --git a/drivers/net/sfc/gmii.h b/drivers/net/sfc/gmii.h index d25bbd1297f..dfccaa7b573 100644 --- a/drivers/net/sfc/gmii.h +++ b/drivers/net/sfc/gmii.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006 Solarflare Communications Inc. + * Copyright 2006-2008 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -57,139 +57,4 @@ #define ISR_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */ #define ISR_JABBER 0x0001 /* Bit 0 - jabber */ -/* Logically extended advertisement register */ -#define GM_ADVERTISE_SLCT ADVERTISE_SLCT -#define GM_ADVERTISE_CSMA ADVERTISE_CSMA -#define GM_ADVERTISE_10HALF ADVERTISE_10HALF -#define GM_ADVERTISE_1000XFULL ADVERTISE_1000XFULL -#define GM_ADVERTISE_10FULL ADVERTISE_10FULL -#define GM_ADVERTISE_1000XHALF ADVERTISE_1000XHALF -#define GM_ADVERTISE_100HALF ADVERTISE_100HALF -#define GM_ADVERTISE_1000XPAUSE ADVERTISE_1000XPAUSE -#define GM_ADVERTISE_100FULL ADVERTISE_100FULL -#define GM_ADVERTISE_1000XPSE_ASYM ADVERTISE_1000XPSE_ASYM -#define GM_ADVERTISE_100BASE4 ADVERTISE_100BASE4 -#define GM_ADVERTISE_PAUSE_CAP ADVERTISE_PAUSE_CAP -#define GM_ADVERTISE_PAUSE_ASYM ADVERTISE_PAUSE_ASYM -#define GM_ADVERTISE_RESV ADVERTISE_RESV -#define GM_ADVERTISE_RFAULT ADVERTISE_RFAULT -#define GM_ADVERTISE_LPACK ADVERTISE_LPACK -#define GM_ADVERTISE_NPAGE ADVERTISE_NPAGE -#define GM_ADVERTISE_1000FULL (ADVERTISE_1000FULL << 8) -#define GM_ADVERTISE_1000HALF (ADVERTISE_1000HALF << 8) -#define GM_ADVERTISE_1000 (GM_ADVERTISE_1000FULL | \ - GM_ADVERTISE_1000HALF) -#define GM_ADVERTISE_FULL (GM_ADVERTISE_1000FULL | \ - ADVERTISE_FULL) -#define GM_ADVERTISE_ALL (GM_ADVERTISE_1000FULL | \ - GM_ADVERTISE_1000HALF | \ - ADVERTISE_ALL) - -/* Logically extended link partner ability register */ -#define GM_LPA_SLCT LPA_SLCT -#define GM_LPA_10HALF LPA_10HALF -#define GM_LPA_1000XFULL LPA_1000XFULL -#define GM_LPA_10FULL LPA_10FULL -#define GM_LPA_1000XHALF LPA_1000XHALF -#define GM_LPA_100HALF LPA_100HALF -#define GM_LPA_1000XPAUSE LPA_1000XPAUSE -#define GM_LPA_100FULL LPA_100FULL -#define GM_LPA_1000XPAUSE_ASYM LPA_1000XPAUSE_ASYM -#define GM_LPA_100BASE4 LPA_100BASE4 -#define GM_LPA_PAUSE_CAP LPA_PAUSE_CAP -#define GM_LPA_PAUSE_ASYM LPA_PAUSE_ASYM -#define GM_LPA_RESV LPA_RESV -#define GM_LPA_RFAULT LPA_RFAULT -#define GM_LPA_LPACK LPA_LPACK -#define GM_LPA_NPAGE LPA_NPAGE -#define GM_LPA_1000FULL (LPA_1000FULL << 6) -#define GM_LPA_1000HALF (LPA_1000HALF << 6) -#define GM_LPA_10000FULL 0x00040000 -#define GM_LPA_10000HALF 0x00080000 -#define GM_LPA_DUPLEX (GM_LPA_1000FULL | GM_LPA_10000FULL \ - | LPA_DUPLEX) -#define GM_LPA_10 (LPA_10FULL | LPA_10HALF) -#define GM_LPA_100 LPA_100 -#define GM_LPA_1000 (GM_LPA_1000FULL | GM_LPA_1000HALF) -#define GM_LPA_10000 (GM_LPA_10000FULL | GM_LPA_10000HALF) - -/* Retrieve GMII autonegotiation advertised abilities - * - * The MII advertisment register (MII_ADVERTISE) is logically extended - * to include advertisement bits ADVERTISE_1000FULL and - * ADVERTISE_1000HALF from MII_CTRL1000. The result can be tested - * against the GM_ADVERTISE_xxx constants. - */ -static inline unsigned int gmii_advertised(struct mii_if_info *gmii) -{ - unsigned int advertise; - unsigned int ctrl1000; - - advertise = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_ADVERTISE); - ctrl1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_CTRL1000); - return (((ctrl1000 << 8) & GM_ADVERTISE_1000) | advertise); -} - -/* Retrieve GMII autonegotiation link partner abilities - * - * The MII link partner ability register (MII_LPA) is logically - * extended by adding bits LPA_1000HALF and LPA_1000FULL from - * MII_STAT1000. The result can be tested against the GM_LPA_xxx - * constants. - */ -static inline unsigned int gmii_lpa(struct mii_if_info *gmii) -{ - unsigned int lpa; - unsigned int stat1000; - - lpa = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_LPA); - stat1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_STAT1000); - return (((stat1000 << 6) & GM_LPA_1000) | lpa); -} - -/* Calculate GMII autonegotiated link technology - * - * "negotiated" should be the result of gmii_advertised() logically - * ANDed with the result of gmii_lpa(). - * - * "tech" will be negotiated with the unused bits masked out. For - * example, if both ends of the link are capable of both - * GM_LPA_1000FULL and GM_LPA_100FULL, GM_LPA_100FULL will be masked - * out. - */ -static inline unsigned int gmii_nway_result(unsigned int negotiated) -{ - unsigned int other_bits; - - /* Mask out the speed and duplexity bits */ - other_bits = negotiated & ~(GM_LPA_10 | GM_LPA_100 | GM_LPA_1000); - - if (negotiated & GM_LPA_1000FULL) - return (other_bits | GM_LPA_1000FULL); - else if (negotiated & GM_LPA_1000HALF) - return (other_bits | GM_LPA_1000HALF); - else - return (other_bits | mii_nway_result(negotiated)); -} - -/* Calculate GMII non-autonegotiated link technology - * - * This provides an equivalent to gmii_nway_result for the case when - * autonegotiation is disabled. - */ -static inline unsigned int gmii_forced_result(unsigned int bmcr) -{ - unsigned int result; - int full_duplex; - - full_duplex = bmcr & BMCR_FULLDPLX; - if (bmcr & BMCR_SPEED1000) - result = full_duplex ? GM_LPA_1000FULL : GM_LPA_1000HALF; - else if (bmcr & BMCR_SPEED100) - result = full_duplex ? GM_LPA_100FULL : GM_LPA_100HALF; - else - result = full_duplex ? GM_LPA_10FULL : GM_LPA_10HALF; - return result; -} - #endif /* EFX_GMII_H */ diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index abff9084a98..6cac5ed427b 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -694,7 +694,8 @@ union efx_multicast_hash { * @mii: PHY interface * @phy_mode: PHY operating mode. Serialised by @mac_lock. * @link_up: Link status - * @link_options: Link options (MII/GMII format) + * @link_fd: Link is full duplex + * @link_speed: Link speed (Mbps) * @n_link_state_changes: Number of times the link has changed state * @promiscuous: Promiscuous flag. Protected by netif_tx_lock. * @multicast_hash: Multicast hash table @@ -772,7 +773,8 @@ struct efx_nic { enum efx_phy_mode phy_mode; bool link_up; - unsigned int link_options; + bool link_fd; + unsigned int link_speed; unsigned int n_link_state_changes; bool promiscuous; diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index 8d41c29b9d7..3fa7ccb08d5 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -10,7 +10,6 @@ #include #include #include "efx.h" -#include "gmii.h" #include "mdio_10g.h" #include "falcon.h" #include "phy.h" @@ -362,7 +361,8 @@ static void tenxpress_phy_reconfigure(struct efx_nic *efx) phy_data->loopback_mode = efx->loopback_mode; phy_data->phy_mode = efx->phy_mode; efx->link_up = tenxpress_link_ok(efx, false); - efx->link_options = GM_LPA_10000FULL; + efx->link_speed = 10000; + efx->link_fd = true; } static void tenxpress_phy_clear_interrupt(struct efx_nic *efx) diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/xfp_phy.c index 91f02466210..971a24b59fa 100644 --- a/drivers/net/sfc/xfp_phy.c +++ b/drivers/net/sfc/xfp_phy.c @@ -14,7 +14,6 @@ #include #include #include "efx.h" -#include "gmii.h" #include "mdio_10g.h" #include "xenpack.h" #include "phy.h" @@ -154,7 +153,8 @@ static void xfp_phy_reconfigure(struct efx_nic *efx) phy_data->phy_mode = efx->phy_mode; efx->link_up = xfp_link_ok(efx); - efx->link_options = GM_LPA_10000FULL; + efx->link_speed = 10000; + efx->link_fd = true; } -- cgit v1.2.3-70-g09d2 From 27dd2caca4eabe7c13a052b7456495ba75535e6a Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 12 Dec 2008 21:44:14 -0800 Subject: sfc: Add support for MMDs numbered >15 Combine DEVS0 and DEVS1 registers into a 32-bit mask instead of reading just DEVS0. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/mdio_10g.c | 24 +++++++++++++----------- drivers/net/sfc/mdio_10g.h | 8 ++++---- drivers/net/sfc/tenxpress.c | 6 +++--- drivers/net/sfc/xfp_phy.c | 8 ++++---- 4 files changed, 24 insertions(+), 22 deletions(-) (limited to 'drivers/net/sfc/xfp_phy.c') diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c index 19e25210b68..1eed48407ed 100644 --- a/drivers/net/sfc/mdio_10g.c +++ b/drivers/net/sfc/mdio_10g.c @@ -121,16 +121,18 @@ int mdio_clause45_wait_reset_mmds(struct efx_nic *efx, int mdio_clause45_check_mmds(struct efx_nic *efx, unsigned int mmd_mask, unsigned int fatal_mask) { - int devices, mmd = 0; - int probe_mmd; + u32 devices; + int mmd = 0, probe_mmd; /* Historically we have probed the PHYXS to find out what devices are * present,but that doesn't work so well if the PHYXS isn't expected * to exist, if so just find the first item in the list supplied. */ - probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS0_PHYXS) ? MDIO_MMD_PHYXS : + probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS_PHYXS) ? MDIO_MMD_PHYXS : __ffs(mmd_mask); - devices = mdio_clause45_read(efx, efx->mii.phy_id, - probe_mmd, MDIO_MMDREG_DEVS0); + devices = (mdio_clause45_read(efx, efx->mii.phy_id, + probe_mmd, MDIO_MMDREG_DEVS0) | + mdio_clause45_read(efx, efx->mii.phy_id, + probe_mmd, MDIO_MMDREG_DEVS1) << 16); /* Check all the expected MMDs are present */ if (devices < 0) { @@ -175,14 +177,14 @@ bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask) else if (efx_phy_mode_disabled(efx->phy_mode)) return false; else if (efx->loopback_mode == LOOPBACK_PHYXS) - mmd_mask &= ~(MDIO_MMDREG_DEVS0_PHYXS | - MDIO_MMDREG_DEVS0_PCS | - MDIO_MMDREG_DEVS0_PMAPMD); + mmd_mask &= ~(MDIO_MMDREG_DEVS_PHYXS | + MDIO_MMDREG_DEVS_PCS | + MDIO_MMDREG_DEVS_PMAPMD); else if (efx->loopback_mode == LOOPBACK_PCS) - mmd_mask &= ~(MDIO_MMDREG_DEVS0_PCS | - MDIO_MMDREG_DEVS0_PMAPMD); + mmd_mask &= ~(MDIO_MMDREG_DEVS_PCS | + MDIO_MMDREG_DEVS_PMAPMD); else if (efx->loopback_mode == LOOPBACK_PMAPMD) - mmd_mask &= ~MDIO_MMDREG_DEVS0_PMAPMD; + mmd_mask &= ~MDIO_MMDREG_DEVS_PMAPMD; while (mmd_mask) { if (mmd_mask & 1) { diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/sfc/mdio_10g.h index db9f358349c..45163838130 100644 --- a/drivers/net/sfc/mdio_10g.h +++ b/drivers/net/sfc/mdio_10g.h @@ -73,14 +73,14 @@ #define MDIO_ID_MODEL(_id32) ((_id32 >> 4) & 0x3f) #define MDIO_ID_OUI(_id32) (_id32 >> 10) -/* Bits in MMDREG_DEVS0. Someone thoughtfully layed things out +/* Bits in MMDREG_DEVS0/1. Someone thoughtfully layed things out * so the 'bit present' bit number of an MMD is the number of * that MMD */ #define DEV_PRESENT_BIT(_b) (1 << _b) -#define MDIO_MMDREG_DEVS0_PHYXS DEV_PRESENT_BIT(MDIO_MMD_PHYXS) -#define MDIO_MMDREG_DEVS0_PCS DEV_PRESENT_BIT(MDIO_MMD_PCS) -#define MDIO_MMDREG_DEVS0_PMAPMD DEV_PRESENT_BIT(MDIO_MMD_PMAPMD) +#define MDIO_MMDREG_DEVS_PHYXS DEV_PRESENT_BIT(MDIO_MMD_PHYXS) +#define MDIO_MMDREG_DEVS_PCS DEV_PRESENT_BIT(MDIO_MMD_PCS) +#define MDIO_MMDREG_DEVS_PMAPMD DEV_PRESENT_BIT(MDIO_MMD_PMAPMD) /* Bits in MMDREG_STAT2 */ #define MDIO_MMDREG_STAT2_PRESENT_VAL (2) diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index 3fa7ccb08d5..197b5449ab1 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -19,9 +19,9 @@ /* We expect these MMDs to be in the package */ /* AN not here as mdio_check_mmds() requires STAT2 support */ -#define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PMAPMD | \ - MDIO_MMDREG_DEVS0_PCS | \ - MDIO_MMDREG_DEVS0_PHYXS) +#define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS_PMAPMD | \ + MDIO_MMDREG_DEVS_PCS | \ + MDIO_MMDREG_DEVS_PHYXS) #define TENXPRESS_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ (1 << LOOPBACK_PCS) | \ diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/xfp_phy.c index 971a24b59fa..0413d135e4a 100644 --- a/drivers/net/sfc/xfp_phy.c +++ b/drivers/net/sfc/xfp_phy.c @@ -19,9 +19,9 @@ #include "phy.h" #include "mac.h" -#define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PCS | \ - MDIO_MMDREG_DEVS0_PMAPMD | \ - MDIO_MMDREG_DEVS0_PHYXS) +#define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS_PCS | \ + MDIO_MMDREG_DEVS_PMAPMD | \ + MDIO_MMDREG_DEVS_PHYXS) #define XFP_LOOPBACKS ((1 << LOOPBACK_PCS) | \ (1 << LOOPBACK_PMAPMD) | \ @@ -64,7 +64,7 @@ static int xfp_reset_phy(struct efx_nic *efx) /* Check that all the MMDs we expect are present and responding. We * expect faults on some if the link is down, but not on the PHY XS */ rc = mdio_clause45_check_mmds(efx, XFP_REQUIRED_DEVS, - MDIO_MMDREG_DEVS0_PHYXS); + MDIO_MMDREG_DEVS_PHYXS); if (rc < 0) goto fail; -- cgit v1.2.3-70-g09d2 From 177dfcd80f28f8fbc3e22c2d8b24d21cb86f1d97 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 12 Dec 2008 21:50:08 -0800 Subject: 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 Signed-off-by: David S. Miller --- drivers/net/sfc/Makefile | 4 +- drivers/net/sfc/efx.c | 62 +++++++---- drivers/net/sfc/enum.h | 29 ++--- drivers/net/sfc/ethtool.c | 38 +++---- drivers/net/sfc/falcon.c | 179 +++++++++++++++++++++++------- drivers/net/sfc/falcon.h | 3 + drivers/net/sfc/falcon_gmac.c | 233 ++++++++++++++++++++++++++++++++++++++++ drivers/net/sfc/falcon_hwdefs.h | 156 +++++++++++++++++++++++++++ drivers/net/sfc/falcon_xmac.c | 222 ++++++-------------------------------- drivers/net/sfc/mac.h | 16 +-- drivers/net/sfc/net_driver.h | 31 ++++++ drivers/net/sfc/selftest.c | 26 ++--- drivers/net/sfc/selftest.h | 2 - drivers/net/sfc/sfe4001.c | 2 +- drivers/net/sfc/tenxpress.c | 10 +- drivers/net/sfc/xfp_phy.c | 7 +- 16 files changed, 699 insertions(+), 321 deletions(-) create mode 100644 drivers/net/sfc/falcon_gmac.c (limited to 'drivers/net/sfc/xfp_phy.c') diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile index e507daa4f0e..b89f9be3cb1 100644 --- a/drivers/net/sfc/Makefile +++ b/drivers/net/sfc/Makefile @@ -1,5 +1,5 @@ -sfc-y += efx.o falcon.o tx.o rx.o falcon_xmac.o \ - selftest.o ethtool.o xfp_phy.o \ +sfc-y += efx.o falcon.o tx.o rx.o falcon_gmac.o \ + falcon_xmac.o selftest.o ethtool.o xfp_phy.o \ mdio_10g.o tenxpress.o boards.o sfe4001.o sfc-$(CONFIG_SFC_MTD) += mtd.o diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 90820d4046f..7214ea6f7e1 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -27,7 +27,6 @@ #include "efx.h" #include "mdio_10g.h" #include "falcon.h" -#include "mac.h" #define EFX_MAX_MTU (9 * 1024) @@ -575,10 +574,28 @@ void __efx_reconfigure_port(struct efx_nic *efx) netif_addr_unlock_bh(efx->net_dev); } - falcon_reconfigure_xmac(efx); + 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); + + if (falcon_switch_mac(efx)) + goto fail; + + efx->mac_op->reconfigure(efx); /* Inform kernel of loss/gain of carrier */ efx_link_status_changed(efx); + return; + +fail: + EFX_ERR(efx, "failed to reconfigure MAC\n"); + efx->phy_op->fini(efx); + efx->port_initialized = false; } /* Reinitialise the MAC to pick up new PHY settings, even if the port is @@ -648,18 +665,25 @@ static int efx_init_port(struct efx_nic *efx) EFX_LOG(efx, "init port\n"); - /* Initialise the MAC and PHY */ - rc = falcon_init_xmac(efx); + rc = efx->phy_op->init(efx); if (rc) return rc; + efx->phy_op->reconfigure(efx); + + mutex_lock(&efx->mac_lock); + rc = falcon_switch_mac(efx); + mutex_unlock(&efx->mac_lock); + if (rc) + goto fail; + efx->mac_op->reconfigure(efx); efx->port_initialized = true; efx->stats_enabled = true; - - /* Reconfigure port to program MAC registers */ - falcon_reconfigure_xmac(efx); - return 0; + +fail: + efx->phy_op->fini(efx); + return rc; } /* Allow efx_reconfigure_port() to be scheduled, and close the window @@ -702,7 +726,7 @@ static void efx_fini_port(struct efx_nic *efx) if (!efx->port_initialized) return; - falcon_fini_xmac(efx); + efx->phy_op->fini(efx); efx->port_initialized = false; efx->link_up = false; @@ -1179,7 +1203,6 @@ static void efx_monitor(struct work_struct *data) { struct efx_nic *efx = container_of(data, struct efx_nic, monitor_work.work); - int rc = 0; EFX_TRACE(efx, "hardware monitor executing on CPU %d\n", raw_smp_processor_id()); @@ -1195,7 +1218,7 @@ static void efx_monitor(struct work_struct *data) } if (efx->port_enabled) - rc = falcon_check_xmac(efx); + efx->mac_op->check_hw(efx); mutex_unlock(&efx->mac_lock); queue_delayed_work(efx->workqueue, &efx->monitor_work, @@ -1331,7 +1354,7 @@ static struct net_device_stats *efx_net_stats(struct net_device *net_dev) if (!spin_trylock(&efx->stats_lock)) return stats; if (efx->stats_enabled) { - falcon_update_stats_xmac(efx); + efx->mac_op->update_stats(efx); falcon_update_nic_stats(efx); } spin_unlock(&efx->stats_lock); @@ -1519,7 +1542,7 @@ static int efx_register_netdev(struct efx_nic *efx) netif_carrier_off(efx->net_dev); /* Clear MAC statistics */ - falcon_update_stats_xmac(efx); + efx->mac_op->update_stats(efx); memset(&efx->mac_stats, 0, sizeof(efx->mac_stats)); rc = register_netdev(net_dev); @@ -1575,8 +1598,6 @@ static void efx_unregister_netdev(struct efx_nic *efx) * before reset. */ void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd) { - int rc; - EFX_ASSERT_RESET_SERIALISED(efx); /* The net_dev->get_stats handler is quite slow, and will fail @@ -1589,9 +1610,7 @@ void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd) mutex_lock(&efx->mac_lock); mutex_lock(&efx->spi_lock); - rc = falcon_xmac_get_settings(efx, ecmd); - if (rc) - EFX_ERR(efx, "could not back up PHY settings\n"); + efx->phy_op->get_settings(efx, ecmd); efx_fini_channels(efx); } @@ -1616,7 +1635,7 @@ int efx_reset_up(struct efx_nic *efx, struct ethtool_cmd *ecmd, bool ok) if (ok) { efx_init_channels(efx); - if (falcon_xmac_set_settings(efx, ecmd)) + if (efx->phy_op->set_settings(efx, ecmd)) EFX_ERR(efx, "could not restore PHY settings\n"); } @@ -1779,6 +1798,10 @@ int efx_port_dummy_op_int(struct efx_nic *efx) void efx_port_dummy_op_void(struct efx_nic *efx) {} void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink) {} +static struct efx_mac_operations efx_dummy_mac_operations = { + .reconfigure = efx_port_dummy_op_void, +}; + static struct efx_phy_operations efx_dummy_phy_operations = { .init = efx_port_dummy_op_int, .reconfigure = efx_port_dummy_op_void, @@ -1831,6 +1854,7 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, spin_lock_init(&efx->netif_stop_lock); spin_lock_init(&efx->stats_lock); mutex_init(&efx->mac_lock); + efx->mac_op = &efx_dummy_mac_operations; efx->phy_op = &efx_dummy_phy_operations; efx->mii.dev = net_dev; INIT_WORK(&efx->reconfigure_work, efx_reconfigure_work); diff --git a/drivers/net/sfc/enum.h b/drivers/net/sfc/enum.h index 41e758e8fdb..0c2e41ce8b4 100644 --- a/drivers/net/sfc/enum.h +++ b/drivers/net/sfc/enum.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007 Solarflare Communications Inc. + * Copyright 2007-2008 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -13,22 +13,24 @@ /** * enum efx_loopback_mode - loopback modes * @LOOPBACK_NONE: no loopback - * @LOOPBACK_XGMII: loopback within MAC at XGMII level - * @LOOPBACK_XGXS: loopback within MAC at XGXS level - * @LOOPBACK_XAUI: loopback within MAC at XAUI level - * @LOOPBACK_PHYXS: loopback within PHY at PHYXS level - * @LOOPBACK_PCS: loopback within PHY at PCS level - * @LOOPBACK_PMAPMD: loopback within PHY at PMAPMD level + * @LOOPBACK_GMAC: loopback within GMAC at unspecified level + * @LOOPBACK_XGMII: loopback within XMAC at XGMII level + * @LOOPBACK_XGXS: loopback within XMAC at XGXS level + * @LOOPBACK_XAUI: loopback within XMAC at XAUI level + * @LOOPBACK_GPHY: loopback within 1G PHY at unspecified level + * @LOOPBACK_PHYXS: loopback within 10G PHY at PHYXS level + * @LOOPBACK_PCS: loopback within 10G PHY at PCS level + * @LOOPBACK_PMAPMD: loopback within 10G PHY at PMAPMD level * @LOOPBACK_NETWORK: reflecting loopback (even further than furthest!) */ /* Please keep in order and up-to-date w.r.t the following two #defines */ enum efx_loopback_mode { LOOPBACK_NONE = 0, - LOOPBACK_MAC = 1, + LOOPBACK_GMAC = 1, LOOPBACK_XGMII = 2, LOOPBACK_XGXS = 3, LOOPBACK_XAUI = 4, - LOOPBACK_PHY = 5, + LOOPBACK_GPHY = 5, LOOPBACK_PHYXS = 6, LOOPBACK_PCS = 7, LOOPBACK_PMAPMD = 8, @@ -45,15 +47,16 @@ extern const char *efx_loopback_mode_names[]; LOOPBACK_MODE_NAME(efx->loopback_mode) /* These loopbacks occur within the controller */ -#define LOOPBACKS_10G_INTERNAL ((1 << LOOPBACK_XGMII)| \ - (1 << LOOPBACK_XGXS) | \ - (1 << LOOPBACK_XAUI)) +#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_GMAC) | \ + (1 << LOOPBACK_XGMII)| \ + (1 << LOOPBACK_XGXS) | \ + (1 << LOOPBACK_XAUI)) #define LOOPBACK_MASK(_efx) \ (1 << (_efx)->loopback_mode) #define LOOPBACK_INTERNAL(_efx) \ - (!!(LOOPBACKS_10G_INTERNAL & LOOPBACK_MASK(_efx))) + (!!(LOOPBACKS_INTERNAL & LOOPBACK_MASK(_efx))) #define LOOPBACK_OUT_OF(_from, _to, _mask) \ ((LOOPBACK_MASK(_from) & (_mask)) && !(LOOPBACK_MASK(_to) & (_mask))) diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 43d6d8b4429..1b33f89df4d 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -17,15 +17,14 @@ #include "ethtool.h" #include "falcon.h" #include "spi.h" -#include "mac.h" const char *efx_loopback_mode_names[] = { [LOOPBACK_NONE] = "NONE", - [LOOPBACK_MAC] = "MAC", + [LOOPBACK_GMAC] = "GMAC", [LOOPBACK_XGMII] = "XGMII", [LOOPBACK_XGXS] = "XGXS", [LOOPBACK_XAUI] = "XAUI", - [LOOPBACK_PHY] = "PHY", + [LOOPBACK_GPHY] = "GPHY", [LOOPBACK_PHYXS] = "PHYXS", [LOOPBACK_PCS] = "PCS", [LOOPBACK_PMAPMD] = "PMA/PMD", @@ -200,13 +199,15 @@ int efx_ethtool_get_settings(struct net_device *net_dev, struct ethtool_cmd *ecmd) { struct efx_nic *efx = netdev_priv(net_dev); - int rc; mutex_lock(&efx->mac_lock); - rc = falcon_xmac_get_settings(efx, ecmd); + efx->phy_op->get_settings(efx, ecmd); mutex_unlock(&efx->mac_lock); - return rc; + /* Falcon GMAC does not support 1000Mbps HD */ + ecmd->supported &= ~SUPPORTED_1000baseT_Half; + + return 0; } /* This must be called with rtnl_lock held. */ @@ -216,8 +217,15 @@ int efx_ethtool_set_settings(struct net_device *net_dev, struct efx_nic *efx = netdev_priv(net_dev); int rc; + /* Falcon GMAC does not support 1000Mbps HD */ + if (ecmd->speed == SPEED_1000 && ecmd->duplex != DUPLEX_FULL) { + EFX_LOG(efx, "rejecting unsupported 1000Mbps HD" + " setting\n"); + return -EINVAL; + } + mutex_lock(&efx->mac_lock); - rc = falcon_xmac_set_settings(efx, ecmd); + rc = efx->phy_op->set_settings(efx, ecmd); mutex_unlock(&efx->mac_lock); if (!rc) efx_reconfigure_port(efx); @@ -362,10 +370,6 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx, EFX_PORT_NAME, "phy", NULL); /* Loopback tests */ - efx_fill_test(n++, strings, data, &tests->loopback_speed, - EFX_PORT_NAME, "loopback.speed", NULL); - efx_fill_test(n++, strings, data, &tests->loopback_full_duplex, - EFX_PORT_NAME, "loopback.full_duplex", NULL); for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) { if (!(efx->loopback_modes & (1 << mode))) continue; @@ -671,22 +675,14 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, { struct efx_nic *efx = netdev_priv(net_dev); enum efx_fc_type flow_control = efx->flow_control; - int rc; flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO); flow_control |= pause->rx_pause ? EFX_FC_RX : 0; flow_control |= pause->tx_pause ? EFX_FC_TX : 0; flow_control |= pause->autoneg ? EFX_FC_AUTO : 0; - /* Try to push the pause parameters */ - mutex_lock(&efx->mac_lock); - rc = falcon_xmac_set_pause(efx, flow_control); - mutex_unlock(&efx->mac_lock); - - if (!rc) - efx_reconfigure_port(efx); - - return rc; + efx_reconfigure_port(efx); + return 0; } static void efx_ethtool_get_pauseparam(struct net_device *net_dev, diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index 448bba9eed0..f09eded40fb 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -1168,6 +1168,19 @@ void falcon_generate_test_event(struct efx_channel *channel, unsigned int magic) 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 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, * ************************************************************************** */ -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, ®, GM_CFG1_REG); + udelay(1000); + + EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 0); + falcon_write(efx, ®, GM_CFG1_REG); + udelay(1000); + return 0; + } else { + EFX_POPULATE_OWORD_1(reg, XM_CORE_RST, 1); + falcon_write(efx, ®, XM_GLB_CFG_REG); + + for (count = 0; count < 10000; count++) { + falcon_read(efx, ®, XM_GLB_CFG_REG); + if (EFX_OWORD_FIELD(reg, XM_CORE_RST) == 0) + return 0; + udelay(10); + } - 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_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, ®, MAC0_CTRL_REG_KER); + EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0, 1); + falcon_write(efx, ®, 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, ®, 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, ®, 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, ®, 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 @@ void falcon_drain_tx_fifo(struct efx_nic *efx) /* 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, ®, 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, ®, RX_CFG_REG_KER); + EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 0); + falcon_write(efx, ®, RX_CFG_REG_KER); if (!efx->link_up) falcon_drain_tx_fifo(efx); @@ -2030,7 +2082,8 @@ static int falcon_gmii_wait(struct efx_nic *efx) 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 @@ static int falcon_probe_phy(struct efx_nic *efx) 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 @@ static struct { 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_nvconfig(struct efx_nic *efx) 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 @@ static int falcon_probe_nic_variant(struct efx_nic *efx) 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 @@ static int falcon_probe_nic_variant(struct efx_nic *efx) return -ENODEV; } + /* Initial assumed speed */ + efx->link_speed = EFX_OWORD_FIELD(nic_stat, STRAP_10G) ? 10000 : 1000; + return 0; } diff --git a/drivers/net/sfc/falcon.h b/drivers/net/sfc/falcon.h index be025ba7a6c..7869c3d7438 100644 --- a/drivers/net/sfc/falcon.h +++ b/drivers/net/sfc/falcon.h @@ -12,6 +12,7 @@ #define EFX_FALCON_H #include "net_driver.h" +#include "efx.h" /* * Falcon hardware control @@ -65,6 +66,7 @@ extern int falcon_probe_port(struct efx_nic *efx); extern void falcon_remove_port(struct efx_nic *efx); /* MAC/PHY */ +extern int falcon_switch_mac(struct efx_nic *efx); extern bool falcon_xaui_link_ok(struct efx_nic *efx); extern int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset); @@ -77,6 +79,7 @@ extern int falcon_init_interrupt(struct efx_nic *efx); extern void falcon_enable_interrupts(struct efx_nic *efx); extern void falcon_generate_test_event(struct efx_channel *channel, unsigned int magic); +extern void falcon_sim_phy_event(struct efx_nic *efx); extern void falcon_generate_interrupt(struct efx_nic *efx); extern void falcon_set_int_moderation(struct efx_channel *channel); extern void falcon_disable_interrupts(struct efx_nic *efx); diff --git a/drivers/net/sfc/falcon_gmac.c b/drivers/net/sfc/falcon_gmac.c new file mode 100644 index 00000000000..247f8025ef0 --- /dev/null +++ b/drivers/net/sfc/falcon_gmac.c @@ -0,0 +1,233 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include "net_driver.h" +#include "efx.h" +#include "falcon.h" +#include "mac.h" +#include "falcon_hwdefs.h" +#include "falcon_io.h" +#include "gmii.h" + +/************************************************************************** + * + * MAC operations + * + *************************************************************************/ + +static void falcon_reconfigure_gmac(struct efx_nic *efx) +{ + bool loopback, tx_fc, rx_fc, bytemode; + int if_mode; + unsigned int max_frame_len; + efx_oword_t reg; + + /* Configuration register 1 */ + tx_fc = (efx->flow_control & EFX_FC_TX) || !efx->link_fd; + rx_fc = !!(efx->flow_control & EFX_FC_RX); + loopback = (efx->loopback_mode == LOOPBACK_GMAC); + bytemode = (efx->link_speed == 1000); + + EFX_POPULATE_OWORD_5(reg, + GM_LOOP, loopback, + GM_TX_EN, 1, + GM_TX_FC_EN, tx_fc, + GM_RX_EN, 1, + GM_RX_FC_EN, rx_fc); + falcon_write(efx, ®, GM_CFG1_REG); + udelay(10); + + /* Configuration register 2 */ + if_mode = (bytemode) ? 2 : 1; + EFX_POPULATE_OWORD_5(reg, + GM_IF_MODE, if_mode, + GM_PAD_CRC_EN, 1, + GM_LEN_CHK, 1, + GM_FD, efx->link_fd, + GM_PAMBL_LEN, 0x7/*datasheet recommended */); + + falcon_write(efx, ®, GM_CFG2_REG); + udelay(10); + + /* Max frame len register */ + max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu); + EFX_POPULATE_OWORD_1(reg, GM_MAX_FLEN, max_frame_len); + falcon_write(efx, ®, GM_MAX_FLEN_REG); + udelay(10); + + /* FIFO configuration register 0 */ + EFX_POPULATE_OWORD_5(reg, + GMF_FTFENREQ, 1, + GMF_STFENREQ, 1, + GMF_FRFENREQ, 1, + GMF_SRFENREQ, 1, + GMF_WTMENREQ, 1); + falcon_write(efx, ®, GMF_CFG0_REG); + udelay(10); + + /* FIFO configuration register 1 */ + EFX_POPULATE_OWORD_2(reg, + GMF_CFGFRTH, 0x12, + GMF_CFGXOFFRTX, 0xffff); + falcon_write(efx, ®, GMF_CFG1_REG); + udelay(10); + + /* FIFO configuration register 2 */ + EFX_POPULATE_OWORD_2(reg, + GMF_CFGHWM, 0x3f, + GMF_CFGLWM, 0xa); + falcon_write(efx, ®, GMF_CFG2_REG); + udelay(10); + + /* FIFO configuration register 3 */ + EFX_POPULATE_OWORD_2(reg, + GMF_CFGHWMFT, 0x1c, + GMF_CFGFTTH, 0x08); + falcon_write(efx, ®, GMF_CFG3_REG); + udelay(10); + + /* FIFO configuration register 4 */ + EFX_POPULATE_OWORD_1(reg, GMF_HSTFLTRFRM_PAUSE, 1); + falcon_write(efx, ®, GMF_CFG4_REG); + udelay(10); + + /* FIFO configuration register 5 */ + falcon_read(efx, ®, GMF_CFG5_REG); + EFX_SET_OWORD_FIELD(reg, GMF_CFGBYTMODE, bytemode); + EFX_SET_OWORD_FIELD(reg, GMF_CFGHDPLX, !efx->link_fd); + EFX_SET_OWORD_FIELD(reg, GMF_HSTDRPLT64, !efx->link_fd); + EFX_SET_OWORD_FIELD(reg, GMF_HSTFLTRFRMDC_PAUSE, 0); + falcon_write(efx, ®, GMF_CFG5_REG); + udelay(10); + + /* MAC address */ + EFX_POPULATE_OWORD_4(reg, + GM_HWADDR_5, efx->net_dev->dev_addr[5], + GM_HWADDR_4, efx->net_dev->dev_addr[4], + GM_HWADDR_3, efx->net_dev->dev_addr[3], + GM_HWADDR_2, efx->net_dev->dev_addr[2]); + falcon_write(efx, ®, GM_ADR1_REG); + udelay(10); + EFX_POPULATE_OWORD_2(reg, + GM_HWADDR_1, efx->net_dev->dev_addr[1], + GM_HWADDR_0, efx->net_dev->dev_addr[0]); + falcon_write(efx, ®, GM_ADR2_REG); + udelay(10); + + falcon_reconfigure_mac_wrapper(efx); +} + +static void falcon_update_stats_gmac(struct efx_nic *efx) +{ + struct efx_mac_stats *mac_stats = &efx->mac_stats; + unsigned long old_rx_pause, old_tx_pause; + unsigned long new_rx_pause, new_tx_pause; + int rc; + + rc = falcon_dma_stats(efx, GDmaDone_offset); + if (rc) + return; + + /* Pause frames are erroneously counted as errors (SFC bug 3269) */ + old_rx_pause = mac_stats->rx_pause; + old_tx_pause = mac_stats->tx_pause; + + /* Update MAC stats from DMAed values */ + FALCON_STAT(efx, GRxGoodOct, rx_good_bytes); + FALCON_STAT(efx, GRxBadOct, rx_bad_bytes); + FALCON_STAT(efx, GRxMissPkt, rx_missed); + FALCON_STAT(efx, GRxFalseCRS, rx_false_carrier); + FALCON_STAT(efx, GRxPausePkt, rx_pause); + FALCON_STAT(efx, GRxBadPkt, rx_bad); + FALCON_STAT(efx, GRxUcastPkt, rx_unicast); + FALCON_STAT(efx, GRxMcastPkt, rx_multicast); + FALCON_STAT(efx, GRxBcastPkt, rx_broadcast); + FALCON_STAT(efx, GRxGoodLt64Pkt, rx_good_lt64); + FALCON_STAT(efx, GRxBadLt64Pkt, rx_bad_lt64); + FALCON_STAT(efx, GRx64Pkt, rx_64); + FALCON_STAT(efx, GRx65to127Pkt, rx_65_to_127); + FALCON_STAT(efx, GRx128to255Pkt, rx_128_to_255); + FALCON_STAT(efx, GRx256to511Pkt, rx_256_to_511); + FALCON_STAT(efx, GRx512to1023Pkt, rx_512_to_1023); + FALCON_STAT(efx, GRx1024to15xxPkt, rx_1024_to_15xx); + FALCON_STAT(efx, GRx15xxtoJumboPkt, rx_15xx_to_jumbo); + FALCON_STAT(efx, GRxGtJumboPkt, rx_gtjumbo); + FALCON_STAT(efx, GRxFcsErr64to15xxPkt, rx_bad_64_to_15xx); + FALCON_STAT(efx, GRxFcsErr15xxtoJumboPkt, rx_bad_15xx_to_jumbo); + FALCON_STAT(efx, GRxFcsErrGtJumboPkt, rx_bad_gtjumbo); + FALCON_STAT(efx, GTxGoodBadOct, tx_bytes); + FALCON_STAT(efx, GTxGoodOct, tx_good_bytes); + FALCON_STAT(efx, GTxSglColPkt, tx_single_collision); + FALCON_STAT(efx, GTxMultColPkt, tx_multiple_collision); + FALCON_STAT(efx, GTxExColPkt, tx_excessive_collision); + FALCON_STAT(efx, GTxDefPkt, tx_deferred); + FALCON_STAT(efx, GTxLateCol, tx_late_collision); + FALCON_STAT(efx, GTxExDefPkt, tx_excessive_deferred); + FALCON_STAT(efx, GTxPausePkt, tx_pause); + FALCON_STAT(efx, GTxBadPkt, tx_bad); + FALCON_STAT(efx, GTxUcastPkt, tx_unicast); + FALCON_STAT(efx, GTxMcastPkt, tx_multicast); + FALCON_STAT(efx, GTxBcastPkt, tx_broadcast); + FALCON_STAT(efx, GTxLt64Pkt, tx_lt64); + FALCON_STAT(efx, GTx64Pkt, tx_64); + FALCON_STAT(efx, GTx65to127Pkt, tx_65_to_127); + FALCON_STAT(efx, GTx128to255Pkt, tx_128_to_255); + FALCON_STAT(efx, GTx256to511Pkt, tx_256_to_511); + FALCON_STAT(efx, GTx512to1023Pkt, tx_512_to_1023); + FALCON_STAT(efx, GTx1024to15xxPkt, tx_1024_to_15xx); + FALCON_STAT(efx, GTx15xxtoJumboPkt, tx_15xx_to_jumbo); + FALCON_STAT(efx, GTxGtJumboPkt, tx_gtjumbo); + FALCON_STAT(efx, GTxNonTcpUdpPkt, tx_non_tcpudp); + FALCON_STAT(efx, GTxMacSrcErrPkt, tx_mac_src_error); + FALCON_STAT(efx, GTxIpSrcErrPkt, tx_ip_src_error); + + /* Pause frames are erroneously counted as errors (SFC bug 3269) */ + new_rx_pause = mac_stats->rx_pause; + new_tx_pause = mac_stats->tx_pause; + mac_stats->rx_bad -= (new_rx_pause - old_rx_pause); + mac_stats->tx_bad -= (new_tx_pause - old_tx_pause); + + /* Derive stats that the MAC doesn't provide directly */ + mac_stats->tx_bad_bytes = + mac_stats->tx_bytes - mac_stats->tx_good_bytes; + mac_stats->tx_packets = + mac_stats->tx_lt64 + mac_stats->tx_64 + + mac_stats->tx_65_to_127 + mac_stats->tx_128_to_255 + + mac_stats->tx_256_to_511 + mac_stats->tx_512_to_1023 + + mac_stats->tx_1024_to_15xx + mac_stats->tx_15xx_to_jumbo + + mac_stats->tx_gtjumbo; + mac_stats->tx_collision = + mac_stats->tx_single_collision + + mac_stats->tx_multiple_collision + + mac_stats->tx_excessive_collision + + mac_stats->tx_late_collision; + mac_stats->rx_bytes = + mac_stats->rx_good_bytes + mac_stats->rx_bad_bytes; + mac_stats->rx_packets = + mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64 + + mac_stats->rx_64 + mac_stats->rx_65_to_127 + + mac_stats->rx_128_to_255 + mac_stats->rx_256_to_511 + + mac_stats->rx_512_to_1023 + mac_stats->rx_1024_to_15xx + + mac_stats->rx_15xx_to_jumbo + mac_stats->rx_gtjumbo; + mac_stats->rx_good = mac_stats->rx_packets - mac_stats->rx_bad; + mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64; +} + +static int falcon_check_gmac(struct efx_nic *efx) +{ + return efx->phy_op->check_hw(efx); +} + +struct efx_mac_operations falcon_gmac_operations = { + .reconfigure = falcon_reconfigure_gmac, + .update_stats = falcon_update_stats_gmac, + .check_hw = falcon_check_gmac, +}; diff --git a/drivers/net/sfc/falcon_hwdefs.h b/drivers/net/sfc/falcon_hwdefs.h index 040e70ed4ec..5553df888b8 100644 --- a/drivers/net/sfc/falcon_hwdefs.h +++ b/drivers/net/sfc/falcon_hwdefs.h @@ -111,12 +111,18 @@ /* NIC status register */ #define NIC_STAT_REG 0x0200 +#define EE_STRAP_EN_LBN 31 +#define EE_STRAP_EN_WIDTH 1 +#define EE_STRAP_OVR_LBN 24 +#define EE_STRAP_OVR_WIDTH 4 #define ONCHIP_SRAM_LBN 16 #define ONCHIP_SRAM_WIDTH 1 #define SF_PRST_LBN 9 #define SF_PRST_WIDTH 1 #define EE_PRST_LBN 8 #define EE_PRST_WIDTH 1 +#define STRAP_PINS_LBN 0 +#define STRAP_PINS_WIDTH 3 /* These bit definitions are extrapolated from the list of numerical * values for STRAP_PINS. */ @@ -492,6 +498,107 @@ #define MAC_MCAST_HASH_REG0_KER 0xca0 #define MAC_MCAST_HASH_REG1_KER 0xcb0 +/* GMAC configuration register 1 */ +#define GM_CFG1_REG 0xe00 +#define GM_SW_RST_LBN 31 +#define GM_SW_RST_WIDTH 1 +#define GM_LOOP_LBN 8 +#define GM_LOOP_WIDTH 1 +#define GM_RX_FC_EN_LBN 5 +#define GM_RX_FC_EN_WIDTH 1 +#define GM_TX_FC_EN_LBN 4 +#define GM_TX_FC_EN_WIDTH 1 +#define GM_RX_EN_LBN 2 +#define GM_RX_EN_WIDTH 1 +#define GM_TX_EN_LBN 0 +#define GM_TX_EN_WIDTH 1 + +/* GMAC configuration register 2 */ +#define GM_CFG2_REG 0xe10 +#define GM_PAMBL_LEN_LBN 12 +#define GM_PAMBL_LEN_WIDTH 4 +#define GM_IF_MODE_LBN 8 +#define GM_IF_MODE_WIDTH 2 +#define GM_LEN_CHK_LBN 4 +#define GM_LEN_CHK_WIDTH 1 +#define GM_PAD_CRC_EN_LBN 2 +#define GM_PAD_CRC_EN_WIDTH 1 +#define GM_FD_LBN 0 +#define GM_FD_WIDTH 1 + +/* GMAC maximum frame length register */ +#define GM_MAX_FLEN_REG 0xe40 +#define GM_MAX_FLEN_LBN 0 +#define GM_MAX_FLEN_WIDTH 16 + +/* GMAC station address register 1 */ +#define GM_ADR1_REG 0xf00 +#define GM_HWADDR_5_LBN 24 +#define GM_HWADDR_5_WIDTH 8 +#define GM_HWADDR_4_LBN 16 +#define GM_HWADDR_4_WIDTH 8 +#define GM_HWADDR_3_LBN 8 +#define GM_HWADDR_3_WIDTH 8 +#define GM_HWADDR_2_LBN 0 +#define GM_HWADDR_2_WIDTH 8 + +/* GMAC station address register 2 */ +#define GM_ADR2_REG 0xf10 +#define GM_HWADDR_1_LBN 24 +#define GM_HWADDR_1_WIDTH 8 +#define GM_HWADDR_0_LBN 16 +#define GM_HWADDR_0_WIDTH 8 + +/* GMAC FIFO configuration register 0 */ +#define GMF_CFG0_REG 0xf20 +#define GMF_FTFENREQ_LBN 12 +#define GMF_FTFENREQ_WIDTH 1 +#define GMF_STFENREQ_LBN 11 +#define GMF_STFENREQ_WIDTH 1 +#define GMF_FRFENREQ_LBN 10 +#define GMF_FRFENREQ_WIDTH 1 +#define GMF_SRFENREQ_LBN 9 +#define GMF_SRFENREQ_WIDTH 1 +#define GMF_WTMENREQ_LBN 8 +#define GMF_WTMENREQ_WIDTH 1 + +/* GMAC FIFO configuration register 1 */ +#define GMF_CFG1_REG 0xf30 +#define GMF_CFGFRTH_LBN 16 +#define GMF_CFGFRTH_WIDTH 5 +#define GMF_CFGXOFFRTX_LBN 0 +#define GMF_CFGXOFFRTX_WIDTH 16 + +/* GMAC FIFO configuration register 2 */ +#define GMF_CFG2_REG 0xf40 +#define GMF_CFGHWM_LBN 16 +#define GMF_CFGHWM_WIDTH 6 +#define GMF_CFGLWM_LBN 0 +#define GMF_CFGLWM_WIDTH 6 + +/* GMAC FIFO configuration register 3 */ +#define GMF_CFG3_REG 0xf50 +#define GMF_CFGHWMFT_LBN 16 +#define GMF_CFGHWMFT_WIDTH 6 +#define GMF_CFGFTTH_LBN 0 +#define GMF_CFGFTTH_WIDTH 6 + +/* GMAC FIFO configuration register 4 */ +#define GMF_CFG4_REG 0xf60 +#define GMF_HSTFLTRFRM_PAUSE_LBN 12 +#define GMF_HSTFLTRFRM_PAUSE_WIDTH 12 + +/* GMAC FIFO configuration register 5 */ +#define GMF_CFG5_REG 0xf70 +#define GMF_CFGHDPLX_LBN 22 +#define GMF_CFGHDPLX_WIDTH 1 +#define GMF_CFGBYTMODE_LBN 19 +#define GMF_CFGBYTMODE_WIDTH 1 +#define GMF_HSTDRPLT64_LBN 18 +#define GMF_HSTDRPLT64_WIDTH 1 +#define GMF_HSTFLTRFRMDC_PAUSE_LBN 12 +#define GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1 + /* XGMAC address register low */ #define XM_ADR_LO_REG 0x1200 #define XM_ADR_3_LBN 24 @@ -962,54 +1069,103 @@ ************************************************************************** * */ + #define GRxGoodOct_offset 0x0 +#define GRxGoodOct_WIDTH 48 #define GRxBadOct_offset 0x8 +#define GRxBadOct_WIDTH 48 #define GRxMissPkt_offset 0x10 +#define GRxMissPkt_WIDTH 32 #define GRxFalseCRS_offset 0x14 +#define GRxFalseCRS_WIDTH 32 #define GRxPausePkt_offset 0x18 +#define GRxPausePkt_WIDTH 32 #define GRxBadPkt_offset 0x1C +#define GRxBadPkt_WIDTH 32 #define GRxUcastPkt_offset 0x20 +#define GRxUcastPkt_WIDTH 32 #define GRxMcastPkt_offset 0x24 +#define GRxMcastPkt_WIDTH 32 #define GRxBcastPkt_offset 0x28 +#define GRxBcastPkt_WIDTH 32 #define GRxGoodLt64Pkt_offset 0x2C +#define GRxGoodLt64Pkt_WIDTH 32 #define GRxBadLt64Pkt_offset 0x30 +#define GRxBadLt64Pkt_WIDTH 32 #define GRx64Pkt_offset 0x34 +#define GRx64Pkt_WIDTH 32 #define GRx65to127Pkt_offset 0x38 +#define GRx65to127Pkt_WIDTH 32 #define GRx128to255Pkt_offset 0x3C +#define GRx128to255Pkt_WIDTH 32 #define GRx256to511Pkt_offset 0x40 +#define GRx256to511Pkt_WIDTH 32 #define GRx512to1023Pkt_offset 0x44 +#define GRx512to1023Pkt_WIDTH 32 #define GRx1024to15xxPkt_offset 0x48 +#define GRx1024to15xxPkt_WIDTH 32 #define GRx15xxtoJumboPkt_offset 0x4C +#define GRx15xxtoJumboPkt_WIDTH 32 #define GRxGtJumboPkt_offset 0x50 +#define GRxGtJumboPkt_WIDTH 32 #define GRxFcsErr64to15xxPkt_offset 0x54 +#define GRxFcsErr64to15xxPkt_WIDTH 32 #define GRxFcsErr15xxtoJumboPkt_offset 0x58 +#define GRxFcsErr15xxtoJumboPkt_WIDTH 32 #define GRxFcsErrGtJumboPkt_offset 0x5C +#define GRxFcsErrGtJumboPkt_WIDTH 32 #define GTxGoodBadOct_offset 0x80 +#define GTxGoodBadOct_WIDTH 48 #define GTxGoodOct_offset 0x88 +#define GTxGoodOct_WIDTH 48 #define GTxSglColPkt_offset 0x90 +#define GTxSglColPkt_WIDTH 32 #define GTxMultColPkt_offset 0x94 +#define GTxMultColPkt_WIDTH 32 #define GTxExColPkt_offset 0x98 +#define GTxExColPkt_WIDTH 32 #define GTxDefPkt_offset 0x9C +#define GTxDefPkt_WIDTH 32 #define GTxLateCol_offset 0xA0 +#define GTxLateCol_WIDTH 32 #define GTxExDefPkt_offset 0xA4 +#define GTxExDefPkt_WIDTH 32 #define GTxPausePkt_offset 0xA8 +#define GTxPausePkt_WIDTH 32 #define GTxBadPkt_offset 0xAC +#define GTxBadPkt_WIDTH 32 #define GTxUcastPkt_offset 0xB0 +#define GTxUcastPkt_WIDTH 32 #define GTxMcastPkt_offset 0xB4 +#define GTxMcastPkt_WIDTH 32 #define GTxBcastPkt_offset 0xB8 +#define GTxBcastPkt_WIDTH 32 #define GTxLt64Pkt_offset 0xBC +#define GTxLt64Pkt_WIDTH 32 #define GTx64Pkt_offset 0xC0 +#define GTx64Pkt_WIDTH 32 #define GTx65to127Pkt_offset 0xC4 +#define GTx65to127Pkt_WIDTH 32 #define GTx128to255Pkt_offset 0xC8 +#define GTx128to255Pkt_WIDTH 32 #define GTx256to511Pkt_offset 0xCC +#define GTx256to511Pkt_WIDTH 32 #define GTx512to1023Pkt_offset 0xD0 +#define GTx512to1023Pkt_WIDTH 32 #define GTx1024to15xxPkt_offset 0xD4 +#define GTx1024to15xxPkt_WIDTH 32 #define GTx15xxtoJumboPkt_offset 0xD8 +#define GTx15xxtoJumboPkt_WIDTH 32 #define GTxGtJumboPkt_offset 0xDC +#define GTxGtJumboPkt_WIDTH 32 #define GTxNonTcpUdpPkt_offset 0xE0 +#define GTxNonTcpUdpPkt_WIDTH 16 #define GTxMacSrcErrPkt_offset 0xE4 +#define GTxMacSrcErrPkt_WIDTH 16 #define GTxIpSrcErrPkt_offset 0xE8 +#define GTxIpSrcErrPkt_WIDTH 16 #define GDmaDone_offset 0xEC +#define GDmaDone_WIDTH 32 #define XgRxOctets_offset 0x0 #define XgRxOctets_WIDTH 48 diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c index 4a54d0933e7..2206eada346 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, ®, XM_GLB_CFG_REG); - - for (count = 0; count < 10000; count++) { /* wait upto 100ms */ - falcon_read(efx, ®, 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) +static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) { 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, ®, XM_MGT_INT_REG_B0); - falcon_read(efx, ®, 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; -} + if ((falcon_rev(efx) != FALCON_REV_B0) || LOOPBACK_INTERNAL(efx)) + return; -static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) -{ - efx_oword_t reg; + /* We expect xgmii faults if the wireside link is up */ + if (!EFX_WORKAROUND_5147(efx) || !efx->link_up) + return; - if ((falcon_rev(efx) < FALCON_REV_B0) || LOOPBACK_INTERNAL(efx)) + /* 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, ®, 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, ®, 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; - - while (tries) { - if (falcon_xaui_link_ok(efx)) - return true; + /* XAUI link is expected to be down */ + return; - 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); -} - -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); + falcon_check_xaui_link_up(efx, 5); + falcon_mask_status_intr(efx, true); } -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, +}; diff --git a/drivers/net/sfc/mac.h b/drivers/net/sfc/mac.h index a31571c6913..4e7074278fe 100644 --- a/drivers/net/sfc/mac.h +++ b/drivers/net/sfc/mac.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2007 Solarflare Communications Inc. + * Copyright 2006-2008 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -13,17 +13,7 @@ #include "net_driver.h" -extern int falcon_init_xmac(struct efx_nic *efx); -extern void falcon_reconfigure_xmac(struct efx_nic *efx); -extern void falcon_update_stats_xmac(struct efx_nic *efx); -extern void falcon_fini_xmac(struct efx_nic *efx); -extern int falcon_check_xmac(struct efx_nic *efx); -extern void falcon_xmac_sim_phy_event(struct efx_nic *efx); -extern int falcon_xmac_get_settings(struct efx_nic *efx, - struct ethtool_cmd *ecmd); -extern int falcon_xmac_set_settings(struct efx_nic *efx, - struct ethtool_cmd *ecmd); -extern int falcon_xmac_set_pause(struct efx_nic *efx, - enum efx_fc_type pause_params); +extern struct efx_mac_operations falcon_gmac_operations; +extern struct efx_mac_operations falcon_xmac_operations; #endif diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 6cac5ed427b..883086e3945 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -463,6 +463,8 @@ enum phy_type { #define PHY_ADDR_INVALID 0xff +#define EFX_IS10G(efx) ((efx)->link_speed == 10000) + enum nic_state { STATE_INIT = 0, STATE_RUNNING = 1, @@ -503,6 +505,24 @@ enum efx_fc_type { EFX_FC_AUTO = 4, }; +/* Supported MAC bit-mask */ +enum efx_mac_type { + EFX_GMAC = 1, + EFX_XMAC = 2, +}; + +/** + * struct efx_mac_operations - Efx MAC operations table + * @reconfigure: Reconfigure MAC. Serialised by the mac_lock + * @update_stats: Update statistics + * @check_hw: Check hardware. Serialised by the mac_lock + */ +struct efx_mac_operations { + void (*reconfigure) (struct efx_nic *efx); + void (*update_stats) (struct efx_nic *efx); + int (*check_hw) (struct efx_nic *efx); +}; + /** * struct efx_phy_operations - Efx PHY operations table * @init: Initialise PHY @@ -511,16 +531,23 @@ enum efx_fc_type { * @clear_interrupt: Clear down interrupt * @blink: Blink LEDs * @check_hw: Check hardware + * @get_settings: Get ethtool settings. Serialised by the mac_lock. + * @set_settings: Set ethtool settings. Serialised by the mac_lock. * @mmds: MMD presence mask * @loopbacks: Supported loopback modes mask */ struct efx_phy_operations { + enum efx_mac_type macs; int (*init) (struct efx_nic *efx); void (*fini) (struct efx_nic *efx); void (*reconfigure) (struct efx_nic *efx); void (*clear_interrupt) (struct efx_nic *efx); int (*check_hw) (struct efx_nic *efx); int (*test) (struct efx_nic *efx); + void (*get_settings) (struct efx_nic *efx, + struct ethtool_cmd *ecmd); + int (*set_settings) (struct efx_nic *efx, + struct ethtool_cmd *ecmd); int mmds; unsigned loopbacks; }; @@ -686,6 +713,7 @@ union efx_multicast_hash { * @stats_lock: Statistics update lock. Serialises statistics fetches * @stats_enabled: Temporarily disable statistics fetches. * Serialised by @stats_lock + * @mac_op: MAC interface * @mac_address: Permanent MAC address * @phy_type: PHY type * @phy_lock: PHY access lock @@ -693,6 +721,7 @@ union efx_multicast_hash { * @phy_data: PHY private data (including PHY-specific stats) * @mii: PHY interface * @phy_mode: PHY operating mode. Serialised by @mac_lock. + * @mac_up: MAC link state * @link_up: Link status * @link_fd: Link is full duplex * @link_speed: Link speed (Mbps) @@ -763,6 +792,7 @@ struct efx_nic { spinlock_t stats_lock; bool stats_enabled; + struct efx_mac_operations *mac_op; unsigned char mac_address[ETH_ALEN]; enum phy_type phy_type; @@ -772,6 +802,7 @@ struct efx_nic { struct mii_if_info mii; enum efx_phy_mode phy_mode; + bool mac_up; bool link_up; bool link_fd; unsigned int link_speed; diff --git a/drivers/net/sfc/selftest.c b/drivers/net/sfc/selftest.c index 362956e3fe1..8142e37a518 100644 --- a/drivers/net/sfc/selftest.c +++ b/drivers/net/sfc/selftest.c @@ -26,7 +26,6 @@ #include "selftest.h" #include "boards.h" #include "workarounds.h" -#include "mac.h" #include "spi.h" #include "falcon_io.h" #include "mdio_10g.h" @@ -105,9 +104,11 @@ static int efx_test_mii(struct efx_nic *efx, struct efx_self_tests *tests) goto out; } - rc = mdio_clause45_check_mmds(efx, efx->phy_op->mmds, 0); - if (rc) - goto out; + if (EFX_IS10G(efx)) { + rc = mdio_clause45_check_mmds(efx, efx->phy_op->mmds, 0); + if (rc) + goto out; + } out: mutex_unlock(&efx->mac_lock); @@ -598,7 +599,7 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct ethtool_cmd ecmd, do { struct efx_channel *channel = &efx->channel[0]; - falcon_check_xmac(efx); + efx->mac_op->check_hw(efx); schedule_timeout_uninterruptible(HZ / 10); if (channel->work_pending) efx_process_channel_now(channel); @@ -606,13 +607,12 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct ethtool_cmd ecmd, flush_workqueue(efx->workqueue); rmb(); - /* efx->link_up can be 1 even if the XAUI link is down, - * (bug5762). Usually, it's not worth bothering with the - * difference, but for selftests, we need that extra - * guarantee that the link is really, really, up. - */ + /* We need both the phy and xaui links to be ok. + * rather than relying on the falcon_xmac irq/poll + * regime, just poll xaui directly */ link_up = efx->link_up; - if (!falcon_xaui_link_ok(efx)) + if (link_up && EFX_IS10G(efx) && + !falcon_xaui_link_ok(efx)) link_up = false; } while ((++count < 20) && !link_up); @@ -721,7 +721,6 @@ int efx_offline_test(struct efx_nic *efx, if (ecmd_test.autoneg == AUTONEG_ENABLE) { ecmd_test.autoneg = AUTONEG_DISABLE; ecmd_test.duplex = DUPLEX_FULL; - ecmd_test.speed = SPEED_10000; } efx->loopback_mode = LOOPBACK_NONE; @@ -732,9 +731,6 @@ int efx_offline_test(struct efx_nic *efx, return rc; } - tests->loopback_speed = ecmd_test.speed; - tests->loopback_full_duplex = ecmd_test.duplex; - rc = efx_test_phy(efx, tests); if (rc && !rc2) rc2 = rc; diff --git a/drivers/net/sfc/selftest.h b/drivers/net/sfc/selftest.h index fc15df15d76..252f7d71724 100644 --- a/drivers/net/sfc/selftest.h +++ b/drivers/net/sfc/selftest.h @@ -39,8 +39,6 @@ struct efx_self_tests { /* offline tests */ int registers; int phy; - int loopback_speed; - int loopback_full_duplex; struct efx_loopback_self_tests loopback[LOOPBACK_TEST_MAX + 1]; }; diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c index aa576c559ec..af652844bae 100644 --- a/drivers/net/sfc/sfe4001.c +++ b/drivers/net/sfc/sfe4001.c @@ -176,7 +176,7 @@ static int sfe4001_check_hw(struct efx_nic *efx) s32 status; /* If XAUI link is up then do not monitor */ - if (EFX_WORKAROUND_7884(efx) && falcon_xaui_link_ok(efx)) + if (EFX_WORKAROUND_7884(efx) && efx->mac_up) return 0; /* Check the powered status of the PHY. Lack of power implies that diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index 197b5449ab1..d60353bb40b 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -1,6 +1,6 @@ /**************************************************************************** - * Driver for Solarflare 802.3an compliant PHY - * Copyright 2007 Solarflare Communications Inc. + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2007-2008 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -15,7 +15,6 @@ #include "phy.h" #include "falcon_hwdefs.h" #include "boards.h" -#include "mac.h" /* We expect these MMDs to be in the package */ /* AN not here as mdio_check_mmds() requires STAT2 support */ @@ -381,7 +380,7 @@ static int tenxpress_phy_check_hw(struct efx_nic *efx) link_ok = tenxpress_link_ok(efx, true); if (link_ok != efx->link_up) - falcon_xmac_sim_phy_event(efx); + falcon_sim_phy_event(efx); if (phy_data->phy_mode != PHY_MODE_NORMAL) return 0; @@ -453,12 +452,15 @@ static int tenxpress_phy_test(struct efx_nic *efx) } struct efx_phy_operations falcon_tenxpress_phy_ops = { + .macs = EFX_XMAC, .init = tenxpress_phy_init, .reconfigure = tenxpress_phy_reconfigure, .check_hw = tenxpress_phy_check_hw, .fini = tenxpress_phy_fini, .clear_interrupt = tenxpress_phy_clear_interrupt, .test = tenxpress_phy_test, + .get_settings = mdio_clause45_get_settings, + .set_settings = mdio_clause45_set_settings, .mmds = TENXPRESS_REQUIRED_DEVS, .loopbacks = TENXPRESS_LOOPBACKS, }; diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/xfp_phy.c index 0413d135e4a..d4e203ddcf1 100644 --- a/drivers/net/sfc/xfp_phy.c +++ b/drivers/net/sfc/xfp_phy.c @@ -17,7 +17,7 @@ #include "mdio_10g.h" #include "xenpack.h" #include "phy.h" -#include "mac.h" +#include "falcon.h" #define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS_PCS | \ MDIO_MMDREG_DEVS_PMAPMD | \ @@ -125,7 +125,7 @@ static int xfp_phy_check_hw(struct efx_nic *efx) int link_up = xfp_link_ok(efx); /* Simulate a PHY event if link state has changed */ if (link_up != efx->link_up) - falcon_xmac_sim_phy_event(efx); + falcon_sim_phy_event(efx); rc = efx->board_info.monitor(efx); if (rc) { @@ -169,11 +169,14 @@ static void xfp_phy_fini(struct efx_nic *efx) } struct efx_phy_operations falcon_xfp_phy_ops = { + .macs = EFX_XMAC, .init = xfp_phy_init, .reconfigure = xfp_phy_reconfigure, .check_hw = xfp_phy_check_hw, .fini = xfp_phy_fini, .clear_interrupt = xfp_phy_clear_interrupt, + .get_settings = mdio_clause45_get_settings, + .set_settings = mdio_clause45_set_settings, .mmds = XFP_REQUIRED_DEVS, .loopbacks = XFP_LOOPBACKS, }; -- cgit v1.2.3-70-g09d2 From 04cc8cacb01c09fba2297faf1477cd570ba43f0b Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 12 Dec 2008 21:50:46 -0800 Subject: sfc: Implement auto-negotiation Add infrastructure for auto-negotiation of speed, duplex and flow control. When using 10Xpress, auto-negotiate flow control. While we're at it, clean up the code to warn when partner is not 10GBASE-T capable. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/ethtool.c | 57 +++++-- drivers/net/sfc/falcon.c | 6 +- drivers/net/sfc/falcon_gmac.c | 4 +- drivers/net/sfc/falcon_xmac.c | 2 +- drivers/net/sfc/mdio_10g.c | 348 +++++++++++++++++++++++++++++++++--------- drivers/net/sfc/mdio_10g.h | 32 ++++ drivers/net/sfc/net_driver.h | 38 ++++- drivers/net/sfc/tenxpress.c | 138 +++++++++-------- drivers/net/sfc/xfp_phy.c | 1 + 9 files changed, 472 insertions(+), 154 deletions(-) (limited to 'drivers/net/sfc/xfp_phy.c') diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 1b33f89df4d..0e81af6d834 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -12,11 +12,13 @@ #include #include #include "net_driver.h" +#include "workarounds.h" #include "selftest.h" #include "efx.h" #include "ethtool.h" #include "falcon.h" #include "spi.h" +#include "mdio_10g.h" const char *efx_loopback_mode_names[] = { [LOOPBACK_NONE] = "NONE", @@ -674,14 +676,51 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, struct ethtool_pauseparam *pause) { struct efx_nic *efx = netdev_priv(net_dev); - enum efx_fc_type flow_control = efx->flow_control; + enum efx_fc_type wanted_fc; + bool reset; - flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO); - flow_control |= pause->rx_pause ? EFX_FC_RX : 0; - flow_control |= pause->tx_pause ? EFX_FC_TX : 0; - flow_control |= pause->autoneg ? EFX_FC_AUTO : 0; + wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) | + (pause->tx_pause ? EFX_FC_TX : 0) | + (pause->autoneg ? EFX_FC_AUTO : 0)); + + if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) { + EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n"); + return -EINVAL; + } + + if (!(efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) && + (wanted_fc & EFX_FC_AUTO)) { + EFX_LOG(efx, "PHY does not support flow control " + "autonegotiation\n"); + 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 = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & 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); + } + } + + /* Try to push the pause parameters */ + mutex_lock(&efx->mac_lock); + + efx->wanted_fc = wanted_fc; + mdio_clause45_set_pause(efx); + __efx_reconfigure_port(efx); + + mutex_unlock(&efx->mac_lock); - efx_reconfigure_port(efx); return 0; } @@ -690,9 +729,9 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev, { struct efx_nic *efx = netdev_priv(net_dev); - pause->rx_pause = !!(efx->flow_control & EFX_FC_RX); - pause->tx_pause = !!(efx->flow_control & EFX_FC_TX); - pause->autoneg = !!(efx->flow_control & EFX_FC_AUTO); + pause->rx_pause = !!(efx->wanted_fc & EFX_FC_RX); + pause->tx_pause = !!(efx->wanted_fc & EFX_FC_TX); + pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO); } diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index f09eded40fb..fde4e7912c3 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -1998,7 +1998,7 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) /* Transmission of pause frames when RX crosses the threshold is * covered by RX_XOFF_MAC_EN and XM_TX_CFG_REG:XM_FCNTL. * Action on receipt of pause frames is controller by XM_DIS_FCNTL */ - tx_fc = !!(efx->flow_control & EFX_FC_TX); + tx_fc = !!(efx->link_fc & EFX_FC_TX); falcon_read(efx, ®, RX_CFG_REG_KER); EFX_SET_OWORD_FIELD_VER(efx, reg, RX_XOFF_MAC_EN, tx_fc); @@ -2328,9 +2328,9 @@ int falcon_probe_port(struct efx_nic *efx) /* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */ if (falcon_rev(efx) >= FALCON_REV_B0) - efx->flow_control = EFX_FC_RX | EFX_FC_TX; + efx->wanted_fc = EFX_FC_RX | EFX_FC_TX; else - efx->flow_control = EFX_FC_RX; + efx->wanted_fc = EFX_FC_RX; /* Allocate buffer for stats */ rc = falcon_alloc_buffer(efx, &efx->stats_buffer, diff --git a/drivers/net/sfc/falcon_gmac.c b/drivers/net/sfc/falcon_gmac.c index 247f8025ef0..b6e6eb96390 100644 --- a/drivers/net/sfc/falcon_gmac.c +++ b/drivers/net/sfc/falcon_gmac.c @@ -31,8 +31,8 @@ static void falcon_reconfigure_gmac(struct efx_nic *efx) efx_oword_t reg; /* Configuration register 1 */ - tx_fc = (efx->flow_control & EFX_FC_TX) || !efx->link_fd; - rx_fc = !!(efx->flow_control & EFX_FC_RX); + tx_fc = (efx->link_fc & EFX_FC_TX) || !efx->link_fd; + rx_fc = !!(efx->link_fc & EFX_FC_RX); loopback = (efx->loopback_mode == LOOPBACK_GMAC); bytemode = (efx->link_speed == 1000); diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c index 2206eada346..0ce8f015386 100644 --- a/drivers/net/sfc/falcon_xmac.c +++ b/drivers/net/sfc/falcon_xmac.c @@ -142,7 +142,7 @@ static void falcon_reconfigure_xmac_core(struct efx_nic *efx) { unsigned int max_frame_len; efx_oword_t reg; - bool rx_fc = !!(efx->flow_control & EFX_FC_RX); + bool rx_fc = !!(efx->link_fc & EFX_FC_RX); /* Configure MAC - cut-thru mode is hard wired on */ EFX_POPULATE_DWORD_3(reg, diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c index 8d91131aa5a..037601e0b9d 100644 --- a/drivers/net/sfc/mdio_10g.c +++ b/drivers/net/sfc/mdio_10g.c @@ -47,13 +47,16 @@ static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd, if (LOOPBACK_INTERNAL(efx)) return 0; - /* Read MMD STATUS2 to check it is responding. */ - status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT2); - if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) & - ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) != - MDIO_MMDREG_STAT2_PRESENT_VAL) { - EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd); - return -EIO; + if (mmd != MDIO_MMD_AN) { + /* Read MMD STATUS2 to check it is responding. */ + status = mdio_clause45_read(efx, phy_id, mmd, + MDIO_MMDREG_STAT2); + if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) & + ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) != + MDIO_MMDREG_STAT2_PRESENT_VAL) { + EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd); + return -EIO; + } } /* Read MMD STATUS 1 to check for fault. */ @@ -179,12 +182,15 @@ bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask) else if (efx->loopback_mode == LOOPBACK_PHYXS) mmd_mask &= ~(MDIO_MMDREG_DEVS_PHYXS | MDIO_MMDREG_DEVS_PCS | - MDIO_MMDREG_DEVS_PMAPMD); + MDIO_MMDREG_DEVS_PMAPMD | + MDIO_MMDREG_DEVS_AN); else if (efx->loopback_mode == LOOPBACK_PCS) mmd_mask &= ~(MDIO_MMDREG_DEVS_PCS | - MDIO_MMDREG_DEVS_PMAPMD); + MDIO_MMDREG_DEVS_PMAPMD | + MDIO_MMDREG_DEVS_AN); else if (efx->loopback_mode == LOOPBACK_PMAPMD) - mmd_mask &= ~MDIO_MMDREG_DEVS_PMAPMD; + mmd_mask &= ~(MDIO_MMDREG_DEVS_PMAPMD | + MDIO_MMDREG_DEVS_AN); while (mmd_mask) { if (mmd_mask & 1) { @@ -244,6 +250,7 @@ void mdio_clause45_set_mmds_lpower(struct efx_nic *efx, int low_power, unsigned int mmd_mask) { int mmd = 0; + mmd_mask &= ~MDIO_MMDREG_DEVS_AN; while (mmd_mask) { if (mmd_mask & 1) mdio_clause45_set_mmd_lpower(efx, low_power, mmd); @@ -252,103 +259,302 @@ void mdio_clause45_set_mmds_lpower(struct efx_nic *efx, } } +static u32 mdio_clause45_get_an(struct efx_nic *efx, u16 addr, u32 xnp) +{ + int phy_id = efx->mii.phy_id; + u32 result = 0; + int reg; + + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, addr); + if (reg & ADVERTISE_10HALF) + result |= ADVERTISED_10baseT_Half; + if (reg & ADVERTISE_10FULL) + result |= ADVERTISED_10baseT_Full; + if (reg & ADVERTISE_100HALF) + result |= ADVERTISED_100baseT_Half; + if (reg & ADVERTISE_100FULL) + result |= ADVERTISED_100baseT_Full; + if (reg & LPA_RESV) + result |= xnp; + + return result; +} + /** * mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO. * @efx: Efx NIC * @ecmd: Buffer for settings * * On return the 'port', 'speed', 'supported' and 'advertising' fields of - * ecmd have been filled out based on the PMA type. + * ecmd have been filled out. */ void mdio_clause45_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) { - int pma_type; + mdio_clause45_get_settings_ext(efx, ecmd, 0, 0); +} - /* If no PMA is present we are presumably talking something XAUI-ish - * like CX4. Which we report as FIBRE (see below) */ - if ((efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)) == 0) { - ecmd->speed = SPEED_10000; - ecmd->port = PORT_FIBRE; - ecmd->supported = SUPPORTED_FIBRE; - ecmd->advertising = ADVERTISED_FIBRE; - return; - } +/** + * mdio_clause45_get_settings_ext - Read (some of) the PHY settings over MDIO. + * @efx: Efx NIC + * @ecmd: Buffer for settings + * @xnp: Advertised Extended Next Page state + * @xnp_lpa: Link Partner's advertised XNP state + * + * On return the 'port', 'speed', 'supported' and 'advertising' fields of + * ecmd have been filled out. + */ +void mdio_clause45_get_settings_ext(struct efx_nic *efx, + struct ethtool_cmd *ecmd, + u32 xnp, u32 xnp_lpa) +{ + int phy_id = efx->mii.phy_id; + int reg; - pma_type = mdio_clause45_read(efx, efx->mii.phy_id, - MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL2); - pma_type &= MDIO_PMAPMD_CTRL2_TYPE_MASK; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = phy_id; - switch (pma_type) { - /* We represent CX4 as fibre in the absence of anything - better. */ - case MDIO_PMAPMD_CTRL2_10G_CX4: - ecmd->speed = SPEED_10000; - ecmd->port = PORT_FIBRE; - ecmd->supported = SUPPORTED_FIBRE; - ecmd->advertising = ADVERTISED_FIBRE; - break; - /* 10G Base-T */ + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_MMDREG_CTRL2); + switch (reg & MDIO_PMAPMD_CTRL2_TYPE_MASK) { case MDIO_PMAPMD_CTRL2_10G_BT: - ecmd->speed = SPEED_10000; - ecmd->port = PORT_TP; - ecmd->supported = SUPPORTED_TP | SUPPORTED_10000baseT_Full; - ecmd->advertising = (ADVERTISED_FIBRE - | ADVERTISED_10000baseT_Full); - break; case MDIO_PMAPMD_CTRL2_1G_BT: - ecmd->speed = SPEED_1000; - ecmd->port = PORT_TP; - ecmd->supported = SUPPORTED_TP | SUPPORTED_1000baseT_Full; - ecmd->advertising = (ADVERTISED_FIBRE - | ADVERTISED_1000baseT_Full); - break; case MDIO_PMAPMD_CTRL2_100_BT: - ecmd->speed = SPEED_100; - ecmd->port = PORT_TP; - ecmd->supported = SUPPORTED_TP | SUPPORTED_100baseT_Full; - ecmd->advertising = (ADVERTISED_FIBRE - | ADVERTISED_100baseT_Full); - break; case MDIO_PMAPMD_CTRL2_10_BT: - ecmd->speed = SPEED_10; ecmd->port = PORT_TP; - ecmd->supported = SUPPORTED_TP | SUPPORTED_10baseT_Full; - ecmd->advertising = ADVERTISED_FIBRE | ADVERTISED_10baseT_Full; + ecmd->supported = SUPPORTED_TP; + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_MMDREG_SPEED); + if (reg & (1 << MDIO_MMDREG_SPEED_10G_LBN)) + ecmd->supported |= SUPPORTED_10000baseT_Full; + if (reg & (1 << MDIO_MMDREG_SPEED_1000M_LBN)) + ecmd->supported |= (SUPPORTED_1000baseT_Full | + SUPPORTED_1000baseT_Half); + if (reg & (1 << MDIO_MMDREG_SPEED_100M_LBN)) + ecmd->supported |= (SUPPORTED_100baseT_Full | + SUPPORTED_100baseT_Half); + if (reg & (1 << MDIO_MMDREG_SPEED_10M_LBN)) + ecmd->supported |= (SUPPORTED_10baseT_Full | + SUPPORTED_10baseT_Half); + ecmd->advertising = ADVERTISED_TP; break; - /* All the other defined modes are flavours of - * 10G optical */ + + /* We represent CX4 as fibre in the absence of anything better */ + case MDIO_PMAPMD_CTRL2_10G_CX4: + /* All the other defined modes are flavours of optical */ default: - ecmd->speed = SPEED_10000; ecmd->port = PORT_FIBRE; ecmd->supported = SUPPORTED_FIBRE; ecmd->advertising = ADVERTISED_FIBRE; break; } + + if (efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) { + ecmd->supported |= SUPPORTED_Autoneg; + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, + MDIO_MMDREG_CTRL1); + if (reg & BMCR_ANENABLE) { + ecmd->autoneg = AUTONEG_ENABLE; + ecmd->advertising |= + ADVERTISED_Autoneg | + mdio_clause45_get_an(efx, + MDIO_AN_ADVERTISE, xnp); + } else + ecmd->autoneg = AUTONEG_DISABLE; + } else + ecmd->autoneg = AUTONEG_DISABLE; + + /* If AN is enabled and complete, report best common mode */ + if (ecmd->autoneg && + (mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, MDIO_MMDREG_STAT1) & + (1 << MDIO_AN_STATUS_AN_DONE_LBN))) { + u32 common, lpa; + lpa = mdio_clause45_get_an(efx, MDIO_AN_LPA, xnp_lpa); + common = ecmd->advertising & lpa; + if (common & ADVERTISED_10000baseT_Full) { + ecmd->speed = SPEED_10000; + ecmd->duplex = DUPLEX_FULL; + } else if (common & (ADVERTISED_1000baseT_Full | + ADVERTISED_1000baseT_Half)) { + ecmd->speed = SPEED_1000; + ecmd->duplex = !!(common & ADVERTISED_1000baseT_Full); + } else if (common & (ADVERTISED_100baseT_Full | + ADVERTISED_100baseT_Half)) { + ecmd->speed = SPEED_100; + ecmd->duplex = !!(common & ADVERTISED_100baseT_Full); + } else { + ecmd->speed = SPEED_10; + ecmd->duplex = !!(common & ADVERTISED_10baseT_Full); + } + } else { + /* Report forced settings */ + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_MMDREG_CTRL1); + ecmd->speed = (((reg & BMCR_SPEED1000) ? 100 : 1) * + ((reg & BMCR_SPEED100) ? 100 : 10)); + ecmd->duplex = (reg & BMCR_FULLDPLX || + ecmd->speed == SPEED_10000); + } } /** * mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO. * @efx: Efx NIC * @ecmd: New settings - * - * Currently this just enforces that we are _not_ changing the - * 'port', 'speed', 'supported' or 'advertising' settings as these - * cannot be changed on any currently supported PHY. */ int mdio_clause45_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) { - struct ethtool_cmd tmpcmd; - mdio_clause45_get_settings(efx, &tmpcmd); - /* None of the current PHYs support more than one mode - * of operation (and only 10GBT ever will), so keep things - * simple for now */ - if ((ecmd->speed == tmpcmd.speed) && (ecmd->port == tmpcmd.port) && - (ecmd->supported == tmpcmd.supported) && - (ecmd->advertising == tmpcmd.advertising)) + int phy_id = efx->mii.phy_id; + struct ethtool_cmd prev; + u32 required; + int ctrl1_bits, reg; + + efx->phy_op->get_settings(efx, &prev); + + if (ecmd->advertising == prev.advertising && + ecmd->speed == prev.speed && + ecmd->duplex == prev.duplex && + ecmd->port == prev.port && + ecmd->autoneg == prev.autoneg) return 0; - return -EOPNOTSUPP; + + /* We can only change these settings for -T PHYs */ + if (prev.port != PORT_TP || ecmd->port != PORT_TP) + return -EINVAL; + + /* Check that PHY supports these settings and work out the + * basic control bits */ + if (ecmd->duplex) { + switch (ecmd->speed) { + case SPEED_10: + ctrl1_bits = BMCR_FULLDPLX; + required = SUPPORTED_10baseT_Full; + break; + case SPEED_100: + ctrl1_bits = BMCR_SPEED100 | BMCR_FULLDPLX; + required = SUPPORTED_100baseT_Full; + break; + case SPEED_1000: + ctrl1_bits = BMCR_SPEED1000 | BMCR_FULLDPLX; + required = SUPPORTED_1000baseT_Full; + break; + case SPEED_10000: + ctrl1_bits = (BMCR_SPEED1000 | BMCR_SPEED100 | + BMCR_FULLDPLX); + required = SUPPORTED_10000baseT_Full; + break; + default: + return -EINVAL; + } + } else { + switch (ecmd->speed) { + case SPEED_10: + ctrl1_bits = 0; + required = SUPPORTED_10baseT_Half; + break; + case SPEED_100: + ctrl1_bits = BMCR_SPEED100; + required = SUPPORTED_100baseT_Half; + break; + case SPEED_1000: + ctrl1_bits = BMCR_SPEED1000; + required = SUPPORTED_1000baseT_Half; + break; + default: + return -EINVAL; + } + } + if (ecmd->autoneg) + required |= SUPPORTED_Autoneg; + required |= ecmd->advertising; + if (required & ~prev.supported) + return -EINVAL; + + /* Set the basic control bits */ + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_MMDREG_CTRL1); + reg &= ~(BMCR_SPEED1000 | BMCR_SPEED100 | BMCR_FULLDPLX | 0x003c); + reg |= ctrl1_bits; + mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL1, + reg); + + /* Set the AN registers */ + if (ecmd->autoneg != prev.autoneg || + ecmd->advertising != prev.advertising) { + bool xnp = false; + + if (efx->phy_op->set_xnp_advertise) + xnp = efx->phy_op->set_xnp_advertise(efx, + ecmd->advertising); + + if (ecmd->autoneg) { + reg = 0; + if (ecmd->advertising & ADVERTISED_10baseT_Half) + reg |= ADVERTISE_10HALF; + if (ecmd->advertising & ADVERTISED_10baseT_Full) + reg |= ADVERTISE_10FULL; + if (ecmd->advertising & ADVERTISED_100baseT_Half) + reg |= ADVERTISE_100HALF; + if (ecmd->advertising & ADVERTISED_100baseT_Full) + reg |= ADVERTISE_100FULL; + if (xnp) + reg |= ADVERTISE_RESV; + mdio_clause45_write(efx, phy_id, MDIO_MMD_AN, + MDIO_AN_ADVERTISE, reg); + } + + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, + MDIO_MMDREG_CTRL1); + if (ecmd->autoneg) + reg |= BMCR_ANENABLE | BMCR_ANRESTART; + else + reg &= ~BMCR_ANENABLE; + if (xnp) + reg |= 1 << MDIO_AN_CTRL_XNP_LBN; + else + reg &= ~(1 << MDIO_AN_CTRL_XNP_LBN); + mdio_clause45_write(efx, phy_id, MDIO_MMD_AN, + MDIO_MMDREG_CTRL1, reg); + } + + return 0; +} + +void mdio_clause45_set_pause(struct efx_nic *efx) +{ + int phy_id = efx->mii.phy_id; + int reg; + + if (efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) { + /* Set pause capability advertising */ + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, + MDIO_AN_ADVERTISE); + reg &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + reg |= efx_fc_advertise(efx->wanted_fc); + mdio_clause45_write(efx, phy_id, MDIO_MMD_AN, + MDIO_AN_ADVERTISE, reg); + + /* Restart auto-negotiation */ + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, + MDIO_MMDREG_CTRL1); + if (reg & BMCR_ANENABLE) { + reg |= BMCR_ANRESTART; + mdio_clause45_write(efx, phy_id, MDIO_MMD_AN, + MDIO_MMDREG_CTRL1, reg); + } + } +} + +enum efx_fc_type mdio_clause45_get_pause(struct efx_nic *efx) +{ + int phy_id = efx->mii.phy_id; + int lpa; + + if (!(efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN))) + return efx->wanted_fc; + lpa = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, MDIO_AN_LPA); + return efx_fc_resolve(efx->wanted_fc, lpa); } void mdio_clause45_set_flag(struct efx_nic *efx, u8 prt, u8 dev, diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/sfc/mdio_10g.h index 4830e0c1da0..80c63dde886 100644 --- a/drivers/net/sfc/mdio_10g.h +++ b/drivers/net/sfc/mdio_10g.h @@ -81,6 +81,17 @@ #define MDIO_MMDREG_DEVS_PHYXS DEV_PRESENT_BIT(MDIO_MMD_PHYXS) #define MDIO_MMDREG_DEVS_PCS DEV_PRESENT_BIT(MDIO_MMD_PCS) #define MDIO_MMDREG_DEVS_PMAPMD DEV_PRESENT_BIT(MDIO_MMD_PMAPMD) +#define MDIO_MMDREG_DEVS_AN DEV_PRESENT_BIT(MDIO_MMD_AN) + +/* Bits in MMDREG_SPEED */ +#define MDIO_MMDREG_SPEED_10G_LBN 0 +#define MDIO_MMDREG_SPEED_10G_WIDTH 1 +#define MDIO_MMDREG_SPEED_1000M_LBN 4 +#define MDIO_MMDREG_SPEED_1000M_WIDTH 1 +#define MDIO_MMDREG_SPEED_100M_LBN 5 +#define MDIO_MMDREG_SPEED_100M_WIDTH 1 +#define MDIO_MMDREG_SPEED_10M_LBN 6 +#define MDIO_MMDREG_SPEED_10M_WIDTH 1 /* Bits in MMDREG_STAT2 */ #define MDIO_MMDREG_STAT2_PRESENT_VAL (2) @@ -119,12 +130,20 @@ #define MDIO_PHYXS_LANE_ALIGNED_LBN (12) /* AN registers */ +#define MDIO_AN_CTRL_XNP_LBN 13 #define MDIO_AN_STATUS (1) #define MDIO_AN_STATUS_XNP_LBN (7) #define MDIO_AN_STATUS_PAGE_LBN (6) #define MDIO_AN_STATUS_AN_DONE_LBN (5) #define MDIO_AN_STATUS_LP_AN_CAP_LBN (0) +#define MDIO_AN_ADVERTISE 16 +#define MDIO_AN_ADVERTISE_XNP_LBN 12 +#define MDIO_AN_LPA 19 +#define MDIO_AN_XNP 22 +#define MDIO_AN_LPA_XNP 25 + +#define MDIO_AN_10GBT_ADVERTISE 32 #define MDIO_AN_10GBT_STATUS (33) #define MDIO_AN_10GBT_STATUS_MS_FLT_LBN (15) /* MASTER/SLAVE config fault */ #define MDIO_AN_10GBT_STATUS_MS_LBN (14) /* MASTER/SLAVE config */ @@ -251,10 +270,23 @@ extern void mdio_clause45_set_mmds_lpower(struct efx_nic *efx, extern void mdio_clause45_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); +/* Read (some of) the PHY settings over MDIO */ +extern void +mdio_clause45_get_settings_ext(struct efx_nic *efx, struct ethtool_cmd *ecmd, + u32 xnp, u32 xnp_lpa); + /* Set (some of) the PHY settings over MDIO */ extern int mdio_clause45_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); +/* Set pause parameters to be advertised through AN (if available) */ +extern void mdio_clause45_set_pause(struct efx_nic *efx); + +/* Get pause parameters from AN if available (otherwise return + * requested pause parameters) + */ +enum efx_fc_type mdio_clause45_get_pause(struct efx_nic *efx); + /* Wait for specified MMDs to exit reset within a timeout */ extern int mdio_clause45_wait_reset_mmds(struct efx_nic *efx, unsigned int mmd_mask); diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 883086e3945..fb8d72527a3 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -511,6 +511,35 @@ enum efx_mac_type { EFX_XMAC = 2, }; +static inline unsigned int efx_fc_advertise(enum efx_fc_type wanted_fc) +{ + unsigned int adv = 0; + if (wanted_fc & EFX_FC_RX) + adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; + if (wanted_fc & EFX_FC_TX) + adv ^= ADVERTISE_PAUSE_ASYM; + return adv; +} + +static inline enum efx_fc_type efx_fc_resolve(enum efx_fc_type wanted_fc, + unsigned int lpa) +{ + unsigned int adv = efx_fc_advertise(wanted_fc); + + if (!(wanted_fc & EFX_FC_AUTO)) + return wanted_fc; + + if (adv & lpa & ADVERTISE_PAUSE_CAP) + return EFX_FC_RX | EFX_FC_TX; + if (adv & lpa & ADVERTISE_PAUSE_ASYM) { + if (adv & ADVERTISE_PAUSE_CAP) + return EFX_FC_RX; + if (lpa & ADVERTISE_PAUSE_CAP) + return EFX_FC_TX; + } + return 0; +} + /** * struct efx_mac_operations - Efx MAC operations table * @reconfigure: Reconfigure MAC. Serialised by the mac_lock @@ -533,6 +562,8 @@ struct efx_mac_operations { * @check_hw: Check hardware * @get_settings: Get ethtool settings. Serialised by the mac_lock. * @set_settings: Set ethtool settings. Serialised by the mac_lock. + * @set_xnp_advertise: Set abilities advertised in Extended Next Page + * (only needed where AN bit is set in mmds) * @mmds: MMD presence mask * @loopbacks: Supported loopback modes mask */ @@ -548,6 +579,7 @@ struct efx_phy_operations { struct ethtool_cmd *ecmd); int (*set_settings) (struct efx_nic *efx, struct ethtool_cmd *ecmd); + bool (*set_xnp_advertise) (struct efx_nic *efx, u32); int mmds; unsigned loopbacks; }; @@ -724,11 +756,12 @@ union efx_multicast_hash { * @mac_up: MAC link state * @link_up: Link status * @link_fd: Link is full duplex + * @link_fc: Actualy flow control flags * @link_speed: Link speed (Mbps) * @n_link_state_changes: Number of times the link has changed state * @promiscuous: Promiscuous flag. Protected by netif_tx_lock. * @multicast_hash: Multicast hash table - * @flow_control: Flow control flags - separate RX/TX so can't use link_options + * @wanted_fc: Wanted flow control flags * @reconfigure_work: work item for dealing with PHY events * @loopback_mode: Loopback status * @loopback_modes: Supported loopback mode bitmask @@ -805,12 +838,13 @@ struct efx_nic { bool mac_up; bool link_up; bool link_fd; + enum efx_fc_type link_fc; unsigned int link_speed; unsigned int n_link_state_changes; bool promiscuous; union efx_multicast_hash multicast_hash; - enum efx_fc_type flow_control; + enum efx_fc_type wanted_fc; struct work_struct reconfigure_work; atomic_t rx_reset; diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index d60353bb40b..634ff919882 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -17,10 +17,10 @@ #include "boards.h" /* We expect these MMDs to be in the package */ -/* AN not here as mdio_check_mmds() requires STAT2 support */ #define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS_PMAPMD | \ MDIO_MMDREG_DEVS_PCS | \ - MDIO_MMDREG_DEVS_PHYXS) + MDIO_MMDREG_DEVS_PHYXS | \ + MDIO_MMDREG_DEVS_AN) #define TENXPRESS_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ (1 << LOOPBACK_PCS) | \ @@ -57,6 +57,7 @@ #define PMA_PMD_LED_ON (1) #define PMA_PMD_LED_OFF (2) #define PMA_PMD_LED_FLASH (3) +#define PMA_PMD_LED_MASK 3 /* All LEDs under hardware control */ #define PMA_PMD_LED_FULL_AUTO (0) /* Green and Amber under hardware control, Red off */ @@ -242,78 +243,60 @@ unlock: return rc; } -static void tenxpress_set_bad_lp(struct efx_nic *efx, bool bad_lp) +static void tenxpress_check_bad_lp(struct efx_nic *efx, bool link_ok) { struct tenxpress_phy_data *pd = efx->phy_data; + int phy_id = efx->mii.phy_id; + bool bad_lp; int reg; + if (link_ok) { + bad_lp = false; + } else { + /* Check that AN has started but not completed. */ + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, + MDIO_AN_STATUS); + if (!(reg & (1 << MDIO_AN_STATUS_LP_AN_CAP_LBN))) + return; /* LP status is unknown */ + bad_lp = !(reg & (1 << MDIO_AN_STATUS_AN_DONE_LBN)); + if (bad_lp) + pd->bad_lp_tries++; + } + /* Nothing to do if all is well and was previously so. */ - if (!(bad_lp || pd->bad_lp_tries)) + if (!pd->bad_lp_tries) return; - reg = mdio_clause45_read(efx, efx->mii.phy_id, - MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG); - - if (bad_lp) - pd->bad_lp_tries++; - else - pd->bad_lp_tries = 0; - - if (pd->bad_lp_tries == MAX_BAD_LP_TRIES) { - pd->bad_lp_tries = 0; /* Restart count */ - reg &= ~(PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN); - reg |= (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN); - EFX_ERR(efx, "This NIC appears to be plugged into" - " a port that is not 10GBASE-T capable.\n" - " This PHY is 10GBASE-T ONLY, so no link can" - " be established.\n"); - } else { - reg |= (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN); + /* Use the RX (red) LED as an error indicator once we've seen AN + * failure several times in a row, and also log a message. */ + if (!bad_lp || pd->bad_lp_tries == MAX_BAD_LP_TRIES) { + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG); + reg &= ~(PMA_PMD_LED_MASK << PMA_PMD_LED_RX_LBN); + if (!bad_lp) { + reg |= PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN; + } else { + reg |= PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN; + EFX_ERR(efx, "appears to be plugged into a port" + " that is not 10GBASE-T capable. The PHY" + " supports 10GBASE-T ONLY, so no link can" + " be established\n"); + } + mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG, reg); + pd->bad_lp_tries = bad_lp; } - mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, - PMA_PMD_LED_OVERR_REG, reg); } -/* Check link status and return a boolean OK value. If the link is NOT - * OK we have a quick rummage round to see if we appear to be plugged - * into a non-10GBT port and if so warn the user that they won't get - * link any time soon as we are 10GBT only, unless caller specified - * not to do this check (it isn't useful in loopback) */ -static bool tenxpress_link_ok(struct efx_nic *efx, bool check_lp) +static bool tenxpress_link_ok(struct efx_nic *efx) { - bool ok = mdio_clause45_links_ok(efx, TENXPRESS_REQUIRED_DEVS); - - if (ok) { - tenxpress_set_bad_lp(efx, false); - } else if (check_lp) { - /* Are we plugged into the wrong sort of link? */ - bool bad_lp = false; - int phy_id = efx->mii.phy_id; - int an_stat = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, - MDIO_AN_STATUS); - int xphy_stat = mdio_clause45_read(efx, phy_id, - MDIO_MMD_PMAPMD, - PMA_PMD_XSTATUS_REG); - /* Are we plugged into anything that sends FLPs? If - * not we can't distinguish between not being plugged - * in and being plugged into a non-AN antique. The FLP - * bit has the advantage of not clearing when autoneg - * restarts. */ - if (!(xphy_stat & (1 << PMA_PMD_XSTAT_FLP_LBN))) { - tenxpress_set_bad_lp(efx, false); - return ok; - } - - /* If it can do 10GBT it must be XNP capable */ - bad_lp = !(an_stat & (1 << MDIO_AN_STATUS_XNP_LBN)); - if (!bad_lp && (an_stat & (1 << MDIO_AN_STATUS_PAGE_LBN))) { - bad_lp = !(mdio_clause45_read(efx, phy_id, - MDIO_MMD_AN, MDIO_AN_10GBT_STATUS) & - (1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN)); - } - tenxpress_set_bad_lp(efx, bad_lp); - } - return ok; + if (efx->loopback_mode == LOOPBACK_NONE) + return mdio_clause45_links_ok(efx, MDIO_MMDREG_DEVS_AN); + else + return mdio_clause45_links_ok(efx, + MDIO_MMDREG_DEVS_PMAPMD | + MDIO_MMDREG_DEVS_PCS | + MDIO_MMDREG_DEVS_PHYXS); } static void tenxpress_phyxs_loopback(struct efx_nic *efx) @@ -359,9 +342,10 @@ static void tenxpress_phy_reconfigure(struct efx_nic *efx) phy_data->loopback_mode = efx->loopback_mode; phy_data->phy_mode = efx->phy_mode; - efx->link_up = tenxpress_link_ok(efx, false); + efx->link_up = tenxpress_link_ok(efx); efx->link_speed = 10000; efx->link_fd = true; + efx->link_fc = mdio_clause45_get_pause(efx); } static void tenxpress_phy_clear_interrupt(struct efx_nic *efx) @@ -377,7 +361,8 @@ static int tenxpress_phy_check_hw(struct efx_nic *efx) bool link_ok; int rc = 0; - link_ok = tenxpress_link_ok(efx, true); + link_ok = tenxpress_link_ok(efx); + tenxpress_check_bad_lp(efx, link_ok); if (link_ok != efx->link_up) falcon_sim_phy_event(efx); @@ -451,6 +436,27 @@ static int tenxpress_phy_test(struct efx_nic *efx) return tenxpress_special_reset(efx); } +static u32 tenxpress_get_xnp_lpa(struct efx_nic *efx) +{ + int phy = efx->mii.phy_id; + u32 lpa = 0; + int reg; + + reg = mdio_clause45_read(efx, phy, MDIO_MMD_AN, MDIO_AN_10GBT_STATUS); + if (reg & (1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN)) + lpa |= ADVERTISED_10000baseT_Full; + return lpa; +} + +static void +tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + mdio_clause45_get_settings_ext(efx, ecmd, ADVERTISED_10000baseT_Full, + tenxpress_get_xnp_lpa(efx)); + ecmd->supported |= SUPPORTED_10000baseT_Full; + ecmd->advertising |= ADVERTISED_10000baseT_Full; +} + struct efx_phy_operations falcon_tenxpress_phy_ops = { .macs = EFX_XMAC, .init = tenxpress_phy_init, @@ -459,7 +465,7 @@ struct efx_phy_operations falcon_tenxpress_phy_ops = { .fini = tenxpress_phy_fini, .clear_interrupt = tenxpress_phy_clear_interrupt, .test = tenxpress_phy_test, - .get_settings = mdio_clause45_get_settings, + .get_settings = tenxpress_get_settings, .set_settings = mdio_clause45_set_settings, .mmds = TENXPRESS_REQUIRED_DEVS, .loopbacks = TENXPRESS_LOOPBACKS, diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/xfp_phy.c index d4e203ddcf1..fbe8e25a1ed 100644 --- a/drivers/net/sfc/xfp_phy.c +++ b/drivers/net/sfc/xfp_phy.c @@ -155,6 +155,7 @@ static void xfp_phy_reconfigure(struct efx_nic *efx) efx->link_up = xfp_link_ok(efx); efx->link_speed = 10000; efx->link_fd = true; + efx->link_fc = efx->wanted_fc; } -- cgit v1.2.3-70-g09d2 From 766ca0fa6bf1600bdf4bc7726c74f14c8455c6b8 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 12 Dec 2008 21:59:24 -0800 Subject: sfc: Rework MAC, PHY and board event handling From: Steve Hodgson MAC, PHY and board events may be separately enabled and signalled. Our current arrangement of chaining the polling functions can result in events being missed. Change them to be more independent. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/efx.c | 63 +++++++++++++++++++++++++++-------------- drivers/net/sfc/falcon.c | 20 ++++++------- drivers/net/sfc/falcon_gmac.c | 8 ++---- drivers/net/sfc/falcon_hwdefs.h | 2 ++ drivers/net/sfc/falcon_xmac.c | 42 ++++++++++++++------------- drivers/net/sfc/net_driver.h | 24 +++++++++------- drivers/net/sfc/selftest.c | 6 ++-- drivers/net/sfc/tenxpress.c | 44 +++++++++------------------- drivers/net/sfc/xfp_phy.c | 16 ++--------- 9 files changed, 111 insertions(+), 114 deletions(-) (limited to 'drivers/net/sfc/xfp_phy.c') diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 7214ea6f7e1..ce1c7d3cd12 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -612,10 +612,9 @@ void efx_reconfigure_port(struct efx_nic *efx) /* Asynchronous efx_reconfigure_port work item. To speed up efx_flush_all() * we don't efx_reconfigure_port() if the port is disabled. Care is taken * in efx_stop_all() and efx_start_port() to prevent PHY events being lost */ -static void efx_reconfigure_work(struct work_struct *data) +static void efx_phy_work(struct work_struct *data) { - struct efx_nic *efx = container_of(data, struct efx_nic, - reconfigure_work); + struct efx_nic *efx = container_of(data, struct efx_nic, phy_work); mutex_lock(&efx->mac_lock); if (efx->port_enabled) @@ -623,6 +622,16 @@ static void efx_reconfigure_work(struct work_struct *data) mutex_unlock(&efx->mac_lock); } +static void efx_mac_work(struct work_struct *data) +{ + struct efx_nic *efx = container_of(data, struct efx_nic, mac_work); + + mutex_lock(&efx->mac_lock); + if (efx->port_enabled) + efx->mac_op->irq(efx); + mutex_unlock(&efx->mac_lock); +} + static int efx_probe_port(struct efx_nic *efx) { int rc; @@ -688,7 +697,7 @@ fail: /* Allow efx_reconfigure_port() to be scheduled, and close the window * between efx_stop_port and efx_flush_all whereby a previously scheduled - * efx_reconfigure_port() may have been cancelled */ + * efx_phy_work()/efx_mac_work() may have been cancelled */ static void efx_start_port(struct efx_nic *efx) { EFX_LOG(efx, "start port\n"); @@ -697,13 +706,14 @@ static void efx_start_port(struct efx_nic *efx) mutex_lock(&efx->mac_lock); efx->port_enabled = true; __efx_reconfigure_port(efx); + efx->mac_op->irq(efx); mutex_unlock(&efx->mac_lock); } -/* Prevent efx_reconfigure_work and efx_monitor() from executing, and - * efx_set_multicast_list() from scheduling efx_reconfigure_work. - * efx_reconfigure_work can still be scheduled via NAPI processing - * until efx_flush_all() is called */ +/* Prevent efx_phy_work, efx_mac_work, and efx_monitor() from executing, + * and efx_set_multicast_list() from scheduling efx_phy_work. efx_phy_work + * and efx_mac_work may still be scheduled via NAPI processing until + * efx_flush_all() is called */ static void efx_stop_port(struct efx_nic *efx) { EFX_LOG(efx, "stop port\n"); @@ -1094,7 +1104,8 @@ static void efx_flush_all(struct efx_nic *efx) cancel_delayed_work_sync(&rx_queue->work); /* Stop scheduled port reconfigurations */ - cancel_work_sync(&efx->reconfigure_work); + cancel_work_sync(&efx->mac_work); + cancel_work_sync(&efx->phy_work); } @@ -1131,7 +1142,7 @@ static void efx_stop_all(struct efx_nic *efx) * window to loose phy events */ efx_stop_port(efx); - /* Flush reconfigure_work, refill_workqueue, monitor_work */ + /* Flush efx_phy_work, efx_mac_work, refill_workqueue, monitor_work */ efx_flush_all(efx); /* Isolate the MAC from the TX and RX engines, so that queue @@ -1203,24 +1214,31 @@ static void efx_monitor(struct work_struct *data) { struct efx_nic *efx = container_of(data, struct efx_nic, monitor_work.work); + int rc; EFX_TRACE(efx, "hardware monitor executing on CPU %d\n", raw_smp_processor_id()); - /* If the mac_lock is already held then it is likely a port * reconfiguration is already in place, which will likely do * most of the work of check_hw() anyway. */ - if (!mutex_trylock(&efx->mac_lock)) { - queue_delayed_work(efx->workqueue, &efx->monitor_work, - efx_monitor_interval); - return; + if (!mutex_trylock(&efx->mac_lock)) + goto out_requeue; + if (!efx->port_enabled) + goto out_unlock; + rc = efx->board_info.monitor(efx); + if (rc) { + EFX_ERR(efx, "Board sensor %s; shutting down PHY\n", + (rc == -ERANGE) ? "reported fault" : "failed"); + efx->phy_mode |= PHY_MODE_LOW_POWER; + falcon_sim_phy_event(efx); } + efx->phy_op->poll(efx); + efx->mac_op->poll(efx); - if (efx->port_enabled) - efx->mac_op->check_hw(efx); +out_unlock: mutex_unlock(&efx->mac_lock); - +out_requeue: queue_delayed_work(efx->workqueue, &efx->monitor_work, efx_monitor_interval); } @@ -1477,7 +1495,7 @@ static void efx_set_multicast_list(struct net_device *net_dev) return; if (changed) - queue_work(efx->workqueue, &efx->reconfigure_work); + queue_work(efx->workqueue, &efx->phy_work); /* Create and activate new global multicast hash table */ falcon_set_multicast_hash(efx); @@ -1800,12 +1818,14 @@ void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink) {} static struct efx_mac_operations efx_dummy_mac_operations = { .reconfigure = efx_port_dummy_op_void, + .poll = efx_port_dummy_op_void, + .irq = efx_port_dummy_op_void, }; static struct efx_phy_operations efx_dummy_phy_operations = { .init = efx_port_dummy_op_int, .reconfigure = efx_port_dummy_op_void, - .check_hw = efx_port_dummy_op_int, + .poll = efx_port_dummy_op_void, .fini = efx_port_dummy_op_void, .clear_interrupt = efx_port_dummy_op_void, }; @@ -1857,7 +1877,8 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, efx->mac_op = &efx_dummy_mac_operations; efx->phy_op = &efx_dummy_phy_operations; efx->mii.dev = net_dev; - INIT_WORK(&efx->reconfigure_work, efx_reconfigure_work); + INIT_WORK(&efx->phy_work, efx_phy_work); + INIT_WORK(&efx->mac_work, efx_mac_work); atomic_set(&efx->netif_stop_count, 1); for (i = 0; i < EFX_MAX_CHANNELS; i++) { diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index fde4e7912c3..5a70ee7e814 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -910,22 +910,20 @@ static void falcon_handle_global_event(struct efx_channel *channel, efx_qword_t *event) { struct efx_nic *efx = channel->efx; - bool is_phy_event = false, handled = false; + bool handled = false; - /* Check for interrupt on either port. Some boards have a - * single PHY wired to the interrupt line for port 1. */ if (EFX_QWORD_FIELD(*event, G_PHY0_INTR) || EFX_QWORD_FIELD(*event, G_PHY1_INTR) || - EFX_QWORD_FIELD(*event, XG_PHY_INTR)) - is_phy_event = true; + EFX_QWORD_FIELD(*event, XG_PHY_INTR) || + EFX_QWORD_FIELD(*event, XFP_PHY_INTR)) { + efx->phy_op->clear_interrupt(efx); + queue_work(efx->workqueue, &efx->phy_work); + handled = true; + } if ((falcon_rev(efx) >= FALCON_REV_B0) && - EFX_QWORD_FIELD(*event, XG_MNT_INTR_B0)) - is_phy_event = true; - - if (is_phy_event) { - efx->phy_op->clear_interrupt(efx); - queue_work(efx->workqueue, &efx->reconfigure_work); + EFX_QWORD_FIELD(*event, XG_MNT_INTR_B0)) { + queue_work(efx->workqueue, &efx->mac_work); handled = true; } diff --git a/drivers/net/sfc/falcon_gmac.c b/drivers/net/sfc/falcon_gmac.c index b6e6eb96390..8865eae20ac 100644 --- a/drivers/net/sfc/falcon_gmac.c +++ b/drivers/net/sfc/falcon_gmac.c @@ -221,13 +221,9 @@ static void falcon_update_stats_gmac(struct efx_nic *efx) mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64; } -static int falcon_check_gmac(struct efx_nic *efx) -{ - return efx->phy_op->check_hw(efx); -} - struct efx_mac_operations falcon_gmac_operations = { .reconfigure = falcon_reconfigure_gmac, .update_stats = falcon_update_stats_gmac, - .check_hw = falcon_check_gmac, + .irq = efx_port_dummy_op_void, + .poll = efx_port_dummy_op_void, }; diff --git a/drivers/net/sfc/falcon_hwdefs.h b/drivers/net/sfc/falcon_hwdefs.h index 5553df888b8..a58c627f51a 100644 --- a/drivers/net/sfc/falcon_hwdefs.h +++ b/drivers/net/sfc/falcon_hwdefs.h @@ -1051,6 +1051,8 @@ #define XG_MNT_INTR_B0_WIDTH 1 #define RX_RECOVERY_A1_LBN 11 #define RX_RECOVERY_A1_WIDTH 1 +#define XFP_PHY_INTR_LBN 10 +#define XFP_PHY_INTR_WIDTH 1 #define XG_PHY_INTR_LBN 9 #define XG_PHY_INTR_WIDTH 1 #define G_PHY1_INTR_LBN 8 diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c index 0ce8f015386..5a03713685a 100644 --- a/drivers/net/sfc/falcon_xmac.c +++ b/drivers/net/sfc/falcon_xmac.c @@ -342,33 +342,35 @@ static void falcon_update_stats_xmac(struct efx_nic *efx) mac_stats->rx_control * 64); } -static int falcon_check_xmac(struct efx_nic *efx) +static void falcon_xmac_irq(struct efx_nic *efx) { - bool xaui_link_ok; - int rc; + /* The XGMII link has a transient fault, which indicates either: + * - there's a transient xgmii fault + * - falcon's end of the xaui link may need a kick + * - the wire-side link may have gone down, but the lasi/poll() + * hasn't noticed yet. + * + * We only want to even bother polling XAUI if we're confident it's + * not (1) or (3). In both cases, the only reliable way to spot this + * is to wait a bit. We do this here by forcing the mac link state + * to down, and waiting for the mac poll to come round and check + */ + efx->mac_up = false; +} - if ((efx->loopback_mode == LOOPBACK_NETWORK) || - efx_phy_mode_disabled(efx->phy_mode)) - return 0; +static void falcon_poll_xmac(struct efx_nic *efx) +{ + if (!EFX_WORKAROUND_5147(efx) || !efx->link_up || efx->mac_up) + return; falcon_mask_status_intr(efx, false); - xaui_link_ok = falcon_xaui_link_ok(efx); - - if (EFX_WORKAROUND_5147(efx) && !xaui_link_ok) - falcon_reset_xaui(efx); - - /* Call the PHY check_hw routine */ - rc = efx->phy_op->check_hw(efx); - - /* Unmask interrupt if everything was (and still is) ok */ - if (xaui_link_ok && efx->link_up) - falcon_mask_status_intr(efx, true); - - return rc; + falcon_check_xaui_link_up(efx, 1); + falcon_mask_status_intr(efx, true); } struct efx_mac_operations falcon_xmac_operations = { .reconfigure = falcon_reconfigure_xmac, .update_stats = falcon_update_stats_xmac, - .check_hw = falcon_check_xmac, + .irq = falcon_xmac_irq, + .poll = falcon_poll_xmac, }; diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index fb8d72527a3..61d09a2fa72 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -544,12 +544,14 @@ static inline enum efx_fc_type efx_fc_resolve(enum efx_fc_type wanted_fc, * struct efx_mac_operations - Efx MAC operations table * @reconfigure: Reconfigure MAC. Serialised by the mac_lock * @update_stats: Update statistics - * @check_hw: Check hardware. Serialised by the mac_lock + * @irq: Hardware MAC event callback. Serialised by the mac_lock + * @poll: Poll for hardware state. Serialised by the mac_lock */ struct efx_mac_operations { void (*reconfigure) (struct efx_nic *efx); void (*update_stats) (struct efx_nic *efx); - int (*check_hw) (struct efx_nic *efx); + void (*irq) (struct efx_nic *efx); + void (*poll) (struct efx_nic *efx); }; /** @@ -559,7 +561,7 @@ struct efx_mac_operations { * @reconfigure: Reconfigure PHY (e.g. for new link parameters) * @clear_interrupt: Clear down interrupt * @blink: Blink LEDs - * @check_hw: Check hardware + * @poll: Poll for hardware state. Serialised by the mac_lock. * @get_settings: Get ethtool settings. Serialised by the mac_lock. * @set_settings: Set ethtool settings. Serialised by the mac_lock. * @set_xnp_advertise: Set abilities advertised in Extended Next Page @@ -573,7 +575,7 @@ struct efx_phy_operations { void (*fini) (struct efx_nic *efx); void (*reconfigure) (struct efx_nic *efx); void (*clear_interrupt) (struct efx_nic *efx); - int (*check_hw) (struct efx_nic *efx); + void (*poll) (struct efx_nic *efx); int (*test) (struct efx_nic *efx); void (*get_settings) (struct efx_nic *efx, struct ethtool_cmd *ecmd); @@ -728,10 +730,10 @@ union efx_multicast_hash { * @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode, * @port_inhibited, efx_monitor() and efx_reconfigure_port() * @port_enabled: Port enabled indicator. - * Serialises efx_stop_all(), efx_start_all() and efx_monitor() and - * efx_reconfigure_work with kernel interfaces. Safe to read under any - * one of the rtnl_lock, mac_lock, or netif_tx_lock, but all three must - * be held to modify it. + * Serialises efx_stop_all(), efx_start_all(), efx_monitor(), + * efx_phy_work(), and efx_mac_work() with kernel interfaces. Safe to read + * under any one of the rtnl_lock, mac_lock, or netif_tx_lock, but all + * three must be held to modify it. * @port_inhibited: If set, the netif_carrier is always off. Hold the mac_lock * @port_initialized: Port initialized? * @net_dev: Operating system network device. Consider holding the rtnl lock @@ -762,7 +764,8 @@ union efx_multicast_hash { * @promiscuous: Promiscuous flag. Protected by netif_tx_lock. * @multicast_hash: Multicast hash table * @wanted_fc: Wanted flow control flags - * @reconfigure_work: work item for dealing with PHY events + * @phy_work: work item for dealing with PHY events + * @mac_work: work item for dealing with MAC events * @loopback_mode: Loopback status * @loopback_modes: Supported loopback mode bitmask * @loopback_selftest: Offline self-test private state @@ -810,6 +813,7 @@ struct efx_nic { struct falcon_nic_data *nic_data; struct mutex mac_lock; + struct work_struct mac_work; bool port_enabled; bool port_inhibited; @@ -830,6 +834,7 @@ struct efx_nic { enum phy_type phy_type; spinlock_t phy_lock; + struct work_struct phy_work; struct efx_phy_operations *phy_op; void *phy_data; struct mii_if_info mii; @@ -845,7 +850,6 @@ struct efx_nic { bool promiscuous; union efx_multicast_hash multicast_hash; enum efx_fc_type wanted_fc; - struct work_struct reconfigure_work; atomic_t rx_reset; enum efx_loopback_mode loopback_mode; diff --git a/drivers/net/sfc/selftest.c b/drivers/net/sfc/selftest.c index 8142e37a518..578b7f410ed 100644 --- a/drivers/net/sfc/selftest.c +++ b/drivers/net/sfc/selftest.c @@ -594,12 +594,14 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct ethtool_cmd ecmd, efx->loopback_mode = mode; efx_reconfigure_port(efx); - /* Wait for the PHY to signal the link is up */ + /* Wait for the PHY to signal the link is up. Interrupts + * are enabled for PHY's using LASI, otherwise we poll() + * quickly */ count = 0; do { struct efx_channel *channel = &efx->channel[0]; - efx->mac_op->check_hw(efx); + efx->phy_op->poll(efx); schedule_timeout_uninterruptible(HZ / 10); if (channel->work_pending) efx_process_channel_now(channel); diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index 634ff919882..7256ea4abf9 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -348,50 +348,34 @@ static void tenxpress_phy_reconfigure(struct efx_nic *efx) efx->link_fc = mdio_clause45_get_pause(efx); } -static void tenxpress_phy_clear_interrupt(struct efx_nic *efx) -{ - /* Nothing done here - LASI interrupts aren't reliable so poll */ -} - - /* Poll PHY for interrupt */ -static int tenxpress_phy_check_hw(struct efx_nic *efx) +static void tenxpress_phy_poll(struct efx_nic *efx) { struct tenxpress_phy_data *phy_data = efx->phy_data; - bool link_ok; - int rc = 0; + bool change = false, link_ok; + unsigned link_fc; link_ok = tenxpress_link_ok(efx); + if (link_ok != efx->link_up) { + change = true; + } else { + link_fc = mdio_clause45_get_pause(efx); + if (link_fc != efx->link_fc) + change = true; + } tenxpress_check_bad_lp(efx, link_ok); - if (link_ok != efx->link_up) + if (change) falcon_sim_phy_event(efx); if (phy_data->phy_mode != PHY_MODE_NORMAL) - return 0; + return; if (atomic_read(&phy_data->bad_crc_count) > crc_error_reset_threshold) { EFX_ERR(efx, "Resetting XAUI due to too many CRC errors\n"); falcon_reset_xaui(efx); atomic_set(&phy_data->bad_crc_count, 0); } - - rc = efx->board_info.monitor(efx); - if (rc) { - EFX_ERR(efx, "Board sensor %s; shutting down PHY\n", - (rc == -ERANGE) ? "reported fault" : "failed"); - if (efx->phy_mode & PHY_MODE_OFF) { - /* Assume that board has shut PHY off */ - phy_data->phy_mode = PHY_MODE_OFF; - } else { - efx->phy_mode |= PHY_MODE_LOW_POWER; - mdio_clause45_set_mmds_lpower(efx, true, - efx->phy_op->mmds); - phy_data->phy_mode |= PHY_MODE_LOW_POWER; - } - } - - return rc; } static void tenxpress_phy_fini(struct efx_nic *efx) @@ -461,9 +445,9 @@ struct efx_phy_operations falcon_tenxpress_phy_ops = { .macs = EFX_XMAC, .init = tenxpress_phy_init, .reconfigure = tenxpress_phy_reconfigure, - .check_hw = tenxpress_phy_check_hw, + .poll = tenxpress_phy_poll, .fini = tenxpress_phy_fini, - .clear_interrupt = tenxpress_phy_clear_interrupt, + .clear_interrupt = efx_port_dummy_op_void, .test = tenxpress_phy_test, .get_settings = tenxpress_get_settings, .set_settings = mdio_clause45_set_settings, diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/xfp_phy.c index fbe8e25a1ed..345ffc310dc 100644 --- a/drivers/net/sfc/xfp_phy.c +++ b/drivers/net/sfc/xfp_phy.c @@ -119,24 +119,12 @@ static int xfp_link_ok(struct efx_nic *efx) return mdio_clause45_links_ok(efx, XFP_REQUIRED_DEVS); } -static int xfp_phy_check_hw(struct efx_nic *efx) +static void xfp_phy_poll(struct efx_nic *efx) { - int rc = 0; int link_up = xfp_link_ok(efx); /* Simulate a PHY event if link state has changed */ if (link_up != efx->link_up) falcon_sim_phy_event(efx); - - rc = efx->board_info.monitor(efx); - if (rc) { - struct xfp_phy_data *phy_data = efx->phy_data; - EFX_ERR(efx, "XFP sensor alert; putting PHY into low power\n"); - efx->phy_mode |= PHY_MODE_LOW_POWER; - mdio_clause45_set_mmds_lpower(efx, 1, XFP_REQUIRED_DEVS); - phy_data->phy_mode |= PHY_MODE_LOW_POWER; - } - - return rc; } static void xfp_phy_reconfigure(struct efx_nic *efx) @@ -173,7 +161,7 @@ struct efx_phy_operations falcon_xfp_phy_ops = { .macs = EFX_XMAC, .init = xfp_phy_init, .reconfigure = xfp_phy_reconfigure, - .check_hw = xfp_phy_check_hw, + .poll = xfp_phy_poll, .fini = xfp_phy_fini, .clear_interrupt = xfp_phy_clear_interrupt, .get_settings = mdio_clause45_get_settings, -- cgit v1.2.3-70-g09d2 From ab377358701a981e7fae1332e76b1e5d3b3b313d Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 12 Dec 2008 22:06:54 -0800 Subject: sfc: Use model numbers for PHY type names Some of the PHY type names are overly generic. Change them to include the model numbers of the PHYs they represent. Correct the model number reference at the top of xfp_phy.c. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/falcon.c | 2 +- drivers/net/sfc/net_driver.h | 6 +++--- drivers/net/sfc/xfp_phy.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net/sfc/xfp_phy.c') diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index 13627a64bce..df844defc0a 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -2252,7 +2252,7 @@ static int falcon_probe_phy(struct efx_nic *efx) case PHY_TYPE_SFT9001B: efx->phy_op = &falcon_sft9001_phy_ops; break; - case PHY_TYPE_XFP: + case PHY_TYPE_QT2022C2: efx->phy_op = &falcon_xfp_phy_ops; break; default: diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 17283293302..7033dba87ed 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -453,10 +453,10 @@ enum efx_int_mode { enum phy_type { PHY_TYPE_NONE = 0, - PHY_TYPE_CX4_RTMR = 1, - PHY_TYPE_1G_ALASKA = 2, + PHY_TYPE_TXC43128 = 1, + PHY_TYPE_88E1111 = 2, PHY_TYPE_SFX7101 = 3, - PHY_TYPE_XFP = 4, + PHY_TYPE_QT2022C2 = 4, PHY_TYPE_PM8358 = 6, PHY_TYPE_SFT9001A = 8, PHY_TYPE_SFT9001B = 10, diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/xfp_phy.c index 345ffc310dc..2d50b6ecf5f 100644 --- a/drivers/net/sfc/xfp_phy.c +++ b/drivers/net/sfc/xfp_phy.c @@ -7,7 +7,7 @@ * by the Free Software Foundation, incorporated herein by reference. */ /* - * Driver for XFP optical PHYs (plus some support specific to the Quake 2032) + * Driver for XFP optical PHYs (plus some support specific to the Quake 2022/32) * See www.amcc.com for details (search for qt2032) */ -- cgit v1.2.3-70-g09d2