Commit 79ce0477 authored by Brian Hill's avatar Brian Hill Committed by David S. Miller

net: phy: Correctly handle MII ioctl which changes autonegotiation.

When advertised capabilities are changed with mii-tool, such as:
mii-tool -A 10baseT
the existing handler has two errors.

- An actual PHY register value is provided by mii-tool, and this
  must be mapped to internal state with mii_adv_to_ethtool_adv_t().
- The PHY state machine needs to be told that autonegotiation has
  again been performed.  If not, the MAC will not be notified of
  the new link speed and duplex, resulting in a possible config
  mismatch.
Signed-off-by: default avatarBrian Hill <Brian@houston-radar.com>
Acked-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5337b5b7
...@@ -352,6 +352,7 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) ...@@ -352,6 +352,7 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
{ {
struct mii_ioctl_data *mii_data = if_mii(ifr); struct mii_ioctl_data *mii_data = if_mii(ifr);
u16 val = mii_data->val_in; u16 val = mii_data->val_in;
bool change_autoneg = false;
switch (cmd) { switch (cmd) {
case SIOCGMIIPHY: case SIOCGMIIPHY:
...@@ -367,22 +368,29 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) ...@@ -367,22 +368,29 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
if (mii_data->phy_id == phydev->addr) { if (mii_data->phy_id == phydev->addr) {
switch (mii_data->reg_num) { switch (mii_data->reg_num) {
case MII_BMCR: case MII_BMCR:
if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) {
if (phydev->autoneg == AUTONEG_ENABLE)
change_autoneg = true;
phydev->autoneg = AUTONEG_DISABLE; phydev->autoneg = AUTONEG_DISABLE;
else if (val & BMCR_FULLDPLX)
phydev->duplex = DUPLEX_FULL;
else
phydev->duplex = DUPLEX_HALF;
if (val & BMCR_SPEED1000)
phydev->speed = SPEED_1000;
else if (val & BMCR_SPEED100)
phydev->speed = SPEED_100;
else phydev->speed = SPEED_10;
}
else {
if (phydev->autoneg == AUTONEG_DISABLE)
change_autoneg = true;
phydev->autoneg = AUTONEG_ENABLE; phydev->autoneg = AUTONEG_ENABLE;
if (!phydev->autoneg && (val & BMCR_FULLDPLX)) }
phydev->duplex = DUPLEX_FULL;
else
phydev->duplex = DUPLEX_HALF;
if (!phydev->autoneg && (val & BMCR_SPEED1000))
phydev->speed = SPEED_1000;
else if (!phydev->autoneg &&
(val & BMCR_SPEED100))
phydev->speed = SPEED_100;
break; break;
case MII_ADVERTISE: case MII_ADVERTISE:
phydev->advertising = val; phydev->advertising = mii_adv_to_ethtool_adv_t(val);
change_autoneg = true;
break; break;
default: default:
/* do nothing */ /* do nothing */
...@@ -396,6 +404,10 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) ...@@ -396,6 +404,10 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
if (mii_data->reg_num == MII_BMCR && if (mii_data->reg_num == MII_BMCR &&
val & BMCR_RESET) val & BMCR_RESET)
return phy_init_hw(phydev); return phy_init_hw(phydev);
if (change_autoneg)
return phy_start_aneg(phydev);
return 0; return 0;
case SIOCSHWTSTAMP: case SIOCSHWTSTAMP:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment