mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-12-27 02:16:49 +02:00
1258 lines
32 KiB
Diff
1258 lines
32 KiB
Diff
|
From 67d56859d24864af530506c76523f0fc3c5cb502 Mon Sep 17 00:00:00 2001
|
||
|
From: Alison Wang <b18965@freescale.com>
|
||
|
Date: Thu, 4 Aug 2011 09:59:44 +0800
|
||
|
Subject: [PATCH 20/52] Add dual FEC 1588 timer support
|
||
|
|
||
|
Add Modelo dual FEC 1588 function with IXXXAT statck.
|
||
|
|
||
|
Signed-off-by: Alison Wang <b18965@freescale.com>
|
||
|
---
|
||
|
drivers/net/Kconfig | 6 +
|
||
|
drivers/net/Makefile | 3 +
|
||
|
drivers/net/fec.c | 153 ++++++++++++-
|
||
|
drivers/net/fec.h | 25 ++
|
||
|
drivers/net/fec_1588.c | 626 ++++++++++++++++++++++++++++++++++++++++++++++++
|
||
|
drivers/net/fec_1588.h | 195 +++++++++++++++
|
||
|
6 files changed, 1006 insertions(+), 2 deletions(-)
|
||
|
create mode 100644 drivers/net/fec_1588.c
|
||
|
create mode 100644 drivers/net/fec_1588.h
|
||
|
|
||
|
--- a/drivers/net/Kconfig
|
||
|
+++ b/drivers/net/Kconfig
|
||
|
@@ -1958,6 +1958,12 @@ config FEC2
|
||
|
Say Y here if you want to use the second built-in 10/100 Fast
|
||
|
ethernet controller on some Motorola ColdFire processors.
|
||
|
|
||
|
+config FEC_1588
|
||
|
+ bool "Enable 1588 interface(on some ColdFire designs)"
|
||
|
+ depends on M5441X && FEC
|
||
|
+ help
|
||
|
+ Say Y here if 1588 function is enabled.
|
||
|
+
|
||
|
config FEC_548x
|
||
|
tristate "MCF547x/MCF548x Fast Ethernet Controller support"
|
||
|
depends on M547X_8X
|
||
|
--- a/drivers/net/Makefile
|
||
|
+++ b/drivers/net/Makefile
|
||
|
@@ -123,6 +123,9 @@ obj-$(CONFIG_PCMCIA_PCNET) += 8390.o
|
||
|
obj-$(CONFIG_HP100) += hp100.o
|
||
|
obj-$(CONFIG_SMC9194) += smc9194.o
|
||
|
obj-$(CONFIG_FEC) += fec.o
|
||
|
+ifeq ($(CONFIG_FEC_1588), y)
|
||
|
+obj-$(CONFIG_FEC) += fec_1588.o
|
||
|
+endif
|
||
|
obj-$(CONFIG_FEC_548x) += fec_m547x.o
|
||
|
obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
|
||
|
ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
|
||
|
--- a/drivers/net/fec.c
|
||
|
+++ b/drivers/net/fec.c
|
||
|
@@ -53,6 +53,7 @@
|
||
|
#endif
|
||
|
|
||
|
#include "fec.h"
|
||
|
+#include "fec_1588.h"
|
||
|
|
||
|
#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28)
|
||
|
#define FEC_ALIGNMENT 0xf
|
||
|
@@ -135,8 +136,15 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet
|
||
|
#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)
|
||
|
+#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.
|
||
|
*/
|
||
|
@@ -209,6 +217,10 @@ struct fec_enet_private {
|
||
|
int link;
|
||
|
int full_duplex;
|
||
|
struct completion mdio_done;
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ struct fec_ptp_private *ptp_priv;
|
||
|
+ uint ptimer_present;
|
||
|
+#endif
|
||
|
};
|
||
|
|
||
|
static irqreturn_t fec_enet_interrupt(int irq, void * dev_id);
|
||
|
@@ -252,6 +264,9 @@ fec_enet_start_xmit(struct sk_buff *skb,
|
||
|
struct bufdesc *bdp;
|
||
|
void *bufaddr;
|
||
|
unsigned short status;
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ unsigned long estatus;
|
||
|
+#endif
|
||
|
unsigned long flags;
|
||
|
|
||
|
if (!fep->link) {
|
||
|
@@ -293,6 +308,17 @@ fec_enet_start_xmit(struct sk_buff *skb,
|
||
|
bufaddr = fep->tx_bounce[index];
|
||
|
}
|
||
|
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ if (fep->ptimer_present) {
|
||
|
+ if (fec_ptp_do_txstamp(skb))
|
||
|
+ estatus = BD_ENET_TX_TS;
|
||
|
+ else
|
||
|
+ estatus = 0;
|
||
|
+
|
||
|
+ 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
|
||
|
@@ -357,6 +383,9 @@ fec_enet_interrupt(int irq, void * dev_i
|
||
|
{
|
||
|
struct net_device *dev = dev_id;
|
||
|
struct fec_enet_private *fep = netdev_priv(dev);
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ struct fec_ptp_private *fpp = fep->ptp_priv;
|
||
|
+#endif
|
||
|
uint int_events;
|
||
|
irqreturn_t ret = IRQ_NONE;
|
||
|
|
||
|
@@ -364,6 +393,10 @@ fec_enet_interrupt(int irq, void * dev_i
|
||
|
int_events = readl(fep->hwp + FEC_IEVENT);
|
||
|
writel(int_events, fep->hwp + FEC_IEVENT);
|
||
|
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ if (__raw_readb(MCF_DTIM1_DTER) & MCF_DTIM_DTER_REF)
|
||
|
+ __raw_writeb(MCF_DTIM_DTER_REF, MCF_DTIM1_DTER);
|
||
|
+#endif
|
||
|
if (int_events & FEC_ENET_RXF) {
|
||
|
ret = IRQ_HANDLED;
|
||
|
fec_enet_rx(dev);
|
||
|
@@ -378,6 +411,19 @@ fec_enet_interrupt(int irq, void * dev_i
|
||
|
fec_enet_tx(dev);
|
||
|
}
|
||
|
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ if (int_events & FEC_ENET_TS_AVAIL) {
|
||
|
+ ret = IRQ_HANDLED;
|
||
|
+ fec_ptp_store_txstamp(fep->ptp_priv);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (int_events & FEC_ENET_TS_TIMER) {
|
||
|
+ ret = IRQ_HANDLED;
|
||
|
+ if (fep->ptimer_present)
|
||
|
+ fpp->prtc++;
|
||
|
+ }
|
||
|
+#endif
|
||
|
+
|
||
|
if (int_events & FEC_ENET_MII) {
|
||
|
ret = IRQ_HANDLED;
|
||
|
complete(&fep->mdio_done);
|
||
|
@@ -394,6 +440,9 @@ fec_enet_tx(struct net_device *dev)
|
||
|
struct fec_enet_private *fep;
|
||
|
struct bufdesc *bdp;
|
||
|
unsigned short status;
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ unsigned long estatus;
|
||
|
+#endif
|
||
|
struct sk_buff *skb;
|
||
|
|
||
|
fep = netdev_priv(dev);
|
||
|
@@ -437,6 +486,13 @@ fec_enet_tx(struct net_device *dev)
|
||
|
if (status & BD_ENET_TX_DEF)
|
||
|
dev->stats.collisions++;
|
||
|
|
||
|
+#if defined(CONFIG_FEC_1588)
|
||
|
+ if (fep->ptimer_present) {
|
||
|
+ estatus = bdp->cbd_esc;
|
||
|
+ if (estatus & BD_ENET_TX_TS)
|
||
|
+ fec_ptp_store_txstamp(fep->ptp_priv);
|
||
|
+ }
|
||
|
+#endif
|
||
|
/* Free the sk buffer associated with this last transmit */
|
||
|
dev_kfree_skb_any(skb);
|
||
|
fep->tx_skbuff[fep->skb_dirty] = NULL;
|
||
|
@@ -470,6 +526,9 @@ static void
|
||
|
fec_enet_rx(struct net_device *dev)
|
||
|
{
|
||
|
struct fec_enet_private *fep = netdev_priv(dev);
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ struct fec_ptp_private *fpp = fep->ptp_priv;
|
||
|
+#endif
|
||
|
const struct platform_device_id *id_entry =
|
||
|
platform_get_device_id(fep->pdev);
|
||
|
struct bufdesc *bdp;
|
||
|
@@ -554,6 +613,12 @@ fec_enet_rx(struct net_device *dev)
|
||
|
skb_put(skb, pkt_len - 4); /* Make room */
|
||
|
skb_copy_to_linear_data(skb, data, pkt_len - 4);
|
||
|
skb->protocol = eth_type_trans(skb, dev);
|
||
|
+
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ /* 1588 messeage TS handle */
|
||
|
+ if (fep->ptimer_present)
|
||
|
+ fec_ptp_store_rxstamp(fpp, skb, bdp);
|
||
|
+#endif
|
||
|
netif_rx(skb);
|
||
|
}
|
||
|
|
||
|
@@ -567,6 +632,11 @@ rx_processing_done:
|
||
|
status |= BD_ENET_RX_EMPTY;
|
||
|
bdp->cbd_sc = status;
|
||
|
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ 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)
|
||
|
bdp = fep->rx_bd_base;
|
||
|
@@ -669,8 +739,11 @@ static void fec_enet_adjust_link(struct
|
||
|
fec_stop(dev);
|
||
|
|
||
|
if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
|
||
|
- writel(2, fep->hwp + FEC_ECNTRL);
|
||
|
-
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ writel(0x00000012, fep->hwp + FEC_ECNTRL);
|
||
|
+#else
|
||
|
+ writel(0x00000002, fep->hwp + FEC_ECNTRL);
|
||
|
+#endif
|
||
|
status_change = 1;
|
||
|
}
|
||
|
|
||
|
@@ -983,6 +1056,10 @@ static int fec_enet_alloc_buffers(struct
|
||
|
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_FEC_1588
|
||
|
+ bdp->cbd_esc = BD_ENET_RX_INT;
|
||
|
+#endif
|
||
|
bdp++;
|
||
|
}
|
||
|
|
||
|
@@ -996,6 +1073,9 @@ static int fec_enet_alloc_buffers(struct
|
||
|
|
||
|
bdp->cbd_sc = 0;
|
||
|
bdp->cbd_bufaddr = 0;
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ bdp->cbd_esc = BD_ENET_TX_INT;
|
||
|
+#endif
|
||
|
bdp++;
|
||
|
}
|
||
|
|
||
|
@@ -1256,8 +1336,12 @@ fec_restart(struct net_device *dev, int
|
||
|
writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
|
||
|
}
|
||
|
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ writel(0x7fff8000, fep->hwp + FEC_IEVENT);
|
||
|
+#else
|
||
|
/* Clear any outstanding interrupt. */
|
||
|
writel(0xffc00000, fep->hwp + FEC_IEVENT);
|
||
|
+#endif
|
||
|
|
||
|
/* Reset all multicast. */
|
||
|
writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
|
||
|
@@ -1342,8 +1426,25 @@ fec_restart(struct net_device *dev, int
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ if (fep->ptimer_present) {
|
||
|
+ int ret;
|
||
|
+ /* Set Timer count */
|
||
|
+ ret = fec_ptp_start(fep->ptp_priv);
|
||
|
+ if (ret) {
|
||
|
+ fep->ptimer_present = 0;
|
||
|
+ writel(2, fep->hwp + FEC_ECNTRL);
|
||
|
+ } else {
|
||
|
+ val = readl(fep->hwp + FEC_ECNTRL);
|
||
|
+ val |= 0x00000012;
|
||
|
+ writel(val, fep->hwp + FEC_ECNTRL);
|
||
|
+ }
|
||
|
+ } else
|
||
|
+ writel(2, fep->hwp + FEC_ECNTRL);
|
||
|
+#else
|
||
|
/* And last, enable the transmit and receive processing */
|
||
|
writel(2, fep->hwp + FEC_ECNTRL);
|
||
|
+#endif
|
||
|
writel(0, fep->hwp + FEC_R_DES_ACTIVE);
|
||
|
|
||
|
/* Enable interrupts we wish to service */
|
||
|
@@ -1367,6 +1468,10 @@ fec_stop(struct net_device *dev)
|
||
|
writel(1, fep->hwp + FEC_ECNTRL);
|
||
|
udelay(10);
|
||
|
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ if (fep->ptimer_present)
|
||
|
+ fec_ptp_stop(fep->ptp_priv);
|
||
|
+#endif
|
||
|
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
|
||
|
}
|
||
|
|
||
|
@@ -1428,6 +1533,24 @@ fec_probe(struct platform_device *pdev)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ i = (pdev->id) ? (64 + 64 + 64 + 7) : (64 + 64 + 64);
|
||
|
+ if (request_irq(i + 48, fec_enet_interrupt, IRQF_DISABLED,
|
||
|
+ "1588 TS AVAIL", ndev) != 0)
|
||
|
+ printk(KERN_ERR "FEC: Could not alloc FEC %x 1588 TS AVAIL "
|
||
|
+ "IRQ(%d)!\n", pdev->id, i + 48);
|
||
|
+
|
||
|
+ if (pdev->id == 0) {
|
||
|
+ printk("setup TS timer interrupt through DMA timer1\n");
|
||
|
+ __raw_writew(MCF_DTIM_DTMR_RST_RST, MCF_DTIM1_DTMR);
|
||
|
+
|
||
|
+ if (request_irq(64 + 33, fec_enet_interrupt, IRQF_DISABLED,
|
||
|
+ "1588 TS TIMER", ndev) != 0)
|
||
|
+ printk(KERN_ERR "FEC: Could not alloc FEC %x 1588 TS"
|
||
|
+ " TIMER IRQ(%d)!\n", pdev->id, 64 + 33);
|
||
|
+ }
|
||
|
+#endif
|
||
|
+
|
||
|
fep->clk = clk_get(&pdev->dev, "fec_clk");
|
||
|
if (IS_ERR(fep->clk)) {
|
||
|
ret = PTR_ERR(fep->clk);
|
||
|
@@ -1443,6 +1566,20 @@ fec_probe(struct platform_device *pdev)
|
||
|
if (ret)
|
||
|
goto failed_mii_init;
|
||
|
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ fep->ptp_priv = kzalloc(sizeof(struct fec_ptp_private),
|
||
|
+ GFP_KERNEL);
|
||
|
+ if (fep->ptp_priv) {
|
||
|
+ fep->ptp_priv->hwp = fep->hwp;
|
||
|
+ ret = fec_ptp_init(fep->ptp_priv, pdev->id);
|
||
|
+ if (ret)
|
||
|
+ printk(KERN_ERR "IEEE1588: ptp-timer init failed\n");
|
||
|
+ else
|
||
|
+ fep->ptimer_present = 1;
|
||
|
+ } else
|
||
|
+ printk(KERN_ERR "IEEE1588: failed to malloc memory\n");
|
||
|
+#endif
|
||
|
+
|
||
|
/* Carrier starts down, phylib will bring it up */
|
||
|
netif_carrier_off(ndev);
|
||
|
|
||
|
@@ -1454,6 +1591,12 @@ fec_probe(struct platform_device *pdev)
|
||
|
|
||
|
failed_register:
|
||
|
fec_enet_mii_remove(fep);
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ if (fep->ptimer_present)
|
||
|
+ fec_ptp_cleanup(fep->ptp_priv);
|
||
|
+
|
||
|
+ kfree(fep->ptp_priv);
|
||
|
+#endif
|
||
|
failed_mii_init:
|
||
|
failed_init:
|
||
|
clk_disable(fep->clk);
|
||
|
@@ -1485,6 +1628,12 @@ fec_drv_remove(struct platform_device *p
|
||
|
clk_disable(fep->clk);
|
||
|
clk_put(fep->clk);
|
||
|
iounmap((void __iomem *)ndev->base_addr);
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+ if (fep->ptimer_present)
|
||
|
+ fec_ptp_cleanup(fep->ptp_priv);
|
||
|
+
|
||
|
+ kfree(fep->ptp_priv);
|
||
|
+#endif
|
||
|
unregister_netdev(ndev);
|
||
|
free_netdev(ndev);
|
||
|
return 0;
|
||
|
--- a/drivers/net/fec.h
|
||
|
+++ b/drivers/net/fec.h
|
||
|
@@ -50,6 +50,16 @@
|
||
|
#define FEC_MIIGSK_CFGR 0x300 /* MIIGSK Configuration reg */
|
||
|
#define FEC_MIIGSK_ENR 0x308 /* MIIGSK Enable reg */
|
||
|
|
||
|
+#if defined(CONFIG_FEC_1588)
|
||
|
+#define FEC_ATIME_CTRL 0x400
|
||
|
+#define FEC_ATIME 0x404
|
||
|
+#define FEC_ATIME_EVT_OFFSET 0x408
|
||
|
+#define FEC_ATIME_EVT_PERIOD 0x40c
|
||
|
+#define FEC_ATIME_CORR 0x410
|
||
|
+#define FEC_ATIME_INC 0x414
|
||
|
+#define FEC_TS_TIMESTAMP 0x418
|
||
|
+#endif
|
||
|
+
|
||
|
#else
|
||
|
|
||
|
#define FEC_ECNTRL 0x000 /* Ethernet control reg */
|
||
|
@@ -78,6 +88,9 @@
|
||
|
|
||
|
#endif /* CONFIG_M5272 */
|
||
|
|
||
|
+#if defined(CONFIG_FEC_1588)
|
||
|
+#define FEC_ENHANCED_MODE 1
|
||
|
+#endif
|
||
|
|
||
|
/*
|
||
|
* Define the buffer descriptor structure.
|
||
|
@@ -93,6 +106,14 @@ struct bufdesc {
|
||
|
unsigned short cbd_sc; /* Control and status info */
|
||
|
unsigned short cbd_datlen; /* Data length */
|
||
|
unsigned long cbd_bufaddr; /* Buffer address */
|
||
|
+
|
||
|
+#ifdef FEC_ENHANCED_MODE
|
||
|
+ unsigned long cbd_esc;
|
||
|
+ unsigned long cbd_prot;
|
||
|
+ unsigned long cbd_bdu;
|
||
|
+ unsigned long ts;
|
||
|
+ unsigned short res0[4];
|
||
|
+#endif
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
@@ -128,6 +149,7 @@ struct bufdesc {
|
||
|
#define BD_ENET_RX_OV ((ushort)0x0002)
|
||
|
#define BD_ENET_RX_CL ((ushort)0x0001)
|
||
|
#define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */
|
||
|
+#define BD_ENET_RX_INT 0x00800000
|
||
|
|
||
|
/* Buffer descriptor control/status used by Ethernet transmit.
|
||
|
*/
|
||
|
@@ -146,6 +168,9 @@ struct bufdesc {
|
||
|
#define BD_ENET_TX_CSL ((ushort)0x0001)
|
||
|
#define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */
|
||
|
|
||
|
+#define BD_ENET_TX_TS 0x20000000
|
||
|
+#define BD_ENET_TX_INT 0x40000000
|
||
|
+#define BD_ENET_TX_BDU 0x80000000
|
||
|
|
||
|
/****************************************************************************/
|
||
|
#endif /* FEC_H */
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/net/fec_1588.c
|
||
|
@@ -0,0 +1,626 @@
|
||
|
+/*
|
||
|
+ * drivers/net/fec_1588.c
|
||
|
+ *
|
||
|
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
|
||
|
+ * Copyright (C) 2009 IXXAT Automation, GmbH
|
||
|
+ *
|
||
|
+ * FEC Ethernet Driver -- IEEE 1588 interface functionality
|
||
|
+ *
|
||
|
+ * This program is free software; you can redistribute it and/or modify
|
||
|
+ * it under the terms of the GNU General Public License as published by
|
||
|
+ * the Free Software Foundation; either version 2 of the License, or
|
||
|
+ * (at your option) any later version.
|
||
|
+ *
|
||
|
+ * This program is distributed in the hope that it will be useful,
|
||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
+ * GNU General Public License for more details.
|
||
|
+ *
|
||
|
+ * You should have received a copy of the GNU General Public License along
|
||
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#include <linux/io.h>
|
||
|
+#include <linux/device.h>
|
||
|
+#include <linux/fs.h>
|
||
|
+#include <linux/vmalloc.h>
|
||
|
+#include <linux/spinlock.h>
|
||
|
+#include <linux/ip.h>
|
||
|
+#include <linux/udp.h>
|
||
|
+#include <asm/mcf5441x_ccm.h>
|
||
|
+#include <asm/mcf5441x_dtim.h>
|
||
|
+#include <asm/mcfsim.h>
|
||
|
+#include "fec_1588.h"
|
||
|
+
|
||
|
+static DECLARE_WAIT_QUEUE_HEAD(ptp_rx_ts_wait);
|
||
|
+#define PTP_GET_RX_TIMEOUT (HZ/10)
|
||
|
+#define COLDFIRE_DTIM1_INT (64+32+1)
|
||
|
+
|
||
|
+static struct fec_ptp_private *ptp_private[2];
|
||
|
+
|
||
|
+static void init_DTIM1_for_1588(struct fec_ptp_private *priv)
|
||
|
+{
|
||
|
+ printk(KERN_INFO "Initializing DTIM1 for 1588 TS timer\n");
|
||
|
+
|
||
|
+ __raw_writew(MCF_DTIM_DTMR_RST_RST, MCF_DTIM1_DTMR);
|
||
|
+
|
||
|
+ /*Enable 1588*/
|
||
|
+
|
||
|
+ __raw_writeb(MCF_DTIM_DTXMR_1588EN, MCF_DTIM1_DTXMR);
|
||
|
+
|
||
|
+ /*Compare to the 1588 timerbase*/
|
||
|
+ __raw_writel(FEC_T_PERIOD_ONE_SEC - FEC_T_INC_40MHZ, MCF_DTIM1_DTRR);
|
||
|
+
|
||
|
+ __raw_writeb(MCF_DTIM_DTER_REF, MCF_DTIM1_DTER);
|
||
|
+
|
||
|
+ MCF_GPIO_PAR_TIMER = (MCF_GPIO_PAR_TIMER & MCF_GPIO_PAR_TIMER_T1IN_MASK)
|
||
|
+ | MCF_GPIO_PAR_TIMER_T1IN_T1OUT;
|
||
|
+}
|
||
|
+
|
||
|
+static void start_DTIM1(void)
|
||
|
+{
|
||
|
+ __raw_writew(MCF_DTIM_DTMR_RST_EN | MCF_DTIM_DTMR_ORRI |
|
||
|
+ MCF_DTIM_DTMR_OM, MCF_DTIM1_DTMR);
|
||
|
+}
|
||
|
+
|
||
|
+static void stop_DTIM1(void)
|
||
|
+{
|
||
|
+ __raw_writew(MCF_DTIM_DTMR_RST_RST, MCF_DTIM1_DTMR);
|
||
|
+}
|
||
|
+
|
||
|
+/* Alloc the ring resource */
|
||
|
+static int fec_ptp_init_circ(struct circ_buf *ptp_buf)
|
||
|
+{
|
||
|
+ ptp_buf->buf = vmalloc(DEFAULT_PTP_RX_BUF_SZ *
|
||
|
+ sizeof(struct fec_ptp_data_t));
|
||
|
+
|
||
|
+ if (!ptp_buf->buf)
|
||
|
+ return 1;
|
||
|
+ ptp_buf->head = 0;
|
||
|
+ ptp_buf->tail = 0;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static inline int fec_ptp_calc_index(int size, int curr_index, int offset)
|
||
|
+{
|
||
|
+ return (curr_index + offset) % size;
|
||
|
+}
|
||
|
+
|
||
|
+static int fec_ptp_is_empty(struct circ_buf *buf)
|
||
|
+{
|
||
|
+ return (buf->head == buf->tail);
|
||
|
+}
|
||
|
+
|
||
|
+static int fec_ptp_nelems(struct circ_buf *buf)
|
||
|
+{
|
||
|
+ const int front = buf->head;
|
||
|
+ const int end = buf->tail;
|
||
|
+ const int size = DEFAULT_PTP_RX_BUF_SZ;
|
||
|
+ int n_items;
|
||
|
+
|
||
|
+ if (end > front)
|
||
|
+ n_items = end - front;
|
||
|
+ else if (end < front)
|
||
|
+ n_items = size - (front - end);
|
||
|
+ else
|
||
|
+ n_items = 0;
|
||
|
+
|
||
|
+ return n_items;
|
||
|
+}
|
||
|
+
|
||
|
+static int fec_ptp_is_full(struct circ_buf *buf)
|
||
|
+{
|
||
|
+ if (fec_ptp_nelems(buf) ==
|
||
|
+ (DEFAULT_PTP_RX_BUF_SZ - 1))
|
||
|
+ return 1;
|
||
|
+ else
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int fec_ptp_insert(struct circ_buf *ptp_buf,
|
||
|
+ struct fec_ptp_data_t *data,
|
||
|
+ struct fec_ptp_private *priv)
|
||
|
+{
|
||
|
+ struct fec_ptp_data_t *tmp;
|
||
|
+
|
||
|
+ if (fec_ptp_is_full(ptp_buf))
|
||
|
+ return 1;
|
||
|
+
|
||
|
+ spin_lock(&priv->ptp_lock);
|
||
|
+ tmp = (struct fec_ptp_data_t *)(ptp_buf->buf) + ptp_buf->tail;
|
||
|
+
|
||
|
+ tmp->key = data->key;
|
||
|
+ tmp->ts_time.sec = data->ts_time.sec;
|
||
|
+ tmp->ts_time.nsec = data->ts_time.nsec;
|
||
|
+
|
||
|
+ ptp_buf->tail = fec_ptp_calc_index(DEFAULT_PTP_RX_BUF_SZ,
|
||
|
+ ptp_buf->tail, 1);
|
||
|
+ spin_unlock(&priv->ptp_lock);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int fec_ptp_find_and_remove(struct circ_buf *ptp_buf,
|
||
|
+ int key,
|
||
|
+ struct fec_ptp_data_t *data,
|
||
|
+ struct fec_ptp_private *priv)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+ int size = DEFAULT_PTP_RX_BUF_SZ;
|
||
|
+ int end = ptp_buf->tail;
|
||
|
+ unsigned long flags;
|
||
|
+ struct fec_ptp_data_t *tmp;
|
||
|
+
|
||
|
+ if (fec_ptp_is_empty(ptp_buf))
|
||
|
+ return 1;
|
||
|
+
|
||
|
+ i = ptp_buf->head;
|
||
|
+ while (i != end) {
|
||
|
+ tmp = (struct fec_ptp_data_t *)(ptp_buf->buf) + i;
|
||
|
+ if (tmp->key == key)
|
||
|
+ break;
|
||
|
+ i = fec_ptp_calc_index(size, i, 1);
|
||
|
+ }
|
||
|
+
|
||
|
+ spin_lock_irqsave(&priv->ptp_lock, flags);
|
||
|
+ if (i == end) {
|
||
|
+ ptp_buf->head = end;
|
||
|
+ spin_unlock_irqrestore(&priv->ptp_lock, flags);
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ data->ts_time.sec = tmp->ts_time.sec;
|
||
|
+ data->ts_time.nsec = tmp->ts_time.nsec;
|
||
|
+
|
||
|
+ ptp_buf->head = fec_ptp_calc_index(size, i, 1);
|
||
|
+ spin_unlock_irqrestore(&priv->ptp_lock, flags);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/* 1588 Module intialization */
|
||
|
+int fec_ptp_start(struct fec_ptp_private *priv)
|
||
|
+{
|
||
|
+ struct fec_ptp_private *fpp = priv;
|
||
|
+
|
||
|
+ MCF_CCM_MISCCR3 = 0x0000;
|
||
|
+
|
||
|
+ init_DTIM1_for_1588(priv);
|
||
|
+
|
||
|
+ /* Select 1588 Timer source and enable module for starting Tmr Clock */
|
||
|
+ fec_writel(FEC_T_CTRL_RESTART, fpp->hwp + FEC_ATIME_CTRL);
|
||
|
+ fec_writel(FEC_T_INC_40MHZ << FEC_T_INC_OFFSET,
|
||
|
+ fpp->hwp + FEC_ATIME_INC);
|
||
|
+ fec_writel(FEC_T_PERIOD_ONE_SEC, fpp->hwp + FEC_ATIME_EVT_PERIOD);
|
||
|
+ /* start counter */
|
||
|
+ fec_writel(FEC_T_CTRL_PERIOD_RST | FEC_T_CTRL_ENABLE |
|
||
|
+ FEC_T_CTRL_PINPER, fpp->hwp + FEC_ATIME_CTRL);
|
||
|
+
|
||
|
+ start_DTIM1();
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/* Cleanup routine for 1588 module.
|
||
|
+ * When PTP is disabled this routing is called */
|
||
|
+void fec_ptp_stop(struct fec_ptp_private *priv)
|
||
|
+{
|
||
|
+ struct fec_ptp_private *fpp = priv;
|
||
|
+
|
||
|
+ fec_writel(0, fpp->hwp + FEC_ATIME_CTRL);
|
||
|
+ fec_writel(FEC_T_CTRL_RESTART, fpp->hwp + FEC_ATIME_CTRL);
|
||
|
+ stop_DTIM1();
|
||
|
+}
|
||
|
+
|
||
|
+static void fec_get_curr_cnt(struct fec_ptp_private *priv,
|
||
|
+ struct ptp_rtc_time *curr_time)
|
||
|
+{
|
||
|
+ u32 tempval;
|
||
|
+
|
||
|
+ fec_writel(FEC_T_CTRL_CAPTURE, priv->hwp + FEC_ATIME_CTRL);
|
||
|
+ fec_writel(FEC_T_CTRL_CAPTURE, priv->hwp + FEC_ATIME_CTRL);
|
||
|
+ curr_time->rtc_time.nsec = fec_readl(priv->hwp + FEC_ATIME);
|
||
|
+ curr_time->rtc_time.sec = priv->prtc;
|
||
|
+
|
||
|
+ fec_writel(FEC_T_CTRL_CAPTURE, priv->hwp + FEC_ATIME_CTRL);
|
||
|
+ tempval = fec_readl(priv->hwp + FEC_ATIME);
|
||
|
+ if (tempval < curr_time->rtc_time.nsec) {
|
||
|
+ curr_time->rtc_time.nsec = tempval;
|
||
|
+ curr_time->rtc_time.sec = priv->prtc;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/* Set the 1588 timer counter registers */
|
||
|
+static void fec_set_1588cnt(struct fec_ptp_private *priv,
|
||
|
+ struct ptp_rtc_time *fec_time)
|
||
|
+{
|
||
|
+ u32 tempval;
|
||
|
+ unsigned long flags;
|
||
|
+
|
||
|
+ spin_lock_irqsave(&priv->cnt_lock, flags);
|
||
|
+
|
||
|
+ priv->prtc = fec_time->rtc_time.sec;
|
||
|
+
|
||
|
+ tempval = fec_time->rtc_time.nsec;
|
||
|
+ fec_writel(tempval, priv->hwp + FEC_ATIME);
|
||
|
+ spin_unlock_irqrestore(&priv->cnt_lock, flags);
|
||
|
+}
|
||
|
+
|
||
|
+/* Set the BD to ptp */
|
||
|
+int fec_ptp_do_txstamp(struct sk_buff *skb)
|
||
|
+{
|
||
|
+ struct iphdr *iph;
|
||
|
+ struct udphdr *udph;
|
||
|
+
|
||
|
+ if (skb->len > 44) {
|
||
|
+ /* Check if port is 319 for PTP Event, and check for UDP */
|
||
|
+ iph = ip_hdr(skb);
|
||
|
+ if (iph == NULL || iph->protocol != FEC_PACKET_TYPE_UDP)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ udph = udp_hdr(skb);
|
||
|
+ if (udph != NULL && ntohs(udph->source) == 319)
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+void fec_ptp_store_txstamp(struct fec_ptp_private *priv)
|
||
|
+{
|
||
|
+ struct fec_ptp_private *fpp = priv;
|
||
|
+ unsigned int reg;
|
||
|
+
|
||
|
+ reg = fec_readl(fpp->hwp + FEC_TS_TIMESTAMP);
|
||
|
+ fpp->txstamp.nsec = reg;
|
||
|
+ fpp->txstamp.sec = fpp->prtc;
|
||
|
+}
|
||
|
+
|
||
|
+void fec_ptp_store_rxstamp(struct fec_ptp_private *priv,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ struct bufdesc *bdp)
|
||
|
+{
|
||
|
+ int msg_type, seq_id, control;
|
||
|
+ struct fec_ptp_data_t tmp_rx_time;
|
||
|
+ struct fec_ptp_private *fpp = priv;
|
||
|
+ struct iphdr *iph;
|
||
|
+ struct udphdr *udph;
|
||
|
+
|
||
|
+ /* Check for UDP, and Check if port is 319 for PTP Event */
|
||
|
+ iph = (struct iphdr *)(skb->data + FEC_PTP_IP_OFFS);
|
||
|
+ if (iph->protocol != FEC_PACKET_TYPE_UDP)
|
||
|
+ return;
|
||
|
+
|
||
|
+ udph = (struct udphdr *)(skb->data + FEC_PTP_UDP_OFFS);
|
||
|
+ if (ntohs(udph->source) != 319)
|
||
|
+ return;
|
||
|
+
|
||
|
+ seq_id = *((u16 *)(skb->data + FEC_PTP_SEQ_ID_OFFS));
|
||
|
+ control = *((u8 *)(skb->data + FEC_PTP_CTRL_OFFS));
|
||
|
+
|
||
|
+ tmp_rx_time.key = ntohs(seq_id);
|
||
|
+ tmp_rx_time.ts_time.sec = fpp->prtc;
|
||
|
+ tmp_rx_time.ts_time.nsec = bdp->ts;
|
||
|
+
|
||
|
+ switch (control) {
|
||
|
+
|
||
|
+ case PTP_MSG_SYNC:
|
||
|
+ fec_ptp_insert(&(priv->rx_time_sync), &tmp_rx_time, priv);
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PTP_MSG_DEL_REQ:
|
||
|
+ fec_ptp_insert(&(priv->rx_time_del_req), &tmp_rx_time, priv);
|
||
|
+ break;
|
||
|
+
|
||
|
+ /* clear transportSpecific field*/
|
||
|
+ case PTP_MSG_ALL_OTHER:
|
||
|
+ msg_type = (*((u8 *)(skb->data +
|
||
|
+ FEC_PTP_MSG_TYPE_OFFS))) & 0x0F;
|
||
|
+ switch (msg_type) {
|
||
|
+ case PTP_MSG_P_DEL_REQ:
|
||
|
+ fec_ptp_insert(&(priv->rx_time_pdel_req),
|
||
|
+ &tmp_rx_time, priv);
|
||
|
+ break;
|
||
|
+ case PTP_MSG_P_DEL_RESP:
|
||
|
+ fec_ptp_insert(&(priv->rx_time_pdel_resp),
|
||
|
+ &tmp_rx_time, priv);
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ wake_up_interruptible(&ptp_rx_ts_wait);
|
||
|
+}
|
||
|
+
|
||
|
+static void fec_get_tx_timestamp(struct fec_ptp_private *priv,
|
||
|
+ struct ptp_time *tx_time)
|
||
|
+{
|
||
|
+ tx_time->sec = priv->txstamp.sec;
|
||
|
+ tx_time->nsec = priv->txstamp.nsec;
|
||
|
+}
|
||
|
+
|
||
|
+static uint8_t fec_get_rx_time(struct fec_ptp_private *priv,
|
||
|
+ struct ptp_ts_data *pts,
|
||
|
+ struct ptp_time *rx_time)
|
||
|
+{
|
||
|
+ struct fec_ptp_data_t tmp;
|
||
|
+ int key, flag;
|
||
|
+ u8 mode;
|
||
|
+
|
||
|
+ key = pts->seq_id;
|
||
|
+ mode = pts->message_type;
|
||
|
+ switch (mode) {
|
||
|
+ case PTP_MSG_SYNC:
|
||
|
+ flag = fec_ptp_find_and_remove(&(priv->rx_time_sync),
|
||
|
+ key, &tmp, priv);
|
||
|
+ break;
|
||
|
+ case PTP_MSG_DEL_REQ:
|
||
|
+ flag = fec_ptp_find_and_remove(&(priv->rx_time_del_req),
|
||
|
+ key, &tmp, priv);
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PTP_MSG_P_DEL_REQ:
|
||
|
+ flag = fec_ptp_find_and_remove(&(priv->rx_time_pdel_req),
|
||
|
+ key, &tmp, priv);
|
||
|
+ break;
|
||
|
+ case PTP_MSG_P_DEL_RESP:
|
||
|
+ flag = fec_ptp_find_and_remove(&(priv->rx_time_pdel_resp),
|
||
|
+ key, &tmp, priv);
|
||
|
+ break;
|
||
|
+
|
||
|
+ default:
|
||
|
+ flag = 1;
|
||
|
+ printk(KERN_ERR "ERROR\n");
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!flag) {
|
||
|
+ rx_time->sec = tmp.ts_time.sec;
|
||
|
+ rx_time->nsec = tmp.ts_time.nsec;
|
||
|
+ return 0;
|
||
|
+ } else {
|
||
|
+ wait_event_interruptible_timeout(ptp_rx_ts_wait, 0,
|
||
|
+ PTP_GET_RX_TIMEOUT);
|
||
|
+
|
||
|
+ switch (mode) {
|
||
|
+ case PTP_MSG_SYNC:
|
||
|
+ flag = fec_ptp_find_and_remove(&(priv->rx_time_sync),
|
||
|
+ key, &tmp, priv);
|
||
|
+ break;
|
||
|
+ case PTP_MSG_DEL_REQ:
|
||
|
+ flag = fec_ptp_find_and_remove(
|
||
|
+ &(priv->rx_time_del_req), key, &tmp, priv);
|
||
|
+ break;
|
||
|
+ case PTP_MSG_P_DEL_REQ:
|
||
|
+ flag = fec_ptp_find_and_remove(
|
||
|
+ &(priv->rx_time_pdel_req), key, &tmp, priv);
|
||
|
+ break;
|
||
|
+ case PTP_MSG_P_DEL_RESP:
|
||
|
+ flag = fec_ptp_find_and_remove(
|
||
|
+ &(priv->rx_time_pdel_resp), key, &tmp, priv);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (flag == 0) {
|
||
|
+ rx_time->sec = tmp.ts_time.sec;
|
||
|
+ rx_time->nsec = tmp.ts_time.nsec;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void fec_handle_ptpdrift(
|
||
|
+ struct ptp_set_comp *comp,
|
||
|
+ struct ptp_time_correct *ptc)
|
||
|
+{
|
||
|
+ u32 ndrift;
|
||
|
+ u32 i;
|
||
|
+ u32 tmp, tmp_ns, tmp_prid;
|
||
|
+ u32 min_ns, min_prid, miss_ns;
|
||
|
+
|
||
|
+ ndrift = comp->drift;
|
||
|
+ if (ndrift == 0) {
|
||
|
+ ptc->corr_inc = 0;
|
||
|
+ ptc->corr_period = 0;
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (ndrift >= FEC_ATIME_40MHZ) {
|
||
|
+ ptc->corr_inc = (u32)(ndrift / FEC_ATIME_40MHZ);
|
||
|
+ ptc->corr_period = 1;
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ min_ns = 1;
|
||
|
+ tmp = FEC_ATIME_40MHZ % ndrift;
|
||
|
+ tmp_prid = (u32)(FEC_ATIME_40MHZ / ndrift);
|
||
|
+ min_prid = tmp_prid;
|
||
|
+ miss_ns = tmp / tmp_prid;
|
||
|
+ for (i = 2; i <= FEC_T_INC_40MHZ; i++) {
|
||
|
+ tmp = (FEC_ATIME_40MHZ * i) % ndrift;
|
||
|
+ tmp_prid = (FEC_ATIME_40MHZ * i) / ndrift;
|
||
|
+ tmp_ns = tmp / tmp_prid;
|
||
|
+ if (tmp_ns <= 10) {
|
||
|
+ min_ns = i;
|
||
|
+ min_prid = tmp_prid;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (tmp_ns < miss_ns) {
|
||
|
+ min_ns = i;
|
||
|
+ min_prid = tmp_prid;
|
||
|
+ miss_ns = tmp_ns;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ ptc->corr_inc = min_ns;
|
||
|
+ ptc->corr_period = min_prid;
|
||
|
+}
|
||
|
+
|
||
|
+static void fec_set_drift(struct fec_ptp_private *priv,
|
||
|
+ struct ptp_set_comp *comp)
|
||
|
+{
|
||
|
+ struct ptp_time_correct tc;
|
||
|
+ struct fec_ptp_private *fpp = priv;
|
||
|
+ u32 tmp, corr_ns;
|
||
|
+
|
||
|
+ fec_handle_ptpdrift(comp, &tc);
|
||
|
+ if (tc.corr_inc == 0)
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (comp->o_ops == TRUE)
|
||
|
+ corr_ns = FEC_T_INC_40MHZ + tc.corr_inc;
|
||
|
+ else
|
||
|
+ corr_ns = FEC_T_INC_40MHZ - tc.corr_inc;
|
||
|
+
|
||
|
+ tmp = fec_readl(fpp->hwp + FEC_ATIME_INC) & FEC_T_INC_MASK;
|
||
|
+ tmp |= corr_ns << FEC_T_INC_CORR_OFFSET;
|
||
|
+ fec_writel(tmp, fpp->hwp + FEC_ATIME_INC);
|
||
|
+
|
||
|
+ fec_writel(tc.corr_period, fpp->hwp + FEC_ATIME_CORR);
|
||
|
+}
|
||
|
+
|
||
|
+static int ptp_open(struct inode *inode, struct file *file)
|
||
|
+{
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int ptp_release(struct inode *inode, struct file *file)
|
||
|
+{
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static long ptp_unlocked_ioctl(
|
||
|
+ struct file *file,
|
||
|
+ unsigned int cmd,
|
||
|
+ unsigned long arg)
|
||
|
+{
|
||
|
+ struct ptp_rtc_time *cnt;
|
||
|
+ struct ptp_rtc_time curr_time;
|
||
|
+ struct ptp_time rx_time, tx_time;
|
||
|
+ struct ptp_ts_data *p_ts;
|
||
|
+ struct ptp_set_comp *p_comp;
|
||
|
+ struct fec_ptp_private *priv;
|
||
|
+ struct inode *inode = file->f_mapping->host;
|
||
|
+ unsigned int minor = MINOR(inode->i_rdev);
|
||
|
+ long retval = 0;
|
||
|
+
|
||
|
+ priv = (struct fec_ptp_private *) ptp_private[minor];
|
||
|
+ switch (cmd) {
|
||
|
+ case PTP_GET_RX_TIMESTAMP:
|
||
|
+ p_ts = (struct ptp_ts_data *)arg;
|
||
|
+ retval = fec_get_rx_time(priv, p_ts, &rx_time);
|
||
|
+ if (retval == 0)
|
||
|
+ copy_to_user((void __user *)(&(p_ts->ts)), &rx_time,
|
||
|
+ sizeof(rx_time));
|
||
|
+ break;
|
||
|
+ case PTP_GET_TX_TIMESTAMP:
|
||
|
+ p_ts = (struct ptp_ts_data *)arg;
|
||
|
+ fec_get_tx_timestamp(priv, &tx_time);
|
||
|
+ copy_to_user((void __user *)(&(p_ts->ts)), &tx_time,
|
||
|
+ sizeof(tx_time));
|
||
|
+ break;
|
||
|
+ case PTP_GET_CURRENT_TIME:
|
||
|
+ fec_get_curr_cnt(priv, &curr_time);
|
||
|
+ copy_to_user((void __user *)arg, &curr_time, sizeof(curr_time));
|
||
|
+ break;
|
||
|
+ case PTP_SET_RTC_TIME:
|
||
|
+ cnt = (struct ptp_rtc_time *)arg;
|
||
|
+ fec_set_1588cnt(priv, cnt);
|
||
|
+ break;
|
||
|
+ case PTP_FLUSH_TIMESTAMP:
|
||
|
+ /* reset sync buffer */
|
||
|
+ priv->rx_time_sync.head = 0;
|
||
|
+ priv->rx_time_sync.tail = 0;
|
||
|
+ /* reset delay_req buffer */
|
||
|
+ priv->rx_time_del_req.head = 0;
|
||
|
+ priv->rx_time_del_req.tail = 0;
|
||
|
+ /* reset pdelay_req buffer */
|
||
|
+ priv->rx_time_pdel_req.head = 0;
|
||
|
+ priv->rx_time_pdel_req.tail = 0;
|
||
|
+ /* reset pdelay_resp buffer */
|
||
|
+ priv->rx_time_pdel_resp.head = 0;
|
||
|
+ priv->rx_time_pdel_resp.tail = 0;
|
||
|
+ break;
|
||
|
+ case PTP_SET_COMPENSATION:
|
||
|
+ p_comp = (struct ptp_set_comp *)arg;
|
||
|
+ fec_set_drift(priv, p_comp);
|
||
|
+ break;
|
||
|
+ case PTP_GET_ORIG_COMP:
|
||
|
+ ((struct ptp_get_comp *)arg)->dw_origcomp = FEC_PTP_ORIG_COMP;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+ return retval;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct file_operations ptp_fops = {
|
||
|
+ .owner = THIS_MODULE,
|
||
|
+ .llseek = NULL,
|
||
|
+ .read = NULL,
|
||
|
+ .write = NULL,
|
||
|
+ .unlocked_ioctl = ptp_unlocked_ioctl,
|
||
|
+ .open = ptp_open,
|
||
|
+ .release = ptp_release,
|
||
|
+};
|
||
|
+
|
||
|
+static int init_ptp(void)
|
||
|
+{
|
||
|
+ if (register_chrdev(PTP_MAJOR, "ptp", &ptp_fops))
|
||
|
+ printk(KERN_ERR "Unable to register PTP deivce as char\n");
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void ptp_free(void)
|
||
|
+{
|
||
|
+ /*unregister the PTP device*/
|
||
|
+ unregister_chrdev(PTP_MAJOR, "ptp");
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+/*
|
||
|
+ * Resource required for accessing 1588 Timer Registers.
|
||
|
+ */
|
||
|
+int fec_ptp_init(struct fec_ptp_private *priv, int id)
|
||
|
+{
|
||
|
+ fec_ptp_init_circ(&(priv->rx_time_sync));
|
||
|
+ fec_ptp_init_circ(&(priv->rx_time_del_req));
|
||
|
+ fec_ptp_init_circ(&(priv->rx_time_pdel_req));
|
||
|
+ fec_ptp_init_circ(&(priv->rx_time_pdel_resp));
|
||
|
+
|
||
|
+ spin_lock_init(&priv->ptp_lock);
|
||
|
+ spin_lock_init(&priv->cnt_lock);
|
||
|
+ ptp_private[id] = priv;
|
||
|
+ if (id == 0)
|
||
|
+ init_ptp();
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(fec_ptp_init);
|
||
|
+
|
||
|
+void fec_ptp_cleanup(struct fec_ptp_private *priv)
|
||
|
+{
|
||
|
+
|
||
|
+ if (priv->rx_time_sync.buf)
|
||
|
+ vfree(priv->rx_time_sync.buf);
|
||
|
+ if (priv->rx_time_del_req.buf)
|
||
|
+ vfree(priv->rx_time_del_req.buf);
|
||
|
+ if (priv->rx_time_pdel_req.buf)
|
||
|
+ vfree(priv->rx_time_pdel_req.buf);
|
||
|
+ if (priv->rx_time_pdel_resp.buf)
|
||
|
+ vfree(priv->rx_time_pdel_resp.buf);
|
||
|
+
|
||
|
+ ptp_free();
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(fec_ptp_cleanup);
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/net/fec_1588.h
|
||
|
@@ -0,0 +1,195 @@
|
||
|
+/*
|
||
|
+ * drivers/net/fec_1588.h
|
||
|
+ *
|
||
|
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
|
||
|
+ *
|
||
|
+ * This program is free software; you can redistribute it and/or modify
|
||
|
+ * it under the terms of the GNU General Public License as published by
|
||
|
+ * the Free Software Foundation; either version 2 of the License, or
|
||
|
+ * (at your option) any later version.
|
||
|
+ *
|
||
|
+ * This program is distributed in the hope that it will be useful,
|
||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
+ * GNU General Public License for more details.
|
||
|
+ *
|
||
|
+ * You should have received a copy of the GNU General Public License along
|
||
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#ifndef FEC_1588_H
|
||
|
+#define FEC_1588_H
|
||
|
+
|
||
|
+#include <linux/circ_buf.h>
|
||
|
+#include "fec.h"
|
||
|
+
|
||
|
+#define fec_readl(addr) \
|
||
|
+ ({ unsigned int __v = (*(volatile unsigned int *) (addr)); __v; })
|
||
|
+
|
||
|
+#define fec_writel(b, addr) (void)((*(volatile unsigned int *) (addr)) = (b))
|
||
|
+
|
||
|
+#define FALSE 0
|
||
|
+#define TRUE 1
|
||
|
+
|
||
|
+/* FEC 1588 register bits */
|
||
|
+#define FEC_T_CTRL_CAPTURE 0x00000800
|
||
|
+#define FEC_T_CTRL_RESTART 0x00000200
|
||
|
+#define FEC_T_CTRL_PERIOD_RST 0x00000010
|
||
|
+#define FEC_T_CTRL_PINPER 0x00000080
|
||
|
+#define FEC_T_CTRL_ENABLE 0x00000001
|
||
|
+
|
||
|
+#define FEC_T_INC_MASK 0x0000007f
|
||
|
+#define FEC_T_INC_OFFSET 0
|
||
|
+#define FEC_T_INC_CORR_MASK 0x00007f00
|
||
|
+#define FEC_T_INC_CORR_OFFSET 8
|
||
|
+
|
||
|
+#define FEC_T_INC_40MHZ 8
|
||
|
+#define FEC_ATIME_40MHZ 125000000
|
||
|
+
|
||
|
+#define FEC_T_PERIOD_ONE_SEC 0x3B9ACA00
|
||
|
+
|
||
|
+/* IEEE 1588 definition */
|
||
|
+#define FEC_ECNTRL_TS_EN 0x10
|
||
|
+#define PTP_MAJOR 232 /*the temporary major number
|
||
|
+ *used by PTP driver, the major
|
||
|
+ *number 232~239 is unassigned*/
|
||
|
+
|
||
|
+#define DEFAULT_PTP_RX_BUF_SZ 2048
|
||
|
+#define PTP_MSG_SYNC 0x0
|
||
|
+#define PTP_MSG_DEL_REQ 0x1
|
||
|
+#define PTP_MSG_P_DEL_REQ 0x2
|
||
|
+#define PTP_MSG_P_DEL_RESP 0x3
|
||
|
+#define PTP_MSG_DEL_RESP 0x4
|
||
|
+#define PTP_MSG_ALL_OTHER 0x5
|
||
|
+
|
||
|
+#define PTP_GET_TX_TIMESTAMP 0x1
|
||
|
+#define PTP_GET_RX_TIMESTAMP 0x9
|
||
|
+#define PTP_SET_RTC_TIME 0x3
|
||
|
+#define PTP_SET_COMPENSATION 0x4
|
||
|
+#define PTP_GET_CURRENT_TIME 0x5
|
||
|
+#define PTP_FLUSH_TIMESTAMP 0x6
|
||
|
+#define PTP_ADJ_ADDEND 0x7
|
||
|
+#define PTP_GET_ORIG_COMP 0x8
|
||
|
+#define PTP_GET_ADDEND 0xB
|
||
|
+#define PTP_GET_RX_TIMESTAMP_PDELAY_REQ 0xC
|
||
|
+#define PTP_GET_RX_TIMESTAMP_PDELAY_RESP 0xD
|
||
|
+
|
||
|
+#define FEC_PTP_DOMAIN_DLFT 0xe0000181
|
||
|
+#define FEC_PTP_IP_OFFS 0x0
|
||
|
+#define FEC_PTP_UDP_OFFS 0x14
|
||
|
+#define FEC_PTP_MSG_TYPE_OFFS 0x1C
|
||
|
+#define FEC_PTP_SEQ_ID_OFFS 0x3A
|
||
|
+#define FEC_PTP_COR_NS 0x24
|
||
|
+#define FEC_PTP_CTRL_OFFS 0x3C
|
||
|
+#define FEC_PACKET_TYPE_UDP 0x11
|
||
|
+
|
||
|
+#define FEC_PTP_ORIG_COMP 0x15555
|
||
|
+
|
||
|
+/* PTP standard time representation structure */
|
||
|
+struct ptp_time {
|
||
|
+ u64 sec; /* seconds */
|
||
|
+ u32 nsec; /* nanoseconds */
|
||
|
+};
|
||
|
+
|
||
|
+/* Structure for PTP Time Stamp */
|
||
|
+struct fec_ptp_data_t {
|
||
|
+ int key;
|
||
|
+ struct ptp_time ts_time;
|
||
|
+};
|
||
|
+
|
||
|
+/* interface for PTP driver command GET_TX_TIME */
|
||
|
+struct ptp_ts_data {
|
||
|
+ /* PTP version */
|
||
|
+ u8 version;
|
||
|
+ /* PTP source port ID */
|
||
|
+ u8 spid[10];
|
||
|
+ /* PTP sequence ID */
|
||
|
+ u16 seq_id;
|
||
|
+ /* PTP message type */
|
||
|
+ u8 message_type;
|
||
|
+ /* PTP timestamp */
|
||
|
+ struct ptp_time ts;
|
||
|
+};
|
||
|
+
|
||
|
+/* interface for PTP driver command SET_RTC_TIME/GET_CURRENT_TIME */
|
||
|
+struct ptp_rtc_time {
|
||
|
+ struct ptp_time rtc_time;
|
||
|
+};
|
||
|
+
|
||
|
+/* interface for PTP driver command SET_COMPENSATION */
|
||
|
+struct ptp_set_comp {
|
||
|
+ u32 drift;
|
||
|
+ u32 o_ops;
|
||
|
+};
|
||
|
+
|
||
|
+/* interface for PTP driver command GET_ORIG_COMP */
|
||
|
+struct ptp_get_comp {
|
||
|
+ /* the initial compensation value */
|
||
|
+ u32 dw_origcomp;
|
||
|
+ /* the minimum compensation value */
|
||
|
+ u32 dw_mincomp;
|
||
|
+ /*the max compensation value*/
|
||
|
+ u32 dw_maxcomp;
|
||
|
+ /*the min drift applying min compensation value in ppm*/
|
||
|
+ u32 dw_mindrift;
|
||
|
+ /*the max drift applying max compensation value in ppm*/
|
||
|
+ u32 dw_maxdrift;
|
||
|
+};
|
||
|
+
|
||
|
+struct ptp_time_correct {
|
||
|
+ u32 corr_period;
|
||
|
+ u32 corr_inc;
|
||
|
+};
|
||
|
+
|
||
|
+/* PTP message version */
|
||
|
+#define PTP_1588_MSG_VER_1 1
|
||
|
+#define PTP_1588_MSG_VER_2 2
|
||
|
+
|
||
|
+struct fec_ptp_private {
|
||
|
+ void __iomem *hwp;
|
||
|
+
|
||
|
+ struct circ_buf rx_time_sync;
|
||
|
+ struct circ_buf rx_time_del_req;
|
||
|
+ struct circ_buf rx_time_pdel_req;
|
||
|
+ struct circ_buf rx_time_pdel_resp;
|
||
|
+ spinlock_t ptp_lock;
|
||
|
+ spinlock_t cnt_lock;
|
||
|
+
|
||
|
+ u64 prtc;
|
||
|
+ struct ptp_time txstamp;
|
||
|
+};
|
||
|
+
|
||
|
+#ifdef CONFIG_FEC_1588
|
||
|
+extern int fec_ptp_init(struct fec_ptp_private *priv, int id);
|
||
|
+extern void fec_ptp_cleanup(struct fec_ptp_private *priv);
|
||
|
+extern int fec_ptp_start(struct fec_ptp_private *priv);
|
||
|
+extern void fec_ptp_stop(struct fec_ptp_private *priv);
|
||
|
+extern int fec_ptp_do_txstamp(struct sk_buff *skb);
|
||
|
+extern void fec_ptp_store_txstamp(struct fec_ptp_private *priv);
|
||
|
+extern void fec_ptp_store_rxstamp(struct fec_ptp_private *priv,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ struct bufdesc *bdp);
|
||
|
+#else
|
||
|
+static inline int fec_ptp_init(struct fec_ptp_private *priv, int id)
|
||
|
+{
|
||
|
+ return 1;
|
||
|
+}
|
||
|
+static inline void fec_ptp_cleanup(struct fec_ptp_private *priv) { }
|
||
|
+static inline int fec_ptp_start(struct fec_ptp_private *priv)
|
||
|
+{
|
||
|
+ return 1;
|
||
|
+}
|
||
|
+static inline void fec_ptp_stop(struct fec_ptp_private *priv) {}
|
||
|
+static inline int fec_ptp_do_txstamp(struct sk_buff *skb)
|
||
|
+{
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+static inline void fec_ptp_store_txstamp(struct fec_ptp_private *priv) {}
|
||
|
+static inline void fec_ptp_store_rxstamp(struct fec_ptp_private *priv,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ struct bufdesc *bdp) {}
|
||
|
+#endif /* 1588 */
|
||
|
+
|
||
|
+#endif
|