mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-03 00:54:09 +08:00
Merge branch 'fix-phy-ignore-interrupts'
Florian Fainelli says: ==================== net: phy: Finally fix PHY_IGNORE_INTERRUPTS This patch series finally fixes how PHY_IGNORE_INTERRUPTS are treated by avoiding to poll the PHY *and* getting notified from link state changes by the Ethernet MAC interrupt service routine. Tested with bcmgenet since this is the HW that I have access to. Targetting the "net" tree since these are bugfixes, but I would like Woojun and Andrew to take a look and test that on their respective HW setups as well. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
4f2aaf7dd9
@ -401,7 +401,7 @@ int bcmgenet_mii_probe(struct net_device *dev)
|
||||
* Ethernet MAC ISRs
|
||||
*/
|
||||
if (priv->internal_phy)
|
||||
priv->mii_bus->irq[phydev->mdio.addr] = PHY_IGNORE_INTERRUPT;
|
||||
priv->phydev->irq = PHY_IGNORE_INTERRUPT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -692,25 +692,29 @@ void phy_change(struct work_struct *work)
|
||||
struct phy_device *phydev =
|
||||
container_of(work, struct phy_device, phy_queue);
|
||||
|
||||
if (phydev->drv->did_interrupt &&
|
||||
!phydev->drv->did_interrupt(phydev))
|
||||
goto ignore;
|
||||
if (phy_interrupt_is_valid(phydev)) {
|
||||
if (phydev->drv->did_interrupt &&
|
||||
!phydev->drv->did_interrupt(phydev))
|
||||
goto ignore;
|
||||
|
||||
if (phy_disable_interrupts(phydev))
|
||||
goto phy_err;
|
||||
if (phy_disable_interrupts(phydev))
|
||||
goto phy_err;
|
||||
}
|
||||
|
||||
mutex_lock(&phydev->lock);
|
||||
if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state))
|
||||
phydev->state = PHY_CHANGELINK;
|
||||
mutex_unlock(&phydev->lock);
|
||||
|
||||
atomic_dec(&phydev->irq_disable);
|
||||
enable_irq(phydev->irq);
|
||||
if (phy_interrupt_is_valid(phydev)) {
|
||||
atomic_dec(&phydev->irq_disable);
|
||||
enable_irq(phydev->irq);
|
||||
|
||||
/* Reenable interrupts */
|
||||
if (PHY_HALTED != phydev->state &&
|
||||
phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED))
|
||||
goto irq_enable_err;
|
||||
/* Reenable interrupts */
|
||||
if (PHY_HALTED != phydev->state &&
|
||||
phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED))
|
||||
goto irq_enable_err;
|
||||
}
|
||||
|
||||
/* reschedule state queue work to run as soon as possible */
|
||||
cancel_delayed_work_sync(&phydev->state_queue);
|
||||
@ -905,10 +909,10 @@ void phy_state_machine(struct work_struct *work)
|
||||
phydev->adjust_link(phydev->attached_dev);
|
||||
break;
|
||||
case PHY_RUNNING:
|
||||
/* Only register a CHANGE if we are polling or ignoring
|
||||
* interrupts and link changed since latest checking.
|
||||
/* Only register a CHANGE if we are polling and link changed
|
||||
* since latest checking.
|
||||
*/
|
||||
if (!phy_interrupt_is_valid(phydev)) {
|
||||
if (phydev->irq == PHY_POLL) {
|
||||
old_link = phydev->link;
|
||||
err = phy_read_status(phydev);
|
||||
if (err)
|
||||
@ -1000,15 +1004,21 @@ void phy_state_machine(struct work_struct *work)
|
||||
phy_state_to_str(old_state),
|
||||
phy_state_to_str(phydev->state));
|
||||
|
||||
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue,
|
||||
PHY_STATE_TIME * HZ);
|
||||
/* Only re-schedule a PHY state machine change if we are polling the
|
||||
* PHY, if PHY_IGNORE_INTERRUPT is set, then we will be moving
|
||||
* between states from phy_mac_interrupt()
|
||||
*/
|
||||
if (phydev->irq == PHY_POLL)
|
||||
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue,
|
||||
PHY_STATE_TIME * HZ);
|
||||
}
|
||||
|
||||
void phy_mac_interrupt(struct phy_device *phydev, int new_link)
|
||||
{
|
||||
cancel_work_sync(&phydev->phy_queue);
|
||||
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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user