diff options
Diffstat (limited to 'drivers/net/phy/phy.c')
| -rw-r--r-- | drivers/net/phy/phy.c | 113 | 
1 files changed, 110 insertions, 3 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 5590b9c182c9..c5dc2c363f96 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -362,6 +362,60 @@ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)  }  EXPORT_SYMBOL(phy_ethtool_sset); +int phy_ethtool_ksettings_set(struct phy_device *phydev, +			      const struct ethtool_link_ksettings *cmd) +{ +	u8 autoneg = cmd->base.autoneg; +	u8 duplex = cmd->base.duplex; +	u32 speed = cmd->base.speed; +	u32 advertising; + +	if (cmd->base.phy_address != phydev->mdio.addr) +		return -EINVAL; + +	ethtool_convert_link_mode_to_legacy_u32(&advertising, +						cmd->link_modes.advertising); + +	/* We make sure that we don't pass unsupported values in to the PHY */ +	advertising &= phydev->supported; + +	/* Verify the settings we care about. */ +	if (autoneg != AUTONEG_ENABLE && autoneg != AUTONEG_DISABLE) +		return -EINVAL; + +	if (autoneg == AUTONEG_ENABLE && advertising == 0) +		return -EINVAL; + +	if (autoneg == AUTONEG_DISABLE && +	    ((speed != SPEED_1000 && +	      speed != SPEED_100 && +	      speed != SPEED_10) || +	     (duplex != DUPLEX_HALF && +	      duplex != DUPLEX_FULL))) +		return -EINVAL; + +	phydev->autoneg = autoneg; + +	phydev->speed = speed; + +	phydev->advertising = advertising; + +	if (autoneg == AUTONEG_ENABLE) +		phydev->advertising |= ADVERTISED_Autoneg; +	else +		phydev->advertising &= ~ADVERTISED_Autoneg; + +	phydev->duplex = duplex; + +	phydev->mdix = cmd->base.eth_tp_mdix_ctrl; + +	/* Restart the PHY */ +	phy_start_aneg(phydev); + +	return 0; +} +EXPORT_SYMBOL(phy_ethtool_ksettings_set); +  int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd)  {  	cmd->supported = phydev->supported; @@ -385,6 +439,33 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd)  }  EXPORT_SYMBOL(phy_ethtool_gset); +int phy_ethtool_ksettings_get(struct phy_device *phydev, +			      struct ethtool_link_ksettings *cmd) +{ +	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, +						phydev->supported); + +	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, +						phydev->advertising); + +	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising, +						phydev->lp_advertising); + +	cmd->base.speed = phydev->speed; +	cmd->base.duplex = phydev->duplex; +	if (phydev->interface == PHY_INTERFACE_MODE_MOCA) +		cmd->base.port = PORT_BNC; +	else +		cmd->base.port = PORT_MII; + +	cmd->base.phy_address = phydev->mdio.addr; +	cmd->base.autoneg = phydev->autoneg; +	cmd->base.eth_tp_mdix_ctrl = phydev->mdix; + +	return 0; +} +EXPORT_SYMBOL(phy_ethtool_ksettings_get); +  /**   * phy_mii_ioctl - generic PHY MII ioctl interface   * @phydev: the phy_device struct @@ -790,9 +871,11 @@ void phy_start(struct phy_device *phydev)  		break;  	case PHY_HALTED:  		/* make sure interrupts are re-enabled for the PHY */ -		err = phy_enable_interrupts(phydev); -		if (err < 0) -			break; +		if (phydev->irq != PHY_POLL) { +			err = phy_enable_interrupts(phydev); +			if (err < 0) +				break; +		}  		phydev->state = PHY_RESUMING;  		do_resume = true; @@ -1266,3 +1349,27 @@ void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)  		phydev->drv->get_wol(phydev, wol);  }  EXPORT_SYMBOL(phy_ethtool_get_wol); + +int phy_ethtool_get_link_ksettings(struct net_device *ndev, +				   struct ethtool_link_ksettings *cmd) +{ +	struct phy_device *phydev = ndev->phydev; + +	if (!phydev) +		return -ENODEV; + +	return phy_ethtool_ksettings_get(phydev, cmd); +} +EXPORT_SYMBOL(phy_ethtool_get_link_ksettings); + +int phy_ethtool_set_link_ksettings(struct net_device *ndev, +				   const struct ethtool_link_ksettings *cmd) +{ +	struct phy_device *phydev = ndev->phydev; + +	if (!phydev) +		return -ENODEV; + +	return phy_ethtool_ksettings_set(phydev, cmd); +} +EXPORT_SYMBOL(phy_ethtool_set_link_ksettings);  |