]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
ENGR00255406 net: fec: Workaround tx hang due to TDAR bit cleared by uDMA
authorFugang Duan <B38611@freescale.com>
Wed, 27 Mar 2013 10:23:16 +0000 (18:23 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:35:59 +0000 (08:35 +0200)
MTIP enet IP have one IC issue recorded at PDM ticket:TKT168103

The issue description:
The TDAR bit after being set by software is not acted upon by the ENET
module due to the timing of when the ENET state machine clearing the
TDAR bit occurring coincident or momentarily after the software sets
the bit.

The result:
The corresponding transmit packet for an incoming ping is delayed.

Workaround:
This forces the ENET module to check the Transmit buffer descriptor
and take action if the “ready” flag is set. Otherwise the ENET module
returns to idle mode.

Signed-off-by: Fugang Duan <B38611@freescale.com>
drivers/net/fec.c

index 4b5818e4d71ed1e3f53ecfe657e0d0cdd3d533b5..5f0e4e0e3a3e8da5c41fd18ca4061425066067d2 100755 (executable)
@@ -19,7 +19,7 @@
  * Copyright (c) 2004-2006 Macq Electronique SA.
  *
  * Support for FEC IEEE 1588.
- * Copyright (C) 2010-2012 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010-2013 Freescale Semiconductor, Inc.
  */
 
 #include <linux/module.h>
 #define FEC_QUIRK_ENET_MAC             (1 << 0)
 /* Controller needs driver to swap frame */
 #define FEC_QUIRK_SWAP_FRAME           (1 << 1)
+/* ENET IP errata ticket TKT168103 */
+#define FEC_QUIRK_BUG_TKT168103                (1 << 2)
 
 static struct platform_device_id fec_devtype[] = {
        {
                .name = "enet",
-               .driver_data = FEC_QUIRK_ENET_MAC,
+               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_BUG_TKT168103,
        },
        {
                .name = "fec",
@@ -81,7 +83,8 @@ static struct platform_device_id fec_devtype[] = {
        },
        {
                .name = "imx28-fec",
-               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
+               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
+                               FEC_QUIRK_BUG_TKT168103,
        },
        { }
 };
@@ -228,6 +231,7 @@ struct fec_enet_private {
        int     link;
        int     full_duplex;
        struct  completion mdio_done;
+       struct delayed_work fixup_trigger_tx;
 
        struct  fec_ptp_private *ptp_priv;
        uint    ptimer_present;
@@ -277,13 +281,44 @@ static void *swap_buffer(void *bufaddr, int len)
        return bufaddr;
 }
 
+static inline
+void *fec_enet_get_pre_txbd(struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       struct bufdesc *bdp = fep->cur_tx;
+
+       if (bdp == fep->tx_bd_base)
+               return bdp + TX_RING_SIZE;
+       else
+               return bdp - 1;
+
+}
+
+/* MTIP enet IP have one IC issue recorded at PDM ticket:TKT168103
+ * The TDAR bit after being set by software is not acted upon by the
+ * ENET module due to the timing of when the ENET state machine
+ * clearing the TDAR bit occurring coincident or momentarily after
+ * the software sets the bit.
+ * This forces ENET module to check the Transmit buffer descriptor
+ * and take action if the “ready” flag is set. Otherwise the ENET
+ * returns to idle mode.
+ */
+static void fixup_trigger_tx_func(struct work_struct *work)
+{
+       struct fec_enet_private *fep =
+                       container_of(work, struct fec_enet_private,
+                                       fixup_trigger_tx.work);
+
+       writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+}
+
 static netdev_tx_t
 fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
        const struct platform_device_id *id_entry =
                                platform_get_device_id(fep->pdev);
-       struct bufdesc *bdp;
+       struct bufdesc *bdp, *bdp_pre;
        void *bufaddr;
        unsigned short  status;
        unsigned long   estatus;
@@ -299,7 +334,6 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 
        /* Fill in a Tx ring entry */
        bdp = fep->cur_tx;
-
        status = bdp->cbd_sc;
 
        if (status & BD_ENET_TX_READY) {
@@ -372,6 +406,12 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        /* Trigger transmission start */
        writel(0, fep->hwp + FEC_X_DES_ACTIVE);
 
+       bdp_pre = fec_enet_get_pre_txbd(ndev);
+       if ((id_entry->driver_data & FEC_QUIRK_BUG_TKT168103) &&
+               !(bdp_pre->cbd_sc & BD_ENET_TX_READY))
+               schedule_delayed_work(&fep->fixup_trigger_tx,
+                                        msecs_to_jiffies(1));
+
        /* If this was the last BD in the ring, start at the beginning again. */
        if (status & BD_ENET_TX_WRAP)
                bdp = fep->tx_bd_base;
@@ -1872,6 +1912,8 @@ fec_probe(struct platform_device *pdev)
        netif_carrier_off(ndev);
        clk_disable(fep->clk);
 
+       INIT_DELAYED_WORK(&fep->fixup_trigger_tx, fixup_trigger_tx_func);
+
        ret = register_netdev(ndev);
        if (ret)
                goto failed_register;
@@ -1910,6 +1952,7 @@ fec_drv_remove(struct platform_device *pdev)
        struct fec_enet_private *fep = netdev_priv(ndev);
        struct resource *r;
 
+       cancel_delayed_work_sync(&fep->fixup_trigger_tx);
        fec_stop(ndev);
        fec_enet_mii_remove(fep);
        clk_disable(fep->clk);