net/cpsw: fix irq_disable() with threaded interrupts
[deliverable/linux.git] / drivers / net / ethernet / ti / cpsw.c
index d9f5e74cdcfc1bacdf96d7cbe621f567624162ba..4e2d224dd68065e39beeee42a52f48a4af36f964 100644 (file)
@@ -348,6 +348,7 @@ struct cpsw_priv {
        /* snapshot of IRQ numbers */
        u32 irqs_table[4];
        u32 num_irqs;
+       bool irq_enabled;
        struct cpts *cpts;
        u32 emac_port;
 };
@@ -355,12 +356,15 @@ struct cpsw_priv {
 #define napi_to_priv(napi)     container_of(napi, struct cpsw_priv, napi)
 #define for_each_slave(priv, func, arg...)                             \
        do {                                                            \
-               int idx;                                                \
+               struct cpsw_slave *slave;                               \
+               int n;                                                  \
                if (priv->data.dual_emac)                               \
                        (func)((priv)->slaves + priv->emac_port, ##arg);\
                else                                                    \
-                       for (idx = 0; idx < (priv)->data.slaves; idx++) \
-                               (func)((priv)->slaves + idx, ##arg);    \
+                       for (n = (priv)->data.slaves,                   \
+                                       slave = (priv)->slaves;         \
+                                       n; n--)                         \
+                               (func)(slave++, ##arg);                 \
        } while (0)
 #define cpsw_get_slave_ndev(priv, __slave_no__)                                \
        (priv->slaves[__slave_no__].ndev)
@@ -512,7 +516,10 @@ static irqreturn_t cpsw_interrupt(int irq, void *dev_id)
                return IRQ_NONE;
 
        cpsw_intr_disable(priv);
-       cpsw_disable_irq(priv);
+       if (priv->irq_enabled == true) {
+               cpsw_disable_irq(priv);
+               priv->irq_enabled = false;
+       }
 
        if (netif_running(priv->ndev)) {
                napi_schedule(&priv->napi);
@@ -541,10 +548,16 @@ static int cpsw_poll(struct napi_struct *napi, int budget)
 
        num_rx = cpdma_chan_process(priv->rxch, budget);
        if (num_rx < budget) {
+               struct cpsw_priv *prim_cpsw;
+
                napi_complete(napi);
                cpsw_intr_enable(priv);
                cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX);
-               cpsw_enable_irq(priv);
+               prim_cpsw = cpsw_get_slave_priv(priv, 0);
+               if (prim_cpsw->irq_enabled == false) {
+                       cpsw_enable_irq(priv);
+                       prim_cpsw->irq_enabled = true;
+               }
        }
 
        if (num_rx || num_tx)
@@ -883,6 +896,7 @@ static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv)
 static int cpsw_ndo_open(struct net_device *ndev)
 {
        struct cpsw_priv *priv = netdev_priv(ndev);
+       struct cpsw_priv *prim_cpsw;
        int i, ret;
        u32 reg;
 
@@ -950,6 +964,14 @@ static int cpsw_ndo_open(struct net_device *ndev)
                cpsw_set_coalesce(ndev, &coal);
        }
 
+       prim_cpsw = cpsw_get_slave_priv(priv, 0);
+       if (prim_cpsw->irq_enabled == false) {
+               if ((priv == prim_cpsw) || !netif_running(prim_cpsw->ndev)) {
+                       prim_cpsw->irq_enabled = true;
+                       cpsw_enable_irq(prim_cpsw);
+               }
+       }
+
        cpdma_ctlr_start(priv->dma);
        cpsw_intr_enable(priv);
        napi_enable(&priv->napi);
@@ -1611,7 +1633,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
                priv_sl2->irqs_table[i] = priv->irqs_table[i];
                priv_sl2->num_irqs = priv->num_irqs;
        }
-
+       priv->irq_enabled = true;
        ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
        ndev->netdev_ops = &cpsw_netdev_ops;
@@ -1632,7 +1654,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
 
 static int cpsw_probe(struct platform_device *pdev)
 {
-       struct cpsw_platform_data       *data = pdev->dev.platform_data;
+       struct cpsw_platform_data       *data;
        struct net_device               *ndev;
        struct cpsw_priv                *priv;
        struct cpdma_params             dma_params;
@@ -1845,7 +1867,7 @@ static int cpsw_probe(struct platform_device *pdev)
                                goto clean_ale_ret;
                        }
                        priv->irqs_table[k] = i;
-                       priv->num_irqs = k;
+                       priv->num_irqs = k + 1;
                }
                k++;
        }
@@ -1883,7 +1905,8 @@ static int cpsw_probe(struct platform_device *pdev)
        return 0;
 
 clean_irq_ret:
-       free_irq(ndev->irq, priv);
+       for (i = 0; i < priv->num_irqs; i++)
+               free_irq(priv->irqs_table[i], priv);
 clean_ale_ret:
        cpsw_ale_destroy(priv->ale);
 clean_dma_ret:
@@ -1906,7 +1929,8 @@ clean_slave_ret:
        pm_runtime_disable(&pdev->dev);
        kfree(priv->slaves);
 clean_ndev_ret:
-       free_netdev(ndev);
+       kfree(priv->data.slave_data);
+       free_netdev(priv->ndev);
        return ret;
 }
 
@@ -1914,12 +1938,17 @@ static int cpsw_remove(struct platform_device *pdev)
 {
        struct net_device *ndev = platform_get_drvdata(pdev);
        struct cpsw_priv *priv = netdev_priv(ndev);
+       int i;
 
-       pr_info("removing device");
        platform_set_drvdata(pdev, NULL);
+       if (priv->data.dual_emac)
+               unregister_netdev(cpsw_get_slave_ndev(priv, 1));
+       unregister_netdev(ndev);
 
        cpts_unregister(priv->cpts);
-       free_irq(ndev->irq, priv);
+       for (i = 0; i < priv->num_irqs; i++)
+               free_irq(priv->irqs_table[i], priv);
+
        cpsw_ale_destroy(priv->ale);
        cpdma_chan_destroy(priv->txch);
        cpdma_chan_destroy(priv->rxch);
@@ -1933,8 +1962,10 @@ static int cpsw_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        clk_put(priv->clk);
        kfree(priv->slaves);
+       kfree(priv->data.slave_data);
+       if (priv->data.dual_emac)
+               free_netdev(cpsw_get_slave_ndev(priv, 1));
        free_netdev(ndev);
-
        return 0;
 }
 
This page took 0.030056 seconds and 5 git commands to generate.