mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 14:04:26 -04: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
 |