diff options
author | Frank Li <Frank.Li@freescale.com> | 2013-07-25 02:05:53 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-07-27 23:14:48 -0400 |
commit | 031916568a1aa2ef1809f86d26f0bcfa215ff5c0 (patch) | |
tree | cd6081f93a38a1eb924896ed77b53188e405bbe9 /drivers/net/ethernet/freescale/fec_main.c | |
parent | c2534384233647a8702eca291199b94ffedc12d3 (diff) |
net: fec: workaround stop tx during errata ERR006358
If the ready bit in the transmit buffer descriptor (TxBD[R])
is previously detected as not set during a prior frame transmission,
then the ENET_TDAR[TDAR] bit is cleared at a later time, even if
additional TxBDs were added to the ring and the ENET_TDAR[TDAR]
bit is set. This results in frames not being transmitted until
there is a 0-to-1 transition on ENET_TDAR[TDAR].
Workarounds:
code can use the transmit frame interrupt flag (ENET_EIR[TXF])
as a method to detect whether the ENET has completed transmission
and the ENET_TDAR[TDAR] has been cleared. If ENET_TDAR[TDAR] is
detected as cleared when packets are queued and waiting for transmit,
then a write to the TDAR bit will restart TxBD processing.
This case main happen when loading is light. A ethernet package may
not send out utile next package put into tx queue.
How to test:
while [ true ]
do
ping <IP> -s 10000 -w 4
ping <IP> -s 6000 -w 2
ping <IP> -s 4000 -w 2
ping <IP> -s 10000 -w 2
done
You will see below result in overnight test.
6008 bytes from 10.192.242.116: seq=1 ttl=128 time=0.722 ms
4008 bytes from 10.192.242.116: seq=0 ttl=128 time=1001.008 ms
4008 bytes from 10.192.242.116: seq=1 ttl=128 time=1.010 ms
10008 bytes from 10.192.242.116: seq=0 ttl=128 time=0.896 ms
After apply this patch, >1000ms delay disappear.
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Acked-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/freescale/fec_main.c')
-rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 32 |
1 files changed, 30 insertions, 2 deletions
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index f0f0e96453a0..0dda45481d16 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c | |||
@@ -93,6 +93,20 @@ static void set_multicast_list(struct net_device *ndev); | |||
93 | #define FEC_QUIRK_HAS_CSUM (1 << 5) | 93 | #define FEC_QUIRK_HAS_CSUM (1 << 5) |
94 | /* Controller has hardware vlan support */ | 94 | /* Controller has hardware vlan support */ |
95 | #define FEC_QUIRK_HAS_VLAN (1 << 6) | 95 | #define FEC_QUIRK_HAS_VLAN (1 << 6) |
96 | /* ENET IP errata ERR006358 | ||
97 | * | ||
98 | * If the ready bit in the transmit buffer descriptor (TxBD[R]) is previously | ||
99 | * detected as not set during a prior frame transmission, then the | ||
100 | * ENET_TDAR[TDAR] bit is cleared at a later time, even if additional TxBDs | ||
101 | * were added to the ring and the ENET_TDAR[TDAR] bit is set. This results in | ||
102 | * If the ready bit in the transmit buffer descriptor (TxBD[R]) is previously | ||
103 | * detected as not set during a prior frame transmission, then the | ||
104 | * ENET_TDAR[TDAR] bit is cleared at a later time, even if additional TxBDs | ||
105 | * were added to the ring and the ENET_TDAR[TDAR] bit is set. This results in | ||
106 | * frames not being transmitted until there is a 0-to-1 transition on | ||
107 | * ENET_TDAR[TDAR]. | ||
108 | */ | ||
109 | #define FEC_QUIRK_ERR006358 (1 << 7) | ||
96 | 110 | ||
97 | static struct platform_device_id fec_devtype[] = { | 111 | static struct platform_device_id fec_devtype[] = { |
98 | { | 112 | { |
@@ -112,7 +126,7 @@ static struct platform_device_id fec_devtype[] = { | |||
112 | .name = "imx6q-fec", | 126 | .name = "imx6q-fec", |
113 | .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | | 127 | .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | |
114 | FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | | 128 | FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | |
115 | FEC_QUIRK_HAS_VLAN, | 129 | FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358, |
116 | }, { | 130 | }, { |
117 | .name = "mvf600-fec", | 131 | .name = "mvf600-fec", |
118 | .driver_data = FEC_QUIRK_ENET_MAC, | 132 | .driver_data = FEC_QUIRK_ENET_MAC, |
@@ -275,7 +289,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |||
275 | struct fec_enet_private *fep = netdev_priv(ndev); | 289 | struct fec_enet_private *fep = netdev_priv(ndev); |
276 | const struct platform_device_id *id_entry = | 290 | const struct platform_device_id *id_entry = |
277 | platform_get_device_id(fep->pdev); | 291 | platform_get_device_id(fep->pdev); |
278 | struct bufdesc *bdp; | 292 | struct bufdesc *bdp, *bdp_pre; |
279 | void *bufaddr; | 293 | void *bufaddr; |
280 | unsigned short status; | 294 | unsigned short status; |
281 | unsigned int index; | 295 | unsigned int index; |
@@ -370,6 +384,15 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |||
370 | ebdp->cbd_esc |= BD_ENET_TX_PINS; | 384 | ebdp->cbd_esc |= BD_ENET_TX_PINS; |
371 | } | 385 | } |
372 | } | 386 | } |
387 | |||
388 | bdp_pre = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex); | ||
389 | if ((id_entry->driver_data & FEC_QUIRK_ERR006358) && | ||
390 | !(bdp_pre->cbd_sc & BD_ENET_TX_READY)) { | ||
391 | fep->delay_work.trig_tx = true; | ||
392 | schedule_delayed_work(&(fep->delay_work.delay_work), | ||
393 | msecs_to_jiffies(1)); | ||
394 | } | ||
395 | |||
373 | /* If this was the last BD in the ring, start at the beginning again. */ | 396 | /* If this was the last BD in the ring, start at the beginning again. */ |
374 | if (status & BD_ENET_TX_WRAP) | 397 | if (status & BD_ENET_TX_WRAP) |
375 | bdp = fep->tx_bd_base; | 398 | bdp = fep->tx_bd_base; |
@@ -689,6 +712,11 @@ static void fec_enet_work(struct work_struct *work) | |||
689 | fec_restart(fep->netdev, fep->full_duplex); | 712 | fec_restart(fep->netdev, fep->full_duplex); |
690 | netif_wake_queue(fep->netdev); | 713 | netif_wake_queue(fep->netdev); |
691 | } | 714 | } |
715 | |||
716 | if (fep->delay_work.trig_tx) { | ||
717 | fep->delay_work.trig_tx = false; | ||
718 | writel(0, fep->hwp + FEC_X_DES_ACTIVE); | ||
719 | } | ||
692 | } | 720 | } |
693 | 721 | ||
694 | static void | 722 | static void |