Commit 20c4b089 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] smc91x: use a work queue to reconfigure the phy from smc_timeout()

From: Nicolas Pitre <nico@cam.org>

This way we can reconfigure the phy and sleep as needed.

Also removed unnecessary locking from smc_phy_powerdown() since the chip is
not active anymore when this is called.
Signed-off-by: default avatarNicolas Pitre <nico@cam.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
parent 62d03b5a
...@@ -80,6 +80,7 @@ static const char version[] = ...@@ -80,6 +80,7 @@ static const char version[] =
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/workqueue.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
...@@ -201,6 +202,8 @@ struct smc_local { ...@@ -201,6 +202,8 @@ struct smc_local {
u32 msg_enable; u32 msg_enable;
u32 phy_type; u32 phy_type;
struct mii_if_info mii; struct mii_if_info mii;
struct work_struct phy_configure;
spinlock_t lock; spinlock_t lock;
#ifdef SMC_USE_PXA_DMA #ifdef SMC_USE_PXA_DMA
...@@ -945,13 +948,10 @@ static int smc_phy_reset(struct net_device *dev, int phy) ...@@ -945,13 +948,10 @@ static int smc_phy_reset(struct net_device *dev, int phy)
*/ */
static void smc_phy_powerdown(struct net_device *dev, int phy) static void smc_phy_powerdown(struct net_device *dev, int phy)
{ {
struct smc_local *lp = netdev_priv(dev);
unsigned int bmcr; unsigned int bmcr;
spin_lock_irq(&lp->lock);
bmcr = smc_phy_read(dev, phy, MII_BMCR); bmcr = smc_phy_read(dev, phy, MII_BMCR);
smc_phy_write(dev, phy, MII_BMCR, bmcr | BMCR_PDOWN); smc_phy_write(dev, phy, MII_BMCR, bmcr | BMCR_PDOWN);
spin_unlock_irq(&lp->lock);
} }
/* /*
...@@ -989,8 +989,9 @@ static void smc_phy_check_media(struct net_device *dev, int init) ...@@ -989,8 +989,9 @@ static void smc_phy_check_media(struct net_device *dev, int init)
* of autonegotiation.) If the RPC ANEG bit is cleared, the selection * of autonegotiation.) If the RPC ANEG bit is cleared, the selection
* is controlled by the RPC SPEED and RPC DPLX bits. * is controlled by the RPC SPEED and RPC DPLX bits.
*/ */
static void smc_phy_configure(struct net_device *dev) static void smc_phy_configure(void *data)
{ {
struct net_device *dev = data;
struct smc_local *lp = netdev_priv(dev); struct smc_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr; unsigned long ioaddr = dev->base_addr;
int phyaddr = lp->mii.phy_id; int phyaddr = lp->mii.phy_id;
...@@ -1261,24 +1262,13 @@ static void smc_timeout(struct net_device *dev) ...@@ -1261,24 +1262,13 @@ static void smc_timeout(struct net_device *dev)
smc_reset(dev); smc_reset(dev);
smc_enable(dev); smc_enable(dev);
#if 0
/* /*
* Reconfiguring the PHY doesn't seem like a bad idea here, but * Reconfiguring the PHY doesn't seem like a bad idea here, but
* it introduced a problem. Now that this is a timeout routine, * smc_phy_configure() calls msleep() which calls schedule_timeout()
* we are getting called from within an interrupt context. * which calls schedule(). Ence we use a work queue.
* smc_phy_configure() calls msleep() which calls
* schedule_timeout() which calls schedule(). When schedule()
* is called from an interrupt context, it prints out
* "Scheduling in interrupt" and then calls BUG(). This is
* obviously not desirable. This was worked around by removing
* the call to smc_phy_configure() here because it didn't seem
* absolutely necessary. Ultimately, if msleep() is
* supposed to be usable from an interrupt context (which it
* looks like it thinks it should handle), it should be fixed.
*/ */
if (lp->phy_type != 0) if (lp->phy_type != 0)
smc_phy_configure(dev); schedule_work(&lp->phy_configure);
#endif
/* clear anything saved */ /* clear anything saved */
if (lp->saved_skb != NULL) { if (lp->saved_skb != NULL) {
...@@ -1471,8 +1461,10 @@ static int smc_close(struct net_device *dev) ...@@ -1471,8 +1461,10 @@ static int smc_close(struct net_device *dev)
/* clear everything */ /* clear everything */
smc_shutdown(dev->base_addr); smc_shutdown(dev->base_addr);
if (lp->phy_type != 0) if (lp->phy_type != 0) {
flush_scheduled_work();
smc_phy_powerdown(dev, lp->mii.phy_id); smc_phy_powerdown(dev, lp->mii.phy_id);
}
return 0; return 0;
} }
...@@ -1819,6 +1811,7 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr) ...@@ -1819,6 +1811,7 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
spin_lock_init(&lp->lock); spin_lock_init(&lp->lock);
tasklet_init(&lp->tx_task, smc_hardware_send_pkt, (unsigned long)dev); tasklet_init(&lp->tx_task, smc_hardware_send_pkt, (unsigned long)dev);
INIT_WORK(&lp->phy_configure, smc_phy_configure, dev);
lp->mii.phy_id_mask = 0x1f; lp->mii.phy_id_mask = 0x1f;
lp->mii.reg_num_mask = 0x1f; lp->mii.reg_num_mask = 0x1f;
lp->mii.force_media = 0; lp->mii.force_media = 0;
......
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