summaryrefslogtreecommitdiff
path: root/drivers/net/imx_ptp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/imx_ptp.c')
-rw-r--r--drivers/net/imx_ptp.c1748
1 files changed, 1748 insertions, 0 deletions
diff --git a/drivers/net/imx_ptp.c b/drivers/net/imx_ptp.c
new file mode 100644
index 000000000000..8580b1c14a66
--- /dev/null
+++ b/drivers/net/imx_ptp.c
@@ -0,0 +1,1748 @@
+/*
+ * 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);
+static DECLARE_WAIT_QUEUE_HEAD(ptp_tx_ts_wait);
+#define PTP_GET_RX_TIMEOUT (HZ/10)
+#define PTP_GET_TX_TIMEOUT (HZ/100)
+
+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, int size)
+{
+ ptp_buf->buf = vmalloc(size * 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, int size)
+{
+ const int front = buf->head;
+ const int end = buf->tail;
+ 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, int size)
+{
+ if (fec_ptp_nelems(buf, size) == (size - 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,
+ int size)
+{
+ struct fec_ptp_data_t *tmp;
+
+ if (fec_ptp_is_full(ptp_buf, size))
+ return 1;
+
+ spin_lock(&priv->ptp_lock);
+ tmp = (struct fec_ptp_data_t *)(ptp_buf->buf) + ptp_buf->tail;
+
+ tmp->key = data->key;
+ memcpy(tmp->spid, data->spid, 10);
+ tmp->ts_time.sec = data->ts_time.sec;
+ tmp->ts_time.nsec = data->ts_time.nsec;
+
+ ptp_buf->tail = fec_ptp_calc_index(size, ptp_buf->tail, 1);
+ spin_unlock(&priv->ptp_lock);
+
+ return 0;
+}
+
+static int fec_ptp_find_and_remove(struct circ_buf *ptp_buf,
+ struct fec_ptp_data_t *data,
+ struct fec_ptp_private *priv,
+ int size)
+{
+ int i;
+ 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 == data->key &&
+ !memcmp(tmp->spid, data->spid, 10))
+ 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 */
+#ifdef CONFIG_IN_BAND
+void fec_ptp_store_txstamp(struct fec_ptp_private *priv,
+ struct sk_buff *skb,
+ struct bufdesc *bdp)
+{
+ int msg_type, seq_id, control;
+ struct fec_ptp_data_t tmp_tx_time, tmp;
+ struct fec_ptp_private *fpp = priv;
+ struct ptp *p_ptp = ptp_dev;
+ int flag;
+ unsigned char *sp_id;
+ unsigned short portnum;
+ u64 timestamp;
+
+ /* Check for PTP Event */
+ if ((bdp->cbd_sc & BD_ENET_TX_PTP) == 0)
+ return;
+
+ /* Get ts from tx ts queue */
+ memset(&tmp, 0, sizeof(struct fec_ptp_data_t));
+ tmp.key = SEQ_ID_OUT_OF_BAND;
+ flag = fec_ptp_find_and_remove(&(priv->txstamp), &tmp,
+ priv, DEFAULT_PTP_TX_BUF_SZ);
+ if (!flag) {
+ tmp_tx_time.ts_time.sec = tmp.ts_time.sec;
+ tmp_tx_time.ts_time.nsec = tmp.ts_time.nsec;
+ } else {
+ /*read timestamp from register*/
+ timestamp = ((u64)readl(p_ptp->mem_map + PTP_TMR_TXTS_H)
+ << 32) |
+ (readl(p_ptp->mem_map + PTP_TMR_TXTS_L));
+ convert_rtc_time(&timestamp, &(tmp_tx_time.ts_time));
+ }
+
+ seq_id = *((u16 *)(skb->data + FEC_PTP_SEQ_ID_OFFS));
+ control = *((u8 *)(skb->data + FEC_PTP_CTRL_OFFS));
+ sp_id = skb->data + FEC_PTP_SPORT_ID_OFFS;
+ portnum = ntohs(*((unsigned short *)(sp_id + 8)));
+
+ tmp_tx_time.key = ntohs(seq_id);
+ memcpy(tmp_tx_time.spid, sp_id, 8);
+ memcpy(tmp_tx_time.spid + 8, (unsigned char *)&portnum, 2);
+
+ switch (control) {
+
+ case PTP_MSG_SYNC:
+ fec_ptp_insert(&(priv->tx_time_sync), &tmp_tx_time, priv,
+ DEFAULT_PTP_TX_BUF_SZ);
+ break;
+
+ case PTP_MSG_DEL_REQ:
+ fec_ptp_insert(&(priv->tx_time_del_req), &tmp_tx_time, priv,
+ DEFAULT_PTP_TX_BUF_SZ);
+ 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->tx_time_pdel_req), &tmp_tx_time,
+ priv, DEFAULT_PTP_TX_BUF_SZ);
+ break;
+ case PTP_MSG_P_DEL_RESP:
+ fec_ptp_insert(&(priv->tx_time_pdel_resp), &tmp_tx_time,
+ priv, DEFAULT_PTP_TX_BUF_SZ);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ wake_up_interruptible(&ptp_tx_ts_wait);
+}
+#else
+void fec_ptp_store_txstamp(struct fec_ptp_private *priv,
+ struct sk_buff *skb,
+ struct bufdesc *bdp)
+{
+}
+#endif
+
+static void ptp_store_txstamp(struct fec_ptp_private *priv,
+ struct ptp *p_ptp,
+ struct ptp_time *pts,
+ u32 events)
+{
+ struct fec_ptp_data_t tmp_tx_time;
+ u16 seq_id;
+
+ seq_id = SEQ_ID_OUT_OF_BAND;
+
+ memset(&tmp_tx_time, 0, sizeof(struct fec_ptp_data_t));
+ tmp_tx_time.key = ntohs(seq_id);
+ tmp_tx_time.ts_time.sec = pts->sec;
+ tmp_tx_time.ts_time.nsec = pts->nsec;
+ fec_ptp_insert(&(priv->txstamp), &tmp_tx_time,
+ priv, DEFAULT_PTP_TX_BUF_SZ);
+}
+
+/* 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;
+
+ memset(&tmp_rx_time, 0, sizeof(struct fec_ptp_data_t));
+ 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, DEFAULT_PTP_RX_BUF_SZ);
+ break;
+
+ case PTP_MSG_DEL_REQ:
+ fec_ptp_insert(&(priv->rx_time_del_req), &tmp_rx_time,
+ priv, DEFAULT_PTP_RX_BUF_SZ);
+ break;
+
+ case PTP_MSG_P_DEL_REQ:
+ fec_ptp_insert(&(priv->rx_time_pdel_req), &tmp_rx_time,
+ priv, DEFAULT_PTP_RX_BUF_SZ);
+ break;
+
+ case PTP_MSG_P_DEL_RESP:
+ fec_ptp_insert(&(priv->rx_time_pdel_resp), &tmp_rx_time,
+ priv, DEFAULT_PTP_RX_BUF_SZ);
+ 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;
+ unsigned char *sp_id;
+ unsigned short portnum;
+
+ /* Check for PTP Event */
+ if ((bdp->cbd_sc & BD_ENET_RX_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));
+ sp_id = skb->data + FEC_PTP_SPORT_ID_OFFS;
+ portnum = ntohs(*((unsigned short *)(sp_id + 8)));
+
+ tmp_rx_time.key = ntohs(seq_id);
+ memcpy(tmp_rx_time.spid, sp_id, 8);
+ memcpy(tmp_rx_time.spid + 8, (unsigned char *)&portnum, 2);
+
+ switch (control) {
+
+ case PTP_MSG_SYNC:
+ fec_ptp_insert(&(priv->rx_time_sync), &tmp_rx_time, priv,
+ DEFAULT_PTP_RX_BUF_SZ);
+ break;
+
+ case PTP_MSG_DEL_REQ:
+ fec_ptp_insert(&(priv->rx_time_del_req), &tmp_rx_time, priv,
+ DEFAULT_PTP_RX_BUF_SZ);
+ 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, DEFAULT_PTP_RX_BUF_SZ);
+ break;
+ case PTP_MSG_P_DEL_RESP:
+ fec_ptp_insert(&(priv->rx_time_pdel_resp), &tmp_rx_time,
+ priv, DEFAULT_PTP_RX_BUF_SZ);
+ 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, p_ptp,
+ &ps, orig_events);
+
+ /*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 = 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_RX_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->dest) == 319)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int fec_get_tx_timestamp(struct fec_ptp_private *priv,
+ struct ptp_ts_data *pts,
+ struct ptp_time *tx_time)
+{
+ struct fec_ptp_data_t tmp;
+ int flag;
+
+#ifdef CONFIG_IN_BAND
+ u8 mode;
+ tmp.key = pts->seq_id;
+ memcpy(tmp.spid, pts->spid, 10);
+ mode = pts->message_type;
+
+ switch (mode) {
+ case PTP_MSG_SYNC:
+ flag = fec_ptp_find_and_remove(&(priv->tx_time_sync), &tmp,
+ priv, DEFAULT_PTP_TX_BUF_SZ);
+ break;
+ case PTP_MSG_DEL_REQ:
+ flag = fec_ptp_find_and_remove(&(priv->tx_time_del_req), &tmp,
+ priv, DEFAULT_PTP_TX_BUF_SZ);
+ break;
+
+ case PTP_MSG_P_DEL_REQ:
+ flag = fec_ptp_find_and_remove(&(priv->tx_time_pdel_req), &tmp,
+ priv, DEFAULT_PTP_TX_BUF_SZ);
+ break;
+ case PTP_MSG_P_DEL_RESP:
+ flag = fec_ptp_find_and_remove(&(priv->tx_time_pdel_resp),
+ &tmp, priv, DEFAULT_PTP_TX_BUF_SZ);
+ break;
+
+ default:
+ flag = 1;
+ printk(KERN_ERR "ERROR\n");
+ break;
+ }
+
+ if (!flag) {
+ tx_time->sec = tmp.ts_time.sec;
+ tx_time->nsec = tmp.ts_time.nsec;
+ return 0;
+ } else {
+ wait_event_interruptible_timeout(ptp_tx_ts_wait, 0,
+ PTP_GET_TX_TIMEOUT);
+
+ switch (mode) {
+ case PTP_MSG_SYNC:
+ flag = fec_ptp_find_and_remove(&(priv->tx_time_sync),
+ &tmp, priv, DEFAULT_PTP_TX_BUF_SZ);
+ break;
+ case PTP_MSG_DEL_REQ:
+ flag = fec_ptp_find_and_remove(
+ &(priv->tx_time_del_req), &tmp,
+ priv, DEFAULT_PTP_TX_BUF_SZ);
+ break;
+ case PTP_MSG_P_DEL_REQ:
+ flag = fec_ptp_find_and_remove(
+ &(priv->tx_time_pdel_req), &tmp,
+ priv, DEFAULT_PTP_TX_BUF_SZ);
+ break;
+ case PTP_MSG_P_DEL_RESP:
+ flag = fec_ptp_find_and_remove(
+ &(priv->tx_time_pdel_resp), &tmp,
+ priv, DEFAULT_PTP_TX_BUF_SZ);
+ break;
+ }
+
+ if (flag == 0) {
+ tx_time->sec = tmp.ts_time.sec;
+ tx_time->nsec = tmp.ts_time.nsec;
+ return 0;
+ }
+
+ return -1;
+ }
+
+#else
+ memset(tmp.spid, 0, 10);
+ tmp.key = SEQ_ID_OUT_OF_BAND;
+
+ flag = fec_ptp_find_and_remove(&(priv->txstamp), &tmp,
+ priv, DEFAULT_PTP_TX_BUF_SZ);
+ tx_time->sec = tmp.ts_time.sec;
+ tx_time->nsec = tmp.ts_time.nsec;
+ return 0;
+#endif
+}
+
+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 flag;
+ u8 mode;
+
+#ifdef CONFIG_IN_BAND
+ tmp.key = pts->seq_id;
+ memcpy(tmp.spid, pts->spid, 10);
+#else
+ memset(tmp.spid, 0, 10);
+ tmp.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), &tmp,
+ priv, DEFAULT_PTP_RX_BUF_SZ);
+ break;
+ case PTP_MSG_DEL_REQ:
+ flag = fec_ptp_find_and_remove(&(priv->rx_time_del_req), &tmp,
+ priv, DEFAULT_PTP_RX_BUF_SZ);
+ break;
+
+ case PTP_MSG_P_DEL_REQ:
+ flag = fec_ptp_find_and_remove(&(priv->rx_time_pdel_req), &tmp,
+ priv, DEFAULT_PTP_RX_BUF_SZ);
+ break;
+ case PTP_MSG_P_DEL_RESP:
+ flag = fec_ptp_find_and_remove(&(priv->rx_time_pdel_resp),
+ &tmp, priv, DEFAULT_PTP_RX_BUF_SZ);
+ 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),
+ &tmp, priv, DEFAULT_PTP_RX_BUF_SZ);
+ break;
+ case PTP_MSG_DEL_REQ:
+ flag = fec_ptp_find_and_remove(
+ &(priv->rx_time_del_req), &tmp,
+ priv, DEFAULT_PTP_RX_BUF_SZ);
+ break;
+ case PTP_MSG_P_DEL_REQ:
+ flag = fec_ptp_find_and_remove(
+ &(priv->rx_time_pdel_req), &tmp,
+ priv, DEFAULT_PTP_RX_BUF_SZ);
+ break;
+ case PTP_MSG_P_DEL_RESP:
+ flag = fec_ptp_find_and_remove(
+ &(priv->rx_time_pdel_resp), &tmp,
+ priv, DEFAULT_PTP_RX_BUF_SZ);
+ 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, p_ts, &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;
+ /* reset sync buffer */
+ priv->tx_time_sync.head = 0;
+ priv->tx_time_sync.tail = 0;
+ /* reset delay_req buffer */
+ priv->tx_time_del_req.head = 0;
+ priv->tx_time_del_req.tail = 0;
+ /* reset pdelay_req buffer */
+ priv->tx_time_pdel_req.head = 0;
+ priv->tx_time_pdel_req.tail = 0;
+ /* reset pdelay_resp buffer */
+ priv->tx_time_pdel_resp.head = 0;
+ priv->tx_time_pdel_resp.tail = 0;
+ priv->txstamp.head = 0;
+ priv->txstamp.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), DEFAULT_PTP_RX_BUF_SZ);
+ fec_ptp_init_circ(&(priv->rx_time_del_req), DEFAULT_PTP_RX_BUF_SZ);
+ fec_ptp_init_circ(&(priv->rx_time_pdel_req), DEFAULT_PTP_RX_BUF_SZ);
+ fec_ptp_init_circ(&(priv->rx_time_pdel_resp), DEFAULT_PTP_RX_BUF_SZ);
+ fec_ptp_init_circ(&(priv->tx_time_sync), DEFAULT_PTP_TX_BUF_SZ);
+ fec_ptp_init_circ(&(priv->tx_time_del_req), DEFAULT_PTP_TX_BUF_SZ);
+ fec_ptp_init_circ(&(priv->tx_time_pdel_req), DEFAULT_PTP_TX_BUF_SZ);
+ fec_ptp_init_circ(&(priv->tx_time_pdel_resp), DEFAULT_PTP_TX_BUF_SZ);
+
+ fec_ptp_init_circ(&(priv->txstamp), DEFAULT_PTP_TX_BUF_SZ);
+
+ 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);
+ if (priv->tx_time_sync.buf)
+ vfree(priv->tx_time_sync.buf);
+ if (priv->tx_time_del_req.buf)
+ vfree(priv->tx_time_del_req.buf);
+ if (priv->tx_time_pdel_req.buf)
+ vfree(priv->tx_time_pdel_req.buf);
+ if (priv->tx_time_pdel_resp.buf)
+ vfree(priv->tx_time_pdel_resp.buf);
+ if (priv->txstamp.buf)
+ vfree(priv->txstamp.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");