Commit deccd16f authored by Florian Fainelli's avatar Florian Fainelli Committed by David S. Miller

net: phy: Fix phy_mac_interrupt()

Commit 5ea94e76 ("phy: add phy_mac_interrupt()") to use with
PHY_IGNORE_INTERRUPT added a cancel_work_sync() into phy_mac_interrupt()
which is allowed to sleep, whereas phy_mac_interrupt() is expected to be
callable from interrupt context.

Now that we have fixed how the PHY state machine treats
PHY_IGNORE_INTERRUPT with respect to state changes, we can just set the
new link state, and queue the PHY state machine for execution so it is
going to read the new link state.

For that to work properly, we need to update phy_change() not to try to
invoke any interrupt callbacks if we have configured the PHY device for
PHY_IGNORE_INTERRUPT, because that PHY device and its driver are not
required to implement those.

Fixes: 5ea94e76 ("phy: add phy_mac_interrupt() to use with PHY_IGNORE_INTERRUPT")
Signed-off-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d5c3d846
...@@ -692,25 +692,29 @@ void phy_change(struct work_struct *work) ...@@ -692,25 +692,29 @@ void phy_change(struct work_struct *work)
struct phy_device *phydev = struct phy_device *phydev =
container_of(work, struct phy_device, phy_queue); container_of(work, struct phy_device, phy_queue);
if (phydev->drv->did_interrupt && if (phy_interrupt_is_valid(phydev)) {
!phydev->drv->did_interrupt(phydev)) if (phydev->drv->did_interrupt &&
goto ignore; !phydev->drv->did_interrupt(phydev))
goto ignore;
if (phy_disable_interrupts(phydev)) if (phy_disable_interrupts(phydev))
goto phy_err; goto phy_err;
}
mutex_lock(&phydev->lock); mutex_lock(&phydev->lock);
if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state)) if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state))
phydev->state = PHY_CHANGELINK; phydev->state = PHY_CHANGELINK;
mutex_unlock(&phydev->lock); mutex_unlock(&phydev->lock);
atomic_dec(&phydev->irq_disable); if (phy_interrupt_is_valid(phydev)) {
enable_irq(phydev->irq); atomic_dec(&phydev->irq_disable);
enable_irq(phydev->irq);
/* Reenable interrupts */ /* Reenable interrupts */
if (PHY_HALTED != phydev->state && if (PHY_HALTED != phydev->state &&
phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED)) phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED))
goto irq_enable_err; goto irq_enable_err;
}
/* reschedule state queue work to run as soon as possible */ /* reschedule state queue work to run as soon as possible */
cancel_delayed_work_sync(&phydev->state_queue); cancel_delayed_work_sync(&phydev->state_queue);
...@@ -1011,9 +1015,10 @@ void phy_state_machine(struct work_struct *work) ...@@ -1011,9 +1015,10 @@ void phy_state_machine(struct work_struct *work)
void phy_mac_interrupt(struct phy_device *phydev, int new_link) void phy_mac_interrupt(struct phy_device *phydev, int new_link)
{ {
cancel_work_sync(&phydev->phy_queue);
phydev->link = new_link; phydev->link = new_link;
schedule_work(&phydev->phy_queue);
/* Trigger a state machine change */
queue_work(system_power_efficient_wq, &phydev->phy_queue);
} }
EXPORT_SYMBOL(phy_mac_interrupt); EXPORT_SYMBOL(phy_mac_interrupt);
......
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