Commit 3719109d authored by Sergei Shtylyov's avatar Sergei Shtylyov Committed by David S. Miller

sh_eth: add NAPI support

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: default avatarSergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ea7d69e7
...@@ -1244,7 +1244,7 @@ static int sh_eth_txfree(struct net_device *ndev) ...@@ -1244,7 +1244,7 @@ static int sh_eth_txfree(struct net_device *ndev)
} }
/* Packet receive function */ /* 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_private *mdp = netdev_priv(ndev);
struct sh_eth_rxdesc *rxdesc; struct sh_eth_rxdesc *rxdesc;
...@@ -1252,6 +1252,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status) ...@@ -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 entry = mdp->cur_rx % mdp->num_rx_ring;
int boguscnt = (mdp->dirty_rx + mdp->num_rx_ring) - mdp->cur_rx; int boguscnt = (mdp->dirty_rx + mdp->num_rx_ring) - mdp->cur_rx;
struct sk_buff *skb; struct sk_buff *skb;
int exceeded = 0;
u16 pkt_len = 0; u16 pkt_len = 0;
u32 desc_status; u32 desc_status;
...@@ -1263,6 +1264,12 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status) ...@@ -1263,6 +1264,12 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status)
if (--boguscnt < 0) if (--boguscnt < 0)
break; break;
if (*quota <= 0) {
exceeded = 1;
break;
}
(*quota)--;
if (!(desc_status & RDFEND)) if (!(desc_status & RDFEND))
ndev->stats.rx_length_errors++; ndev->stats.rx_length_errors++;
...@@ -1350,7 +1357,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status) ...@@ -1350,7 +1357,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status)
sh_eth_write(ndev, EDRRR_R, EDRRR); sh_eth_write(ndev, EDRRR_R, EDRRR);
} }
return 0; return exceeded;
} }
static void sh_eth_rcv_snd_disable(struct net_device *ndev) 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) ...@@ -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_private *mdp = netdev_priv(ndev);
struct sh_eth_cpu_data *cd = mdp->cd; struct sh_eth_cpu_data *cd = mdp->cd;
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
unsigned long intr_status; unsigned long intr_status, intr_enable;
spin_lock(&mdp->lock); spin_lock(&mdp->lock);
...@@ -1502,25 +1509,41 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev) ...@@ -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 * 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... * 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; intr_enable = sh_eth_read(ndev, EESIPR);
/* Clear interrupt */ intr_status &= intr_enable | DMAC_M_ECI;
if (intr_status & (EESR_RX_CHECK | cd->tx_check | cd->eesr_err_check)) { if (intr_status & (EESR_RX_CHECK | cd->tx_check | cd->eesr_err_check))
sh_eth_write(ndev, intr_status, EESR);
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
} else else
goto other_irq; goto other_irq;
if (intr_status & EESR_RX_CHECK) if (intr_status & EESR_RX_CHECK) {
sh_eth_rx(ndev, intr_status); 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 */ /* Tx Check */
if (intr_status & cd->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); sh_eth_txfree(ndev);
netif_wake_queue(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); sh_eth_error(ndev, intr_status);
}
other_irq: other_irq:
spin_unlock(&mdp->lock); spin_unlock(&mdp->lock);
...@@ -1528,6 +1551,33 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev) ...@@ -1528,6 +1551,33 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev)
return ret; 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 */ /* PHY state control function */
static void sh_eth_adjust_link(struct net_device *ndev) static void sh_eth_adjust_link(struct net_device *ndev)
{ {
...@@ -1839,6 +1889,8 @@ static int sh_eth_open(struct net_device *ndev) ...@@ -1839,6 +1889,8 @@ static int sh_eth_open(struct net_device *ndev)
if (ret) if (ret)
goto out_free_irq; goto out_free_irq;
napi_enable(&mdp->napi);
return ret; return ret;
out_free_irq: out_free_irq:
...@@ -1934,6 +1986,8 @@ static int sh_eth_close(struct net_device *ndev) ...@@ -1934,6 +1986,8 @@ static int sh_eth_close(struct net_device *ndev)
{ {
struct sh_eth_private *mdp = netdev_priv(ndev); struct sh_eth_private *mdp = netdev_priv(ndev);
napi_disable(&mdp->napi);
netif_stop_queue(ndev); netif_stop_queue(ndev);
/* Disable interrupts by clearing the interrupt mask. */ /* Disable interrupts by clearing the interrupt mask. */
...@@ -2623,10 +2677,12 @@ static int sh_eth_drv_probe(struct platform_device *pdev) ...@@ -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 */ /* network device register */
ret = register_netdev(ndev); ret = register_netdev(ndev);
if (ret) if (ret)
goto out_release; goto out_napi_del;
/* mdio bus init */ /* mdio bus init */
ret = sh_mdio_init(ndev, pdev->id, pd); ret = sh_mdio_init(ndev, pdev->id, pd);
...@@ -2644,6 +2700,9 @@ static int sh_eth_drv_probe(struct platform_device *pdev) ...@@ -2644,6 +2700,9 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
out_unregister: out_unregister:
unregister_netdev(ndev); unregister_netdev(ndev);
out_napi_del:
netif_napi_del(&mdp->napi);
out_release: out_release:
/* net_dev free */ /* net_dev free */
if (ndev) if (ndev)
...@@ -2656,9 +2715,11 @@ static int sh_eth_drv_probe(struct platform_device *pdev) ...@@ -2656,9 +2715,11 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
static int sh_eth_drv_remove(struct platform_device *pdev) static int sh_eth_drv_remove(struct platform_device *pdev)
{ {
struct net_device *ndev = platform_get_drvdata(pdev); struct net_device *ndev = platform_get_drvdata(pdev);
struct sh_eth_private *mdp = netdev_priv(ndev);
sh_mdio_release(ndev); sh_mdio_release(ndev);
unregister_netdev(ndev); unregister_netdev(ndev);
netif_napi_del(&mdp->napi);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
free_netdev(ndev); free_netdev(ndev);
......
...@@ -505,6 +505,7 @@ struct sh_eth_private { ...@@ -505,6 +505,7 @@ struct sh_eth_private {
u32 cur_tx, dirty_tx; u32 cur_tx, dirty_tx;
u32 rx_buf_sz; /* Based on MTU+slack. */ u32 rx_buf_sz; /* Based on MTU+slack. */
int edmac_endian; int edmac_endian;
struct napi_struct napi;
/* MII transceiver section. */ /* MII transceiver section. */
u32 phy_id; /* PHY ID */ u32 phy_id; /* PHY ID */
struct mii_bus *mii_bus; /* MDIO bus control */ struct mii_bus *mii_bus; /* MDIO bus control */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment