From 03fc3a09675f441da0aa0c5416a9bc689dbf5044 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Tue, 3 Oct 2006 16:18:35 +0100 Subject: [PATCH] --- yaml --- r: 41451 b: refs/heads/master c: 3c3070d713d798f7f9e7ee3614e49b47655d14d8 h: refs/heads/master i: 41449: efa6c9c9ae297e760c4e1bb593931bf83eaaa69d 41447: 9be948a9f98daf49c1717dfd70e00c93513a7fa1 v: v3 --- [refs] | 2 +- trunk/drivers/net/phy/phy.c | 32 ++++++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/[refs] b/[refs] index 24c4476b3d79..43ff9c07101e 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 13df29f69749a61b5209d52b71fcbf7300e5d6fb +refs/heads/master: 3c3070d713d798f7f9e7ee3614e49b47655d14d8 diff --git a/trunk/drivers/net/phy/phy.c b/trunk/drivers/net/phy/phy.c index 3af9fcf76c81..95f0419ba21e 100644 --- a/trunk/drivers/net/phy/phy.c +++ b/trunk/drivers/net/phy/phy.c @@ -7,6 +7,7 @@ * Author: Andy Fleming * * Copyright (c) 2004 Freescale Semiconductor, Inc. + * Copyright (c) 2006 Maciej W. Rozycki * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -32,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -484,6 +487,9 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) { struct phy_device *phydev = phy_dat; + if (PHY_HALTED == phydev->state) + return IRQ_NONE; /* It can't be ours. */ + /* The MDIO bus is not allowed to be written in interrupt * context, so we need to disable the irq here. A work * queue will write the PHY to disable and clear the @@ -577,6 +583,13 @@ int phy_stop_interrupts(struct phy_device *phydev) if (err) phy_error(phydev); + /* + * Finish any pending work; we might have been scheduled + * to be called from keventd ourselves, though. + */ + if (!current_is_keventd()) + flush_scheduled_work(); + free_irq(phydev->irq, phydev); return err; @@ -603,7 +616,8 @@ static void phy_change(void *data) enable_irq(phydev->irq); /* Reenable interrupts */ - err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); + if (PHY_HALTED != phydev->state) + err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); if (err) goto irq_enable_err; @@ -624,18 +638,24 @@ void phy_stop(struct phy_device *phydev) if (PHY_HALTED == phydev->state) goto out_unlock; - if (phydev->irq != PHY_POLL) { - /* Clear any pending interrupts */ - phy_clear_interrupt(phydev); + phydev->state = PHY_HALTED; + if (phydev->irq != PHY_POLL) { /* Disable PHY Interrupts */ phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED); - } - phydev->state = PHY_HALTED; + /* Clear any pending interrupts */ + phy_clear_interrupt(phydev); + } out_unlock: spin_unlock(&phydev->lock); + + /* + * Cannot call flush_scheduled_work() here as desired because + * of rtnl_lock(), but PHY_HALTED shall guarantee phy_change() + * will not reenable interrupts. + */ }