sh_eth: add NAPI support
authorSergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Wed, 19 Jun 2013 19:30:23 +0000 (23:30 +0400)
committerDavid S. Miller <davem@davemloft.net>
Thu, 20 Jun 2013 00:01:39 +0000 (17:01 -0700)
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>
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/renesas/sh_eth.h

index cd5987ecee4b1267878dcdc593439c0ba047e747..5233edab2dd5e1fc96a297fbbe2e25aaed7323e4 100644 (file)
@@ -1244,7 +1244,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;
@@ -1252,6 +1252,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;
 
@@ -1263,6 +1264,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++;
 
@@ -1350,7 +1357,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)
@@ -1491,7 +1498,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);
 
@@ -1502,25 +1509,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);
@@ -1528,6 +1551,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)
 {
@@ -1839,6 +1889,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:
@@ -1934,6 +1986,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. */
@@ -2623,10 +2677,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);
@@ -2644,6 +2700,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)
@@ -2656,9 +2715,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);
 
index d05b60b826eb03aa3a51d6ecd78501bcf6796107..029744f77af628a70b82c46fec562db3584f7b72 100644 (file)
@@ -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 */