]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/ethernet/freescale/fec_main.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/ide
[karo-tx-linux.git] / drivers / net / ethernet / freescale / fec_main.c
index b349e6f36ea75fa6a471d3c2a37a7bee21d8e59c..b2a32209ffbfc2806d725d7924b470d2752e03d5 100644 (file)
@@ -364,7 +364,7 @@ fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
        return 0;
 }
 
-static int
+static struct bufdesc *
 fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq,
                             struct sk_buff *skb,
                             struct net_device *ndev)
@@ -439,10 +439,7 @@ fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq,
                bdp->cbd_sc = status;
        }
 
-       txq->cur_tx = bdp;
-
-       return 0;
-
+       return bdp;
 dma_mapping_error:
        bdp = txq->cur_tx;
        for (i = 0; i < frag; i++) {
@@ -450,7 +447,7 @@ dma_mapping_error:
                dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
                                bdp->cbd_datlen, DMA_TO_DEVICE);
        }
-       return NETDEV_TX_OK;
+       return ERR_PTR(-ENOMEM);
 }
 
 static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
@@ -467,7 +464,6 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
        unsigned int estatus = 0;
        unsigned int index;
        int entries_free;
-       int ret;
 
        entries_free = fec_enet_get_free_txdesc_num(fep, txq);
        if (entries_free < MAX_SKB_FRAGS + 1) {
@@ -485,6 +481,7 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
 
        /* Fill in a Tx ring entry */
        bdp = txq->cur_tx;
+       last_bdp = bdp;
        status = bdp->cbd_sc;
        status &= ~BD_ENET_TX_STATS;
 
@@ -513,9 +510,9 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
        }
 
        if (nr_frags) {
-               ret = fec_enet_txq_submit_frag_skb(txq, skb, ndev);
-               if (ret)
-                       return ret;
+               last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev);
+               if (IS_ERR(last_bdp))
+                       return NETDEV_TX_OK;
        } else {
                status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
                if (fep->bufdesc_ex) {
@@ -544,7 +541,6 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
                ebdp->cbd_esc = estatus;
        }
 
-       last_bdp = txq->cur_tx;
        index = fec_enet_get_bd_index(txq->tx_bd_base, last_bdp, fep);
        /* Save skb pointer */
        txq->tx_skbuff[index] = skb;
@@ -563,6 +559,10 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
 
        skb_tx_timestamp(skb);
 
+       /* Make sure the update to bdp and tx_skbuff are performed before
+        * cur_tx.
+        */
+       wmb();
        txq->cur_tx = bdp;
 
        /* Trigger transmission start */
@@ -1218,10 +1218,11 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id)
        /* get next bdp of dirty_tx */
        bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
 
-       while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
-
-               /* current queue is empty */
-               if (bdp == txq->cur_tx)
+       while (bdp != READ_ONCE(txq->cur_tx)) {
+               /* Order the load of cur_tx and cbd_sc */
+               rmb();
+               status = READ_ONCE(bdp->cbd_sc);
+               if (status & BD_ENET_TX_READY)
                        break;
 
                index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
@@ -1275,6 +1276,10 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id)
                /* Free the sk buffer associated with this last transmit */
                dev_kfree_skb_any(skb);
 
+               /* Make sure the update to bdp and tx_skbuff are performed
+                * before dirty_tx
+                */
+               wmb();
                txq->dirty_tx = bdp;
 
                /* Update pointer to next buffer descriptor to be transmitted */
@@ -1402,6 +1407,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
                if ((status & BD_ENET_RX_LAST) == 0)
                        netdev_err(ndev, "rcv is not +last\n");
 
+               writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT);
 
                /* Check for errors. */
                if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
@@ -1774,7 +1780,7 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
        int ret = 0;
 
        ret = pm_runtime_get_sync(dev);
-       if (IS_ERR_VALUE(ret))
+       if (ret < 0)
                return ret;
 
        fep->mii_timeout = 0;
@@ -1810,11 +1816,13 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
        struct fec_enet_private *fep = bus->priv;
        struct device *dev = &fep->pdev->dev;
        unsigned long time_left;
-       int ret = 0;
+       int ret;
 
        ret = pm_runtime_get_sync(dev);
-       if (IS_ERR_VALUE(ret))
+       if (ret < 0)
                return ret;
+       else
+               ret = 0;
 
        fep->mii_timeout = 0;
        reinit_completion(&fep->mdio_done);
@@ -2865,7 +2873,7 @@ fec_enet_open(struct net_device *ndev)
        int ret;
 
        ret = pm_runtime_get_sync(&fep->pdev->dev);
-       if (IS_ERR_VALUE(ret))
+       if (ret < 0)
                return ret;
 
        pinctrl_pm_select_default_state(&fep->pdev->dev);
@@ -3023,6 +3031,14 @@ fec_set_mac_address(struct net_device *ndev, void *p)
                memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
        }
 
+       /* Add netif status check here to avoid system hang in below case:
+        * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx;
+        * After ethx down, fec all clocks are gated off and then register
+        * access causes system hang.
+        */
+       if (!netif_running(ndev))
+               return 0;
+
        writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |
                (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),
                fep->hwp + FEC_ADDR_LOW);
@@ -3054,7 +3070,6 @@ static void fec_poll_controller(struct net_device *dev)
 }
 #endif
 
-#define FEATURES_NEED_QUIESCE NETIF_F_RXCSUM
 static inline void fec_enet_set_netdev_features(struct net_device *netdev,
        netdev_features_t features)
 {
@@ -3078,7 +3093,7 @@ static int fec_set_features(struct net_device *netdev,
        struct fec_enet_private *fep = netdev_priv(netdev);
        netdev_features_t changed = features ^ netdev->features;
 
-       if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
+       if (netif_running(netdev) && changed & NETIF_F_RXCSUM) {
                napi_disable(&fep->napi);
                netif_tx_lock_bh(netdev);
                fec_stop(netdev);
@@ -3246,7 +3261,7 @@ static void fec_reset_phy(struct platform_device *pdev)
                return;
        }
        msleep(msec);
-       gpio_set_value(phy_reset, 1);
+       gpio_set_value_cansleep(phy_reset, 1);
 }
 #else /* CONFIG_OF */
 static void fec_reset_phy(struct platform_device *pdev)