diff options
Diffstat (limited to 'drivers/net/phy/phy.c')
-rw-r--r-- | drivers/net/phy/phy.c | 128 |
1 files changed, 64 insertions, 64 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index e8885429293a..ef7aa738e0dc 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -29,6 +29,8 @@ #include <linux/uaccess.h> #include <linux/atomic.h> +#define PHY_STATE_TIME HZ + #define PHY_STATE_STR(_state) \ case PHY_##_state: \ return __stringify(_state); \ @@ -41,7 +43,6 @@ static const char *phy_state_to_str(enum phy_state st) PHY_STATE_STR(UP) PHY_STATE_STR(RUNNING) PHY_STATE_STR(NOLINK) - PHY_STATE_STR(FORCING) PHY_STATE_STR(HALTED) } @@ -297,12 +298,8 @@ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd) linkmode_copy(phydev->advertising, advertising); - if (AUTONEG_ENABLE == cmd->autoneg) - linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, - phydev->advertising); - else - linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, - phydev->advertising); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + phydev->advertising, AUTONEG_ENABLE == cmd->autoneg); phydev->duplex = cmd->duplex; @@ -352,12 +349,8 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev, linkmode_copy(phydev->advertising, advertising); - if (autoneg == AUTONEG_ENABLE) - linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, - phydev->advertising); - else - linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, - phydev->advertising); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + phydev->advertising, autoneg == AUTONEG_ENABLE); phydev->duplex = duplex; @@ -407,6 +400,7 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) struct mii_ioctl_data *mii_data = if_mii(ifr); u16 val = mii_data->val_in; bool change_autoneg = false; + int prtad, devad; switch (cmd) { case SIOCGMIIPHY: @@ -414,14 +408,29 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) /* fall through */ case SIOCGMIIREG: - mii_data->val_out = mdiobus_read(phydev->mdio.bus, - mii_data->phy_id, - mii_data->reg_num); + if (mdio_phy_id_is_c45(mii_data->phy_id)) { + prtad = mdio_phy_id_prtad(mii_data->phy_id); + devad = mdio_phy_id_devad(mii_data->phy_id); + devad = MII_ADDR_C45 | devad << 16 | mii_data->reg_num; + } else { + prtad = mii_data->phy_id; + devad = mii_data->reg_num; + } + mii_data->val_out = mdiobus_read(phydev->mdio.bus, prtad, + devad); return 0; case SIOCSMIIREG: - if (mii_data->phy_id == phydev->mdio.addr) { - switch (mii_data->reg_num) { + if (mdio_phy_id_is_c45(mii_data->phy_id)) { + prtad = mdio_phy_id_prtad(mii_data->phy_id); + devad = mdio_phy_id_devad(mii_data->phy_id); + devad = MII_ADDR_C45 | devad << 16 | mii_data->reg_num; + } else { + prtad = mii_data->phy_id; + devad = mii_data->reg_num; + } + if (prtad == phydev->mdio.addr) { + switch (devad) { case MII_BMCR: if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) { if (phydev->autoneg == AUTONEG_ENABLE) @@ -454,11 +463,10 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) } } - mdiobus_write(phydev->mdio.bus, mii_data->phy_id, - mii_data->reg_num, val); + mdiobus_write(phydev->mdio.bus, prtad, devad, val); - if (mii_data->phy_id == phydev->mdio.addr && - mii_data->reg_num == MII_BMCR && + if (prtad == phydev->mdio.addr && + devad == MII_BMCR && val & BMCR_RESET) return phy_init_hw(phydev); @@ -478,12 +486,12 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) } EXPORT_SYMBOL(phy_mii_ioctl); -static void phy_queue_state_machine(struct phy_device *phydev, - unsigned int secs) +void phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies) { mod_delayed_work(system_power_efficient_wq, &phydev->state_queue, - secs * HZ); + jiffies); } +EXPORT_SYMBOL(phy_queue_state_machine); static void phy_trigger_machine(struct phy_device *phydev) { @@ -560,15 +568,8 @@ int phy_start_aneg(struct phy_device *phydev) if (err < 0) goto out_unlock; - if (phy_is_started(phydev)) { - if (phydev->autoneg == AUTONEG_ENABLE) { - err = phy_check_link_status(phydev); - } else { - phydev->state = PHY_FORCING; - phydev->link_timeout = PHY_FORCE_TIMEOUT; - } - } - + if (phy_is_started(phydev)) + err = phy_check_link_status(phydev); out_unlock: mutex_unlock(&phydev->lock); @@ -772,8 +773,13 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) if (phydev->drv->did_interrupt && !phydev->drv->did_interrupt(phydev)) return IRQ_NONE; - /* reschedule state queue work to run as soon as possible */ - phy_trigger_machine(phydev); + if (phydev->drv->handle_interrupt) { + if (phydev->drv->handle_interrupt(phydev)) + goto phy_err; + } else { + /* reschedule state queue work to run as soon as possible */ + phy_trigger_machine(phydev); + } if (phy_clear_interrupt(phydev)) goto phy_err; @@ -799,10 +805,10 @@ static int phy_enable_interrupts(struct phy_device *phydev) } /** - * phy_request_interrupt - request interrupt for a PHY device + * phy_request_interrupt - request and enable interrupt for a PHY device * @phydev: target phy_device struct * - * Description: Request the interrupt for the given PHY. + * Description: Request and enable the interrupt for the given PHY. * If this fails, then we set irq to PHY_POLL. * This should only be called with a valid IRQ number. */ @@ -817,11 +823,31 @@ void phy_request_interrupt(struct phy_device *phydev) phydev_warn(phydev, "Error %d requesting IRQ %d, falling back to polling\n", err, phydev->irq); phydev->irq = PHY_POLL; + } else { + if (phy_enable_interrupts(phydev)) { + phydev_warn(phydev, "Can't enable interrupt, falling back to polling\n"); + phy_free_interrupt(phydev); + phydev->irq = PHY_POLL; + } } } EXPORT_SYMBOL(phy_request_interrupt); /** + * phy_free_interrupt - disable and free interrupt for a PHY device + * @phydev: target phy_device struct + * + * Description: Disable and free the interrupt for the given PHY. + * This should only be called with a valid IRQ number. + */ +void phy_free_interrupt(struct phy_device *phydev) +{ + phy_disable_interrupts(phydev); + free_irq(phydev->irq, phydev); +} +EXPORT_SYMBOL(phy_free_interrupt); + +/** * phy_stop - Bring down the PHY link, and stop checking the status * @phydev: target phy_device struct */ @@ -835,9 +861,6 @@ void phy_stop(struct phy_device *phydev) mutex_lock(&phydev->lock); - if (phy_interrupt_is_valid(phydev)) - phy_disable_interrupts(phydev); - phydev->state = PHY_HALTED; mutex_unlock(&phydev->lock); @@ -864,8 +887,6 @@ EXPORT_SYMBOL(phy_stop); */ void phy_start(struct phy_device *phydev) { - int err; - mutex_lock(&phydev->lock); if (phydev->state != PHY_READY && phydev->state != PHY_HALTED) { @@ -877,13 +898,6 @@ void phy_start(struct phy_device *phydev) /* if phy was suspended, bring the physical link up again */ __phy_resume(phydev); - /* make sure interrupts are enabled for the PHY */ - if (phy_interrupt_is_valid(phydev)) { - err = phy_enable_interrupts(phydev); - if (err < 0) - goto out; - } - phydev->state = PHY_UP; phy_start_machine(phydev); @@ -921,20 +935,6 @@ void phy_state_machine(struct work_struct *work) case PHY_RUNNING: err = phy_check_link_status(phydev); break; - case PHY_FORCING: - err = genphy_update_link(phydev); - if (err) - break; - - if (phydev->link) { - phydev->state = PHY_RUNNING; - phy_link_up(phydev); - } else { - if (0 == phydev->link_timeout--) - needs_aneg = true; - phy_link_down(phydev, false); - } - break; case PHY_HALTED: if (phydev->link) { phydev->link = 0; |