aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/mdio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/mdio.c')
-rw-r--r--drivers/net/mdio.c93
1 files changed, 51 insertions, 42 deletions
diff --git a/drivers/net/mdio.c b/drivers/net/mdio.c
index 66483035f68..3e027ed0b3b 100644
--- a/drivers/net/mdio.c
+++ b/drivers/net/mdio.c
@@ -14,6 +14,10 @@
#include <linux/mdio.h>
#include <linux/module.h>
+MODULE_DESCRIPTION("Generic support for MDIO-compatible transceivers");
+MODULE_AUTHOR("Copyright 2006-2009 Solarflare Communications Inc.");
+MODULE_LICENSE("GPL");
+
/**
* mdio45_probe - probe for an MDIO (clause 45) device
* @mdio: MDIO interface
@@ -105,13 +109,20 @@ int mdio45_links_ok(const struct mdio_if_info *mdio, u32 mmd_mask)
if (mmd_mask & (1 << devad)) {
mmd_mask &= ~(1 << devad);
- /* Read twice because link state is latched and a
- * read moves the current state into the register */
+ /* Reset the latched status and fault flags */
mdio->mdio_read(mdio->dev, mdio->prtad,
devad, MDIO_STAT1);
+ if (devad == MDIO_MMD_PMAPMD || devad == MDIO_MMD_PCS ||
+ devad == MDIO_MMD_PHYXS || devad == MDIO_MMD_DTEXS)
+ mdio->mdio_read(mdio->dev, mdio->prtad,
+ devad, MDIO_STAT2);
+
+ /* Check the current status and fault flags */
reg = mdio->mdio_read(mdio->dev, mdio->prtad,
devad, MDIO_STAT1);
- if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
+ if (reg < 0 ||
+ (reg & (MDIO_STAT1_FAULT | MDIO_STAT1_LSTATUS)) !=
+ MDIO_STAT1_LSTATUS)
return false;
}
}
@@ -151,6 +162,10 @@ static u32 mdio45_get_an(const struct mdio_if_info *mdio, u16 addr)
result |= ADVERTISED_100baseT_Half;
if (reg & ADVERTISE_100FULL)
result |= ADVERTISED_100baseT_Full;
+ if (reg & ADVERTISE_PAUSE_CAP)
+ result |= ADVERTISED_Pause;
+ if (reg & ADVERTISE_PAUSE_ASYM)
+ result |= ADVERTISED_Asym_Pause;
return result;
}
@@ -161,6 +176,9 @@ static u32 mdio45_get_an(const struct mdio_if_info *mdio, u16 addr)
* @npage_adv: Modes currently advertised on next pages
* @npage_lpa: Modes advertised by link partner on next pages
*
+ * The @ecmd parameter is expected to have been cleared before calling
+ * mdio45_ethtool_gset_npage().
+ *
* Since the CSRs for auto-negotiation using next pages are not fully
* standardised, this function does not attempt to decode them. The
* caller must pass them in.
@@ -170,6 +188,10 @@ void mdio45_ethtool_gset_npage(const struct mdio_if_info *mdio,
u32 npage_adv, u32 npage_lpa)
{
int reg;
+ u32 speed;
+
+ BUILD_BUG_ON(MDIO_SUPPORTS_C22 != ETH_MDIO_SUPPORTS_C22);
+ BUILD_BUG_ON(MDIO_SUPPORTS_C45 != ETH_MDIO_SUPPORTS_C45);
ecmd->transceiver = XCVR_INTERNAL;
ecmd->phy_address = mdio->prtad;
@@ -272,62 +294,52 @@ void mdio45_ethtool_gset_npage(const struct mdio_if_info *mdio,
if (modes & (ADVERTISED_10000baseT_Full |
ADVERTISED_10000baseKX4_Full |
ADVERTISED_10000baseKR_Full)) {
- ecmd->speed = SPEED_10000;
+ speed = SPEED_10000;
ecmd->duplex = DUPLEX_FULL;
} else if (modes & (ADVERTISED_1000baseT_Full |
ADVERTISED_1000baseT_Half |
ADVERTISED_1000baseKX_Full)) {
- ecmd->speed = SPEED_1000;
+ speed = SPEED_1000;
ecmd->duplex = !(modes & ADVERTISED_1000baseT_Half);
} else if (modes & (ADVERTISED_100baseT_Full |
ADVERTISED_100baseT_Half)) {
- ecmd->speed = SPEED_100;
+ speed = SPEED_100;
ecmd->duplex = !!(modes & ADVERTISED_100baseT_Full);
} else {
- ecmd->speed = SPEED_10;
+ speed = SPEED_10;
ecmd->duplex = !!(modes & ADVERTISED_10baseT_Full);
}
} else {
/* Report forced settings */
reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
MDIO_CTRL1);
- ecmd->speed = (((reg & MDIO_PMA_CTRL1_SPEED1000) ? 100 : 1) *
- ((reg & MDIO_PMA_CTRL1_SPEED100) ? 100 : 10));
+ speed = (((reg & MDIO_PMA_CTRL1_SPEED1000) ? 100 : 1)
+ * ((reg & MDIO_PMA_CTRL1_SPEED100) ? 100 : 10));
ecmd->duplex = (reg & MDIO_CTRL1_FULLDPLX ||
- ecmd->speed == SPEED_10000);
+ speed == SPEED_10000);
}
-}
-EXPORT_SYMBOL(mdio45_ethtool_gset_npage);
-/**
- * mdio45_ethtool_spauseparam_an - set auto-negotiated pause parameters
- * @mdio: MDIO interface
- * @ecmd: Ethtool request structure
- *
- * This function assumes that the PHY has an auto-negotiation MMD. It
- * will enable and disable advertising of flow control as appropriate.
- */
-void mdio45_ethtool_spauseparam_an(const struct mdio_if_info *mdio,
- const struct ethtool_pauseparam *ecmd)
-{
- int adv, old_adv;
-
- WARN_ON(!(mdio->mmds & MDIO_DEVS_AN));
-
- old_adv = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN,
- MDIO_AN_ADVERTISE);
- adv = old_adv & ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
- if (ecmd->autoneg)
- adv |= mii_advertise_flowctrl(
- (ecmd->rx_pause ? FLOW_CTRL_RX : 0) |
- (ecmd->tx_pause ? FLOW_CTRL_TX : 0));
- if (adv != old_adv) {
- mdio->mdio_write(mdio->dev, mdio->prtad, MDIO_MMD_AN,
- MDIO_AN_ADVERTISE, adv);
- mdio45_nway_restart(mdio);
+ ethtool_cmd_speed_set(ecmd, speed);
+
+ /* 10GBASE-T MDI/MDI-X */
+ if (ecmd->port == PORT_TP
+ && (ethtool_cmd_speed(ecmd) == SPEED_10000)) {
+ switch (mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
+ MDIO_PMA_10GBT_SWAPPOL)) {
+ case MDIO_PMA_10GBT_SWAPPOL_ABNX | MDIO_PMA_10GBT_SWAPPOL_CDNX:
+ ecmd->eth_tp_mdix = ETH_TP_MDI;
+ break;
+ case 0:
+ ecmd->eth_tp_mdix = ETH_TP_MDI_X;
+ break;
+ default:
+ /* It's complicated... */
+ ecmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
+ break;
+ }
}
}
-EXPORT_SYMBOL(mdio45_ethtool_spauseparam_an);
+EXPORT_SYMBOL(mdio45_ethtool_gset_npage);
/**
* mdio_mii_ioctl - MII ioctl interface for MDIO (clause 22 or 45) PHYs
@@ -352,10 +364,7 @@ int mdio_mii_ioctl(const struct mdio_if_info *mdio,
cmd = SIOCGMIIREG;
break;
case SIOCGMIIREG:
- break;
case SIOCSMIIREG:
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
break;
default:
return -EOPNOTSUPP;