diff mbox

[ltsi-3.10,510/595] sh_eth: add NAPI support

Message ID 1384917713-15962-511-git-send-email-horms+renesas@verge.net.au (mailing list archive)
State New, archived
Headers show

Commit Message

Simon Horman Nov. 20, 2013, 3:20 a.m. UTC
From: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

The driver hasn't used NAPI so far; implement its support at last...

The patch was tested on Renesas R8A77781 BOCK-W board.

Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
(cherry picked from commit 3719109d61ca96746c733538ec776d02a6952640)
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
 drivers/net/ethernet/renesas/sh_eth.c | 85 ++++++++++++++++++++++++++++++-----
 drivers/net/ethernet/renesas/sh_eth.h |  1 +
 2 files changed, 74 insertions(+), 12 deletions(-)
diff mbox

Patch

diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index cc9886c..0389ac0 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -1247,7 +1247,7 @@  static int sh_eth_txfree(struct net_device *ndev)
 }
 
 /* Packet receive function */
-static int sh_eth_rx(struct net_device *ndev, u32 intr_status)
+static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
 {
 	struct sh_eth_private *mdp = netdev_priv(ndev);
 	struct sh_eth_rxdesc *rxdesc;
@@ -1255,6 +1255,7 @@  static int sh_eth_rx(struct net_device *ndev, u32 intr_status)
 	int entry = mdp->cur_rx % mdp->num_rx_ring;
 	int boguscnt = (mdp->dirty_rx + mdp->num_rx_ring) - mdp->cur_rx;
 	struct sk_buff *skb;
+	int exceeded = 0;
 	u16 pkt_len = 0;
 	u32 desc_status;
 
@@ -1266,6 +1267,12 @@  static int sh_eth_rx(struct net_device *ndev, u32 intr_status)
 		if (--boguscnt < 0)
 			break;
 
+		if (*quota <= 0) {
+			exceeded = 1;
+			break;
+		}
+		(*quota)--;
+
 		if (!(desc_status & RDFEND))
 			ndev->stats.rx_length_errors++;
 
@@ -1353,7 +1360,7 @@  static int sh_eth_rx(struct net_device *ndev, u32 intr_status)
 		sh_eth_write(ndev, EDRRR_R, EDRRR);
 	}
 
-	return 0;
+	return exceeded;
 }
 
 static void sh_eth_rcv_snd_disable(struct net_device *ndev)
@@ -1495,7 +1502,7 @@  static irqreturn_t sh_eth_interrupt(int irq, void *netdev)
 	struct sh_eth_private *mdp = netdev_priv(ndev);
 	struct sh_eth_cpu_data *cd = mdp->cd;
 	irqreturn_t ret = IRQ_NONE;
-	unsigned long intr_status;
+	unsigned long intr_status, intr_enable;
 
 	spin_lock(&mdp->lock);
 
@@ -1506,25 +1513,41 @@  static irqreturn_t sh_eth_interrupt(int irq, void *netdev)
 	 * and we need to fully handle it in sh_eth_error() in order to quench
 	 * it as it doesn't get cleared by just writing 1 to the ECI bit...
 	 */
-	intr_status &= sh_eth_read(ndev, EESIPR) | DMAC_M_ECI;
-	/* Clear interrupt */
-	if (intr_status & (EESR_RX_CHECK | cd->tx_check | cd->eesr_err_check)) {
-		sh_eth_write(ndev, intr_status, EESR);
+	intr_enable = sh_eth_read(ndev, EESIPR);
+	intr_status &= intr_enable | DMAC_M_ECI;
+	if (intr_status & (EESR_RX_CHECK | cd->tx_check | cd->eesr_err_check))
 		ret = IRQ_HANDLED;
-	} else
+	else
 		goto other_irq;
 
-	if (intr_status & EESR_RX_CHECK)
-		sh_eth_rx(ndev, intr_status);
+	if (intr_status & EESR_RX_CHECK) {
+		if (napi_schedule_prep(&mdp->napi)) {
+			/* Mask Rx interrupts */
+			sh_eth_write(ndev, intr_enable & ~EESR_RX_CHECK,
+				     EESIPR);
+			__napi_schedule(&mdp->napi);
+		} else {
+			dev_warn(&ndev->dev,
+				 "ignoring interrupt, status 0x%08lx, mask 0x%08lx.\n",
+				 intr_status, intr_enable);
+		}
+	}
 
 	/* Tx Check */
 	if (intr_status & cd->tx_check) {
+		/* Clear Tx interrupts */
+		sh_eth_write(ndev, intr_status & cd->tx_check, EESR);
+
 		sh_eth_txfree(ndev);
 		netif_wake_queue(ndev);
 	}
 
-	if (intr_status & cd->eesr_err_check)
+	if (intr_status & cd->eesr_err_check) {
+		/* Clear error interrupts */
+		sh_eth_write(ndev, intr_status & cd->eesr_err_check, EESR);
+
 		sh_eth_error(ndev, intr_status);
+	}
 
 other_irq:
 	spin_unlock(&mdp->lock);
@@ -1532,6 +1555,33 @@  other_irq:
 	return ret;
 }
 
+static int sh_eth_poll(struct napi_struct *napi, int budget)
+{
+	struct sh_eth_private *mdp = container_of(napi, struct sh_eth_private,
+						  napi);
+	struct net_device *ndev = napi->dev;
+	int quota = budget;
+	unsigned long intr_status;
+
+	for (;;) {
+		intr_status = sh_eth_read(ndev, EESR);
+		if (!(intr_status & EESR_RX_CHECK))
+			break;
+		/* Clear Rx interrupts */
+		sh_eth_write(ndev, intr_status & EESR_RX_CHECK, EESR);
+
+		if (sh_eth_rx(ndev, intr_status, &quota))
+			goto out;
+	}
+
+	napi_complete(napi);
+
+	/* Reenable Rx interrupts */
+	sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR);
+out:
+	return budget - quota;
+}
+
 /* PHY state control function */
 static void sh_eth_adjust_link(struct net_device *ndev)
 {
@@ -1843,6 +1893,8 @@  static int sh_eth_open(struct net_device *ndev)
 	if (ret)
 		goto out_free_irq;
 
+	napi_enable(&mdp->napi);
+
 	return ret;
 
 out_free_irq:
@@ -1938,6 +1990,8 @@  static int sh_eth_close(struct net_device *ndev)
 {
 	struct sh_eth_private *mdp = netdev_priv(ndev);
 
+	napi_disable(&mdp->napi);
+
 	netif_stop_queue(ndev);
 
 	/* Disable interrupts by clearing the interrupt mask. */
@@ -2627,10 +2681,12 @@  static int sh_eth_drv_probe(struct platform_device *pdev)
 		}
 	}
 
+	netif_napi_add(ndev, &mdp->napi, sh_eth_poll, 64);
+
 	/* network device register */
 	ret = register_netdev(ndev);
 	if (ret)
-		goto out_release;
+		goto out_napi_del;
 
 	/* mdio bus init */
 	ret = sh_mdio_init(ndev, pdev->id, pd);
@@ -2648,6 +2704,9 @@  static int sh_eth_drv_probe(struct platform_device *pdev)
 out_unregister:
 	unregister_netdev(ndev);
 
+out_napi_del:
+	netif_napi_del(&mdp->napi);
+
 out_release:
 	/* net_dev free */
 	if (ndev)
@@ -2660,9 +2719,11 @@  out:
 static int sh_eth_drv_remove(struct platform_device *pdev)
 {
 	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct sh_eth_private *mdp = netdev_priv(ndev);
 
 	sh_mdio_release(ndev);
 	unregister_netdev(ndev);
+	netif_napi_del(&mdp->napi);
 	pm_runtime_disable(&pdev->dev);
 	free_netdev(ndev);
 	platform_set_drvdata(pdev, NULL);
diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index 117b324..99beb39 100644
--- a/drivers/net/ethernet/renesas/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -505,6 +505,7 @@  struct sh_eth_private {
 	u32 cur_tx, dirty_tx;
 	u32 rx_buf_sz;		/* Based on MTU+slack. */
 	int edmac_endian;
+	struct napi_struct napi;
 	/* MII transceiver section. */
 	u32 phy_id;					/* PHY ID */
 	struct mii_bus *mii_bus;	/* MDIO bus control */