summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
authorXie Xiaobo <r63061@freescale.com>2010-10-15 16:16:28 +0800
committerXie Xiaobo <r63061@freescale.com>2010-10-15 16:50:10 +0800
commita034f7af1935360b8ff45991b0433e502d3bb3d3 (patch)
treecc9d1527b66217d5ad353595e42a6d2d2857e910 /drivers/net
parentc3dbe9cf07ef87e9c03b322ea21d3ea964aed180 (diff)
ENGR00131580-2 PTP: Support IEEE 1588 interface functionality for MX5
IEEE1588 implementation is a Hardware-assisted method in i.MX5, This supply the 1588 support for MX53 FEC. Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/Kconfig24
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/fec.c45
-rw-r--r--drivers/net/fec.h3
-rw-r--r--drivers/net/fec_1588.h202
-rw-r--r--drivers/net/imx_ptp.c1525
-rw-r--r--drivers/net/imx_ptp.h356
7 files changed, 2156 insertions, 0 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 1d1a0b156898..217784200db0 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1914,6 +1914,30 @@ config FEC
Say Y here if you want to use the built-in 10/100 Fast ethernet
controller on some Motorola ColdFire and Freescale i.MX processors.
+config FEC_1588
+ bool "Enable FEC 1588 timestamping"
+ depends on FEC
+
+choice
+ prompt "IEEE 1588 operation mode"
+ depends on ARCH_MX5 && FEC_1588
+ default OUT_OF_BAND
+
+config OUT_OF_BAND
+ bool "out-of-band mode"
+ help
+ IEEE 1588 operation mode is out-of-band, in this mode, the Tx and Rx
+ timestamp will be caputred into special registers.
+
+config IN_BAND
+ bool "in-band mode"
+ help
+ IEEE 1588 operation mode is in-band, in this mode, the Tx timestamp
+ will be captured into special retisters, but the Rx timestamp will be
+ captured into the first eight bytes of received ethernet packet.
+
+endchoice
+
config FEC2
bool "Second FEC ethernet controller (on some ColdFire CPUs)"
depends on FEC
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 0a0512ae77da..c29085716bf8 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -120,6 +120,7 @@ obj-$(CONFIG_PCMCIA_PCNET) += 8390.o
obj-$(CONFIG_HP100) += hp100.o
obj-$(CONFIG_SMC9194) += smc9194.o
obj-$(CONFIG_FEC) += fec.o
+obj-$(CONFIG_FEC_1588) += imx_ptp.o
obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index 1dab8ee87604..66c5b5a1b815 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -17,6 +17,9 @@
*
* 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 Freescale Semiconductor, Inc.
*/
#include <linux/module.h>
@@ -54,6 +57,7 @@
#endif
#include "fec.h"
+#include "fec_1588.h"
#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_MXS)
#define FEC_ALIGNMENT 0xf
@@ -185,6 +189,9 @@ struct fec_enet_private {
int link;
int full_duplex;
struct completion mdio_done;
+
+ struct fec_ptp_private *ptp_priv;
+ uint ptimer_present;
};
/*
@@ -275,6 +282,11 @@ 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))
+ status |= BD_ENET_TX_PTP;
+ }
+
#ifdef CONFIG_ARCH_MXS
swap_buffer(bufaddr, skb->len);
#endif
@@ -446,6 +458,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;
struct bufdesc *bdp;
unsigned short status;
struct sk_buff *skb;
@@ -526,6 +539,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);
}
@@ -1197,6 +1213,7 @@ fec_restart(struct net_device *dev, int duplex)
{
struct fec_enet_private *fep = netdev_priv(dev);
int i;
+ uint ret = 0;
u32 temp_mac[2];
unsigned long reg;
int val;
@@ -1278,6 +1295,13 @@ fec_restart(struct net_device *dev, int duplex)
/* Set MII speed */
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+ if (fep->ptimer_present) {
+ /* Set Timer count */
+ ret = fec_ptp_start(fep->ptp_priv);
+ if (ret)
+ fep->ptimer_present = 0;
+ }
+
#ifdef FEC_MIIGSK_ENR
if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) {
/* disable the gasket and wait */
@@ -1333,6 +1357,8 @@ fec_stop(struct net_device *dev)
/* Clear outstanding MII command interrupts. */
writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
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);
}
@@ -1435,6 +1461,19 @@ fec_probe(struct platform_device *pdev)
fep->mii_bus = fec_mii_bus;
}
+ 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");
+ }
+
ret = register_netdev(ndev);
if (ret)
goto failed_register;
@@ -1445,6 +1484,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);
@@ -1480,6 +1522,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;
diff --git a/drivers/net/fec.h b/drivers/net/fec.h
index 53b3f7db10f4..bdf07c93d506 100644
--- a/drivers/net/fec.h
+++ b/drivers/net/fec.h
@@ -126,6 +126,8 @@ struct bufdesc {
#define BD_ENET_RX_CL ((ushort)0x0001)
#define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */
+#define BD_ENET_RX_PTP ((ushort)0x0400)
+
/* Buffer descriptor control/status used by Ethernet transmit.
*/
#define BD_ENET_TX_READY ((ushort)0x8000)
@@ -143,6 +145,7 @@ struct bufdesc {
#define BD_ENET_TX_CSL ((ushort)0x0001)
#define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */
+#define BD_ENET_TX_PTP ((ushort)0x0100)
/****************************************************************************/
#endif /* FEC_H */
diff --git a/drivers/net/fec_1588.h b/drivers/net/fec_1588.h
new file mode 100644
index 000000000000..85dede1c1de9
--- /dev/null
+++ b/drivers/net/fec_1588.h
@@ -0,0 +1,202 @@
+/*
+ * drivers/net/fec_1588.h
+ *
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ *
+ * 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/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/circ_buf.h>
+
+#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 0x00000030
+#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 25
+#define FEC_ATIME_40MHZ 40000000
+
+#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_CTRL_OFFS 0x3C
+#define FEC_PACKET_TYPE_UDP 0x11
+
+#define FEC_PTP_ORIG_COMP 0x15555555
+
+/* 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;
+ bool o_ops;
+ u32 freq_compensation;
+};
+
+/* 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
+
+#define BD_ENET_TX_TS 0x20000000
+#define BD_ENET_TX_BDU 0x80000000
+
+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
+static inline int fec_ptp_malloc_priv(struct fec_ptp_private **priv)
+{
+ *priv = kzalloc(sizeof(struct fec_ptp_private), GFP_KERNEL);
+ return 1;
+}
+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_malloc_priv(struct fec_ptp_private **priv)
+{
+ return 0;
+}
+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
diff --git a/drivers/net/imx_ptp.c b/drivers/net/imx_ptp.c
new file mode 100644
index 000000000000..177b898cc9ab
--- /dev/null
+++ b/drivers/net/imx_ptp.c
@@ -0,0 +1,1525 @@
+/*
+ * drivers/net/imx_ptp.c
+ *
+ * Copyright (C) 2010 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Description: IEEE 1588 driver supporting imx5 Fast Ethernet Controller.
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/reboot.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/time.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/vmalloc.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+#include "fec.h"
+#include "fec_1588.h"
+#include "imx_ptp.h"
+
+#ifdef PTP_DEBUG
+#define VDBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt "\n", \
+ __func__, ## args)
+#else
+#define VDBG(fmt, args...) do {} while (0)
+#endif
+
+static DECLARE_WAIT_QUEUE_HEAD(ptp_rx_ts_wait);
+#define PTP_GET_RX_TIMEOUT (HZ/10)
+
+static struct fec_ptp_private *ptp_private;
+static void ptp_rtc_get_current_time(struct ptp *p_ptp,
+ struct ptp_time *p_time);
+static struct ptp *ptp_dev;
+
+/* The ring resource create and manage */
+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;
+ tmp = (struct fec_ptp_data_t *)(ptp_buf->buf) + i;
+ 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;
+}
+
+/* ptp and rtc param configuration */
+static void rtc_default_param(struct ptp_rtc *rtc)
+{
+ struct ptp_rtc_driver_param *drv_param = rtc->driver_param;
+ int i;
+
+ rtc->bypass_compensation = DEFAULT_BYPASS_COMPENSATION;
+ rtc->output_clock_divisor = DEFAULT_OUTPUT_CLOCK_DIVISOR;
+
+ drv_param->src_clock = DEFAULT_SRC_CLOCK;
+ drv_param->src_clock_freq_hz = clk_get_rate(rtc->clk);
+
+ drv_param->invert_input_clk_phase = DEFAULT_INVERT_INPUT_CLK_PHASE;
+ drv_param->invert_output_clk_phase = DEFAULT_INVERT_OUTPUT_CLK_PHASE;
+ drv_param->pulse_start_mode = DEFAULT_PULSE_START_MODE;
+ drv_param->events_mask = DEFAULT_EVENTS_RTC_MASK;
+
+ for (i = 0; i < PTP_RTC_NUM_OF_ALARMS; i++)
+ drv_param->alarm_polarity[i] = DEFAULT_ALARM_POLARITY ;
+
+ for (i = 0; i < PTP_RTC_NUM_OF_TRIGGERS; i++)
+ drv_param->trigger_polarity[i] = DEFAULT_TRIGGER_POLARITY;
+}
+
+static int ptp_rtc_config(struct ptp_rtc *rtc)
+{
+ /*allocate memory for RTC driver parameter*/
+ rtc->driver_param = kzalloc(sizeof(struct ptp_rtc_driver_param),
+ GFP_KERNEL);
+ if (!rtc->driver_param) {
+ printk(KERN_ERR "allocate memory failed\n");
+ return -ENOMEM;
+ }
+
+ /* expected RTC input clk frequency */
+ rtc->driver_param->rtc_freq_hz = PTP_RTC_FREQ * MHZ;
+
+ /*set default RTC configuration parameters*/
+ rtc_default_param(rtc);
+
+ return 0;
+}
+
+static void ptp_param_config(struct ptp *p_ptp)
+{
+ struct ptp_driver_param *drv_param;
+
+ p_ptp->driver_param = kzalloc(sizeof(struct ptp_driver_param),
+ GFP_KERNEL);
+ if (!p_ptp->driver_param) {
+ printk(KERN_ERR "allocate memory failed for "
+ "PTP driver parameters\n");
+ return;
+ }
+
+ drv_param = p_ptp->driver_param;
+ /*set the default configuration parameters*/
+ drv_param->eth_type_value = ETH_TYPE_VALUE;
+ drv_param->vlan_type_value = VLAN_TYPE_VALUE;
+ drv_param->udp_general_port = UDP_GENERAL_PORT;
+ drv_param->udp_event_port = UDP_EVENT_PORT;
+ drv_param->ip_type_value = IP_TYPE_VALUE;
+ drv_param->eth_type_offset = ETH_TYPE_OFFSET;
+ drv_param->ip_type_offset = IP_TYPE_OFFSET;
+ drv_param->udp_dest_port_offset = UDP_DEST_PORT_OFFSET;
+ drv_param->ptp_type_offset = PTP_TYPE_OFFSET;
+
+ drv_param->ptp_msg_codes[e_PTP_MSG_SYNC] = DEFAULT_MSG_SYNC;
+ drv_param->ptp_msg_codes[e_PTP_MSG_DELAY_REQ] = DEFAULT_MSG_DELAY_REQ;
+ drv_param->ptp_msg_codes[e_PTP_MSG_FOLLOW_UP] = DEFAULT_MSG_FOLLOW_UP;
+ drv_param->ptp_msg_codes[e_PTP_MSG_DELAY_RESP] = DEFAULT_MSG_DELAY_RESP;
+ drv_param->ptp_msg_codes[e_PTP_MSG_MANAGEMENT] = DEFAULT_MSG_MANAGEMENT;
+}
+
+/* 64 bits operation */
+static u32 div64_oper(u64 dividend, u32 divisor, u32 *quotient)
+{
+ u32 time_h, time_l;
+ u32 result;
+ u64 tmp_dividend;
+ int i;
+
+ time_h = (u32)(dividend >> 32);
+ time_l = (u32)dividend;
+ time_h = time_h % divisor;
+ for (i = 1; i <= 32; i++) {
+ tmp_dividend = (((u64)time_h << 32) | (u64)time_l);
+ tmp_dividend = (tmp_dividend << 1);
+ time_h = (u32)(tmp_dividend >> 32);
+ time_l = (u32)tmp_dividend;
+ result = time_h / divisor;
+ time_h = time_h % divisor;
+ *quotient += (result << (32 - i));
+ }
+
+ return time_h;
+}
+
+/*64 bites add and return the result*/
+static u64 add64_oper(u64 addend, u64 augend)
+{
+ u64 result = 0;
+ u32 addendh, addendl, augendl, augendh;
+
+ addendh = (u32)(addend >> 32);
+ addendl = (u32)addend;
+
+ augendh = (u32)(augend>>32);
+ augendl = (u32)augend;
+
+ __asm__(
+ "adds %0,%2,%3\n"
+ "adc %1,%4,%5"
+ : "=r" (addendl), "=r" (addendh)
+ : "r" (addendl), "r" (augendl), "r" (addendh), "r" (augendh)
+ );
+
+ udelay(1);
+ result = (((u64)addendh << 32) | (u64)addendl);
+
+ return result;
+}
+
+/*64 bits multiplication and return the result*/
+static u64 multi64_oper(u32 multiplier, u32 multiplicand)
+{
+ u64 result = 0;
+ u64 tmp_ret = 0;
+ u32 tmp_multi = multiplicand;
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ if (tmp_multi & 0x1) {
+ tmp_ret = ((u64)multiplier << i);
+ result = add64_oper(result, tmp_ret);
+ }
+ tmp_multi = (tmp_multi >> 1);
+ }
+
+ VDBG("multi 64 low result is 0x%x\n", result);
+ VDBG("multi 64 high result is 0x%x\n", (u32)(result>>32));
+
+ return result;
+}
+
+/*convert the 64 bites time stamp to second and nanosecond*/
+static void convert_rtc_time(u64 *rtc_time, struct ptp_time *p_time)
+{
+ u32 time_h;
+ u32 time_sec = 0;
+
+ time_h = div64_oper(*rtc_time, NANOSEC_IN_SEC, &time_sec);
+
+ p_time->sec = time_sec;
+ p_time->nsec = time_h;
+}
+
+/* convert rtc time to 64 bites timestamp */
+static u64 convert_unsigned_time(struct ptp_time *ptime)
+{
+ return add64_oper(multi64_oper(ptime->sec, NANOSEC_IN_SEC),
+ (u64)ptime->nsec);
+}
+
+/*RTC interrupt handler*/
+static irqreturn_t ptp_rtc_interrupt(int irq, void *_ptp)
+{
+ struct ptp *p_ptp = (struct ptp *)_ptp;
+ struct ptp_rtc *rtc = p_ptp->rtc;
+ struct ptp_time time;
+ register u32 events;
+
+ /*get valid events*/
+ events = readl(rtc->mem_map + PTP_TMR_TEVENT);
+
+ /*clear event bits*/
+ writel(events, rtc->mem_map + PTP_TMR_TEVENT);
+
+ /*get the current time as quickly as possible*/
+ ptp_rtc_get_current_time(p_ptp, &time);
+
+ if (events & RTC_TEVENT_ALARM_1) {
+ p_ptp->alarm_counters[0]++;
+ VDBG("PTP Alarm 1 event, time = %2d:%09d[sec:nsec]\n",
+ time.sec, time.nsec);
+ }
+ if (events & RTC_TEVENT_ALARM_2) {
+ p_ptp->alarm_counters[1]++;
+ VDBG("PTP Alarm 2 event, time = %2d:%09d[sec:nsec]\n",
+ time.sec, time.nsec);
+ }
+ if (events & RTC_TEVENT_PERIODIC_PULSE_1) {
+ p_ptp->pulse_counters[0]++;
+ VDBG("PTP Pulse 1 event, time = %2d:%09d[sec:nsec]\n",
+ time.sec, time.nsec);
+ }
+ if (events & RTC_TEVENT_PERIODIC_PULSE_2) {
+ p_ptp->pulse_counters[1]++;
+ VDBG("PTP Pulse 2 event, time = %2d:%09d[sec:nsec]\n",
+ time.sec, time.nsec);
+ }
+ if (events & RTC_TEVENT_PERIODIC_PULSE_3) {
+ p_ptp->pulse_counters[2]++;
+ VDBG("PTP Pulse 3 event, time = %2d:%09d[sec:nsec]\n",
+ time.sec, time.nsec);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ptp_rtc_init(struct ptp *p_ptp)
+{
+ struct ptp_rtc *rtc = p_ptp->rtc;
+ struct ptp_rtc_driver_param *rtc_drv_param = rtc->driver_param;
+ void __iomem *rtc_mem = rtc->mem_map;
+ u32 freq_compensation = 0;
+ u32 tmr_ctrl = 0;
+ int ret = 0;
+ int i;
+
+ rtc = p_ptp->rtc;
+ rtc_drv_param = rtc->driver_param;
+ rtc_mem = rtc->mem_map;
+
+ if (!rtc->bypass_compensation)
+ rtc->clock_period_nansec = NANOSEC_PER_ONE_HZ_TICK /
+ rtc_drv_param->rtc_freq_hz;
+ else {
+ /*In bypass mode,the RTC clock equals the source clock*/
+ rtc->clock_period_nansec = NANOSEC_PER_ONE_HZ_TICK /
+ rtc_drv_param->src_clock_freq_hz;
+ tmr_ctrl |= RTC_TMR_CTRL_BYP;
+ }
+
+ tmr_ctrl |= ((rtc->clock_period_nansec <<
+ RTC_TMR_CTRL_TCLK_PERIOD_SHIFT) &
+ RTC_TMR_CTRL_TCLK_PERIOD_MSK);
+
+ if (rtc_drv_param->invert_input_clk_phase)
+ tmr_ctrl |= RTC_TMR_CTRL_CIPH;
+ if (rtc_drv_param->invert_output_clk_phase)
+ tmr_ctrl |= RTC_TMR_CTRL_COPH;
+ if (rtc_drv_param->pulse_start_mode == e_PTP_RTC_PULSE_START_ON_ALARM) {
+ tmr_ctrl |= RTC_TMR_CTRL_FS;
+ rtc->start_pulse_on_alarm = TRUE;
+ }
+
+ for (i = 0; i < PTP_RTC_NUM_OF_ALARMS; i++) {
+ if (rtc_drv_param->alarm_polarity[i] ==
+ e_PTP_RTC_ALARM_POLARITY_ACTIVE_LOW)
+ tmr_ctrl |= (RTC_TMR_CTRL_ALMP1 >> i);
+
+ }
+
+ /*clear TMR_ALARM registers*/
+ writel(0xFFFFFFFF, rtc_mem + PTP_TMR_ALARM1_L);
+ writel(0xFFFFFFFF, rtc_mem + PTP_TMR_ALARM1_H);
+ writel(0xFFFFFFFF, rtc_mem + PTP_TMR_ALARM2_L);
+ writel(0xFFFFFFFF, rtc_mem + PTP_TMR_ALARM2_H);
+
+ for (i = 0; i < PTP_RTC_NUM_OF_TRIGGERS; i++) {
+ if (rtc_drv_param->trigger_polarity[i] ==
+ e_PTP_RTC_TRIGGER_ON_FALLING_EDGE)
+ tmr_ctrl |= (RTC_TMR_CTRL_ETEP1 << i);
+ }
+
+ /*clear TMR_FIPER registers*/
+ writel(0xFFFFFFFF, rtc_mem + PTP_TMR_FIPER1);
+ writel(0xFFFFFFFF, rtc_mem + PTP_TMR_FIPER2);
+ writel(0xFFFFFFFF, rtc_mem + PTP_TMR_FIPER3);
+
+ /*set the source clock*/
+ /*use a clock from the QE bank of clocks*/
+ tmr_ctrl |= RTC_TMR_CTRL_CKSEL_EXT_CLK;
+
+ /*write register and perform software reset*/
+ writel((tmr_ctrl | RTC_TMR_CTRL_TMSR), rtc_mem + PTP_TMR_CTRL);
+ writel(tmr_ctrl, rtc_mem + PTP_TMR_CTRL);
+
+ /*clear TMR_TEVEMT*/
+ writel(RTC_EVENT_ALL, rtc_mem + PTP_TMR_TEVENT);
+
+ /*initialize TMR_TEMASK*/
+ writel(rtc_drv_param->events_mask, rtc_mem + PTP_TMR_TEMASK);
+
+ /*initialize TMR_ADD with the initial frequency compensation value:
+ freq_compensation = (2^32 / frequency ratio)*/
+ div64_oper(((u64)rtc_drv_param->rtc_freq_hz << 32),
+ rtc_drv_param->src_clock_freq_hz, &freq_compensation);
+ p_ptp->orig_freq_comp = freq_compensation;
+ writel(freq_compensation, rtc_mem + PTP_TMR_ADD);
+
+ /*initialize TMR_PRSC*/
+ writel(rtc->output_clock_divisor, rtc_mem + PTP_TMR_PRSC);
+
+ /*initialize TMR_OFF*/
+ writel(0, rtc_mem + PTP_TMR_OFF_L);
+ writel(0, rtc_mem + PTP_TMR_OFF_H);
+
+ return ret;
+}
+
+static void init_ptp_parser(struct ptp *p_ptp)
+{
+ void __iomem *mem_map = p_ptp->mem_map;
+ struct ptp_driver_param *drv_param = p_ptp->driver_param;
+ u32 reg32;
+
+ /*initialzie PTP TSPDR1*/
+ reg32 = ((drv_param->eth_type_value << PTP_TSPDR1_ETT_SHIFT) &
+ PTP_TSPDR1_ETT_MASK);
+ reg32 |= ((drv_param->ip_type_value << PTP_TSPDR1_IPT_SHIFT) &
+ PTP_TSPDR1_IPT_MASK);
+ writel(reg32, mem_map + PTP_TSPDR1);
+
+ /*initialize PTP TSPDR2*/
+ reg32 = ((drv_param->udp_general_port << PTP_TSPDR2_DPNGE_SHIFT) &
+ PTP_TSPDR2_DPNGE_MASK);
+ reg32 |= (drv_param->udp_event_port & PTP_TSPDR2_DPNEV_MASK);
+ writel(reg32, mem_map + PTP_TSPDR2);
+
+ /*initialize PTP TSPDR3*/
+ reg32 = ((drv_param->ptp_msg_codes[e_PTP_MSG_SYNC] <<
+ PTP_TSPDR3_SYCTL_SHIFT) & PTP_TSPDR3_SYCTL_MASK);
+ reg32 |= ((drv_param->ptp_msg_codes[e_PTP_MSG_DELAY_REQ] <<
+ PTP_TSPDR3_DRCTL_SHIFT) & PTP_TSPDR3_DRCTL_MASK);
+ reg32 |= ((drv_param->ptp_msg_codes[e_PTP_MSG_DELAY_RESP] <<
+ PTP_TSPDR3_DRPCTL_SHIFT) & PTP_TSPDR3_DRPCTL_MASK);
+ reg32 |= (drv_param->ptp_msg_codes[e_PTP_MSG_FOLLOW_UP] &
+ PTP_TSPDR3_FUCTL_MASK);
+ writel(reg32, mem_map + PTP_TSPDR3);
+
+ /*initialzie PTP TSPDR4*/
+ reg32 = ((drv_param->ptp_msg_codes[e_PTP_MSG_MANAGEMENT] <<
+ PTP_TSPDR4_MACTL_SHIFT) & PTP_TSPDR4_MACTL_MASK);
+ reg32 |= (drv_param->vlan_type_value & PTP_TSPDR4_VLAN_MASK);
+ writel(reg32, mem_map + PTP_TSPDR4);
+
+ /*initialize PTP TSPOV*/
+ reg32 = ((drv_param->eth_type_offset << PTP_TSPOV_ETTOF_SHIFT) &
+ PTP_TSPOV_ETTOF_MASK);
+ reg32 |= ((drv_param->ip_type_offset << PTP_TSPOV_IPTOF_SHIFT) &
+ PTP_TSPOV_IPTOF_MASK);
+ reg32 |= ((drv_param->udp_dest_port_offset << PTP_TSPOV_UDOF_SHIFT) &
+ PTP_TSPOV_UDOF_MASK);
+ reg32 |= (drv_param->ptp_type_offset & PTP_TSPOV_PTOF_MASK);
+ writel(reg32, mem_map + PTP_TSPOV);
+}
+
+/* compatible with MXS 1588 */
+void fec_ptp_store_txstamp(struct fec_ptp_private *priv)
+{
+}
+
+static void ptp_store_txstamp(struct fec_ptp_private *priv,
+ struct ptp_time *pts)
+{
+ priv->txstamp.nsec = pts->nsec;
+ priv->txstamp.sec = pts->sec;
+}
+
+/* out-of-band rx ts store */
+static void ptp_store_rxstamp(struct fec_ptp_private *priv,
+ struct ptp *p_ptp,
+ struct ptp_time *pts,
+ u32 events)
+{
+ int control = PTP_MSG_ALL_OTHER;
+ u16 seq_id;
+ struct fec_ptp_data_t tmp_rx_time;
+
+ /* out-of-band mode can't get seq_id */
+ seq_id = SEQ_ID_OUT_OF_BAND;
+
+ tmp_rx_time.key = ntohs(seq_id);
+ tmp_rx_time.ts_time.sec = pts->sec;
+ tmp_rx_time.ts_time.nsec = pts->nsec;
+ if (events & PTP_TS_RX_SYNC1)
+ control = PTP_MSG_SYNC;
+ else if (events & PTP_TS_RX_DELAY_REQ1)
+ control = PTP_MSG_DEL_REQ;
+ else if (events & PTP_TS_PDRQRE1)
+ control = PTP_MSG_P_DEL_REQ;
+ else if (events & PTP_TS_PDRSRE1)
+ control = PTP_MSG_DEL_RESP;
+
+ 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;
+
+ 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;
+ }
+
+ wake_up_interruptible(&ptp_rx_ts_wait);
+
+}
+
+/* in-band rx ts store */
+#ifdef CONFIG_IN_BAND
+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;
+ u64 timestamp;
+
+ /* Check for PTP Event */
+ if ((bdp->cbd_sc & BD_ENET_TX_PTP) == 0) {
+ skb_pull(skb, 8);
+ return;
+ }
+
+ /* Get ts from skb data */
+ timestamp = *((u64 *)(skb->data));
+ convert_rtc_time(&timestamp, &(tmp_rx_time.ts_time));
+ skb_pull(skb, 8);
+
+ 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);
+
+ 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);
+}
+#else
+void fec_ptp_store_rxstamp(struct fec_ptp_private *priv,
+ struct sk_buff *skb,
+ struct bufdesc *bdp)
+{
+}
+#endif
+
+/*PTP interrupt handler*/
+static irqreturn_t ptp_interrupt(int irq, void *dev_id)
+{
+ struct ptp *p_ptp = (struct ptp *)dev_id;
+ void __iomem *mem_map = p_ptp->mem_map;
+ struct ptp_time ps;
+ u64 timestamp;
+ u32 events, orig_events;
+
+ /*get valid events*/
+ events = readl(mem_map + PTP_TMR_PEVENT);
+ while (events) {
+ if (events & PTP_TS_TX_FRAME1) {
+ /*read timestamp from register*/
+ timestamp = ((u64)readl(mem_map + PTP_TMR_TXTS_H)
+ << 32) |
+ (readl(mem_map + PTP_TMR_TXTS_L));
+
+ /*clear event ASAP,hoping to prevent overrun*/
+ writel((u32)PTP_TS_TX_FRAME1,
+ mem_map + PTP_TMR_PEVENT);
+ /*check for overrun(which incalidates last timestamp)*/
+ events = readl(mem_map + PTP_TMR_PEVENT);
+
+ if (events & PTP_TS_TX_OVR1) {
+ /*lost synchronization with TX timestamps*/
+ /*clear overrun event*/
+ writel(PTP_TS_TX_OVR1,
+ mem_map + PTP_TMR_PEVENT);
+
+ p_ptp->tx_time_stamps_overrun++;
+ } else {
+ /*insert the Tx timestamps into the queue*/
+ convert_rtc_time(&timestamp, &ps);
+ ptp_store_txstamp(ptp_private, &ps);
+
+ /*this event is never really masked,
+ *but it should be reported only
+ *if includeed in usre events mask*/
+ if (p_ptp->events_mask & PTP_TS_TX_FRAME1)
+ p_ptp->tx_time_stamps++;
+ VDBG("tx interrupt\n");
+ }
+ }
+
+ /*typically only one of these events is relevant,
+ *depending on whether the device is PTP master or slave*/
+ if (events & PTP_TS_RX_ALL) {
+ /*out-of-band mode:read timestamp
+ *from registers*/
+ timestamp = ((u64)readl(mem_map + PTP_TMR_RXTS_H)
+ << 32) |
+ (readl(mem_map + PTP_TMR_RXTS_L));
+
+ /*clear event ASAP,hoping to prevent overrun*/
+ orig_events = events;
+ writel((u32)(PTP_TS_RX_ALL),
+ mem_map + PTP_TMR_PEVENT);
+
+ /*check for overrun (which invalidates
+ *last tiemstamp)*/
+ events = readl(mem_map + PTP_TMR_PEVENT);
+
+ if (events & PTP_TS_RX_OVR1) {
+ /*lost synchronization with Rx timestamp*/
+ /*clear overrun event. clear the
+ *timestamp event as well, because
+ *it may have arrived after it was
+ *cleared above,but still it is not
+ *synchronized with received frames*/
+ writel((u32)(PTP_TS_RX_ALL |
+ PTP_TS_RX_OVR1),
+ mem_map + PTP_TMR_RXTS_H);
+ p_ptp->rx_time_stamps_overrun++;
+ } else {
+ /*insert Rx timestamp into the queue*/
+ convert_rtc_time(&timestamp, &ps);
+ ptp_store_rxstamp(ptp_private, p_ptp,
+ &ps, orig_events);
+
+ /*the Rx TS event is never masked in
+ *out-of-ban mode,but it should be
+ *reported only if included in user's
+ *event's mask*/
+ if (p_ptp->events_mask &
+ (PTP_TS_RX_SYNC1 |
+ PTP_TS_RX_DELAY_REQ1))
+ p_ptp->rx_time_stamps++;
+ }
+ }
+
+ writel(~PTP_TMR_PEVENT_VALID, mem_map + PTP_TMR_PEVENT);
+ events = readl(mem_map + PTP_TMR_PEVENT);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void init_ptp_tsu(struct ptp *p_ptp)
+{
+ struct ptp_driver_param *drv_param = p_ptp->driver_param;
+ void __iomem *mem_map;
+ u32 tsmr, pemask, events_mask;
+
+ mem_map = p_ptp->mem_map;
+
+ /*Tx timestamp events are required in all modes*/
+ events_mask = (p_ptp->events_mask | PTP_TS_TX_FRAME1 |
+ PTP_TS_TX_OVR1);
+
+ /*read current values of TSU registers*/
+ tsmr = readl(mem_map + PTP_TSMR);
+ pemask = readl(mem_map + PTP_TMR_PEMASK);
+
+ if (drv_param->delivery_mode == e_PTP_TSU_DELIVERY_IN_BAND) {
+ tsmr |= PTP_TSMR_OPMODE1_IN_BAND;
+ events_mask &= ~(PTP_TS_TX_OVR1);
+ } else
+ /*rx timestamp events are required for out of band mode*/
+ events_mask |= (PTP_TS_RX_SYNC1 | PTP_TS_RX_DELAY_REQ1 |
+ PTP_TS_TX_OVR1);
+
+ pemask |= events_mask;
+
+ /*update TSU register*/
+ writel(tsmr, mem_map + PTP_TSMR);
+ writel(pemask, mem_map + PTP_TMR_PEMASK);
+}
+
+/* ptp module init */
+static void ptp_tsu_init(struct ptp *p_ptp)
+{
+ void __iomem *mem_map = p_ptp->mem_map;
+
+ /*initialization of registered PTP modules*/
+ init_ptp_parser(p_ptp);
+
+ /*reset timestamp*/
+ writel(0, mem_map + PTP_TSMR);
+ writel(0, mem_map + PTP_TMR_PEMASK);
+ writel(PTP_TMR_PEVENT_ALL, mem_map + PTP_TMR_PEVENT);
+
+}
+
+/* TSU configure function */
+static u32 ptp_tsu_enable(struct ptp *p_ptp)
+{
+ void __iomem *mem_map;
+ u32 tsmr;
+
+ /*enable the TSU*/
+ mem_map = p_ptp->mem_map;
+
+ /*set the TSU enable bit*/
+ tsmr = readl(mem_map + PTP_TSMR);
+ tsmr |= PTP_TSMR_EN1;
+
+ writel(tsmr, mem_map + PTP_TSMR);
+
+ return 0;
+}
+
+static u32 ptp_tsu_disable(struct ptp *p_ptp)
+{
+ void __iomem *mem_map;
+ u32 tsmr;
+
+ mem_map = p_ptp->mem_map;
+
+ tsmr = readl(mem_map + PTP_TSMR);
+ tsmr &= ~(PTP_TSMR_EN1);
+ writel(tsmr, mem_map + PTP_TSMR);
+
+ return 0;
+}
+
+static int ptp_tsu_config_events_mask(struct ptp *p_ptp,
+ u32 events_mask)
+{
+
+ p_ptp->events_mask = events_mask;
+
+ return 0;
+}
+
+/* rtc configure function */
+static u32 rtc_enable(struct ptp_rtc *rtc, bool reset_clock)
+{
+ u32 tmr_ctrl;
+
+ tmr_ctrl = readl(rtc->mem_map + PTP_TMR_CTRL);
+ if (reset_clock) {
+ writel((tmr_ctrl | RTC_TMR_CTRL_TMSR),
+ rtc->mem_map + PTP_TMR_CTRL);
+
+ /*clear TMR_OFF*/
+ writel(0, rtc->mem_map + PTP_TMR_OFF_L);
+ writel(0, rtc->mem_map + PTP_TMR_OFF_H);
+ }
+
+ writel((tmr_ctrl | RTC_TMR_CTRL_TE),
+ rtc->mem_map + PTP_TMR_CTRL);
+
+ return 0;
+}
+
+static u32 rtc_disable(struct ptp_rtc *rtc)
+{
+ u32 tmr_ctrl;
+
+ tmr_ctrl = readl(rtc->mem_map + PTP_TMR_CTRL);
+ writel((tmr_ctrl & ~RTC_TMR_CTRL_TE),
+ rtc->mem_map + PTP_TMR_CTRL);
+
+ return 0;
+}
+
+static u32 rtc_set_periodic_pulse(
+ struct ptp_rtc *rtc,
+ enum e_ptp_rtc_pulse_id pulse_ID,
+ u32 pulse_periodic)
+{
+ u32 factor;
+
+ if (rtc->start_pulse_on_alarm) {
+ /*from the spec:the ratio between the prescale register value
+ *and the fiper value should be decisable by the clock period
+ *FIPER_VALUE = (prescale_value * tclk_per * N) - tclk_per*/
+ factor = (u32)((pulse_periodic + rtc->clock_period_nansec) /
+ (rtc->clock_period_nansec * rtc->output_clock_divisor));
+
+ if ((factor * rtc->clock_period_nansec *
+ rtc->output_clock_divisor) <
+ (pulse_periodic + rtc->clock_period_nansec))
+ pulse_periodic = ((factor * rtc->clock_period_nansec *
+ rtc->output_clock_divisor) -
+ rtc->clock_period_nansec);
+ }
+
+ /* Decrease it to fix PPS question (frequecy error)*/
+ pulse_periodic -= rtc->clock_period_nansec;
+
+ writel((u32)pulse_periodic, rtc->mem_map + PTP_TMR_FIPER1 +
+ (pulse_ID * 4));
+ return 0;
+}
+
+static u32 ptp_rtc_set_periodic_pulse(
+ struct ptp *p_ptp,
+ enum e_ptp_rtc_pulse_id pulse_ID,
+ struct ptp_time *ptime)
+{
+ u32 ret;
+ u64 pulse_periodic;
+
+ if (pulse_ID >= PTP_RTC_NUM_OF_PULSES)
+ return -1;
+ if (ptime->nsec < 0)
+ return -1;
+
+ pulse_periodic = convert_unsigned_time(ptime);
+ if (pulse_periodic > 0xFFFFFFFF)
+ return -1;
+
+ ret = rtc_set_periodic_pulse(p_ptp->rtc, pulse_ID, (u32)pulse_periodic);
+
+ return ret;
+}
+
+static u32 rtc_set_alarm(
+ struct ptp_rtc *rtc,
+ enum e_ptp_rtc_alarm_id alarm_ID,
+ u64 alarm_time)
+{
+ u32 fiper;
+ int i;
+
+ if ((alarm_ID == e_PTP_RTC_ALARM_1) && rtc->start_pulse_on_alarm)
+ alarm_time -= (3 * rtc->clock_period_nansec);
+
+ /*TMR_ALARM_L must be written first*/
+ writel((u32)alarm_time, rtc->mem_map + PTP_TMR_ALARM1_L +
+ (alarm_ID * 4));
+ writel((u32)(alarm_time >> 32),
+ rtc->mem_map + PTP_TMR_ALARM1_H + (alarm_ID * 4));
+
+ if ((alarm_ID == e_PTP_RTC_ALARM_1) && rtc->start_pulse_on_alarm) {
+ /*we must write the TMR_FIPER register again(hardware
+ *constraint),From the spec:in order to keep tracking
+ *the prescale output clock each tiem before enabling
+ *the fiper,the user must reset the fiper by writing
+ *a new value to the reigster*/
+ for (i = 0; i < PTP_RTC_NUM_OF_PULSES; i++) {
+ fiper = readl(rtc->mem_map + PTP_TMR_FIPER1 +
+ (i * 4));
+ writel(fiper, rtc->mem_map + PTP_TMR_FIPER1 +
+ (i * 4));
+ }
+ }
+
+ return 0;
+}
+
+static u32 ptp_rtc_set_alarm(
+ struct ptp *p_ptp,
+ enum e_ptp_rtc_alarm_id alarm_ID,
+ struct ptp_time *ptime)
+{
+ u32 ret;
+ u64 alarm_time;
+
+ if (alarm_ID >= PTP_RTC_NUM_OF_ALARMS)
+ return -1;
+ if (ptime->nsec < 0)
+ return -1;
+
+ alarm_time = convert_unsigned_time(ptime);
+
+ ret = rtc_set_alarm(p_ptp->rtc, alarm_ID, alarm_time);
+
+ return ret;
+}
+
+/* rtc ioctl function */
+/*get the current time from RTC time counter register*/
+static void ptp_rtc_get_current_time(struct ptp *p_ptp,
+ struct ptp_time *p_time)
+{
+ u64 times;
+ struct ptp_rtc *rtc = p_ptp->rtc;
+
+ /*TMR_CNT_L must be read first to get an accurate value*/
+ times = (u64)readl(rtc->mem_map + PTP_TMR_CNT_L);
+ times |= ((u64)readl(rtc->mem_map + PTP_TMR_CNT_H)) << 32;
+
+ /*convert RTC time*/
+ convert_rtc_time(&times, p_time);
+}
+
+static void ptp_rtc_reset_counter(struct ptp *p_ptp, struct ptp_time *p_time)
+{
+ u64 times;
+ struct ptp_rtc *rtc = p_ptp->rtc;
+
+ times = convert_unsigned_time(p_time);
+ writel((u32)times, rtc->mem_map + PTP_TMR_CNT_L);
+ writel((u32)(times >> 32), rtc->mem_map + PTP_TMR_CNT_H);
+
+}
+
+static void rtc_modify_frequency_compensation(
+ struct ptp_rtc *rtc,
+ u32 freq_compensation)
+{
+ writel(freq_compensation, rtc->mem_map + PTP_TMR_ADD);
+}
+
+/* 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;
+}
+
+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_timestamp(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;
+
+#ifdef CONFIG_IN_BAND
+ key = pts->seq_id;
+#else
+ key = SEQ_ID_OUT_OF_BAND;
+#endif
+ 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;
+ }
+}
+
+/* 1588 Module start */
+int fec_ptp_start(struct fec_ptp_private *priv)
+{
+ struct ptp *p_ptp = ptp_dev;
+
+ /* Enable TSU clk */
+ clk_enable(p_ptp->clk);
+
+ /*initialize the TSU using the register function*/
+ init_ptp_tsu(p_ptp);
+
+ /* start counter */
+ p_ptp->fpp = ptp_private;
+ ptp_tsu_enable(p_ptp);
+
+ 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 ptp *p_ptp = ptp_dev;
+
+ /* stop counter */
+ ptp_tsu_disable(p_ptp);
+ clk_disable(p_ptp->clk);
+
+ return;
+}
+
+static int ptp_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int ptp_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/* ptp device ioctl function */
+static int ptp_ioctl(
+ struct inode *inode,
+ 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;
+ int retval = 0;
+
+ priv = (struct fec_ptp_private *) ptp_private;
+ switch (cmd) {
+ case PTP_GET_RX_TIMESTAMP:
+ p_ts = (struct ptp_ts_data *)arg;
+ retval = fec_get_rx_timestamp(priv, p_ts, &rx_time);
+ if (retval == 0)
+ retval = 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);
+ retval = copy_to_user((void __user *)(&(p_ts->ts)),
+ &tx_time, sizeof(tx_time));
+ break;
+ case PTP_GET_CURRENT_TIME:
+ ptp_rtc_get_current_time(ptp_dev, &(curr_time.rtc_time));
+ retval = copy_to_user((void __user *)arg, &curr_time,
+ sizeof(curr_time));
+ break;
+ case PTP_SET_RTC_TIME:
+ cnt = (struct ptp_rtc_time *)arg;
+ ptp_rtc_reset_counter(ptp_dev, &(cnt->rtc_time));
+ 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;
+ rtc_modify_frequency_compensation(ptp_dev->rtc,
+ p_comp->freq_compensation);
+ break;
+ case PTP_GET_ORIG_COMP:
+ ((struct ptp_get_comp *)arg)->dw_origcomp =
+ ptp_dev->orig_freq_comp;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return retval;
+}
+
+static const struct file_operations ptp_fops = {
+ .owner = THIS_MODULE,
+ .llseek = NULL,
+ .read = NULL,
+ .write = NULL,
+ .ioctl = ptp_ioctl,
+ .open = ptp_open,
+ .release = ptp_release,
+};
+
+static int init_ptp_driver(struct ptp *p_ptp)
+{
+ struct ptp_time ptime;
+ int ret;
+
+ /* configure RTC param */
+ ret = ptp_rtc_config(p_ptp->rtc);
+ if (ret)
+ return -1;
+
+ /* initialize RTC register */
+ ptp_rtc_init(p_ptp);
+
+ /* initialize PTP TSU param */
+ ptp_param_config(p_ptp);
+
+ /* set TSU configuration parameters */
+#ifdef CONFIG_IN_BAND
+ p_ptp->driver_param->delivery_mode = e_PTP_TSU_DELIVERY_IN_BAND;
+#else
+ p_ptp->driver_param->delivery_mode = e_PTP_TSU_DELIVERY_OUT_OF_BAND;
+#endif
+
+ if (ptp_tsu_config_events_mask(p_ptp, DEFAULT_events_PTP_Mask))
+ goto end;
+
+ /* initialize PTP TSU register */
+ ptp_tsu_init(p_ptp);
+
+ /* set periodic pulses */
+ ptime.sec = USE_CASE_PULSE_1_PERIOD / NANOSEC_IN_SEC;
+ ptime.nsec = USE_CASE_PULSE_1_PERIOD % NANOSEC_IN_SEC;
+ ret = ptp_rtc_set_periodic_pulse(p_ptp, e_PTP_RTC_PULSE_1, &ptime);
+ if (ret)
+ goto end;
+
+ ptime.sec = USE_CASE_PULSE_2_PERIOD / NANOSEC_IN_SEC;
+ ptime.nsec = USE_CASE_PULSE_2_PERIOD % NANOSEC_IN_SEC;
+ ret = ptp_rtc_set_periodic_pulse(p_ptp, e_PTP_RTC_PULSE_2, &ptime);
+ if (ret)
+ goto end;
+
+ ptime.sec = USE_CASE_PULSE_3_PERIOD / NANOSEC_IN_SEC;
+ ptime.nsec = USE_CASE_PULSE_3_PERIOD % NANOSEC_IN_SEC;
+ ret = ptp_rtc_set_periodic_pulse(p_ptp, e_PTP_RTC_PULSE_3, &ptime);
+ if (ret)
+ goto end;
+
+ /* set alarm */
+ ptime.sec = (USE_CASE_ALARM_1_TIME / NANOSEC_IN_SEC);
+ ptime.nsec = (USE_CASE_ALARM_1_TIME % NANOSEC_IN_SEC);
+ ret = ptp_rtc_set_alarm(p_ptp, e_PTP_RTC_ALARM_1, &ptime);
+ if (ret)
+ goto end;
+
+ ptime.sec = (USE_CASE_ALARM_2_TIME / NANOSEC_IN_SEC);
+ ptime.nsec = (USE_CASE_ALARM_2_TIME % NANOSEC_IN_SEC);
+ ret = ptp_rtc_set_alarm(p_ptp, e_PTP_RTC_ALARM_2, &ptime);
+ if (ret)
+ goto end;
+
+ /* enable the RTC */
+ ret = rtc_enable(p_ptp->rtc, FALSE);
+ if (ret)
+ goto end;
+
+ udelay(10);
+ ptp_rtc_get_current_time(p_ptp, &ptime);
+ if (ptime.nsec == 0) {
+ printk(KERN_ERR "PTP RTC is not running\n");
+ goto end;
+ }
+
+ if (register_chrdev(PTP_MAJOR, "ptp", &ptp_fops))
+ printk(KERN_ERR "Unable to register PTP device as char\n");
+ else
+ printk(KERN_INFO "Register PTP as char device\n");
+
+end:
+ return ret;
+}
+
+static void ptp_free(void)
+{
+ rtc_disable(ptp_dev->rtc);
+ /*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 = priv;
+
+ 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);
+
+/* probe just register memory and irq */
+static int __devinit
+ptp_probe(struct platform_device *pdev)
+{
+ int i, irq, ret = 0;
+ struct resource *r;
+
+ /* setup board info structure */
+ ptp_dev = kzalloc(sizeof(struct ptp), GFP_KERNEL);
+ if (!ptp_dev) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+ ptp_dev->rtc = kzalloc(sizeof(struct ptp_rtc),
+ GFP_KERNEL);
+ if (!ptp_dev->rtc) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ /* PTP register memory */
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ ret = -ENXIO;
+ goto err3;
+ }
+
+ r = request_mem_region(r->start, resource_size(r), pdev->name);
+ if (!r) {
+ ret = -EBUSY;
+ goto err3;
+ }
+
+ ptp_dev->mem_map = ioremap(r->start, resource_size(r));
+ if (!ptp_dev->mem_map) {
+ ret = -ENOMEM;
+ goto failed_ioremap;
+ }
+
+ /* RTC register memory */
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!r) {
+ ret = -ENXIO;
+ goto err4;
+ }
+
+ r = request_mem_region(r->start, resource_size(r), "PTP_RTC");
+ if (!r) {
+ ret = -EBUSY;
+ goto err4;
+ }
+
+ ptp_dev->rtc->mem_map = ioremap(r->start, resource_size(r));
+
+ if (!ptp_dev->rtc->mem_map) {
+ ret = -ENOMEM;
+ goto failed_ioremap1;
+ }
+
+ /* This device has up to two irqs on some platforms */
+ for (i = 0; i < 2; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (i && irq < 0)
+ break;
+ if (i == 0)
+ ret = request_irq(irq, ptp_interrupt,
+ IRQF_DISABLED, pdev->name, ptp_dev);
+ else
+ ret = request_irq(irq, ptp_rtc_interrupt,
+ IRQF_DISABLED, "ptp_rtc", ptp_dev);
+ if (ret) {
+ while (i >= 0) {
+ irq = platform_get_irq(pdev, i);
+ free_irq(irq, ptp_dev);
+ i--;
+ }
+ goto failed_irq;
+ }
+ }
+
+ ptp_dev->rtc->clk = clk_get(NULL, "ieee_rtc_clk");
+ if (IS_ERR(ptp_dev->rtc->clk)) {
+ ret = PTR_ERR(ptp_dev->rtc->clk);
+ goto failed_clk1;
+ }
+
+ ptp_dev->clk = clk_get(&pdev->dev, "ieee_1588_clk");
+ if (IS_ERR(ptp_dev->clk)) {
+ ret = PTR_ERR(ptp_dev->clk);
+ goto failed_clk2;
+ }
+
+ clk_enable(ptp_dev->clk);
+
+ init_ptp_driver(ptp_dev);
+ clk_disable(ptp_dev->clk);
+
+ return 0;
+
+failed_clk2:
+ clk_put(ptp_dev->rtc->clk);
+failed_clk1:
+ for (i = 0; i < 2; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq > 0)
+ free_irq(irq, ptp_dev);
+ }
+failed_irq:
+ iounmap((void __iomem *)ptp_dev->rtc->mem_map);
+failed_ioremap1:
+err4:
+ iounmap((void __iomem *)ptp_dev->mem_map);
+failed_ioremap:
+err3:
+ kfree(ptp_dev->rtc);
+err2:
+ kfree(ptp_dev);
+err1:
+ return ret;
+}
+
+static int __devexit
+ptp_drv_remove(struct platform_device *pdev)
+{
+ clk_disable(ptp_dev->clk);
+ clk_put(ptp_dev->clk);
+ clk_put(ptp_dev->rtc->clk);
+ iounmap((void __iomem *)ptp_dev->rtc->mem_map);
+ iounmap((void __iomem *)ptp_dev->mem_map);
+ kfree(ptp_dev->rtc->driver_param);
+ kfree(ptp_dev->rtc);
+ kfree(ptp_dev->driver_param);
+ kfree(ptp_dev);
+ return 0;
+}
+
+static struct platform_driver ptp_driver = {
+ .driver = {
+ .name = "ptp",
+ .owner = THIS_MODULE,
+ },
+ .probe = ptp_probe,
+ .remove = __devexit_p(ptp_drv_remove),
+};
+
+static int __init
+ptp_module_init(void)
+{
+ printk(KERN_INFO "iMX PTP Driver\n");
+
+ return platform_driver_register(&ptp_driver);
+}
+
+static void __exit
+ptp_cleanup(void)
+{
+ platform_driver_unregister(&ptp_driver);
+}
+
+module_exit(ptp_cleanup);
+module_init(ptp_module_init);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/imx_ptp.h b/drivers/net/imx_ptp.h
new file mode 100644
index 000000000000..53a49779010c
--- /dev/null
+++ b/drivers/net/imx_ptp.h
@@ -0,0 +1,356 @@
+/*
+ * drivers/net/imx_ptp.h
+ *
+ * Copyright (C) 2010 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Description: IEEE 1588 driver supporting imx5 Fast Ethernet Controller.
+ *
+ * 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 _PTP_H_
+#define _PTP_H_
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define PTP_NUM_OF_PORTS 2
+#define SEQ_ID_OUT_OF_BAND 0xFFFF
+
+/* PTP message types */
+enum e_ptp_message {
+ e_PTP_MSG_SYNC = 0, /*Sync message*/
+ e_PTP_MSG_DELAY_REQ, /*Dealy_req message*/
+ e_PTP_MSG_FOLLOW_UP, /*Follow_up message*/
+ e_PTP_MSG_DELAY_RESP, /*Delay_resp message*/
+ e_PTP_MSG_MANAGEMENT, /*Management message*/
+ e_PTP_MSG_DUMMY_LAST
+};
+
+/* PTP time stamp delivery mode*/
+enum e_ptp_tsu_delivery_mode {
+ e_PTP_TSU_DELIVERY_IN_BAND, /*in-band time-sttamp delivery mode*/
+ e_PTP_TSU_DELIVERY_OUT_OF_BAND /*out-of-band time stamp delivery mode*/
+};
+
+/*PTP RTC Alarm Polarity Options*/
+enum e_ptp_rtc_alarm_polarity {
+ e_PTP_RTC_ALARM_POLARITY_ACTIVE_HIGH, /*active-high output polarity*/
+ e_PTP_RTC_ALARM_POLARITY_ACTIVE_LOW /*active-low output polarity*/
+};
+
+/*PTP RTC Trigger Polarity Options*/
+enum e_ptp_rtc_trig_polarity {
+ e_PTP_RTC_TRIGGER_ON_RISING_EDGE, /*trigger on rising edge*/
+ e_PTP_RTC_TRIGGER_ON_FALLING_EDGE /*trigger on falling edge*/
+};
+
+/*PTP RTC Periodic Pulse Start Mode*/
+enum e_ptp_rtc_pulse_start_mode {
+ e_PTP_RTC_PULSE_START_AUTO, /*start pulse when RTC is enabled*/
+ e_PTP_RTC_PULSE_START_ON_ALARM /*start pulse on alarm 1 event*/
+};
+
+/*PTP RTC Alarm ID*/
+enum e_ptp_rtc_alarm_id {
+ e_PTP_RTC_ALARM_1 = 0, /*alarm signal 1*/
+ e_PTP_RTC_ALARM_2, /*slarm signal 2*/
+ e_PTP_RTC_ALARM_DUMMY_LAST
+};
+#define PTP_RTC_NUM_OF_ALARMS e_PTP_RTC_ALARM_DUMMY_LAST
+
+/*PTP RTC Periodic Pulse ID*/
+enum e_ptp_rtc_pulse_id {
+ e_PTP_RTC_PULSE_1 = 0, /*periodic pulse 1*/
+ e_PTP_RTC_PULSE_2, /*periodic pulse 2*/
+ e_PTP_RTC_PULSE_3, /*periodic pulse 3*/
+ e_PTP_RTC_PULSE_DUMMY_LASR
+};
+#define PTP_RTC_NUM_OF_PULSES e_PTP_RTC_PULSE_DUMMY_LASR
+
+/*PTP RTC External trigger ID*/
+enum e_ptp_rtc_trigger_id {
+ e_PTP_RTC_TRIGGER_1 = 0, /*External trigger 1*/
+ e_PTP_RTC_TRIGGER_2, /*External trigger 2*/
+ e_PTP_RTC_TRIGGER_DUMMY_LAST
+};
+#define PTP_RTC_NUM_OF_TRIGGERS e_PTP_RTC_TRIGGER_DUMMY_LAST
+
+/* PTP register definition */
+#define PTP_TSPDR1 0x0
+#define PTP_TSPDR2 0x4
+#define PTP_TSPDR3 0x8
+#define PTP_TSPDR4 0xc
+#define PTP_TSPOV 0x10
+#define PTP_TSMR 0x14
+#define PTP_TMR_PEVENT 0x18
+#define PTP_TMR_PEMASK 0x1c
+#define PTP_TMR_RXTS_H 0x20
+#define PTP_TMR_RXTS_L 0x30
+#define PTP_TMR_TXTS_H 0x40
+#define PTP_TMR_TXTS_L 0x50
+#define PTP_TSPDR5 0x60
+#define PTP_TSPDR6 0x64
+#define PTP_TSPDR7 0x68
+
+/* RTC register definition */
+#define PTP_TMR_CTRL 0x0
+#define PTP_TMR_TEVENT 0x4
+#define PTP_TMR_TEMASK 0x8
+#define PTP_TMR_CNT_L 0xc
+#define PTP_TMR_CNT_H 0x10
+#define PTP_TMR_ADD 0x14
+#define PTP_TMR_ACC 0x18
+#define PTP_TMR_PRSC 0x1c
+#define PTP_TMR_OFF_L 0x20
+#define PTP_TMR_OFF_H 0x24
+#define PTP_TMR_ALARM1_L 0x28
+#define PTP_TMR_ALARM1_H 0x2c
+#define PTP_TMR_ALARM2_L 0x30
+#define PTP_TMR_ALARM2_H 0x34
+#define PTP_TMR_FIPER1 0x38
+#define PTP_TMR_FIPER2 0x3c
+#define PTP_TMR_FIPER3 0x40
+#define PTP_TMR_ETTS1_L 0x44
+#define PTP_TMR_ETTS1_H 0x48
+#define PTP_TMR_ETTS2_L 0x4c
+#define PTP_TMR_ETTS2_H 0x50
+#define PTP_TMR_FSV_L 0x54
+#define PTP_TMR_FSV_H 0x58
+
+/* PTP parser registers*/
+#define PTP_TSPDR1_ETT_MASK 0xFFFF0000
+#define PTP_TSPDR1_IPT_MASK 0x0000FF00
+#define PTP_TSPDR1_ETT_SHIFT 16
+#define PTP_TSPDR1_IPT_SHIFT 8
+
+#define PTP_TSPDR2_DPNGE_MASK 0xFFFF0000
+#define PTP_TSPDR2_DPNEV_MASK 0x0000FFFF
+#define PTP_TSPDR2_DPNGE_SHIFT 16
+
+#define PTP_TSPDR3_SYCTL_MASK 0xFF000000
+#define PTP_TSPDR3_DRCTL_MASK 0x00FF0000
+#define PTP_TSPDR3_DRPCTL_MASK 0x0000FF00
+#define PTP_TSPDR3_FUCTL_MASK 0x000000FF
+#define PTP_TSPDR3_SYCTL_SHIFT 24
+#define PTP_TSPDR3_DRCTL_SHIFT 16
+#define PTP_TSPDR3_DRPCTL_SHIFT 8
+
+#define PTP_TSPDR4_MACTL_MASK 0xFF000000
+#define PTP_TSPDR4_VLAN_MASK 0x0000FFFF
+#define PTP_TSPDR4_MACTL_SHIFT 24
+
+/*PTP Parsing Offset Values*/
+#define PTP_TSPOV_ETTOF_MASK 0xFF000000
+#define PTP_TSPOV_IPTOF_MASK 0x00FF0000
+#define PTP_TSPOV_UDOF_MASK 0x0000FF00
+#define PTP_TSPOV_PTOF_MASK 0x000000FF
+#define PTP_TSPOV_ETTOF_SHIFT 24
+#define PTP_TSPOV_IPTOF_SHIFT 16
+#define PTP_TSPOV_UDOF_SHIFT 8
+
+/*PTP Mode register*/
+#define PTP_TSMR_OPMODE1_IN_BAND 0x00080000
+#define PTP_TSMR_OPMODE2_IN_BAND 0x00040000
+#define PTP_TSMR_OPMODE3_IN_BAND 0x00020000
+#define PTP_TSMR_OPMODE4_IN_BAND 0x00010000
+#define PTP_TSMR_EN1 0x00000008
+#define PTP_TSMR_EN2 0x00000004
+#define PTP_TSMR_EN3 0x00000002
+#define PTP_TSMR_EN4 0x00000001
+
+/*ptp tsu event register*/
+#define PTP_TS_EXR 0x80000000 /*rx, EX to receiver */
+#define PTP_TS_RX_OVR1 0x40000000 /*rx,overrun */
+#define PTP_TS_TX_OVR1 0x20000000 /*tx,overrun */
+#define PTP_TS_RX_SYNC1 0x10000000 /*rx,Sync Frame */
+#define PTP_TS_RX_DELAY_REQ1 0x08000000 /*rx,dealy_req frame */
+#define PTP_TS_TX_FRAME1 0x04000000 /*tx,PTP frame */
+#define PTP_TS_PDRQRE1 0x02000000 /*rx,Pdelay_Req frame */
+#define PTP_TS_PDRSRE1 0x01000000 /*rx,Pdelay_Resp frame */
+#define PTP_TS_EXT 0x00800000 /*tx, EX to transmitter */
+#define DEFAULT_events_PTP_Mask 0xFF800000
+#define PTP_TMR_PEVENT_ALL DEFAULT_events_PTP_Mask
+#define PTP_TMR_PEVENT_VALID 0x7F000000
+
+#define PTP_TS_RX_ALL \
+ (PTP_TS_RX_SYNC1 |\
+ PTP_TS_RX_DELAY_REQ1 |\
+ PTP_TS_PDRQRE1 |\
+ PTP_TS_PDRSRE1)
+
+/*RTC timer control register*/
+#define RTC_TMR_CTRL_ALMP1 0x80000000 /*active low output*/
+#define RTC_TMR_CTRL_ALMP2 0x40000000 /*active low output*/
+#define RTC_TMR_CTRL_FS 0x10000000
+#define RTC_TMR_CTRL_TCLK_PERIOD_MSK 0x03FF0000
+#define RTC_TMR_CTRL_ETEP2 0x00000200
+#define RTC_TMR_CTRL_ETEP1 0x00000100
+#define RTC_TMR_CTRL_COPH 0x00000080
+#define RTC_TMR_CTRL_CIPH 0x00000040
+#define RTC_TMR_CTRL_TMSR 0x00000020
+#define RTC_TMR_CTRL_DBG 0x00000010
+#define RTC_TMR_CTRL_BYP 0x00000008
+#define RTC_TMR_CTRL_TE 0x00000004
+#define RTC_TMR_CTRL_CKSEL_TX_CLK 0x00000002
+#define RTC_TMR_CTRL_CKSEL_QE_CLK 0x00000001
+#define RTC_TMR_CTRL_CKSEL_EXT_CLK 0x00000000
+#define RTC_TMR_CTRL_TCLK_PERIOD_SHIFT 16
+
+/*RTC event register*/
+#define RTC_TEVENT_EXT_TRIGGER_2_TS 0x02000000 /*External trigger2 TS*/
+#define RTC_TEVENT_EXT_TRIGGER_1_TS 0x01000000 /*External trigger1 TS*/
+#define RTC_TEVENT_ALARM_2 0x00020000 /*Alarm 2*/
+#define RTC_TEVENT_ALARM_1 0x00010000 /*Alarm 1*/
+#define RTC_TEVENT_PERIODIC_PULSE_1 0x00000080 /*Periodic pulse 1*/
+#define RTC_TEVENT_PERIODIC_PULSE_2 0x00000040 /*Periodic pulse 2*/
+#define RTC_TEVENT_PERIODIC_PULSE_3 0x00000020 /*Periodic pulse 3*/
+
+#define RTC_EVENT_ALL \
+ (RTC_TEVENT_EXT_TRIGGER_2_TS |\
+ RTC_TEVENT_EXT_TRIGGER_1_TS |\
+ RTC_TEVENT_ALARM_2 |\
+ RTC_TEVENT_ALARM_1 |\
+ RTC_TEVENT_PERIODIC_PULSE_1 |\
+ RTC_TEVENT_PERIODIC_PULSE_2 |\
+ RTC_TEVENT_PERIODIC_PULSE_3)
+
+#define OFFSET_RTC 0x0000
+#define OFFSET_PTP1 0x0080
+#define OFFSET_PTP2 0x0100
+
+#define NUM_OF_MODULE 2
+
+/*General definitions*/
+#define NANOSEC_PER_ONE_HZ_TICK 1000000000
+#define NANOSEC_IN_SEC NANOSEC_PER_ONE_HZ_TICK
+#define MIN_RTC_CLK_FREQ_HZ 1000
+#define MHZ 1000000
+#define PTP_RTC_FREQ 50 /*MHz*/
+
+/*PTP driver's default values*/
+#define ETH_TYPE_VALUE 0x0800 /*IP frame*/
+#define VLAN_TYPE_VALUE 0x8100
+#define IP_TYPE_VALUE 0x11 /*UDP frame*/
+#define UDP_GENERAL_PORT 320
+#define UDP_EVENT_PORT 319
+#define ETH_TYPE_OFFSET 12
+#define IP_TYPE_OFFSET 23
+#define UDP_DEST_PORT_OFFSET 36
+#define PTP_TYPE_OFFSET 74
+#define PTP_SEQUENCE_OFFSET 72
+#define LENGTH_OF_TS 8
+
+#define PTP_TX 0x00800000
+#define PTP_RX 0x02000000
+
+/*RTC default values*/
+#define DEFAULT_SRC_CLOCK 0 /*external clock source*/
+#define DEFAULT_BYPASS_COMPENSATION FALSE
+#define DEFAULT_INVERT_INPUT_CLK_PHASE FALSE
+#define DEFAULT_INVERT_OUTPUT_CLK_PHASE FALSE
+#define DEFAULT_OUTPUT_CLOCK_DIVISOR 0x0001
+#define DEFAULT_ALARM_POLARITY e_PTP_RTC_ALARM_POLARITY_ACTIVE_HIGH
+#define DEFAULT_TRIGGER_POLARITY e_PTP_RTC_TRIGGER_ON_RISING_EDGE
+#define DEFAULT_PULSE_START_MODE e_PTP_RTC_PULSE_START_AUTO
+#define DEFAULT_EVENTS_RTC_MASK RTC_EVENT_ALL
+
+/*PTP default message type*/
+#define DEFAULT_MSG_SYNC e_PTP_MSG_SYNC
+#define DEFAULT_MSG_DELAY_REQ e_PTP_MSG_DELAY_REQ
+#define DEFAULT_MSG_FOLLOW_UP e_PTP_MSG_FOLLOW_UP
+#define DEFAULT_MSG_DELAY_RESP e_PTP_MSG_DELAY_RESP
+#define DEFAULT_MSG_MANAGEMENT e_PTP_MSG_MANAGEMENT
+
+#define USE_CASE_PULSE_1_PERIOD (NANOSEC_IN_SEC)
+#define USE_CASE_PULSE_2_PERIOD (NANOSEC_IN_SEC / 2)
+#define USE_CASE_PULSE_3_PERIOD (NANOSEC_IN_SEC / 4)
+
+#define USE_CASE_ALARM_1_TIME (NANOSEC_IN_SEC)
+#define USE_CASE_ALARM_2_TIME (NANOSEC_IN_SEC * 2)
+
+#define UCC_PTP_ENABLE 0x40000000
+
+struct ptp_rtc_driver_param {
+ u32 src_clock;
+ u32 src_clock_freq_hz;
+ u32 rtc_freq_hz;
+ bool invert_input_clk_phase;
+ bool invert_output_clk_phase;
+ u32 events_mask;
+ enum e_ptp_rtc_pulse_start_mode pulse_start_mode;
+ enum e_ptp_rtc_alarm_polarity alarm_polarity[PTP_RTC_NUM_OF_ALARMS];
+ enum e_ptp_rtc_trig_polarity trigger_polarity[PTP_RTC_NUM_OF_TRIGGERS];
+};
+
+struct ptp_rtc {
+ void __iomem *mem_map; /*pointer to RTC mem*/
+ bool bypass_compensation; /*is bypass?*/
+ bool start_pulse_on_alarm; /*start on alarm 1*/
+ u32 clock_period_nansec; /*clock periodic in ns*/
+ u16 output_clock_divisor; /*clock divisor*/
+ struct ptp_rtc_driver_param *driver_param; /*driver parameters*/
+ u32 rtc_irq;
+ struct clk *clk;
+ struct {
+ void *ext_trig_timestamp_queue;
+ } ext_trig_ts[2]; /*external trigger ts*/
+};
+
+/*PTP driver parameters*/
+struct ptp_driver_param {
+ u16 eth_type_value;
+ u16 vlan_type_value;
+ u16 udp_general_port;
+ u16 udp_event_port;
+ u8 ip_type_value;
+ u8 eth_type_offset;
+ u8 ip_type_offset;
+ u8 udp_dest_port_offset; /*offset of UDP destination port*/
+ u8 ptp_type_offset;
+ u8 ptp_msg_codes[e_PTP_MSG_DUMMY_LAST];
+
+ enum e_ptp_tsu_delivery_mode delivery_mode;
+};
+
+/*PTP control structure*/
+struct ptp {
+ spinlock_t lock;
+ void __iomem *mem_map;
+
+ /*TSU*/
+ u32 events_mask;
+ struct fec_ptp_private *fpp;
+ struct clk *clk;
+
+ /*RTC*/
+ struct ptp_rtc *rtc; /*pointer to RTC control structure*/
+ u32 orig_freq_comp; /*the initial frequency compensation*/
+
+ /*driver parameters*/
+ struct ptp_driver_param *driver_param;
+
+ u32 tx_time_stamps;
+ u32 rx_time_stamps;
+ u32 tx_time_stamps_overrun;
+ u32 rx_time_stamps_overrun;
+ u32 alarm_counters[PTP_RTC_NUM_OF_ALARMS];
+ u32 pulse_counters[PTP_RTC_NUM_OF_PULSES];
+};
+
+#endif