goto regs_fail;
        }
 
-       spin_lock_init(&priv->lock);
+       spin_lock_init(&priv->txlock);
+       spin_lock_init(&priv->rxlock);
 
        platform_set_drvdata(pdev, dev);
 
        phy_stop(priv->phydev);
 
        /* Lock it down */
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->txlock, flags);
+       spin_lock(&priv->rxlock);
 
        gfar_halt(dev);
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock(&priv->rxlock);
+       spin_unlock_irqrestore(&priv->txlock, flags);
 
        /* Free the IRQs */
        if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
        tempval |= DMACTRL_INIT_SETTINGS;
        gfar_write(&priv->regs->dmactrl, tempval);
 
-       /* Clear THLT, so that the DMA starts polling now */
-       gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
-
        /* Make sure we aren't stopped */
        tempval = gfar_read(&priv->regs->dmactrl);
        tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
        gfar_write(&priv->regs->dmactrl, tempval);
 
+       /* Clear THLT/RHLT, so that the DMA starts polling now */
+       gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
+       gfar_write(®s->rstat, RSTAT_CLEAR_RHALT);
+
        /* Unmask the interrupts we look for */
        gfar_write(®s->imask, IMASK_DEFAULT);
 }
        struct txfcb *fcb = NULL;
        struct txbd8 *txbdp;
        u16 status;
+       unsigned long flags;
 
        /* Update transmit stats */
        priv->stats.tx_bytes += skb->len;
 
        /* Lock priv now */
-       spin_lock_irq(&priv->lock);
+       spin_lock_irqsave(&priv->txlock, flags);
 
        /* Point at the first free tx descriptor */
        txbdp = priv->cur_tx;
        gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
 
        /* Unlock priv */
-       spin_unlock_irq(&priv->lock);
+       spin_unlock_irqrestore(&priv->txlock, flags);
 
        return 0;
 }
        unsigned long flags;
        u32 tempval;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->rxlock, flags);
 
        priv->vlgrp = grp;
 
                gfar_write(&priv->regs->rctrl, tempval);
        }
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->rxlock, flags);
 }
 
 
        struct gfar_private *priv = netdev_priv(dev);
        unsigned long flags;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->rxlock, flags);
 
        if (priv->vlgrp)
                priv->vlgrp->vlan_devices[vid] = NULL;
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->rxlock, flags);
 }
 
 
        gfar_write(&priv->regs->ievent, IEVENT_TX_MASK);
 
        /* Lock priv */
-       spin_lock(&priv->lock);
+       spin_lock(&priv->txlock);
        bdp = priv->dirty_tx;
        while ((bdp->status & TXBD_READY) == 0) {
                /* If dirty_tx and cur_tx are the same, then either the */
        else
                gfar_write(&priv->regs->txic, 0);
 
-       spin_unlock(&priv->lock);
+       spin_unlock(&priv->txlock);
 
        return IRQ_HANDLED;
 }
 {
        struct net_device *dev = (struct net_device *) dev_id;
        struct gfar_private *priv = netdev_priv(dev);
-
 #ifdef CONFIG_GFAR_NAPI
        u32 tempval;
+#else
+       unsigned long flags;
 #endif
 
        /* Clear IEVENT, so rx interrupt isn't called again
        }
 #else
 
-       spin_lock(&priv->lock);
+       spin_lock_irqsave(&priv->rxlock, flags);
        gfar_clean_rx_ring(dev, priv->rx_ring_size);
 
        /* If we are coalescing interrupts, update the timer */
        else
                gfar_write(&priv->regs->rxic, 0);
 
-       spin_unlock(&priv->lock);
+       spin_unlock_irqrestore(&priv->rxlock, flags);
 #endif
 
        return IRQ_HANDLED;
        /* Update the current rxbd pointer to be the next one */
        priv->cur_rx = bdp;
 
-       /* If no packets have arrived since the
-        * last one we processed, clear the IEVENT RX and
-        * BSY bits so that another interrupt won't be
-        * generated when we set IMASK */
-       if (bdp->status & RXBD_EMPTY)
-               gfar_write(&priv->regs->ievent, IEVENT_RX_MASK);
-
        return howmany;
 }
 
        rx_work_limit -= howmany;
        *budget -= howmany;
 
-       if (rx_work_limit >= 0) {
+       if (rx_work_limit > 0) {
                netif_rx_complete(dev);
 
                /* Clear the halt bit in RSTAT */
                        gfar_write(&priv->regs->rxic, 0);
        }
 
-       return (rx_work_limit < 0) ? 1 : 0;
+       /* Return 1 if there's more work to do */
+       return (rx_work_limit > 0) ? 0 : 1;
 }
 #endif
 
        struct phy_device *phydev = priv->phydev;
        int new_state = 0;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->txlock, flags);
        if (phydev->link) {
                u32 tempval = gfar_read(®s->maccfg2);
                u32 ecntrl = gfar_read(®s->ecntrl);
        if (new_state && netif_msg_link(priv))
                phy_print_status(phydev);
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->txlock, flags);
 }
 
 /* Update the hash table based on the current list of multicast
 
  * the buffer descriptor determines the actual condition.
  */
 struct gfar_private {
-       /* pointers to arrays of skbuffs for tx and rx */
+       /* Fields controlled by TX lock */
+       spinlock_t txlock;
+
+       /* Pointer to the array of skbuffs */
        struct sk_buff ** tx_skbuff;
-       struct sk_buff ** rx_skbuff;
 
-       /* indices pointing to the next free sbk in skb arrays */
+       /* next free skb in the array */
        u16 skb_curtx;
-       u16 skb_currx;
 
-       /* index of the first skb which hasn't been transmitted
-        * yet. */
+       /* First skb in line to be transmitted */
        u16 skb_dirtytx;
 
        /* Configuration info for the coalescing features */
        unsigned char txcoalescing;
        unsigned short txcount;
        unsigned short txtime;
+
+       /* Buffer descriptor pointers */
+       struct txbd8 *tx_bd_base;       /* First tx buffer descriptor */
+       struct txbd8 *cur_tx;           /* Next free ring entry */
+       struct txbd8 *dirty_tx;         /* First buffer in line
+                                          to be transmitted */
+       unsigned int tx_ring_size;
+
+       /* RX Locked fields */
+       spinlock_t rxlock;
+
+       /* skb array and index */
+       struct sk_buff ** rx_skbuff;
+       u16 skb_currx;
+
+       /* RX Coalescing values */
        unsigned char rxcoalescing;
        unsigned short rxcount;
        unsigned short rxtime;
 
-       /* GFAR addresses */
-       struct rxbd8 *rx_bd_base;       /* Base addresses of Rx and Tx Buffers */
-       struct txbd8 *tx_bd_base;
+       struct rxbd8 *rx_bd_base;       /* First Rx buffers */
        struct rxbd8 *cur_rx;           /* Next free rx ring entry */
-       struct txbd8 *cur_tx;           /* Next free ring entry */
-       struct txbd8 *dirty_tx;         /* The Ring entry to be freed. */
-       struct gfar __iomem *regs;      /* Pointer to the GFAR memory mapped Registers */
-       u32 __iomem *hash_regs[16];
-       int hash_width;
-       struct net_device_stats stats; /* linux network statistics */
-       struct gfar_extra_stats extra_stats;
-       spinlock_t lock;
+
+       /* RX parameters */
+       unsigned int rx_ring_size;
        unsigned int rx_buffer_size;
        unsigned int rx_stash_size;
        unsigned int rx_stash_index;
-       unsigned int tx_ring_size;
-       unsigned int rx_ring_size;
+
+       struct vlan_group *vlgrp;
+
+       /* Unprotected fields */
+       /* Pointer to the GFAR memory mapped Registers */
+       struct gfar __iomem *regs;
+
+       /* Hash registers and their width */
+       u32 __iomem *hash_regs[16];
+       int hash_width;
+
+       /* global parameters */
        unsigned int fifo_threshold;
        unsigned int fifo_starve;
        unsigned int fifo_starve_off;
                extended_hash:1,
                bd_stash_en:1;
        unsigned short padding;
-       struct vlan_group *vlgrp;
-       /* Info structure initialized by board setup code */
+
        unsigned int interruptTransmit;
        unsigned int interruptReceive;
        unsigned int interruptError;
+
+       /* info structure initialized by platform code */
        struct gianfar_platform_data *einfo;
 
+       /* PHY stuff */
        struct phy_device *phydev;
        struct mii_bus *mii_bus;
        int oldspeed;
        int oldlink;
 
        uint32_t msg_enable;
+
+       /* Network Statistics */
+       struct net_device_stats stats;
+       struct gfar_extra_stats extra_stats;
 };
 
 static inline u32 gfar_read(volatile unsigned __iomem *addr)
 
 
                /* Halt TX and RX, and process the frames which
                 * have already been received */
-               spin_lock_irqsave(&priv->lock, flags);
+               spin_lock_irqsave(&priv->txlock, flags);
+               spin_lock(&priv->rxlock);
+
                gfar_halt(dev);
                gfar_clean_rx_ring(dev, priv->rx_ring_size);
-               spin_unlock_irqrestore(&priv->lock, flags);
+
+               spin_unlock(&priv->rxlock);
+               spin_unlock_irqrestore(&priv->txlock, flags);
 
                /* Now we take down the rings to rebuild them */
                stop_gfar(dev);
 
                /* Halt TX and RX, and process the frames which
                 * have already been received */
-               spin_lock_irqsave(&priv->lock, flags);
+               spin_lock_irqsave(&priv->txlock, flags);
+               spin_lock(&priv->rxlock);
+
                gfar_halt(dev);
                gfar_clean_rx_ring(dev, priv->rx_ring_size);
-               spin_unlock_irqrestore(&priv->lock, flags);
+
+               spin_unlock(&priv->rxlock);
+               spin_unlock_irqrestore(&priv->txlock, flags);
 
                /* Now we take down the rings to rebuild them */
                stop_gfar(dev);
        if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
                return -EOPNOTSUPP;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->txlock, flags);
        gfar_halt(dev);
 
        if (data)
                dev->features &= ~NETIF_F_IP_CSUM;
 
        gfar_start(dev);
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->txlock, flags);
 
        return 0;
 }
 
        else
                return count;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->rxlock, flags);
 
        /* Set the new stashing value */
        priv->bd_stash_en = new_setting;
 
        gfar_write(&priv->regs->attr, temp);
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->rxlock, flags);
 
        return count;
 }
        u32 temp;
        unsigned long flags;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->rxlock, flags);
        if (length > priv->rx_buffer_size)
                return count;
 
 
        gfar_write(&priv->regs->attr, temp);
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->rxlock, flags);
 
        return count;
 }
        u32 temp;
        unsigned long flags;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->rxlock, flags);
        if (index > priv->rx_stash_size)
                return count;
 
        temp |= ATTRELI_EI(index);
        gfar_write(&priv->regs->attreli, flags);
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->rxlock, flags);
 
        return count;
 }
        if (length > GFAR_MAX_FIFO_THRESHOLD)
                return count;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->txlock, flags);
 
        priv->fifo_threshold = length;
 
        temp |= length;
        gfar_write(&priv->regs->fifo_tx_thr, temp);
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->txlock, flags);
 
        return count;
 }
        if (num > GFAR_MAX_FIFO_STARVE)
                return count;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->txlock, flags);
 
        priv->fifo_starve = num;
 
        temp |= num;
        gfar_write(&priv->regs->fifo_tx_starve, temp);
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->txlock, flags);
 
        return count;
 }
        if (num > GFAR_MAX_FIFO_STARVE_OFF)
                return count;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&priv->txlock, flags);
 
        priv->fifo_starve_off = num;
 
        temp |= num;
        gfar_write(&priv->regs->fifo_tx_starve_shutoff, temp);
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&priv->txlock, flags);
 
        return count;
 }