* 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",
},
{
.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,
},
{ }
};
int link;
int full_duplex;
struct completion mdio_done;
+ struct delayed_work fixup_trigger_tx;
struct fec_ptp_private *ptp_priv;
uint ptimer_present;
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;
/* Fill in a Tx ring entry */
bdp = fep->cur_tx;
-
status = bdp->cbd_sc;
if (status & BD_ENET_TX_READY) {
/* 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;
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;
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);