lkml.org 
[lkml]   [2013]   [Jan]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH] cpsw: Fix interrupt storm among other things
Date
Fix interrupt storm on bone A4 cause by non-by-the-book interrupt handling.
While at it, added a non-NAPI mode (which is easier to debug), plus
some general fixes.

Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
---
Documentation/devicetree/bindings/net/cpsw.txt | 1 +
drivers/net/ethernet/ti/cpsw.c | 222 +++++++++++++++++++++----
drivers/net/ethernet/ti/davinci_cpdma.c | 4 +-
drivers/net/ethernet/ti/davinci_cpdma.h | 2 +-
include/linux/platform_data/cpsw.h | 1 +
5 files changed, 194 insertions(+), 36 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt
index 6ddd028..d46b293 100644
--- a/Documentation/devicetree/bindings/net/cpsw.txt
+++ b/Documentation/devicetree/bindings/net/cpsw.txt
@@ -20,6 +20,7 @@ Required properties:
- cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds
- phy_id : Specifies slave phy id
- mac-address : Specifies slave MAC address
+- disable-napi : Disables driver NAPI

Optional properties:
- ti,hwmods : Must be "cpgmac0"
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 40aff68..b6ca4af 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -148,10 +148,37 @@ struct cpsw_wr_regs {
u32 soft_reset;
u32 control;
u32 int_control;
- u32 rx_thresh_en;
- u32 rx_en;
- u32 tx_en;
- u32 misc_en;
+ u32 c0_rx_thresh_en;
+ u32 c0_rx_en;
+ u32 c0_tx_en;
+ u32 c0_misc_en;
+ u32 c1_rx_thresh_en;
+ u32 c1_rx_en;
+ u32 c1_tx_en;
+ u32 c1_misc_en;
+ u32 c2_rx_thresh_en;
+ u32 c2_rx_en;
+ u32 c2_tx_en;
+ u32 c2_misc_en;
+ u32 c0_rx_thresh_stat;
+ u32 c0_rx_stat;
+ u32 c0_tx_stat;
+ u32 c0_misc_stat;
+ u32 c1_rx_thresh_stat;
+ u32 c1_rx_stat;
+ u32 c1_tx_stat;
+ u32 c1_misc_stat;
+ u32 c2_rx_thresh_stat;
+ u32 c2_rx_stat;
+ u32 c2_tx_stat;
+ u32 c2_misc_stat;
+ u32 c0_rx_imax;
+ u32 c0_tx_imax;
+ u32 c1_rx_imax;
+ u32 c1_tx_imax;
+ u32 c2_rx_imax;
+ u32 c2_tx_imax;
+ u32 rgmii_ctl;
};

struct cpsw_ss_regs {
@@ -352,8 +379,8 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)

static void cpsw_intr_enable(struct cpsw_priv *priv)
{
- __raw_writel(0xFF, &priv->wr_regs->tx_en);
- __raw_writel(0xFF, &priv->wr_regs->rx_en);
+ __raw_writel(0xFF, &priv->wr_regs->c0_tx_en);
+ __raw_writel(0xFF, &priv->wr_regs->c0_rx_en);

cpdma_ctlr_int_ctrl(priv->dma, true);
return;
@@ -361,8 +388,8 @@ static void cpsw_intr_enable(struct cpsw_priv *priv)

static void cpsw_intr_disable(struct cpsw_priv *priv)
{
- __raw_writel(0, &priv->wr_regs->tx_en);
- __raw_writel(0, &priv->wr_regs->rx_en);
+ __raw_writel(0, &priv->wr_regs->c0_tx_en);
+ __raw_writel(0, &priv->wr_regs->c0_rx_en);

cpdma_ctlr_int_ctrl(priv->dma, false);
return;
@@ -399,7 +426,10 @@ void cpsw_rx_handler(void *token, int len, int status)
skb_put(skb, len);
cpts_rx_timestamp(&priv->cpts, skb);
skb->protocol = eth_type_trans(skb, ndev);
- netif_receive_skb(skb);
+ if (priv->data.disable_napi)
+ netif_rx(skb);
+ else
+ netif_receive_skb(skb);
priv->stats.rx_bytes += len;
priv->stats.rx_packets++;
skb = NULL;
@@ -431,6 +461,7 @@ static irqreturn_t cpsw_interrupt(int irq, void *dev_id)
cpsw_disable_irq(priv);
napi_schedule(&priv->napi);
}
+
return IRQ_HANDLED;
}

@@ -445,23 +476,104 @@ static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)
static int cpsw_poll(struct napi_struct *napi, int budget)
{
struct cpsw_priv *priv = napi_to_priv(napi);
- int num_tx, num_rx;
+ int num_tx, num_rx, num_total_tx, num_total_rx;
+ int budget_left;
+
+ budget_left = budget;

- num_tx = cpdma_chan_process(priv->txch, 128);
- num_rx = cpdma_chan_process(priv->rxch, budget);
+ /* read status and throw away */
+ (void)__raw_readl(&priv->wr_regs->c0_tx_stat);
+
+ /* handle all transmits */
+ num_total_tx = 0;
+ while (budget_left > 0 &&
+ (num_tx = cpdma_chan_process(priv->txch, 128)) > 0) {
+ budget_left -= num_tx;
+ num_total_tx += num_tx;
+ }

- if (num_rx || num_tx)
- cpsw_dbg(priv, intr, "poll %d rx, %d tx pkts\n",
- num_rx, num_tx);
+ if (num_total_tx > 0 && budget_left > 0)
+ cpdma_ctlr_eoi(priv->dma, 0x02);
+
+ /* read status and throw away */
+ (void)__raw_readl(&priv->wr_regs->c0_rx_stat);
+
+ /* handle all receives */
+ num_total_rx = 0;
+ while (budget_left > 0 &&
+ (num_rx = cpdma_chan_process(priv->rxch, budget_left)) > 0) {
+ budget_left -= num_rx;
+ num_total_rx += num_rx;
+ }

- if (num_rx < budget) {
+ if (num_total_rx > 0 && budget_left > 0)
+ cpdma_ctlr_eoi(priv->dma, 0x01);
+
+ if ((num_total_rx + num_total_tx) < budget) {
napi_complete(napi);
cpsw_intr_enable(priv);
- cpdma_ctlr_eoi(priv->dma);
cpsw_enable_irq(priv);
}

- return num_rx;
+ return num_total_rx + num_total_rx;
+}
+
+static irqreturn_t cpsw_rx_thresh_pend_irq(int irq, void *dev_id)
+{
+ struct cpsw_priv *priv = dev_id;
+
+ (void)priv;
+
+ /* not handling this interrupt yet */
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpsw_rx_pend_irq(int irq, void *dev_id)
+{
+ struct cpsw_priv *priv = dev_id;
+ int num_rx, total_rx;
+ u32 rx_stat;
+
+ rx_stat = __raw_readl(&priv->wr_regs->c0_rx_stat) & 0xff;
+ if (rx_stat == 0)
+ return IRQ_NONE;
+
+ total_rx = 0;
+ while ((num_rx = cpdma_chan_process(priv->rxch,
+ priv->data.rx_descs)) > 0)
+ total_rx += num_rx;
+
+ cpdma_ctlr_eoi(priv->dma, 0x01);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpsw_tx_pend_irq(int irq, void *dev_id)
+{
+ struct cpsw_priv *priv = dev_id;
+ int num_tx, total_tx;
+ u32 tx_stat;
+
+ tx_stat = __raw_readl(&priv->wr_regs->c0_tx_stat) & 0xff;
+ if (tx_stat == 0)
+ return IRQ_NONE;
+
+ total_tx = 0;
+ while ((num_tx = cpdma_chan_process(priv->txch, 128)) > 0)
+ total_tx += num_tx;
+
+ cpdma_ctlr_eoi(priv->dma, 0x02);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpsw_misc_pend_irq(int irq, void *dev_id)
+{
+ struct cpsw_priv *priv = dev_id;
+
+ (void)priv;
+ /* not handling this interrupt yet */
+ return IRQ_HANDLED;
}

static inline void soft_reset(const char *module, void __iomem *reg)
@@ -678,8 +790,10 @@ static int cpsw_ndo_open(struct net_device *ndev)

cpdma_ctlr_start(priv->dma);
cpsw_intr_enable(priv);
- napi_enable(&priv->napi);
- cpdma_ctlr_eoi(priv->dma);
+ if (!priv->data.disable_napi)
+ napi_enable(&priv->napi);
+ cpdma_ctlr_eoi(priv->dma, 0x01);
+ cpdma_ctlr_eoi(priv->dma, 0x02);

return 0;
}
@@ -698,8 +812,10 @@ static int cpsw_ndo_stop(struct net_device *ndev)
struct cpsw_priv *priv = netdev_priv(ndev);

cpsw_info(priv, ifdown, "shutting down cpsw device\n");
+ cpsw_disable_irq(priv);
netif_stop_queue(priv->ndev);
- napi_disable(&priv->napi);
+ if (!priv->data.disable_napi)
+ napi_disable(&priv->napi);
netif_carrier_off(priv->ndev);
cpsw_intr_disable(priv);
cpdma_ctlr_int_ctrl(priv->dma, false);
@@ -901,7 +1017,8 @@ static void cpsw_ndo_tx_timeout(struct net_device *ndev)
cpdma_chan_start(priv->txch);
cpdma_ctlr_int_ctrl(priv->dma, true);
cpsw_intr_enable(priv);
- cpdma_ctlr_eoi(priv->dma);
+ cpdma_ctlr_eoi(priv->dma, 0x01);
+ cpdma_ctlr_eoi(priv->dma, 0x02);
}

static struct net_device_stats *cpsw_ndo_get_stats(struct net_device *ndev)
@@ -917,14 +1034,21 @@ static void cpsw_ndo_poll_controller(struct net_device *ndev)

cpsw_intr_disable(priv);
cpdma_ctlr_int_ctrl(priv->dma, false);
- cpsw_interrupt(ndev->irq, priv);
+ if (!priv->data.disable_napi)
+ cpsw_interrupt(ndev->irq, priv);
+ else {
+ /* bah! */
+ cpsw_rx_pend_irq(ndev->irq, priv);
+ cpsw_tx_pend_irq(ndev->irq, priv);
+ }
cpdma_ctlr_int_ctrl(priv->dma, true);
cpsw_intr_enable(priv);
- cpdma_ctlr_eoi(priv->dma);
+ cpdma_ctlr_eoi(priv->dma, 0x01);
+ cpdma_ctlr_eoi(priv->dma, 0x02);
}
#endif

-static const struct net_device_ops cpsw_netdev_ops = {
+static struct net_device_ops cpsw_netdev_ops = {
.ndo_open = cpsw_ndo_open,
.ndo_stop = cpsw_ndo_stop,
.ndo_start_xmit = cpsw_ndo_start_xmit,
@@ -1079,6 +1203,9 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
}
data->bd_ram_size = prop;

+ if (of_property_read_bool(node, "disable-napi"))
+ data->disable_napi = 1;
+
if (of_property_read_u32(node, "rx_descs", &prop)) {
pr_err("Missing rx_descs property in the DT.\n");
ret = -EINVAL;
@@ -1136,6 +1263,22 @@ error_ret:
return ret;
}

+static irq_handler_t cpsw_get_irq_handler(struct cpsw_priv *priv, int irq_idx)
+{
+ static const irq_handler_t non_napi_irq_tab[4] = {
+ cpsw_rx_thresh_pend_irq, cpsw_rx_pend_irq,
+ cpsw_tx_pend_irq, cpsw_misc_pend_irq
+ };
+
+ if ((unsigned int)irq_idx >= 4)
+ return NULL;
+
+ if (!priv->data.disable_napi)
+ return cpsw_interrupt;
+
+ return non_napi_irq_tab[irq_idx];
+}
+
static int cpsw_probe(struct platform_device *pdev)
{
struct cpsw_platform_data *data = pdev->dev.platform_data;
@@ -1146,7 +1289,8 @@ static int cpsw_probe(struct platform_device *pdev)
void __iomem *ss_regs, *wr_regs;
struct resource *res;
u32 slave_offset, sliver_offset, slave_size;
- int ret = 0, i, k = 0;
+ irq_handler_t irqh;
+ int ret = 0, i, j, k = 0;

ndev = alloc_etherdev(sizeof(struct cpsw_priv));
if (!ndev) {
@@ -1333,24 +1477,36 @@ static int cpsw_probe(struct platform_device *pdev)
goto clean_ale_ret;
}

- while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) {
- for (i = res->start; i <= res->end; i++) {
- if (request_irq(i, cpsw_interrupt, IRQF_DISABLED,
+ dev_info(&pdev->dev, "NAPI %s\n", priv->data.disable_napi ?
+ "disabled" : "enabled");
+
+ /* get interrupts */
+ j = k = 0;
+ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, j++))) {
+ for (i = res->start; k < 4 && i <= res->end; i++) {
+ irqh = cpsw_get_irq_handler(priv, k);
+ if (irqh == NULL) {
+ dev_err(&pdev->dev, "Unable to get handler "
+ "for #%d (%d)\n", k, i);
+ goto clean_ale_ret;
+ }
+ if (request_irq(i, irqh, IRQF_DISABLED,
dev_name(&pdev->dev), priv)) {
dev_err(priv->dev, "error attaching irq\n");
goto clean_ale_ret;
}
- priv->irqs_table[k] = i;
- priv->num_irqs = k;
+ priv->irqs_table[k++] = i;
}
- k++;
}
+ priv->num_irqs = k;

ndev->flags |= IFF_ALLMULTI; /* see cpsw_ndo_change_rx_flags() */

ndev->netdev_ops = &cpsw_netdev_ops;
SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops);
- netif_napi_add(ndev, &priv->napi, cpsw_poll, CPSW_POLL_WEIGHT);
+
+ if (!priv->data.disable_napi)
+ netif_napi_add(ndev, &priv->napi, cpsw_poll, CPSW_POLL_WEIGHT);

/* register the network device */
SET_NETDEV_DEV(ndev, &pdev->dev);
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
index 4995673..6effab2 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.c
+++ b/drivers/net/ethernet/ti/davinci_cpdma.c
@@ -474,9 +474,9 @@ int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable)
return 0;
}

-void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr)
+void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value)
{
- dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, 0);
+ dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, value);
}

struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h
index afa19a0..b7c097d 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.h
+++ b/drivers/net/ethernet/ti/davinci_cpdma.h
@@ -86,7 +86,7 @@ int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
int cpdma_chan_process(struct cpdma_chan *chan, int quota);

int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable);
-void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr);
+void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value);
int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable);

enum cpdma_control {
diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h
index 24368a2..be5b27e 100644
--- a/include/linux/platform_data/cpsw.h
+++ b/include/linux/platform_data/cpsw.h
@@ -35,6 +35,7 @@ struct cpsw_platform_data {
u32 bd_ram_size; /*buffer descriptor ram size */
u32 rx_descs; /* Number of Rx Descriptios */
u32 mac_control; /* Mac control register */
+ int disable_napi; /* disable NAPI */
};

#endif /* __CPSW_H__ */
--
1.7.12


\
 
 \ /
  Last update: 2013-01-28 15:03    [W:0.147 / U:0.832 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site