diff options
author | guoyin.chen <guoyin.chen@freescale.com> | 2013-04-03 11:07:09 +0800 |
---|---|---|
committer | guoyin.chen <guoyin.chen@freescale.com> | 2013-04-03 11:07:09 +0800 |
commit | 8fa723312b9701e767c2eda46e4aabb21e21ccb3 (patch) | |
tree | bcdb59722202cce2d2277d2fd7a785141cdf8bac /drivers/net | |
parent | 029767ec3d45796a2d6a58debe3844cf6b814ec7 (diff) | |
parent | 573bab0be2427d6664420eaf9d8e272dbe9d840f (diff) |
Merge remote-tracking branch 'fsl-linux-sdk/imx_3.0.35_4.0.0' into imx_3.0.35_android
Conflicts:
drivers/net/fec.c
Diffstat (limited to 'drivers/net')
-rwxr-xr-x | drivers/net/fec.c | 70 |
1 files changed, 65 insertions, 5 deletions
diff --git a/drivers/net/fec.c b/drivers/net/fec.c index d09aa984c97e..ebb09eb9a6f2 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) 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. */ #include <linux/module.h> @@ -27,6 +27,7 @@ #include <linux/string.h> #include <linux/ptrace.h> #include <linux/errno.h> +#include <linux/gpio.h> #include <linux/ioport.h> #include <linux/slab.h> #include <linux/interrupt.h> @@ -70,11 +71,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", @@ -82,7 +85,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, }, { } }; @@ -229,6 +233,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; @@ -278,13 +283,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; @@ -300,7 +336,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) { @@ -373,6 +408,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; @@ -1832,6 +1873,17 @@ fec_probe(struct platform_device *pdev) if (pdata) fep->phy_interface = pdata->phy; +#ifdef CONFIG_MX6_ENET_IRQ_TO_GPIO + gpio_request(pdata->gpio_irq, "gpio_enet_irq"); + gpio_direction_input(pdata->gpio_irq); + + irq = gpio_to_irq(pdata->gpio_irq); + ret = request_irq(irq, fec_enet_interrupt, + IRQF_TRIGGER_RISING, + pdev->name, ndev); + if (ret) + goto failed_irq; +#else /* This device has up to three irqs on some platforms */ for (i = 0; i < 3; i++) { irq = platform_get_irq(pdev, i); @@ -1846,6 +1898,7 @@ fec_probe(struct platform_device *pdev) goto failed_irq; } } +#endif fep->clk = clk_get(&pdev->dev, "fec_clk"); if (IS_ERR(fep->clk)) { @@ -1878,6 +1931,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; @@ -1894,11 +1949,15 @@ failed_init: clk_disable(fep->clk); clk_put(fep->clk); failed_clk: +#ifdef CONFIG_MX6_ENET_IRQ_TO_GPIO + free_irq(irq, ndev); +#else for (i = 0; i < 3; i++) { irq = platform_get_irq(pdev, i); if (irq > 0) free_irq(irq, ndev); } +#endif failed_irq: iounmap(fep->hwp); failed_ioremap: @@ -1916,6 +1975,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); |