[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1227456274-10388-2-git-send-email-steve.glendinning@smsc.com>
Date: Sun, 23 Nov 2008 16:04:34 +0000
From: Steve Glendinning <steve.glendinning@...c.com>
To: netdev@...r.kernel.org
Cc: linux-sh@...r.kernel.org, Ian Saturley <ian.saturley@...c.com>,
Steve Glendinning <steve.glendinning@...c.com>
Subject: [RFC PATCH 2/2] smsc911x: add support for sh3 TX DMA
Following on from the RX DMA patch, this patch also adds TX DMA support
for the sh architecture. Tested on SH7709S (sh3), where it works but
actually *reduces* throughput (by about 30%).
The patch disables TX during the transfer, and uses the DMA completion
interrupt to re-enable TX. This causes 1 interrupt per packet, which is
most likely responsible for the bulk of the performance loss.
This implementation is quite a nasty hack, as the sh DMA subsystem also
attaches a DMA completion isr (so dma-sh.c must be modified to also
request the isr as shared). The sh dma subsystem has a wait queue for
this purpose, but I don't think we can sleep in hard_start_xmit?
As with the RX patch, I'm interested to hear any advice on making this
more generic.
Signed-off-by: Steve Glendinning <steve.glendinning@...c.com>
---
drivers/net/smsc911x.c | 139 +++++++++++++++++++++++++++++++++++++++++++++-
drivers/net/smsc911x.h | 1 +
include/linux/smsc911x.h | 1 +
3 files changed, 140 insertions(+), 1 deletions(-)
diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c
index b3d9f9e..90ab783 100644
--- a/drivers/net/smsc911x.c
+++ b/drivers/net/smsc911x.c
@@ -52,7 +52,7 @@
#include <linux/smsc911x.h>
#include "smsc911x.h"
-#ifdef SMSC_USE_SH_RX_DMA
+#if defined(SMSC_USE_SH_TX_DMA) || defined(SMSC_USE_SH_RX_DMA)
#include <asm/dma.h>
#include <../arch/sh/drivers/dma/dma-sh.h>
#endif
@@ -125,6 +125,10 @@ struct smsc911x_data {
#ifdef SMSC_USE_SH_RX_DMA
struct sk_buff *rx_skb;
#endif
+
+#ifdef SMSC_USE_SH_TX_DMA
+ struct sk_buff *tx_skb;
+#endif
};
/* The 16-bit access functions are significantly slower, due to the locking
@@ -1253,6 +1257,40 @@ smsc911x_set_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6])
smsc911x_mac_write(pdata, ADDRL, mac_low32);
}
+#ifdef SMSC_USE_SH_TX_DMA
+static irqreturn_t smsc911x_tx_dma_irqhandler(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int freespace;
+
+ if (!pdata->tx_skb)
+ return IRQ_NONE;
+
+ /* make sure the channel has finished running */
+ BUG_ON(get_dma_residue(pdata->config.tx_dma_ch));
+
+ dev_kfree_skb_irq(pdata->tx_skb);
+ pdata->tx_skb = NULL;
+
+ freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
+
+ if (freespace < TX_FIFO_LOW_THRESHOLD) {
+ /* free space low, leave tx disabled and enable the
+ * tx fifo level interrupt */
+ unsigned int temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp &= 0x00FFFFFF;
+ temp |= 0x32000000;
+ smsc911x_reg_write(pdata, FIFO_INT, temp);
+ } else {
+ /* free space not low, re-enable the tx queue */
+ netif_wake_queue(pdata->dev);
+ }
+
+ return IRQ_HANDLED;
+}
+#endif
+
static int smsc911x_open(struct net_device *dev)
{
struct smsc911x_data *pdata = netdev_priv(dev);
@@ -1388,7 +1426,30 @@ static int smsc911x_open(struct net_device *dev)
printk(KERN_INFO "%s: Rx PIO\n", dev->name);
#endif
+#ifdef SMSC_USE_SH_TX_DMA
+ /* Configure Tx DMA channel for incremented source address, fixed
+ * destination address and enable the transfer complete interrupt */
+ dma_configure_channel(pdata->config.tx_dma_ch,
+ SM_INC | TS_128 | CHCR_IE | 0x400 | TM_BUR);
+
+ if (request_dma(pdata->config.tx_dma_ch, SMSC_CHIPNAME) < 0) {
+ SMSC_WARNING(DRV, "Error requesting Tx DMA channel %d",
+ pdata->config.tx_dma_ch);
+ goto out_release_rx_dma_1;
+ }
+
+ /* TODO: fix this hardcoded IRQ */
+ if (request_irq(DMTE1_IRQ, smsc911x_tx_dma_irqhandler,
+ IRQF_SHARED | IRQF_DISABLED, SMSC_CHIPNAME " Tx DMA", dev)) {
+ SMSC_WARNING(DRV, "Error requesting Tx DMA irq");
+ goto out_release_tx_dma_2;
+ }
+
+ printk(KERN_INFO "%s: Tx DMA %i\n", dev->name,
+ pdata->config.tx_dma_ch);
+#else
printk(KERN_INFO "%s: Tx PIO\n", dev->name);
+#endif
/* enable NAPI polling before enabling RX interrupts */
napi_enable(&pdata->napi);
@@ -1407,6 +1468,16 @@ static int smsc911x_open(struct net_device *dev)
netif_start_queue(dev);
return 0;
+
+#ifdef SMSC_USE_SH_TX_DMA
+out_release_tx_dma_2:
+ free_dma(pdata->config.tx_dma_ch);
+out_release_rx_dma_1:
+#ifdef SMSC_USE_SH_RX_DMA
+ free_dma(pdata->config.rx_dma_ch);
+#endif
+ return -ENODEV;
+#endif
}
/* Entry point for stopping the interface */
@@ -1434,6 +1505,9 @@ static int smsc911x_stop(struct net_device *dev)
phy_stop(pdata->phy_dev);
/* Free DMA channels */
+#ifdef SMSC_USE_SH_TX_DMA
+ free_dma(pdata->config.tx_dma_ch);
+#endif
#ifdef SMSC_USE_SH_RX_DMA
free_dma(pdata->config.rx_dma_ch);
#endif
@@ -1449,9 +1523,23 @@ static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
unsigned int freespace;
unsigned int tx_cmd_a;
unsigned int tx_cmd_b;
+#ifdef SMSC_USE_SH_TX_DMA
+ unsigned int dma_cnt;
+ unsigned long physaddrfrom, physaddrto;
+ int cachebytes = dma_get_cache_alignment();
+ unsigned long alignmask = cachebytes - 1;
+ void *dma_buf;
+#else
unsigned int temp;
u32 wrsz;
ulong bufp;
+#endif
+
+#ifdef SMSC_USE_SH_TX_DMA
+ /* make sure the channel is not already running */
+ BUG_ON(pdata->tx_skb);
+ BUG_ON(get_dma_residue(pdata->config.tx_dma_ch));
+#endif
freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
@@ -1459,17 +1547,62 @@ static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
SMSC_WARNING(TX_ERR,
"Tx data fifo low, space available: %d", freespace);
+#ifdef SMSC_USE_SH_TX_DMA
+ /* 16/32 Byte start alignment */
+ tx_cmd_a = (((unsigned int)skb->data) & alignmask) << 16;
+
+ switch (cachebytes) {
+ case 16:
+ tx_cmd_a |= 0x01 << 24; /* 16 byte end alignment */
+ break;
+
+ case 32:
+ tx_cmd_a |= 0x02 << 24; /* 32 byte end alignment */
+ break;
+
+ default:
+ SMSC_WARNING(DRV, "Unknown DMA alignment");
+ break;
+ }
+#else
/* Word alignment adjustment */
tx_cmd_a = (u32)((ulong)skb->data & 0x03) << 16;
+#endif
+
tx_cmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
tx_cmd_a |= (unsigned int)skb->len;
tx_cmd_b = ((unsigned int)skb->len) << 16;
tx_cmd_b |= (unsigned int)skb->len;
+#ifdef SMSC_USE_SH_TX_DMA
+ dma_buf = (void *)(((unsigned long)skb->data) & (~alignmask));
+ dma_cnt = (((u32)skb->len) + alignmask +
+ (((unsigned long)skb->data) & alignmask)) & (~alignmask);
+#endif
+
smsc911x_reg_write(pdata, TX_DATA_FIFO, tx_cmd_a);
smsc911x_reg_write(pdata, TX_DATA_FIFO, tx_cmd_b);
+#ifdef SMSC_USE_SH_TX_DMA
+ /* store the skb for the completion isr */
+ pdata->tx_skb = skb;
+
+ /* Calculate the physical transfer addresses */
+ physaddrfrom = virt_to_phys(dma_buf);
+ physaddrto = virt_to_phys(pdata->ioaddr + TX_DATA_FIFO);
+ BUG_ON(physaddrfrom & alignmask);
+ BUG_ON(physaddrto & alignmask);
+
+ /* Flush cache */
+ dma_cache_sync(NULL, dma_buf, dma_cnt, DMA_TO_DEVICE);
+
+ /* stop Tx until the DMA transaction is complete */
+ netif_stop_queue(dev);
+
+ /* Start the DMA transfer */
+ dma_write(pdata->config.tx_dma_ch, physaddrfrom, physaddrto, dma_cnt);
+#else
bufp = (ulong)skb->data & (~0x3);
wrsz = (u32)skb->len + 3;
wrsz += (u32)((ulong)skb->data & 0x3);
@@ -1478,11 +1611,14 @@ static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
freespace -= (skb->len + 32);
dev_kfree_skb(skb);
+#endif
+
dev->trans_start = jiffies;
if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
smsc911x_tx_update_txcounters(dev);
+#ifndef SMSC_USE_SH_TX_DMA
if (freespace < TX_FIFO_LOW_THRESHOLD) {
netif_stop_queue(dev);
temp = smsc911x_reg_read(pdata, FIFO_INT);
@@ -1490,6 +1626,7 @@ static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
temp |= 0x32000000;
smsc911x_reg_write(pdata, FIFO_INT, temp);
}
+#endif
return NETDEV_TX_OK;
}
diff --git a/drivers/net/smsc911x.h b/drivers/net/smsc911x.h
index 4634dcf..c21b1f9 100644
--- a/drivers/net/smsc911x.h
+++ b/drivers/net/smsc911x.h
@@ -35,6 +35,7 @@
#ifdef CONFIG_SH_DMA
#define SMSC_USE_SH_RX_DMA
+#define SMSC_USE_SH_TX_DMA
#endif /* CONFIG_SH_DMA */
#define DPRINTK(nlevel, klevel, fmt, args...) \
diff --git a/include/linux/smsc911x.h b/include/linux/smsc911x.h
index 0d9408a..d764f01 100644
--- a/include/linux/smsc911x.h
+++ b/include/linux/smsc911x.h
@@ -32,6 +32,7 @@ struct smsc911x_platform_config {
phy_interface_t phy_interface;
#ifdef CONFIG_SH_DMA
int rx_dma_ch;
+ int tx_dma_ch;
#endif
};
--
1.5.6.5
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists