From b31a1d8b41513b96e9c7ec2f68c5734cef0b26a4 Mon Sep 17 00:00:00 2001 From: Andy Fleming Date: Tue, 16 Dec 2008 15:29:15 -0800 Subject: gianfar: Convert gianfar to an of_platform_driver Does the same for the accompanying MDIO driver, and then modifies the TBI configuration method. The old way used fields in einfo, which no longer exists. The new way is to create an MDIO device-tree node for each instance of gianfar, and create a tbi-handle property to associate ethernet controllers with the TBI PHYs they are connected to. Signed-off-by: Andy Fleming Signed-off-by: David S. Miller --- drivers/net/gianfar_ethtool.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/net/gianfar_ethtool.c') diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index fb7d3ccc0fdc..53944b120a3d 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -121,7 +121,7 @@ static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf) { struct gfar_private *priv = netdev_priv(dev); - if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) + if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN); else memcpy(buf, stat_gstrings, @@ -138,7 +138,7 @@ static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, struct gfar_private *priv = netdev_priv(dev); u64 *extra = (u64 *) & priv->extra_stats; - if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) { + if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) { u32 __iomem *rmon = (u32 __iomem *) & priv->regs->rmon; struct gfar_stats *stats = (struct gfar_stats *) buf; @@ -158,7 +158,7 @@ static int gfar_sset_count(struct net_device *dev, int sset) switch (sset) { case ETH_SS_STATS: - if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) + if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) return GFAR_STATS_LEN; else return GFAR_EXTRA_STATS_LEN; @@ -280,7 +280,7 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals { struct gfar_private *priv = netdev_priv(dev); - if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) + if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) return -EOPNOTSUPP; if (NULL == priv->phydev) @@ -332,7 +332,7 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals { struct gfar_private *priv = netdev_priv(dev); - if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) + if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) return -EOPNOTSUPP; /* Set up rx coalescing */ @@ -482,7 +482,7 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) unsigned long flags; int err = 0; - if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) + if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) return -EOPNOTSUPP; if (dev->flags & IFF_UP) { @@ -515,7 +515,7 @@ static uint32_t gfar_get_rx_csum(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); - if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) + if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) return 0; return priv->rx_csum_enable; @@ -526,7 +526,7 @@ static int gfar_set_tx_csum(struct net_device *dev, uint32_t data) unsigned long flags; struct gfar_private *priv = netdev_priv(dev); - if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) + if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) return -EOPNOTSUPP; spin_lock_irqsave(&priv->txlock, flags); @@ -547,7 +547,7 @@ static uint32_t gfar_get_tx_csum(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); - if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) + if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) return 0; return (dev->features & NETIF_F_IP_CSUM) != 0; @@ -570,7 +570,7 @@ static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct gfar_private *priv = netdev_priv(dev); - if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) { + if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) { wol->supported = WAKE_MAGIC; wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0; } else { @@ -583,7 +583,7 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) struct gfar_private *priv = netdev_priv(dev); unsigned long flags; - if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && + if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && wol->wolopts != 0) return -EINVAL; -- cgit v1.2.3 From b46a8454cd304b5376ba00d3457a612720e47269 Mon Sep 17 00:00:00 2001 From: Dai Haruki Date: Tue, 16 Dec 2008 15:29:52 -0800 Subject: gianfar: Optimize interrupt coalescing configuration Store the interrupt coalescing values in the form in which they will be written to the interrupt coalescing registers. This puts a little overhead into the ethtool configuration, and takes it out of the interrupt handler Signed-off-by: Dai Haruki Acked-by: Andy Fleming Signed-off-by: David S. Miller --- drivers/net/gianfar.c | 24 ++++++++---------------- drivers/net/gianfar.h | 12 ++++++++---- drivers/net/gianfar_ethtool.c | 42 ++++++++++++++++++++++++------------------ 3 files changed, 40 insertions(+), 38 deletions(-) (limited to 'drivers/net/gianfar_ethtool.c') diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 7398704c4b55..5100f75238af 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -428,11 +428,9 @@ static int gfar_probe(struct of_device *ofdev, priv->rx_ring_size = DEFAULT_RX_RING_SIZE; priv->txcoalescing = DEFAULT_TX_COALESCE; - priv->txcount = DEFAULT_TXCOUNT; - priv->txtime = DEFAULT_TXTIME; + priv->txic = DEFAULT_TXIC; priv->rxcoalescing = DEFAULT_RX_COALESCE; - priv->rxcount = DEFAULT_RXCOUNT; - priv->rxtime = DEFAULT_RXTIME; + priv->rxic = DEFAULT_RXIC; /* Enable most messages by default */ priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1; @@ -1060,17 +1058,13 @@ int startup_gfar(struct net_device *dev) phy_start(priv->phydev); /* Configure the coalescing support */ + gfar_write(®s->txic, 0); if (priv->txcoalescing) - gfar_write(®s->txic, - mk_ic_value(priv->txcount, priv->txtime)); - else - gfar_write(®s->txic, 0); + gfar_write(®s->txic, priv->txic); + gfar_write(®s->rxic, 0); if (priv->rxcoalescing) - gfar_write(®s->rxic, - mk_ic_value(priv->rxcount, priv->rxtime)); - else - gfar_write(®s->rxic, 0); + gfar_write(®s->rxic, priv->rxic); if (priv->rx_csum_enable) rctrl |= RCTRL_CHECKSUMMING; @@ -1538,8 +1532,7 @@ static irqreturn_t gfar_transmit(int irq, void *dev_id) /* Otherwise, clear it */ if (likely(priv->txcoalescing)) { gfar_write(&priv->regs->txic, 0); - gfar_write(&priv->regs->txic, - mk_ic_value(priv->txcount, priv->txtime)); + gfar_write(&priv->regs->txic, priv->txic); } spin_unlock(&priv->txlock); @@ -1825,8 +1818,7 @@ static int gfar_poll(struct napi_struct *napi, int budget) /* Otherwise, clear it */ if (likely(priv->rxcoalescing)) { gfar_write(&priv->regs->rxic, 0); - gfar_write(&priv->regs->rxic, - mk_ic_value(priv->rxcount, priv->rxtime)); + gfar_write(&priv->regs->rxic, priv->rxic); } } diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index ca7f0a6a68c5..449f508a5640 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -189,6 +189,12 @@ extern const char gfar_driver_version[]; #define mk_ic_value(count, time) (IC_ICEN | \ mk_ic_icft(count) | \ mk_ic_ictt(time)) +#define get_icft_value(ic) (((unsigned long)ic & IC_ICFT_MASK) >> \ + IC_ICFT_SHIFT) +#define get_ictt_value(ic) ((unsigned long)ic & IC_ICTT_MASK) + +#define DEFAULT_TXIC mk_ic_value(DEFAULT_TXCOUNT, DEFAULT_TXTIME) +#define DEFAULT_RXIC mk_ic_value(DEFAULT_RXCOUNT, DEFAULT_RXTIME) #define RCTRL_PAL_MASK 0x001f0000 #define RCTRL_VLEX 0x00002000 @@ -694,8 +700,7 @@ struct gfar_private { /* Configuration info for the coalescing features */ unsigned char txcoalescing; - unsigned short txcount; - unsigned short txtime; + unsigned long txic; /* Buffer descriptor pointers */ struct txbd8 *tx_bd_base; /* First tx buffer descriptor */ @@ -717,8 +722,7 @@ struct gfar_private { /* RX Coalescing values */ unsigned char rxcoalescing; - unsigned short rxcount; - unsigned short rxtime; + unsigned long rxic; struct rxbd8 *rx_bd_base; /* First Rx buffers */ struct rxbd8 *cur_rx; /* Next free rx ring entry */ diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index 53944b120a3d..c111c532f7b3 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -201,8 +201,8 @@ static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd) if (NULL == phydev) return -ENODEV; - cmd->maxtxpkt = priv->txcount; - cmd->maxrxpkt = priv->rxcount; + cmd->maxtxpkt = get_icft_value(priv->txic); + cmd->maxrxpkt = get_icft_value(priv->rxic); return phy_ethtool_gset(phydev, cmd); } @@ -279,6 +279,10 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) { struct gfar_private *priv = netdev_priv(dev); + unsigned long rxtime; + unsigned long rxcount; + unsigned long txtime; + unsigned long txcount; if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) return -EOPNOTSUPP; @@ -286,11 +290,15 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals if (NULL == priv->phydev) return -ENODEV; - cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime); - cvals->rx_max_coalesced_frames = priv->rxcount; + rxtime = get_ictt_value(priv->rxic); + rxcount = get_icft_value(priv->rxic); + txtime = get_ictt_value(priv->txic); + txcount = get_icft_value(priv->txic);; + cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, rxtime); + cvals->rx_max_coalesced_frames = rxcount; - cvals->tx_coalesce_usecs = gfar_ticks2usecs(priv, priv->txtime); - cvals->tx_max_coalesced_frames = priv->txcount; + cvals->tx_coalesce_usecs = gfar_ticks2usecs(priv, txtime); + cvals->tx_max_coalesced_frames = txcount; cvals->use_adaptive_rx_coalesce = 0; cvals->use_adaptive_tx_coalesce = 0; @@ -358,8 +366,9 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals return -EINVAL; } - priv->rxtime = gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs); - priv->rxcount = cvals->rx_max_coalesced_frames; + priv->rxic = mk_ic_value( + gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs), + cvals->rx_max_coalesced_frames); /* Set up tx coalescing */ if ((cvals->tx_coalesce_usecs == 0) || @@ -381,20 +390,17 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals return -EINVAL; } - priv->txtime = gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs); - priv->txcount = cvals->tx_max_coalesced_frames; + priv->txic = mk_ic_value( + gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs), + cvals->tx_max_coalesced_frames); + gfar_write(&priv->regs->rxic, 0); if (priv->rxcoalescing) - gfar_write(&priv->regs->rxic, - mk_ic_value(priv->rxcount, priv->rxtime)); - else - gfar_write(&priv->regs->rxic, 0); + gfar_write(&priv->regs->rxic, priv->rxic); + gfar_write(&priv->regs->txic, 0); if (priv->txcoalescing) - gfar_write(&priv->regs->txic, - mk_ic_value(priv->txcount, priv->txtime)); - else - gfar_write(&priv->regs->txic, 0); + gfar_write(&priv->regs->txic, priv->txic); return 0; } -- cgit v1.2.3 From 12dea57be552a291e93827baeffbb91e33f587a6 Mon Sep 17 00:00:00 2001 From: Dai Haruki Date: Tue, 16 Dec 2008 15:30:20 -0800 Subject: gianfar: Fix eTSEC configuration procedure Fix some bugs in the ethtool configuration functions: * gfar_clean_rx_ring should not be called with interrupts disabled. * Update last transmission time to avoid tx timeout. * Delete redundant NETIF_F_IP_CSUM check in gfar_start_xmit * Use netif_tx_lock_bh when reconfiguring the tx csum Signed-off-by: Dai Haruki Signed-off-by: Andy Fleming Signed-off-by: David S. Miller --- drivers/net/gianfar.c | 5 +++-- drivers/net/gianfar_ethtool.c | 23 ++++++++++++----------- 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/net/gianfar_ethtool.c') diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 5100f75238af..19fdf93e0ec2 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -892,6 +892,8 @@ void gfar_start(struct net_device *dev) /* Unmask the interrupts we look for */ gfar_write(®s->imask, IMASK_DEFAULT); + + dev->trans_start = jiffies; } /* Bring the controller up and running */ @@ -1233,8 +1235,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) status = txbdp->status & TXBD_WRAP; /* Set up checksumming */ - if (likely((dev->features & NETIF_F_IP_CSUM) - && (CHECKSUM_PARTIAL == skb->ip_summed))) { + if (CHECKSUM_PARTIAL == skb->ip_summed) { fcb = gfar_add_fcb(skb, txbdp); status |= TXBD_TOE; gfar_tx_checksum(skb, fcb); diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index c111c532f7b3..3021057d54ae 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -462,11 +462,12 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva spin_lock(&priv->rxlock); gfar_halt(dev); - gfar_clean_rx_ring(dev, priv->rx_ring_size); spin_unlock(&priv->rxlock); spin_unlock_irqrestore(&priv->txlock, flags); + gfar_clean_rx_ring(dev, priv->rx_ring_size); + /* Now we take down the rings to rebuild them */ stop_gfar(dev); } @@ -476,9 +477,10 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva priv->tx_ring_size = rvals->tx_pending; /* Rebuild the rings with the new size */ - if (dev->flags & IFF_UP) + if (dev->flags & IFF_UP) { err = startup_gfar(dev); - + netif_wake_queue(dev); + } return err; } @@ -498,11 +500,12 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) spin_lock(&priv->rxlock); gfar_halt(dev); - gfar_clean_rx_ring(dev, priv->rx_ring_size); spin_unlock(&priv->rxlock); spin_unlock_irqrestore(&priv->txlock, flags); + gfar_clean_rx_ring(dev, priv->rx_ring_size); + /* Now we take down the rings to rebuild them */ stop_gfar(dev); } @@ -511,9 +514,10 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) priv->rx_csum_enable = data; spin_unlock_irqrestore(&priv->bflock, flags); - if (dev->flags & IFF_UP) + if (dev->flags & IFF_UP) { err = startup_gfar(dev); - + netif_wake_queue(dev); + } return err; } @@ -529,22 +533,19 @@ static uint32_t gfar_get_rx_csum(struct net_device *dev) static int gfar_set_tx_csum(struct net_device *dev, uint32_t data) { - unsigned long flags; struct gfar_private *priv = netdev_priv(dev); if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) return -EOPNOTSUPP; - spin_lock_irqsave(&priv->txlock, flags); - gfar_halt(dev); + netif_tx_lock_bh(dev); if (data) dev->features |= NETIF_F_IP_CSUM; else dev->features &= ~NETIF_F_IP_CSUM; - gfar_start(dev); - spin_unlock_irqrestore(&priv->txlock, flags); + netif_tx_unlock_bh(dev); return 0; } -- cgit v1.2.3 From 4669bc907488f5a3ee399ced132deb6165e489a3 Mon Sep 17 00:00:00 2001 From: Dai Haruki Date: Wed, 17 Dec 2008 16:51:04 -0800 Subject: gianfar: Add Scatter Gather support Scatter Gather support in gianfar driver to handle fragmented frames on the transmit side. Signed-off-by: Poonam Aggrwal Signed-off-by: Dai Haruki Signed-off-by: Andy Fleming Signed-off-by: David S. Miller --- drivers/net/gianfar.c | 217 ++++++++++++++++++++++++++++-------------- drivers/net/gianfar.h | 1 + drivers/net/gianfar_ethtool.c | 2 + 3 files changed, 149 insertions(+), 71 deletions(-) (limited to 'drivers/net/gianfar_ethtool.c') diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index a6efabc28dc8..dbbeee372ef8 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -368,7 +368,7 @@ static int gfar_probe(struct of_device *ofdev, if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) { priv->rx_csum_enable = 1; - dev->features |= NETIF_F_IP_CSUM; + dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA; } else priv->rx_csum_enable = 0; @@ -426,6 +426,7 @@ static int gfar_probe(struct of_device *ofdev, priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE; priv->tx_ring_size = DEFAULT_TX_RING_SIZE; priv->rx_ring_size = DEFAULT_RX_RING_SIZE; + priv->num_txbdfree = DEFAULT_TX_RING_SIZE; priv->txcoalescing = DEFAULT_TX_COALESCE; priv->txic = DEFAULT_TXIC; @@ -819,22 +820,26 @@ static void free_skb_resources(struct gfar_private *priv) { struct rxbd8 *rxbdp; struct txbd8 *txbdp; - int i; + int i, j; /* Go through all the buffer descriptors and free their data buffers */ txbdp = priv->tx_bd_base; for (i = 0; i < priv->tx_ring_size; i++) { + if (!priv->tx_skbuff[i]) + continue; - if (priv->tx_skbuff[i]) { - dma_unmap_single(&priv->dev->dev, txbdp->bufPtr, - txbdp->length, - DMA_TO_DEVICE); - dev_kfree_skb_any(priv->tx_skbuff[i]); - priv->tx_skbuff[i] = NULL; + dma_unmap_single(&priv->dev->dev, txbdp->bufPtr, + txbdp->length, DMA_TO_DEVICE); + txbdp->lstatus = 0; + for (j = 0; j < skb_shinfo(priv->tx_skbuff[i])->nr_frags; j++) { + txbdp++; + dma_unmap_page(&priv->dev->dev, txbdp->bufPtr, + txbdp->length, DMA_TO_DEVICE); } - txbdp++; + dev_kfree_skb_any(priv->tx_skbuff[i]); + priv->tx_skbuff[i] = NULL; } kfree(priv->tx_skbuff); @@ -967,6 +972,7 @@ int startup_gfar(struct net_device *dev) priv->rx_skbuff[i] = NULL; /* Initialize some variables in our dev structure */ + priv->num_txbdfree = priv->tx_ring_size; priv->dirty_tx = priv->cur_tx = priv->tx_bd_base; priv->cur_rx = priv->rx_bd_base; priv->skb_curtx = priv->skb_dirtytx = 0; @@ -1207,28 +1213,84 @@ void inline gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb) fcb->vlctl = vlan_tx_tag_get(skb); } +static inline struct txbd8 *skip_txbd(struct txbd8 *bdp, int stride, + struct txbd8 *base, int ring_size) +{ + struct txbd8 *new_bd = bdp + stride; + + return (new_bd >= (base + ring_size)) ? (new_bd - ring_size) : new_bd; +} + +static inline struct txbd8 *next_txbd(struct txbd8 *bdp, struct txbd8 *base, + int ring_size) +{ + return skip_txbd(bdp, 1, base, ring_size); +} + /* This is called by the kernel when a frame is ready for transmission. */ /* It is pointed to by the dev->hard_start_xmit function pointer */ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); struct txfcb *fcb = NULL; - struct txbd8 *txbdp, *base; + struct txbd8 *txbdp, *txbdp_start, *base; u32 lstatus; + int i; + u32 bufaddr; unsigned long flags; + unsigned int nr_frags, length; + + base = priv->tx_bd_base; + + /* total number of fragments in the SKB */ + nr_frags = skb_shinfo(skb)->nr_frags; + + spin_lock_irqsave(&priv->txlock, flags); + + /* check if there is space to queue this packet */ + if (nr_frags > priv->num_txbdfree) { + /* no space, stop the queue */ + netif_stop_queue(dev); + dev->stats.tx_fifo_errors++; + spin_unlock_irqrestore(&priv->txlock, flags); + return NETDEV_TX_BUSY; + } /* Update transmit stats */ dev->stats.tx_bytes += skb->len; - /* Lock priv now */ - spin_lock_irqsave(&priv->txlock, flags); + txbdp = txbdp_start = priv->cur_tx; - /* Point at the first free tx descriptor */ - txbdp = priv->cur_tx; - base = priv->tx_bd_base; + if (nr_frags == 0) { + lstatus = txbdp->lstatus | BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT); + } else { + /* Place the fragment addresses and lengths into the TxBDs */ + for (i = 0; i < nr_frags; i++) { + /* Point at the next BD, wrapping as needed */ + txbdp = next_txbd(txbdp, base, priv->tx_ring_size); + + length = skb_shinfo(skb)->frags[i].size; + + lstatus = txbdp->lstatus | length | + BD_LFLAG(TXBD_READY); + + /* Handle the last BD specially */ + if (i == nr_frags - 1) + lstatus |= BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT); + + bufaddr = dma_map_page(&dev->dev, + skb_shinfo(skb)->frags[i].page, + skb_shinfo(skb)->frags[i].page_offset, + length, + DMA_TO_DEVICE); + + /* set the TxBD length and buffer pointer */ + txbdp->bufPtr = bufaddr; + txbdp->lstatus = lstatus; + } - /* Clear all but the WRAP status flags */ - lstatus = txbdp->lstatus & BD_LFLAG(TXBD_WRAP); + lstatus = txbdp_start->lstatus; + } /* Set up checksumming */ if (CHECKSUM_PARTIAL == skb->ip_summed) { @@ -1246,48 +1308,45 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) gfar_tx_vlan(skb, fcb); } - /* Set buffer length and pointer */ - txbdp->bufPtr = dma_map_single(&dev->dev, skb->data, - skb->len, DMA_TO_DEVICE); - - /* Save the skb pointer so we can free it later */ + /* setup the TxBD length and buffer pointer for the first BD */ priv->tx_skbuff[priv->skb_curtx] = skb; + txbdp_start->bufPtr = dma_map_single(&dev->dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); - /* Update the current skb pointer (wrapping if this was the last) */ - priv->skb_curtx = - (priv->skb_curtx + 1) & TX_RING_MOD_MASK(priv->tx_ring_size); - - /* Flag the BD as ready, interrupt-causing, last, and in need of CRC */ - lstatus |= - BD_LFLAG(TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT) | - skb->len; - - dev->trans_start = jiffies; + lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | skb_headlen(skb); - /* The powerpc-specific eieio() is used, as wmb() has too strong + /* + * The powerpc-specific eieio() is used, as wmb() has too strong * semantics (it requires synchronization between cacheable and * uncacheable mappings, which eieio doesn't provide and which we * don't need), thus requiring a more expensive sync instruction. At * some point, the set of architecture-independent barrier functions * should be expanded to include weaker barriers. */ - eieio(); - txbdp->lstatus = lstatus; - txbdp = next_bd(txbdp, base, priv->tx_ring_size); + txbdp_start->lstatus = lstatus; + + /* Update the current skb pointer to the next entry we will use + * (wrapping if necessary) */ + priv->skb_curtx = (priv->skb_curtx + 1) & + TX_RING_MOD_MASK(priv->tx_ring_size); + + priv->cur_tx = next_txbd(txbdp, base, priv->tx_ring_size); + + /* reduce TxBD free count */ + priv->num_txbdfree -= (nr_frags + 1); + + dev->trans_start = jiffies; /* If the next BD still needs to be cleaned up, then the bds are full. We need to tell the kernel to stop sending us stuff. */ - if (txbdp == priv->dirty_tx) { + if (!priv->num_txbdfree) { netif_stop_queue(dev); dev->stats.tx_fifo_errors++; } - /* Update the current txbd to the next one */ - priv->cur_tx = txbdp; - /* Tell the DMA to go go go */ gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT); @@ -1461,50 +1520,66 @@ static void gfar_timeout(struct net_device *dev) /* Interrupt Handler for Transmit complete */ static int gfar_clean_tx_ring(struct net_device *dev) { - struct txbd8 *bdp, *base; struct gfar_private *priv = netdev_priv(dev); + struct txbd8 *bdp; + struct txbd8 *lbdp = NULL; + struct txbd8 *base = priv->tx_bd_base; + struct sk_buff *skb; + int skb_dirtytx; + int tx_ring_size = priv->tx_ring_size; + int frags = 0; + int i; int howmany = 0; + u32 lstatus; bdp = priv->dirty_tx; - base = priv->tx_bd_base; - while ((bdp->status & TXBD_READY) == 0) { - /* If dirty_tx and cur_tx are the same, then either the */ - /* ring is empty or full now (it could only be full in the beginning, */ - /* obviously). If it is empty, we are done. */ - if ((bdp == priv->cur_tx) && (netif_queue_stopped(dev) == 0)) - break; + skb_dirtytx = priv->skb_dirtytx; - howmany++; + while ((skb = priv->tx_skbuff[skb_dirtytx])) { + frags = skb_shinfo(skb)->nr_frags; + lbdp = skip_txbd(bdp, frags, base, tx_ring_size); - /* Deferred means some collisions occurred during transmit, */ - /* but we eventually sent the packet. */ - if (bdp->status & TXBD_DEF) - dev->stats.collisions++; + lstatus = lbdp->lstatus; - /* Unmap the DMA memory */ - dma_unmap_single(&priv->dev->dev, bdp->bufPtr, - bdp->length, DMA_TO_DEVICE); + /* Only clean completed frames */ + if ((lstatus & BD_LFLAG(TXBD_READY)) && + (lstatus & BD_LENGTH_MASK)) + break; + + dma_unmap_single(&dev->dev, + bdp->bufPtr, + bdp->length, + DMA_TO_DEVICE); - /* Free the sk buffer associated with this TxBD */ - dev_kfree_skb_irq(priv->tx_skbuff[priv->skb_dirtytx]); + bdp->lstatus &= BD_LFLAG(TXBD_WRAP); + bdp = next_txbd(bdp, base, tx_ring_size); - priv->tx_skbuff[priv->skb_dirtytx] = NULL; - priv->skb_dirtytx = - (priv->skb_dirtytx + - 1) & TX_RING_MOD_MASK(priv->tx_ring_size); + for (i = 0; i < frags; i++) { + dma_unmap_page(&dev->dev, + bdp->bufPtr, + bdp->length, + DMA_TO_DEVICE); + bdp->lstatus &= BD_LFLAG(TXBD_WRAP); + bdp = next_txbd(bdp, base, tx_ring_size); + } - /* Clean BD length for empty detection */ - bdp->length = 0; + dev_kfree_skb_any(skb); + priv->tx_skbuff[skb_dirtytx] = NULL; - bdp = next_bd(bdp, base, priv->tx_ring_size); + skb_dirtytx = (skb_dirtytx + 1) & + TX_RING_MOD_MASK(tx_ring_size); + + howmany++; + priv->num_txbdfree += frags + 1; + } - /* Move dirty_tx to be the next bd */ - priv->dirty_tx = bdp; + /* If we freed a buffer, we can restart transmission, if necessary */ + if (netif_queue_stopped(dev) && priv->num_txbdfree) + netif_wake_queue(dev); - /* We freed a buffer, so now we can restart transmission */ - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); - } /* while ((bdp->status & TXBD_READY) == 0) */ + /* Update dirty indicators */ + priv->skb_dirtytx = skb_dirtytx; + priv->dirty_tx = bdp; dev->stats.tx_packets += howmany; diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 9c8974dc8dd5..7ef3cc5cabe9 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -728,6 +728,7 @@ struct gfar_private { struct txbd8 *dirty_tx; /* First buffer in line to be transmitted */ unsigned int tx_ring_size; + unsigned int num_txbdfree; /* number of TxBDs free */ /* RX Locked fields */ spinlock_t rxlock; diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index 3021057d54ae..59b3b5d98efe 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -475,6 +475,7 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva /* Change the size */ priv->rx_ring_size = rvals->rx_pending; priv->tx_ring_size = rvals->tx_pending; + priv->num_txbdfree = priv->tx_ring_size; /* Rebuild the rings with the new size */ if (dev->flags & IFF_UP) { @@ -623,6 +624,7 @@ const struct ethtool_ops gfar_ethtool_ops = { .get_tx_csum = gfar_get_tx_csum, .set_rx_csum = gfar_set_rx_csum, .set_tx_csum = gfar_set_tx_csum, + .set_sg = ethtool_op_set_sg, .get_msglevel = gfar_get_msglevel, .set_msglevel = gfar_set_msglevel, #ifdef CONFIG_PM -- cgit v1.2.3