diff options
author | Xie Xiaobo <r63061@freescale.com> | 2010-10-27 14:20:13 +0800 |
---|---|---|
committer | Scott Sweeny <scott.sweeny@timesys.com> | 2011-01-19 11:50:15 -0500 |
commit | b479f19aee173e757247ae9c5acdc2b3c91935fb (patch) | |
tree | abafaa214a85e5243fab45a225923df4343f93c1 /drivers | |
parent | b4765a0cf2b8b09cdf3fefa9a8a5b3d1132f806d (diff) |
ENGR00122832 MX28: Support for IEEE 1588 interface functionality
Supply the 1588 support for MX28 FEC.
Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/Makefile | 7 | ||||
-rw-r--r-- | drivers/net/fec.c | 55 | ||||
-rw-r--r-- | drivers/net/fec.h | 21 | ||||
-rw-r--r-- | drivers/net/fec_1588.c | 581 | ||||
-rw-r--r-- | drivers/net/fec_1588.h | 11 |
5 files changed, 665 insertions, 10 deletions
diff --git a/drivers/net/Makefile b/drivers/net/Makefile index d2c9afda12bf..887cc5faddeb 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -120,7 +120,12 @@ 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 +ifeq ($(CONFIG_ARCH_MX53),y) + obj-$(CONFIG_FEC_1588) += imx_ptp.o +endif +ifeq ($(CONFIG_ARCH_MX28),y) + obj-$(CONFIG_FEC_1588) += fec_1588.o +endif obj-$(CONFIG_FEC_L2SWITCH) += fec_switch.o obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 66c5b5a1b815..dc9f2f72b4ab 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -115,8 +115,15 @@ #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ +#define FEC_ENET_TS_AVAIL ((uint)0x00010000) +#define FEC_ENET_TS_TIMER ((uint)0x00008000) +#if defined(CONFIG_FEC_1588) && defined(CONFIG_ARCH_MX28) +#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII | \ + FEC_ENET_TS_AVAIL | FEC_ENET_TS_TIMER) +#else #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII) +#endif /* The FEC stores dest/src/type, data, and checksum for receive packets. */ @@ -241,6 +248,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) struct bufdesc *bdp; void *bufaddr; unsigned short status; + unsigned long estatus; unsigned long flags; if (!fep->link) { @@ -283,8 +291,15 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) } if (fep->ptimer_present) { - if (fec_ptp_do_txstamp(skb)) + if (fec_ptp_do_txstamp(skb)) { + estatus = BD_ENET_TX_TS; status |= BD_ENET_TX_PTP; + } else + estatus = 0; +#ifdef CONFIG_ENHANCED_BD + bdp->cbd_esc = (estatus | BD_ENET_TX_INT); + bdp->cbd_bdu = 0; +#endif } #ifdef CONFIG_ARCH_MXS @@ -346,6 +361,7 @@ fec_enet_interrupt(int irq, void * dev_id) { struct net_device *dev = dev_id; struct fec_enet_private *fep = netdev_priv(dev); + struct fec_ptp_private *fpp = fep->ptp_priv; uint int_events; irqreturn_t ret = IRQ_NONE; @@ -367,6 +383,17 @@ fec_enet_interrupt(int irq, void * dev_id) fec_enet_tx(dev); } + if (int_events & FEC_ENET_TS_AVAIL) { + ret = IRQ_HANDLED; + fec_ptp_store_txstamp(fep->ptp_priv); + } + + if (int_events & FEC_ENET_TS_TIMER) { + ret = IRQ_HANDLED; + if (fep->ptimer_present && fpp) + fpp->prtc++; + } + if (int_events & FEC_ENET_MII) { ret = IRQ_HANDLED; complete(&fep->mdio_done); @@ -555,6 +582,11 @@ rx_processing_done: /* Mark the buffer empty */ status |= BD_ENET_RX_EMPTY; bdp->cbd_sc = status; +#ifdef CONFIG_ENHANCED_BD + bdp->cbd_esc = BD_ENET_RX_INT; + bdp->cbd_prot = 0; + bdp->cbd_bdu = 0; +#endif /* Update BD pointer to next entry */ if (status & BD_ENET_RX_WRAP) @@ -928,6 +960,9 @@ static int fec_enet_alloc_buffers(struct net_device *dev) bdp->cbd_bufaddr = dma_map_single(&dev->dev, skb->data, FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE); bdp->cbd_sc = BD_ENET_RX_EMPTY; +#ifdef CONFIG_ENHANCED_BD + bdp->cbd_esc = BD_ENET_RX_INT; +#endif bdp++; } @@ -941,6 +976,9 @@ static int fec_enet_alloc_buffers(struct net_device *dev) bdp->cbd_sc = 0; bdp->cbd_bufaddr = 0; +#ifdef CONFIG_ENHANCED_BD + bdp->cbd_esc = BD_ENET_TX_INT; +#endif bdp++; } @@ -1298,9 +1336,17 @@ fec_restart(struct net_device *dev, int duplex) if (fep->ptimer_present) { /* Set Timer count */ ret = fec_ptp_start(fep->ptp_priv); - if (ret) + if (ret) { fep->ptimer_present = 0; - } + reg = 0x0; + } else +#ifdef CONFIG_ARCH_MX28 + reg = 0x00000010; +#else + reg = 0x0; +#endif + } else + reg = 0x0; #ifdef FEC_MIIGSK_ENR if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) { @@ -1321,7 +1367,8 @@ fec_restart(struct net_device *dev, int duplex) #endif /* And last, enable the transmit and receive processing */ - writel(2, fep->hwp + FEC_ECNTRL); + reg |= 0x00000002; + writel(reg, fep->hwp + FEC_ECNTRL); writel(0, fep->hwp + FEC_R_DES_ACTIVE); /* Enable interrupts we wish to service */ diff --git a/drivers/net/fec.h b/drivers/net/fec.h index bdf07c93d506..304733014cd1 100644 --- a/drivers/net/fec.h +++ b/drivers/net/fec.h @@ -47,6 +47,15 @@ #define FEC_MIIGSK_CFGR 0x300 /* MIIGSK Configuration reg */ #define FEC_MIIGSK_ENR 0x308 /* MIIGSK Enable reg */ +/* Define the FEC 1588 registers offset */ +#define FEC_ATIME_CTRL 0x400 +#define FEC_ATIME 0x404 +#define FEC_ATIME_EVT_OFFSET 0x408 +#define FEC_ATIME_EVT_PERIOD 0x40c +#define FEC_ATIME_CORR 0x410 +#define FEC_ATIME_INC 0x414 +#define FEC_TS_TIMESTAMP 0x418 + #else #define FEC_ECNTRL 0x000 /* Ethernet control reg */ @@ -75,6 +84,9 @@ #endif /* CONFIG_M5272 */ +#ifdef CONFIG_ARCH_MX28 +#define CONFIG_ENHANCED_BD +#endif /* * Define the buffer descriptor structure. @@ -84,6 +96,13 @@ struct bufdesc { unsigned short cbd_datlen; /* Data length */ unsigned short cbd_sc; /* Control and status info */ unsigned long cbd_bufaddr; /* Buffer address */ +#ifdef CONFIG_ENHANCED_BD + unsigned long cbd_esc; + unsigned long cbd_prot; + unsigned long cbd_bdu; + unsigned long ts; + unsigned short res0[4]; +#endif }; #else struct bufdesc { @@ -126,6 +145,7 @@ struct bufdesc { #define BD_ENET_RX_CL ((ushort)0x0001) #define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ +#define BD_ENET_RX_INT 0x00800000 #define BD_ENET_RX_PTP ((ushort)0x0400) /* Buffer descriptor control/status used by Ethernet transmit. @@ -145,6 +165,7 @@ struct bufdesc { #define BD_ENET_TX_CSL ((ushort)0x0001) #define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */ +#define BD_ENET_TX_INT 0x40000000 #define BD_ENET_TX_PTP ((ushort)0x0100) /****************************************************************************/ diff --git a/drivers/net/fec_1588.c b/drivers/net/fec_1588.c new file mode 100644 index 000000000000..637503682877 --- /dev/null +++ b/drivers/net/fec_1588.c @@ -0,0 +1,581 @@ +/* + * drivers/net/fec_1588.c + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2009 IXXAT Automation, GmbH + * + * FEC Ethernet Driver -- IEEE 1588 interface functionality + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/io.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/vmalloc.h> +#include <linux/spinlock.h> +#include <linux/ip.h> +#include <linux/udp.h> +#include "fec.h" +#include "fec_1588.h" + +static DECLARE_WAIT_QUEUE_HEAD(ptp_rx_ts_wait); +#define PTP_GET_RX_TIMEOUT (HZ/10) + +static struct fec_ptp_private *ptp_private[2]; + +/* Alloc the ring resource */ +static int fec_ptp_init_circ(struct circ_buf *ptp_buf) +{ + ptp_buf->buf = vmalloc(DEFAULT_PTP_RX_BUF_SZ * + sizeof(struct fec_ptp_data_t)); + + if (!ptp_buf->buf) + return 1; + ptp_buf->head = 0; + ptp_buf->tail = 0; + + return 0; +} + +static inline int fec_ptp_calc_index(int size, int curr_index, int offset) +{ + return (curr_index + offset) % size; +} + +static int fec_ptp_is_empty(struct circ_buf *buf) +{ + return (buf->head == buf->tail); +} + +static int fec_ptp_nelems(struct circ_buf *buf) +{ + const int front = buf->head; + const int end = buf->tail; + const int size = DEFAULT_PTP_RX_BUF_SZ; + int n_items; + + if (end > front) + n_items = end - front; + else if (end < front) + n_items = size - (front - end); + else + n_items = 0; + + return n_items; +} + +static int fec_ptp_is_full(struct circ_buf *buf) +{ + if (fec_ptp_nelems(buf) == + (DEFAULT_PTP_RX_BUF_SZ - 1)) + return 1; + else + return 0; +} + +static int fec_ptp_insert(struct circ_buf *ptp_buf, + struct fec_ptp_data_t *data, + struct fec_ptp_private *priv) +{ + struct fec_ptp_data_t *tmp; + + if (fec_ptp_is_full(ptp_buf)) + return 1; + + spin_lock(&priv->ptp_lock); + tmp = (struct fec_ptp_data_t *)(ptp_buf->buf) + ptp_buf->tail; + + tmp->key = data->key; + tmp->ts_time.sec = data->ts_time.sec; + tmp->ts_time.nsec = data->ts_time.nsec; + + ptp_buf->tail = fec_ptp_calc_index(DEFAULT_PTP_RX_BUF_SZ, + ptp_buf->tail, 1); + spin_unlock(&priv->ptp_lock); + + return 0; +} + +static int fec_ptp_find_and_remove(struct circ_buf *ptp_buf, + int key, + struct fec_ptp_data_t *data, + struct fec_ptp_private *priv) +{ + int i; + int size = DEFAULT_PTP_RX_BUF_SZ; + int end = ptp_buf->tail; + unsigned long flags; + struct fec_ptp_data_t *tmp; + + if (fec_ptp_is_empty(ptp_buf)) + return 1; + + i = ptp_buf->head; + while (i != end) { + tmp = (struct fec_ptp_data_t *)(ptp_buf->buf) + i; + if (tmp->key == key) + break; + i = fec_ptp_calc_index(size, i, 1); + } + + spin_lock_irqsave(&priv->ptp_lock, flags); + if (i == end) { + ptp_buf->head = end; + spin_unlock_irqrestore(&priv->ptp_lock, flags); + return 1; + } + + data->ts_time.sec = tmp->ts_time.sec; + data->ts_time.nsec = tmp->ts_time.nsec; + + ptp_buf->head = fec_ptp_calc_index(size, i, 1); + spin_unlock_irqrestore(&priv->ptp_lock, flags); + + return 0; +} + +/* 1588 Module intialization */ +int fec_ptp_start(struct fec_ptp_private *priv) +{ + struct fec_ptp_private *fpp = priv; + + /* Select 1588 Timer source and enable module for starting Tmr Clock */ + writel(FEC_T_CTRL_RESTART, fpp->hwp + FEC_ATIME_CTRL); + writel(FEC_T_INC_40MHZ << FEC_T_INC_OFFSET, fpp->hwp + FEC_ATIME_INC); + writel(FEC_T_PERIOD_ONE_SEC, fpp->hwp + FEC_ATIME_EVT_PERIOD); + /* start counter */ + writel(FEC_T_CTRL_PERIOD_RST | FEC_T_CTRL_ENABLE, + fpp->hwp + FEC_ATIME_CTRL); + + return 0; +} + +/* Cleanup routine for 1588 module. + * When PTP is disabled this routing is called */ +void fec_ptp_stop(struct fec_ptp_private *priv) +{ + struct fec_ptp_private *fpp = priv; + + writel(0, fpp->hwp + FEC_ATIME_CTRL); + writel(FEC_T_CTRL_RESTART, fpp->hwp + FEC_ATIME_CTRL); + +} + +static void fec_get_curr_cnt(struct fec_ptp_private *priv, + struct ptp_rtc_time *curr_time) +{ + u32 tempval; + + writel(FEC_T_CTRL_CAPTURE, priv->hwp + FEC_ATIME_CTRL); + writel(FEC_T_CTRL_CAPTURE, priv->hwp + FEC_ATIME_CTRL); + curr_time->rtc_time.nsec = readl(priv->hwp + FEC_ATIME); + curr_time->rtc_time.sec = priv->prtc; + + writel(FEC_T_CTRL_CAPTURE, priv->hwp + FEC_ATIME_CTRL); + tempval = readl(priv->hwp + FEC_ATIME); + if (tempval < curr_time->rtc_time.nsec) { + curr_time->rtc_time.nsec = tempval; + curr_time->rtc_time.sec = priv->prtc; + } +} + +/* Set the 1588 timer counter registers */ +static void fec_set_1588cnt(struct fec_ptp_private *priv, + struct ptp_rtc_time *fec_time) +{ + u32 tempval; + unsigned long flags; + + spin_lock_irqsave(&priv->cnt_lock, flags); + priv->prtc = fec_time->rtc_time.sec; + + tempval = fec_time->rtc_time.nsec; + writel(tempval, priv->hwp + FEC_ATIME); + spin_unlock_irqrestore(&priv->cnt_lock, flags); +} + +/* Set the BD to ptp */ +int fec_ptp_do_txstamp(struct sk_buff *skb) +{ + struct iphdr *iph; + struct udphdr *udph; + + if (skb->len > 44) { + /* Check if port is 319 for PTP Event, and check for UDP */ + iph = ip_hdr(skb); + if (iph == NULL || iph->protocol != FEC_PACKET_TYPE_UDP) + return 0; + + udph = udp_hdr(skb); + if (udph != NULL && ntohs(udph->dest) == 319) + return 1; + } + + return 0; +} + +void fec_ptp_store_txstamp(struct fec_ptp_private *priv) +{ + struct fec_ptp_private *fpp = priv; + unsigned int reg; + + reg = readl(fpp->hwp + FEC_TS_TIMESTAMP); + fpp->txstamp.nsec = reg; + fpp->txstamp.sec = fpp->prtc; +} + +void fec_ptp_store_rxstamp(struct fec_ptp_private *priv, + struct sk_buff *skb, + struct bufdesc *bdp) +{ + int msg_type, seq_id, control; + struct fec_ptp_data_t tmp_rx_time; + struct fec_ptp_private *fpp = priv; + struct iphdr *iph; + struct udphdr *udph; + + /* Check for UDP, and Check if port is 319 for PTP Event */ + iph = (struct iphdr *)(skb->data + FEC_PTP_IP_OFFS); + if (iph->protocol != FEC_PACKET_TYPE_UDP) + return; + + udph = (struct udphdr *)(skb->data + FEC_PTP_UDP_OFFS); + if (ntohs(udph->dest) != 319) + return; + + seq_id = *((u16 *)(skb->data + FEC_PTP_SEQ_ID_OFFS)); + control = *((u8 *)(skb->data + FEC_PTP_CTRL_OFFS)); + + tmp_rx_time.key = ntohs(seq_id); + tmp_rx_time.ts_time.sec = fpp->prtc; + tmp_rx_time.ts_time.nsec = bdp->ts; + + switch (control) { + + case PTP_MSG_SYNC: + fec_ptp_insert(&(priv->rx_time_sync), &tmp_rx_time, priv); + break; + + case PTP_MSG_DEL_REQ: + fec_ptp_insert(&(priv->rx_time_del_req), &tmp_rx_time, priv); + break; + + /* clear transportSpecific field*/ + case PTP_MSG_ALL_OTHER: + msg_type = (*((u8 *)(skb->data + + FEC_PTP_MSG_TYPE_OFFS))) & 0x0F; + switch (msg_type) { + case PTP_MSG_P_DEL_REQ: + fec_ptp_insert(&(priv->rx_time_pdel_req), + &tmp_rx_time, priv); + break; + case PTP_MSG_P_DEL_RESP: + fec_ptp_insert(&(priv->rx_time_pdel_resp), + &tmp_rx_time, priv); + break; + default: + break; + } + break; + default: + break; + } + + wake_up_interruptible(&ptp_rx_ts_wait); +} + +static void fec_get_tx_timestamp(struct fec_ptp_private *priv, + struct ptp_time *tx_time) +{ + tx_time->sec = priv->txstamp.sec; + tx_time->nsec = priv->txstamp.nsec; +} + +static uint8_t fec_get_rx_time(struct fec_ptp_private *priv, + struct ptp_ts_data *pts, + struct ptp_time *rx_time) +{ + struct fec_ptp_data_t tmp; + int key, flag; + u8 mode; + + key = pts->seq_id; + mode = pts->message_type; + switch (mode) { + case PTP_MSG_SYNC: + flag = fec_ptp_find_and_remove(&(priv->rx_time_sync), + key, &tmp, priv); + break; + case PTP_MSG_DEL_REQ: + flag = fec_ptp_find_and_remove(&(priv->rx_time_del_req), + key, &tmp, priv); + break; + + case PTP_MSG_P_DEL_REQ: + flag = fec_ptp_find_and_remove(&(priv->rx_time_pdel_req), + key, &tmp, priv); + break; + case PTP_MSG_P_DEL_RESP: + flag = fec_ptp_find_and_remove(&(priv->rx_time_pdel_resp), + key, &tmp, priv); + break; + + default: + flag = 1; + printk(KERN_ERR "ERROR\n"); + break; + } + + if (!flag) { + rx_time->sec = tmp.ts_time.sec; + rx_time->nsec = tmp.ts_time.nsec; + return 0; + } else { + wait_event_interruptible_timeout(ptp_rx_ts_wait, 0, + PTP_GET_RX_TIMEOUT); + + switch (mode) { + case PTP_MSG_SYNC: + flag = fec_ptp_find_and_remove(&(priv->rx_time_sync), + key, &tmp, priv); + break; + case PTP_MSG_DEL_REQ: + flag = fec_ptp_find_and_remove( + &(priv->rx_time_del_req), key, &tmp, priv); + break; + case PTP_MSG_P_DEL_REQ: + flag = fec_ptp_find_and_remove( + &(priv->rx_time_pdel_req), key, &tmp, priv); + break; + case PTP_MSG_P_DEL_RESP: + flag = fec_ptp_find_and_remove( + &(priv->rx_time_pdel_resp), key, &tmp, priv); + break; + } + + if (flag == 0) { + rx_time->sec = tmp.ts_time.sec; + rx_time->nsec = tmp.ts_time.nsec; + return 0; + } + + return -1; + } +} + +static void fec_handle_ptpdrift(struct ptp_set_comp *comp, + struct ptp_time_correct *ptc) +{ + u32 ndrift; + u32 i; + u32 tmp, tmp_ns, tmp_prid; + u32 min_ns, min_prid, miss_ns; + + ndrift = comp->drift; + if (ndrift == 0) { + ptc->corr_inc = 0; + ptc->corr_period = 0; + return; + } + + if (ndrift >= FEC_ATIME_40MHZ) { + ptc->corr_inc = (u32)(ndrift / FEC_ATIME_40MHZ); + ptc->corr_period = 1; + return; + } + + min_ns = 1; + tmp = FEC_ATIME_40MHZ % ndrift; + tmp_prid = (u32)(FEC_ATIME_40MHZ / ndrift); + min_prid = tmp_prid; + miss_ns = tmp / tmp_prid; + for (i = 2; i <= FEC_T_INC_40MHZ; i++) { + tmp = (FEC_ATIME_40MHZ * i) % ndrift; + tmp_prid = (FEC_ATIME_40MHZ * i) / ndrift; + tmp_ns = tmp / tmp_prid; + if (tmp_ns <= 10) { + min_ns = i; + min_prid = tmp_prid; + break; + } + if (tmp_ns < miss_ns) { + min_ns = i; + min_prid = tmp_prid; + miss_ns = tmp_ns; + } + } + + ptc->corr_inc = min_ns; + ptc->corr_period = min_prid; +} + +static void fec_set_drift(struct fec_ptp_private *priv, + struct ptp_set_comp *comp) +{ + struct ptp_time_correct tc; + struct fec_ptp_private *fpp = priv; + u32 tmp, corr_ns; + + fec_handle_ptpdrift(comp, &tc); + if (tc.corr_inc == 0) + return; + + if (comp->o_ops == TRUE) + corr_ns = FEC_T_INC_40MHZ + tc.corr_inc; + else + corr_ns = FEC_T_INC_40MHZ - tc.corr_inc; + + tmp = readl(fpp->hwp + FEC_ATIME_INC) & FEC_T_INC_MASK; + tmp |= corr_ns << FEC_T_INC_CORR_OFFSET; + writel(tmp, fpp->hwp + FEC_ATIME_INC); + + writel(tc.corr_period, fpp->hwp + FEC_ATIME_CORR); +} + +static int ptp_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int ptp_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static 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; + unsigned int minor = MINOR(inode->i_rdev); + int retval = 0; + + priv = (struct fec_ptp_private *) ptp_private[minor]; + switch (cmd) { + case PTP_GET_RX_TIMESTAMP: + p_ts = (struct ptp_ts_data *)arg; + retval = fec_get_rx_time(priv, p_ts, &rx_time); + if (retval == 0) + copy_to_user((void __user *)(&(p_ts->ts)), &rx_time, + sizeof(rx_time)); + break; + case PTP_GET_TX_TIMESTAMP: + p_ts = (struct ptp_ts_data *)arg; + fec_get_tx_timestamp(priv, &tx_time); + copy_to_user((void __user *)(&(p_ts->ts)), &tx_time, + sizeof(tx_time)); + break; + case PTP_GET_CURRENT_TIME: + fec_get_curr_cnt(priv, &curr_time); + copy_to_user((void __user *)arg, &curr_time, sizeof(curr_time)); + break; + case PTP_SET_RTC_TIME: + cnt = (struct ptp_rtc_time *)arg; + fec_set_1588cnt(priv, cnt); + break; + case PTP_FLUSH_TIMESTAMP: + /* reset sync buffer */ + priv->rx_time_sync.head = 0; + priv->rx_time_sync.tail = 0; + /* reset delay_req buffer */ + priv->rx_time_del_req.head = 0; + priv->rx_time_del_req.tail = 0; + /* reset pdelay_req buffer */ + priv->rx_time_pdel_req.head = 0; + priv->rx_time_pdel_req.tail = 0; + /* reset pdelay_resp buffer */ + priv->rx_time_pdel_resp.head = 0; + priv->rx_time_pdel_resp.tail = 0; + break; + case PTP_SET_COMPENSATION: + p_comp = (struct ptp_set_comp *)arg; + fec_set_drift(priv, p_comp); + break; + case PTP_GET_ORIG_COMP: + ((struct ptp_get_comp *)arg)->dw_origcomp = FEC_PTP_ORIG_COMP; + break; + default: + return -EINVAL; + } + return retval; +} + +static const struct file_operations ptp_fops = { + .owner = THIS_MODULE, + .llseek = NULL, + .read = NULL, + .write = NULL, + .ioctl = ptp_ioctl, + .open = ptp_open, + .release = ptp_release, +}; + +static int init_ptp(void) +{ + if (register_chrdev(PTP_MAJOR, "ptp", &ptp_fops)) + printk(KERN_ERR "Unable to register PTP deivce as char\n"); + + return 0; +} + +static void ptp_free(void) +{ + /*unregister the PTP device*/ + unregister_chrdev(PTP_MAJOR, "ptp"); +} + +/* + * Resource required for accessing 1588 Timer Registers. + */ +int fec_ptp_init(struct fec_ptp_private *priv, int id) +{ + fec_ptp_init_circ(&(priv->rx_time_sync)); + fec_ptp_init_circ(&(priv->rx_time_del_req)); + fec_ptp_init_circ(&(priv->rx_time_pdel_req)); + fec_ptp_init_circ(&(priv->rx_time_pdel_resp)); + + spin_lock_init(&priv->ptp_lock); + spin_lock_init(&priv->cnt_lock); + ptp_private[id] = priv; + if (id == 0) + init_ptp(); + return 0; +} +EXPORT_SYMBOL(fec_ptp_init); + +void fec_ptp_cleanup(struct fec_ptp_private *priv) +{ + + if (priv->rx_time_sync.buf) + vfree(priv->rx_time_sync.buf); + if (priv->rx_time_del_req.buf) + vfree(priv->rx_time_del_req.buf); + if (priv->rx_time_pdel_req.buf) + vfree(priv->rx_time_pdel_req.buf); + if (priv->rx_time_pdel_resp.buf) + vfree(priv->rx_time_pdel_resp.buf); + + ptp_free(); +} +EXPORT_SYMBOL(fec_ptp_cleanup); diff --git a/drivers/net/fec_1588.h b/drivers/net/fec_1588.h index 85dede1c1de9..9ba246e9d305 100644 --- a/drivers/net/fec_1588.h +++ b/drivers/net/fec_1588.h @@ -72,11 +72,12 @@ #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_PTP_IP_OFFS 0xE +#define FEC_PTP_UDP_OFFS 0x22 +#define FEC_PTP_MSG_TYPE_OFFS 0x2A +#define FEC_PTP_SPORT_ID_OFFS 0x46 +#define FEC_PTP_SEQ_ID_OFFS 0x48 +#define FEC_PTP_CTRL_OFFS 0x4A #define FEC_PACKET_TYPE_UDP 0x11 #define FEC_PTP_ORIG_COMP 0x15555555 |