aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/fec.c
diff options
context:
space:
mode:
authorFugang Duan <B38611@freescale.com>2011-11-02 12:26:49 +0800
committerEric Miao <eric.miao@canonical.com>2011-11-10 07:38:58 +0800
commitf2c1d06e7c1fee70929a7ab9cbd1c080ba412b18 (patch)
treeecd7c0f23066226bf45576d84ab4c95746d88ba5 /drivers/net/fec.c
parent2f47b52e635bd1e7aa6b54072093173ea4819725 (diff)
ENGR00161207 - FEC: Add IEEE 1588 driver for imx6
- Support time stamp sync with networking master timer. - Support ipg 40MHz clock, and precision is about 20ns. - Don't support ipg 66MHz clock. - Test flow: 1. Enable CONFIG_FEC_1588 in imx6_defconfig file. 2. Select pll3 for ipg clk 40M in uboot plugin code. I. set reg 0x20c8028 value to 0x10000; II.set reg 0x20c8024 value to 0x3040; III. set reg 0x20c4014[25] to 0x1 IV. set reg 0x20c4014[12:10] to 0x5 3. Rebuid uboot and setup the ethernet environment. 4. Run the 1588 stack ptp_main in master and slave. Signed-off-by: Fugang Duan <B38611@freescale.com>
Diffstat (limited to 'drivers/net/fec.c')
-rw-r--r--drivers/net/fec.c107
1 files changed, 103 insertions, 4 deletions
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index b294d99abd0..4e793d6c1e7 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -18,6 +18,7 @@
* Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
* Copyright (c) 2004-2006 Macq Electronique SA.
*
+ * Support for FEC IEEE 1588.
* Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
*/
@@ -53,6 +54,7 @@
#endif
#include "fec.h"
+#include "fec_1588.h"
#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28)
#define FEC_ALIGNMENT 0xf
@@ -140,8 +142,16 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */
#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
+#define FEC_ENET_TS_AVAIL ((uint)0x00010000)
+#define FEC_ENET_TS_TIMER ((uint)0x00008000)
+#if defined(CONFIG_FEC_1588) && (defined(CONFIG_ARCH_MX28) || \
+ defined(CONFIG_ARCH_MX6))
+#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII | \
+ FEC_ENET_TS_AVAIL | FEC_ENET_TS_TIMER)
+#else
#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
+#endif
/* The FEC stores dest/src/type, data, and checksum for receive packets.
*/
@@ -210,9 +220,13 @@ struct fec_enet_private {
int mii_timeout;
uint phy_speed;
phy_interface_t phy_interface;
+ int index;
int link;
int full_duplex;
struct completion mdio_done;
+
+ struct fec_ptp_private *ptp_priv;
+ uint ptimer_present;
};
static irqreturn_t fec_enet_interrupt(int irq, void * dev_id);
@@ -256,6 +270,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct bufdesc *bdp;
void *bufaddr;
unsigned short status;
+ unsigned long estatus;
unsigned long flags;
if (!fep->link) {
@@ -297,6 +312,17 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
bufaddr = fep->tx_bounce[index];
}
+ if (fep->ptimer_present) {
+ if (fec_ptp_do_txstamp(skb)) {
+ estatus = BD_ENET_TX_TS;
+ status |= BD_ENET_TX_PTP;
+ } else
+ estatus = 0;
+#ifdef CONFIG_ENHANCED_BD
+ bdp->cbd_esc = (estatus | BD_ENET_TX_INT);
+ bdp->cbd_bdu = 0;
+#endif
+ }
/*
* Some design made an incorrect assumption on endian mode of
* the system that it's running on. As the result, driver has to
@@ -361,6 +387,7 @@ fec_enet_interrupt(int irq, void * dev_id)
{
struct net_device *dev = dev_id;
struct fec_enet_private *fep = netdev_priv(dev);
+ struct fec_ptp_private *fpp = fep->ptp_priv;
uint int_events;
irqreturn_t ret = IRQ_NONE;
@@ -382,6 +409,12 @@ fec_enet_interrupt(int irq, void * dev_id)
fec_enet_tx(dev);
}
+ if (int_events & FEC_ENET_TS_TIMER) {
+ ret = IRQ_HANDLED;
+ if (fep->ptimer_present && fpp)
+ fpp->prtc++;
+ }
+
if (int_events & FEC_ENET_MII) {
ret = IRQ_HANDLED;
complete(&fep->mdio_done);
@@ -396,11 +429,14 @@ static void
fec_enet_tx(struct net_device *dev)
{
struct fec_enet_private *fep;
+ struct fec_ptp_private *fpp;
struct bufdesc *bdp;
unsigned short status;
+ unsigned long estatus;
struct sk_buff *skb;
fep = netdev_priv(dev);
+ fpp = fep->ptp_priv;
spin_lock(&fep->hw_lock);
bdp = fep->dirty_tx;
@@ -440,6 +476,19 @@ fec_enet_tx(struct net_device *dev)
if (status & BD_ENET_TX_DEF)
dev->stats.collisions++;
+#if defined(CONFIG_ENHANCED_BD)
+ if (fep->ptimer_present) {
+ estatus = bdp->cbd_esc;
+ if (estatus & BD_ENET_TX_TS)
+ fec_ptp_store_txstamp(fpp, skb, bdp);
+ }
+#elif defined(CONFIG_IN_BAND)
+ if (fep->ptimer_present) {
+ if (status & BD_ENET_TX_PTP)
+ fec_ptp_store_txstamp(fpp, skb, bdp);
+ }
+#endif
+
/* Free the sk buffer associated with this last transmit */
dev_kfree_skb_any(skb);
fep->tx_skbuff[fep->skb_dirty] = NULL;
@@ -473,6 +522,7 @@ static void
fec_enet_rx(struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
+ struct fec_ptp_private *fpp = fep->ptp_priv;
const struct platform_device_id *id_entry =
platform_get_device_id(fep->pdev);
struct bufdesc *bdp;
@@ -556,6 +606,9 @@ fec_enet_rx(struct net_device *dev)
skb_reserve(skb, NET_IP_ALIGN);
skb_put(skb, pkt_len - 4); /* Make room */
skb_copy_to_linear_data(skb, data, pkt_len - 4);
+ /* 1588 messeage TS handle */
+ if (fep->ptimer_present)
+ fec_ptp_store_rxstamp(fpp, skb, bdp);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
}
@@ -569,6 +622,11 @@ rx_processing_done:
/* Mark the buffer empty */
status |= BD_ENET_RX_EMPTY;
bdp->cbd_sc = status;
+#ifdef CONFIG_ENHANCED_BD
+ bdp->cbd_esc = BD_ENET_RX_INT;
+ bdp->cbd_prot = 0;
+ bdp->cbd_bdu = 0;
+#endif
/* Update BD pointer to next entry */
if (status & BD_ENET_RX_WRAP)
@@ -990,6 +1048,9 @@ static int fec_enet_alloc_buffers(struct net_device *dev)
bdp->cbd_bufaddr = dma_map_single(&dev->dev, skb->data,
FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
bdp->cbd_sc = BD_ENET_RX_EMPTY;
+#ifdef CONFIG_ENHANCED_BD
+ bdp->cbd_esc = BD_ENET_RX_INT;
+#endif
bdp++;
}
@@ -1003,6 +1064,9 @@ static int fec_enet_alloc_buffers(struct net_device *dev)
bdp->cbd_sc = 0;
bdp->cbd_bufaddr = 0;
+#ifdef CONFIG_ENHANCED_BD
+ bdp->cbd_esc = BD_ENET_TX_INT;
+#endif
bdp++;
}
@@ -1253,8 +1317,8 @@ fec_restart(struct net_device *dev, int duplex)
struct fec_enet_private *fep = netdev_priv(dev);
const struct platform_device_id *id_entry =
platform_get_device_id(fep->pdev);
- int i;
- u32 val, temp_mac[2];
+ int i, ret;
+ u32 val, temp_mac[2], reg = 0;
/* Whack a reset. We should wait for this. */
writel(1, fep->hwp + FEC_ECNTRL);
@@ -1338,7 +1402,22 @@ fec_restart(struct net_device *dev, int duplex)
val |= (1 << 9);
writel(val, fep->hwp + FEC_R_CNTRL);
- } else {
+
+ if (fep->ptimer_present) {
+ /* Set Timer count */
+ ret = fec_ptp_start(fep->ptp_priv);
+ if (ret) {
+ fep->ptimer_present = 0;
+ reg = 0x0;
+ } else
+#if defined(CONFIG_SOC_IMX28) || defined(CONFIG_ARCH_MX6)
+ reg = 0x00000010;
+#else
+ reg = 0x0;
+#endif
+ } else
+ reg = 0x0;
+
#ifdef FEC_MIIGSK_ENR
if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) {
/* disable the gasket and wait */
@@ -1359,7 +1438,7 @@ fec_restart(struct net_device *dev, int duplex)
}
/* ENET enable */
- val = (0x1 << 1);
+ val = reg | (0x1 << 1);
/* if phy work at 1G mode, set ENET RGMII speed to 1G */
if (fep->phy_dev && (fep->phy_dev->supported &
@@ -1404,6 +1483,8 @@ fec_stop(struct net_device *dev)
writel(2, fep->hwp + FEC_ECNTRL);
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+ if (fep->ptimer_present)
+ fec_ptp_stop(fep->ptp_priv);
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
}
@@ -1480,6 +1561,18 @@ fec_probe(struct platform_device *pdev)
if (ret)
goto failed_mii_init;
+ if (fec_ptp_malloc_priv(&(fep->ptp_priv))) {
+ if (fep->ptp_priv) {
+ fep->ptp_priv->hwp = fep->hwp;
+ ret = fec_ptp_init(fep->ptp_priv, pdev->id);
+ if (ret)
+ printk(KERN_WARNING "IEEE1588: ptp-timer is unavailable\n");
+ else
+ fep->ptimer_present = 1;
+ } else
+ printk(KERN_ERR "IEEE1588: failed to malloc memory\n");
+ }
+
/* Carrier starts down, phylib will bring it up */
netif_carrier_off(ndev);
@@ -1491,6 +1584,9 @@ fec_probe(struct platform_device *pdev)
failed_register:
fec_enet_mii_remove(fep);
+ if (fep->ptimer_present)
+ fec_ptp_cleanup(fep->ptp_priv);
+ kfree(fep->ptp_priv);
failed_mii_init:
failed_init:
clk_disable(fep->clk);
@@ -1522,6 +1618,9 @@ fec_drv_remove(struct platform_device *pdev)
clk_disable(fep->clk);
clk_put(fep->clk);
iounmap((void __iomem *)ndev->base_addr);
+ if (fep->ptimer_present)
+ fec_ptp_cleanup(fep->ptp_priv);
+ kfree(fep->ptp_priv);
unregister_netdev(ndev);
free_netdev(ndev);
return 0;