diff options
author | Fugang Duan <B38611@freescale.com> | 2013-03-27 18:23:16 +0800 |
---|---|---|
committer | Fugang Duan <B38611@freescale.com> | 2013-03-29 15:13:48 +0800 |
commit | 49f0eca324dc5a84715e39f79e8b53970bbb263e (patch) | |
tree | 014e38766224e0c64732828b70f7334847106020 /drivers/net | |
parent | b051ba50103105f561d8f849b9bdcc9909dfb465 (diff) |
ENGR00255406 net: fec: Workaround tx hang due to TDAR bit cleared by uDMA
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>
Diffstat (limited to 'drivers/net')
-rwxr-xr-x | drivers/net/fec.c | 53 |
1 files changed, 48 insertions, 5 deletions
diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 4b5818e4d71e..5f0e4e0e3a3e 100755 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -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> @@ -69,11 +69,13 @@ #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); |