summaryrefslogtreecommitdiffstats
path: root/drivers/net/gianfar.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/gianfar.c')
-rw-r--r--drivers/net/gianfar.c182
1 files changed, 121 insertions, 61 deletions
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 718cf77e345a..99a4b990939f 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -98,7 +98,6 @@
#include "gianfar_mii.h"
#define TX_TIMEOUT (1*HZ)
-#define SKB_ALLOC_TIMEOUT 1000000
#undef BRIEF_GFAR_ERRORS
#undef VERBOSE_GFAR_ERRORS
@@ -115,7 +114,9 @@ static int gfar_enet_open(struct net_device *dev);
static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
static void gfar_timeout(struct net_device *dev);
static int gfar_close(struct net_device *dev);
-struct sk_buff *gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp);
+struct sk_buff *gfar_new_skb(struct net_device *dev);
+static void gfar_new_rxbdp(struct net_device *dev, struct rxbd8 *bdp,
+ struct sk_buff *skb);
static int gfar_set_mac_address(struct net_device *dev);
static int gfar_change_mtu(struct net_device *dev, int new_mtu);
static irqreturn_t gfar_error(int irq, void *dev_id);
@@ -783,14 +784,21 @@ int startup_gfar(struct net_device *dev)
rxbdp = priv->rx_bd_base;
for (i = 0; i < priv->rx_ring_size; i++) {
- struct sk_buff *skb = NULL;
+ struct sk_buff *skb;
- rxbdp->status = 0;
+ skb = gfar_new_skb(dev);
- skb = gfar_new_skb(dev, rxbdp);
+ if (!skb) {
+ printk(KERN_ERR "%s: Can't allocate RX buffers\n",
+ dev->name);
+
+ goto err_rxalloc_fail;
+ }
priv->rx_skbuff[i] = skb;
+ gfar_new_rxbdp(dev, rxbdp, skb);
+
rxbdp++;
}
@@ -916,6 +924,7 @@ rx_irq_fail:
tx_irq_fail:
free_irq(priv->interruptError, dev);
err_irq_fail:
+err_rxalloc_fail:
rx_skb_fail:
free_skb_resources(priv);
tx_skb_fail:
@@ -1185,7 +1194,7 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
int frame_size = new_mtu + ETH_HLEN;
if (priv->vlan_enable)
- frame_size += VLAN_ETH_HLEN;
+ frame_size += VLAN_HLEN;
if (gfar_uses_fcb(priv))
frame_size += GMAC_FCB_LEN;
@@ -1250,17 +1259,12 @@ static void gfar_timeout(struct net_device *dev)
}
/* Interrupt Handler for Transmit complete */
-static irqreturn_t gfar_transmit(int irq, void *dev_id)
+int gfar_clean_tx_ring(struct net_device *dev)
{
- struct net_device *dev = (struct net_device *) dev_id;
- struct gfar_private *priv = netdev_priv(dev);
struct txbd8 *bdp;
+ struct gfar_private *priv = netdev_priv(dev);
+ int howmany = 0;
- /* Clear IEVENT */
- gfar_write(&priv->regs->ievent, IEVENT_TX_MASK);
-
- /* Lock priv */
- 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 */
@@ -1269,7 +1273,7 @@ static irqreturn_t gfar_transmit(int irq, void *dev_id)
if ((bdp == priv->cur_tx) && (netif_queue_stopped(dev) == 0))
break;
- dev->stats.tx_packets++;
+ howmany++;
/* Deferred means some collisions occurred during transmit, */
/* but we eventually sent the packet. */
@@ -1278,11 +1282,15 @@ static irqreturn_t gfar_transmit(int irq, void *dev_id)
/* Free the sk buffer associated with this TxBD */
dev_kfree_skb_irq(priv->tx_skbuff[priv->skb_dirtytx]);
+
priv->tx_skbuff[priv->skb_dirtytx] = NULL;
priv->skb_dirtytx =
(priv->skb_dirtytx +
1) & TX_RING_MOD_MASK(priv->tx_ring_size);
+ /* Clean BD length for empty detection */
+ bdp->length = 0;
+
/* update bdp to point at next bd in the ring (wrapping if necessary) */
if (bdp->status & TXBD_WRAP)
bdp = priv->tx_bd_base;
@@ -1297,31 +1305,69 @@ static irqreturn_t gfar_transmit(int irq, void *dev_id)
netif_wake_queue(dev);
} /* while ((bdp->status & TXBD_READY) == 0) */
+ dev->stats.tx_packets += howmany;
+
+ return howmany;
+}
+
+/* Interrupt Handler for Transmit complete */
+static irqreturn_t gfar_transmit(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct gfar_private *priv = netdev_priv(dev);
+
+ /* Clear IEVENT */
+ gfar_write(&priv->regs->ievent, IEVENT_TX_MASK);
+
+ /* Lock priv */
+ spin_lock(&priv->txlock);
+
+ gfar_clean_tx_ring(dev);
+
/* If we are coalescing the interrupts, reset the timer */
/* Otherwise, clear it */
- if (priv->txcoalescing)
+ if (likely(priv->txcoalescing)) {
+ gfar_write(&priv->regs->txic, 0);
gfar_write(&priv->regs->txic,
mk_ic_value(priv->txcount, priv->txtime));
- else
- gfar_write(&priv->regs->txic, 0);
+ }
spin_unlock(&priv->txlock);
return IRQ_HANDLED;
}
-struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
+static void gfar_new_rxbdp(struct net_device *dev, struct rxbd8 *bdp,
+ struct sk_buff *skb)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ u32 * status_len = (u32 *)bdp;
+ u16 flags;
+
+ bdp->bufPtr = dma_map_single(&dev->dev, skb->data,
+ priv->rx_buffer_size, DMA_FROM_DEVICE);
+
+ flags = RXBD_EMPTY | RXBD_INTERRUPT;
+
+ if (bdp == priv->rx_bd_base + priv->rx_ring_size - 1)
+ flags |= RXBD_WRAP;
+
+ eieio();
+
+ *status_len = (u32)flags << 16;
+}
+
+
+struct sk_buff * gfar_new_skb(struct net_device *dev)
{
unsigned int alignamount;
struct gfar_private *priv = netdev_priv(dev);
struct sk_buff *skb = NULL;
- unsigned int timeout = SKB_ALLOC_TIMEOUT;
/* We have to allocate the skb, so keep trying till we succeed */
- while ((!skb) && timeout--)
- skb = dev_alloc_skb(priv->rx_buffer_size + RXBUF_ALIGNMENT);
+ skb = netdev_alloc_skb(dev, priv->rx_buffer_size + RXBUF_ALIGNMENT);
- if (NULL == skb)
+ if (!skb)
return NULL;
alignamount = RXBUF_ALIGNMENT -
@@ -1332,15 +1378,6 @@ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
*/
skb_reserve(skb, alignamount);
- bdp->bufPtr = dma_map_single(&dev->dev, skb->data,
- priv->rx_buffer_size, DMA_FROM_DEVICE);
-
- bdp->length = 0;
-
- /* Mark the buffer empty */
- eieio();
- bdp->status |= (RXBD_EMPTY | RXBD_INTERRUPT);
-
return skb;
}
@@ -1392,15 +1429,15 @@ irqreturn_t gfar_receive(int irq, void *dev_id)
unsigned long flags;
#endif
- /* Clear IEVENT, so rx interrupt isn't called again
- * because of this interrupt */
- gfar_write(&priv->regs->ievent, IEVENT_RX_MASK);
-
/* support NAPI */
#ifdef CONFIG_GFAR_NAPI
+ /* Clear IEVENT, so interrupts aren't called again
+ * because of the packets that have already arrived */
+ gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK);
+
if (netif_rx_schedule_prep(dev, &priv->napi)) {
tempval = gfar_read(&priv->regs->imask);
- tempval &= IMASK_RX_DISABLED;
+ tempval &= IMASK_RTX_DISABLED;
gfar_write(&priv->regs->imask, tempval);
__netif_rx_schedule(dev, &priv->napi);
@@ -1411,17 +1448,20 @@ irqreturn_t gfar_receive(int irq, void *dev_id)
gfar_read(&priv->regs->imask));
}
#else
+ /* Clear IEVENT, so rx interrupt isn't called again
+ * because of this interrupt */
+ gfar_write(&priv->regs->ievent, IEVENT_RX_MASK);
spin_lock_irqsave(&priv->rxlock, flags);
gfar_clean_rx_ring(dev, priv->rx_ring_size);
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
- if (priv->rxcoalescing)
+ if (likely(priv->rxcoalescing)) {
+ gfar_write(&priv->regs->rxic, 0);
gfar_write(&priv->regs->rxic,
mk_ic_value(priv->rxcount, priv->rxtime));
- else
- gfar_write(&priv->regs->rxic, 0);
+ }
spin_unlock_irqrestore(&priv->rxlock, flags);
#endif
@@ -1523,12 +1563,31 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
bdp = priv->cur_rx;
while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
+ struct sk_buff *newskb;
rmb();
+
+ /* Add another skb for the future */
+ newskb = gfar_new_skb(dev);
+
skb = priv->rx_skbuff[priv->skb_currx];
- if (!(bdp->status &
- (RXBD_LARGE | RXBD_SHORT | RXBD_NONOCTET
- | RXBD_CRCERR | RXBD_OVERRUN | RXBD_TRUNCATED))) {
+ /* We drop the frame if we failed to allocate a new buffer */
+ if (unlikely(!newskb || !(bdp->status & RXBD_LAST) ||
+ bdp->status & RXBD_ERR)) {
+ count_errors(bdp->status, dev);
+
+ if (unlikely(!newskb))
+ newskb = skb;
+
+ if (skb) {
+ dma_unmap_single(&priv->dev->dev,
+ bdp->bufPtr,
+ priv->rx_buffer_size,
+ DMA_FROM_DEVICE);
+
+ dev_kfree_skb_any(skb);
+ }
+ } else {
/* Increment the number of packets */
dev->stats.rx_packets++;
howmany++;
@@ -1539,23 +1598,14 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
gfar_process_frame(dev, skb, pkt_len);
dev->stats.rx_bytes += pkt_len;
- } else {
- count_errors(bdp->status, dev);
-
- if (skb)
- dev_kfree_skb_any(skb);
-
- priv->rx_skbuff[priv->skb_currx] = NULL;
}
dev->last_rx = jiffies;
- /* Clear the status flags for this buffer */
- bdp->status &= ~RXBD_STATS;
+ priv->rx_skbuff[priv->skb_currx] = newskb;
- /* Add another skb for the future */
- skb = gfar_new_skb(dev, bdp);
- priv->rx_skbuff[priv->skb_currx] = skb;
+ /* Setup the new bdp */
+ gfar_new_rxbdp(dev, bdp, newskb);
/* Update to the next pointer */
if (bdp->status & RXBD_WRAP)
@@ -1565,9 +1615,8 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
/* update to point at the next skb */
priv->skb_currx =
- (priv->skb_currx +
- 1) & RX_RING_MOD_MASK(priv->rx_ring_size);
-
+ (priv->skb_currx + 1) &
+ RX_RING_MOD_MASK(priv->rx_ring_size);
}
/* Update the current rxbd pointer to be the next one */
@@ -1582,6 +1631,13 @@ static int gfar_poll(struct napi_struct *napi, int budget)
struct gfar_private *priv = container_of(napi, struct gfar_private, napi);
struct net_device *dev = priv->dev;
int howmany;
+ unsigned long flags;
+
+ /* If we fail to get the lock, don't bother with the TX BDs */
+ if (spin_trylock_irqsave(&priv->txlock, flags)) {
+ gfar_clean_tx_ring(dev);
+ spin_unlock_irqrestore(&priv->txlock, flags);
+ }
howmany = gfar_clean_rx_ring(dev, budget);
@@ -1595,11 +1651,11 @@ static int gfar_poll(struct napi_struct *napi, int budget)
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
- if (priv->rxcoalescing)
+ if (likely(priv->rxcoalescing)) {
+ gfar_write(&priv->regs->rxic, 0);
gfar_write(&priv->regs->rxic,
mk_ic_value(priv->rxcount, priv->rxtime));
- else
- gfar_write(&priv->regs->rxic, 0);
+ }
}
return howmany;
@@ -1975,12 +2031,16 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:fsl-gianfar");
+
/* Structure for a device driver */
static struct platform_driver gfar_driver = {
.probe = gfar_probe,
.remove = gfar_remove,
.driver = {
.name = "fsl-gianfar",
+ .owner = THIS_MODULE,
},
};