diff options
author | Fugang Duan <B38611@freescale.com> | 2011-11-02 12:26:49 +0800 |
---|---|---|
committer | Eric Miao <eric.miao@canonical.com> | 2011-11-10 07:38:58 +0800 |
commit | f2c1d06e7c1fee70929a7ab9cbd1c080ba412b18 (patch) | |
tree | ecd7c0f23066226bf45576d84ab4c95746d88ba5 /drivers/net/fec.c | |
parent | 2f47b52e635bd1e7aa6b54072093173ea4819725 (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.c | 107 |
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; |