diff options
author | Sebastian Siewior <bigeasy@linutronix.de> | 2008-05-01 14:08:12 +1000 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-05-01 08:08:36 -0700 |
commit | 3b2b74cad34e7a0cf6d4929ee9e8ad4e11a84867 (patch) | |
tree | 363055bb8fb9dcdf04aac3191f6258c798b9a43f /drivers/net/fec.c | |
parent | c1d9615680c3f43a305b1f66dff0f933d5079273 (diff) | |
download | lwn-3b2b74cad34e7a0cf6d4929ee9e8ad4e11a84867.tar.gz lwn-3b2b74cad34e7a0cf6d4929ee9e8ad4e11a84867.zip |
m68knommu: fix FEC driver locking
It's easy: grab locks before talking to hardware and realease
them afterwards. The one big lock has been splitted into a hw_lock
and mii_lock.
Signed-off-by: Sebastian Siewior <bigeasy@linutronix.de>
Signed-off-by: Greg Ungerer <gerg@uclinux.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/net/fec.c')
-rw-r--r-- | drivers/net/fec.c | 55 |
1 files changed, 33 insertions, 22 deletions
diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 5f9c2c1a9d40..32a4f17d35fc 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -209,7 +209,10 @@ struct fec_enet_private { cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ cbd_t *dirty_tx; /* The ring entries to be free()ed. */ uint tx_full; - spinlock_t lock; + /* hold while accessing the HW like ringbuffer for tx/rx but not MAC */ + spinlock_t hw_lock; + /* hold while accessing the mii_list_t() elements */ + spinlock_t mii_lock; uint phy_id; uint phy_id_done; @@ -313,6 +316,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) volatile fec_t *fecp; volatile cbd_t *bdp; unsigned short status; + unsigned long flags; fep = netdev_priv(dev); fecp = (volatile fec_t*)dev->base_addr; @@ -322,6 +326,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) return 1; } + spin_lock_irqsave(&fep->hw_lock, flags); /* Fill in a Tx ring entry */ bdp = fep->cur_tx; @@ -332,6 +337,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) * This should not happen, since dev->tbusy should be set. */ printk("%s: tx queue full!.\n", dev->name); + spin_unlock_irqrestore(&fep->hw_lock, flags); return 1; } #endif @@ -370,8 +376,6 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) flush_dcache_range((unsigned long)skb->data, (unsigned long)skb->data + skb->len); - spin_lock_irq(&fep->lock); - /* Send it on its way. Tell FEC it's ready, interrupt when done, * it's the last BD of the frame, and to put the CRC on the end. */ @@ -400,7 +404,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) fep->cur_tx = (cbd_t *)bdp; - spin_unlock_irq(&fep->lock); + spin_unlock_irqrestore(&fep->hw_lock, flags); return 0; } @@ -458,19 +462,20 @@ fec_enet_interrupt(int irq, void * dev_id) struct net_device *dev = dev_id; volatile fec_t *fecp; uint int_events; - int handled = 0; + irqreturn_t ret = IRQ_NONE; fecp = (volatile fec_t*)dev->base_addr; /* Get the interrupt events that caused us to be here. */ - while ((int_events = fecp->fec_ievent) != 0) { + do { + int_events = fecp->fec_ievent; fecp->fec_ievent = int_events; /* Handle receive event in its own function. */ if (int_events & FEC_ENET_RXF) { - handled = 1; + ret = IRQ_HANDLED; fec_enet_rx(dev); } @@ -479,17 +484,18 @@ fec_enet_interrupt(int irq, void * dev_id) them as part of the transmit process. */ if (int_events & FEC_ENET_TXF) { - handled = 1; + ret = IRQ_HANDLED; fec_enet_tx(dev); } if (int_events & FEC_ENET_MII) { - handled = 1; + ret = IRQ_HANDLED; fec_enet_mii(dev); } - } - return IRQ_RETVAL(handled); + } while (int_events); + + return ret; } @@ -502,7 +508,7 @@ fec_enet_tx(struct net_device *dev) struct sk_buff *skb; fep = netdev_priv(dev); - spin_lock(&fep->lock); + spin_lock_irq(&fep->hw_lock); bdp = fep->dirty_tx; while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) { @@ -561,7 +567,7 @@ fec_enet_tx(struct net_device *dev) } } fep->dirty_tx = (cbd_t *)bdp; - spin_unlock(&fep->lock); + spin_unlock_irq(&fep->hw_lock); } @@ -588,6 +594,8 @@ fec_enet_rx(struct net_device *dev) fep = netdev_priv(dev); fecp = (volatile fec_t*)dev->base_addr; + spin_lock_irq(&fep->hw_lock); + /* First, grab all of the stats for the incoming packet. * These get messed up if we get called due to a busy condition. */ @@ -693,6 +701,8 @@ while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) { */ fecp->fec_r_des_active = 0; #endif + + spin_unlock_irq(&fep->hw_lock); } @@ -706,11 +716,11 @@ fec_enet_mii(struct net_device *dev) uint mii_reg; fep = netdev_priv(dev); + spin_lock_irq(&fep->mii_lock); + ep = fep->hwp; mii_reg = ep->fec_mii_data; - spin_lock(&fep->lock); - if ((mip = mii_head) == NULL) { printk("MII and no head!\n"); goto unlock; @@ -727,7 +737,7 @@ fec_enet_mii(struct net_device *dev) ep->fec_mii_data = mip->mii_regval; unlock: - spin_unlock(&fep->lock); + spin_unlock_irq(&fep->mii_lock); } static int @@ -741,12 +751,11 @@ mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_devi /* Add PHY address to register command. */ fep = netdev_priv(dev); - regval |= fep->phy_addr << 23; + spin_lock_irqsave(&fep->mii_lock, flags); + regval |= fep->phy_addr << 23; retval = 0; - spin_lock_irqsave(&fep->lock,flags); - if ((mip = mii_free) != NULL) { mii_free = mip->mii_next; mip->mii_regval = regval; @@ -763,9 +772,8 @@ mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_devi retval = 1; } - spin_unlock_irqrestore(&fep->lock,flags); - - return(retval); + spin_unlock_irqrestore(&fep->mii_lock, flags); + return retval; } static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c) @@ -2308,6 +2316,9 @@ int __init fec_enet_init(struct net_device *dev) return -ENOMEM; } + spin_lock_init(&fep->hw_lock); + spin_lock_init(&fep->mii_lock); + /* Create an Ethernet device instance. */ fecp = (volatile fec_t *) fec_hw[index]; |