Merge tag 'md/3.17-more-fixes' of git://git.neil.brown.name/md
[cascardo/linux.git] / drivers / net / ethernet / ti / cpsw.c
index 999fb72..e2a0028 100644 (file)
@@ -699,6 +699,28 @@ static void cpsw_rx_handler(void *token, int len, int status)
        cpsw_dual_emac_src_port_detect(status, priv, ndev, skb);
 
        if (unlikely(status < 0) || unlikely(!netif_running(ndev))) {
+               bool ndev_status = false;
+               struct cpsw_slave *slave = priv->slaves;
+               int n;
+
+               if (priv->data.dual_emac) {
+                       /* In dual emac mode check for all interfaces */
+                       for (n = priv->data.slaves; n; n--, slave++)
+                               if (netif_running(slave->ndev))
+                                       ndev_status = true;
+               }
+
+               if (ndev_status && (status >= 0)) {
+                       /* The packet received is for the interface which
+                        * is already down and the other interface is up
+                        * and running, intead of freeing which results
+                        * in reducing of the number of rx descriptor in
+                        * DMA engine, requeue skb back to cpdma.
+                        */
+                       new_skb = skb;
+                       goto requeue;
+               }
+
                /* the interface is going down, skbs are purged */
                dev_kfree_skb_any(skb);
                return;
@@ -717,6 +739,7 @@ static void cpsw_rx_handler(void *token, int len, int status)
                new_skb = skb;
        }
 
+requeue:
        ret = cpdma_chan_submit(priv->rxch, new_skb, new_skb->data,
                        skb_tailroom(new_skb), 0);
        if (WARN_ON(ret < 0))
@@ -2311,10 +2334,19 @@ static int cpsw_suspend(struct device *dev)
        struct net_device       *ndev = platform_get_drvdata(pdev);
        struct cpsw_priv        *priv = netdev_priv(ndev);
 
-       if (netif_running(ndev))
-               cpsw_ndo_stop(ndev);
+       if (priv->data.dual_emac) {
+               int i;
 
-       for_each_slave(priv, soft_reset_slave);
+               for (i = 0; i < priv->data.slaves; i++) {
+                       if (netif_running(priv->slaves[i].ndev))
+                               cpsw_ndo_stop(priv->slaves[i].ndev);
+                       soft_reset_slave(priv->slaves + i);
+               }
+       } else {
+               if (netif_running(ndev))
+                       cpsw_ndo_stop(ndev);
+               for_each_slave(priv, soft_reset_slave);
+       }
 
        pm_runtime_put_sync(&pdev->dev);
 
@@ -2328,14 +2360,24 @@ static int cpsw_resume(struct device *dev)
 {
        struct platform_device  *pdev = to_platform_device(dev);
        struct net_device       *ndev = platform_get_drvdata(pdev);
+       struct cpsw_priv        *priv = netdev_priv(ndev);
 
        pm_runtime_get_sync(&pdev->dev);
 
        /* Select default pin state */
        pinctrl_pm_select_default_state(&pdev->dev);
 
-       if (netif_running(ndev))
-               cpsw_ndo_open(ndev);
+       if (priv->data.dual_emac) {
+               int i;
+
+               for (i = 0; i < priv->data.slaves; i++) {
+                       if (netif_running(priv->slaves[i].ndev))
+                               cpsw_ndo_open(priv->slaves[i].ndev);
+               }
+       } else {
+               if (netif_running(ndev))
+                       cpsw_ndo_open(ndev);
+       }
        return 0;
 }