From 9e4ac5e73b2158f4b9225e87b2d809fa9767db3b Mon Sep 17 00:00:00 2001 From: Xiexiaobo Date: Sun, 9 May 2010 16:27:06 +0800 Subject: [PATCH] ENGR00122832-2 MX28: Support for IEEE 1588 interface functionality Supply the 1588 support for MX28 FEC. Signed-off-by: Xie Xiaobo --- drivers/net/Kconfig | 4 + drivers/net/Makefile | 1 + drivers/net/fec.c | 85 ++++++- drivers/net/fec.h | 23 +- drivers/net/fec_1588.c | 498 +++++++++++++++++++++++++++++++++++++++++ drivers/net/fec_1588.h | 157 +++++++++++++ 6 files changed, 765 insertions(+), 3 deletions(-) create mode 100644 drivers/net/fec_1588.c create mode 100644 drivers/net/fec_1588.h diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 91e240e99df..bd5d0e026b0 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1881,6 +1881,10 @@ 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 + 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 ead8cab3cfe..f3c89fb1b79 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -114,6 +114,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) += fec_1588.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 b41ed384bdc..f95fe039087 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 @@ -54,6 +57,7 @@ #endif #include "fec.h" +#include "fec_1588.h" #if defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_MXS) #include @@ -112,6 +116,8 @@ #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) /* * RMII mode to be configured via a gasket @@ -196,6 +202,9 @@ struct fec_enet_private { int index; int link; int full_duplex; + + struct fec_ptp_private *ptp_priv; + uint ptimer_present; }; /* @@ -245,6 +254,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) { @@ -308,6 +318,16 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) | BD_ENET_TX_LAST | BD_ENET_TX_TC); bdp->cbd_sc = status; + if (fep->ptimer_present) { + if (fec_ptp_do_txstamp(skb)) + estatus = BD_ENET_TX_TS; + else + estatus = 0; +#ifdef CONFIG_FEC_1588 + bdp->cbd_esc = (estatus | BD_ENET_TX_INT); + bdp->cbd_bdu = 0; +#endif + } dev->trans_start = jiffies; /* Trigger transmission start */ @@ -347,6 +367,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 +388,16 @@ fec_enet_interrupt(int irq, void * dev_id) ret = IRQ_HANDLED; 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->prtc++; + } } while (int_events); return ret; @@ -454,6 +485,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; @@ -535,6 +567,9 @@ fec_enet_rx(struct net_device *dev) skb_put(skb, pkt_len - 4); /* Make room */ skb_copy_to_linear_data(skb, data, pkt_len - 4); skb->protocol = eth_type_trans(skb, dev); + /* 1588 messeage TS handle */ + if (fep->ptimer_present) + fec_ptp_store_rxstamp(fpp, skb, bdp); netif_rx(skb); } @@ -547,6 +582,11 @@ rx_processing_done: /* Mark the buffer empty */ status |= BD_ENET_RX_EMPTY; bdp->cbd_sc = status; +#ifdef CONFIG_FEC_1588 + bdp->cbd_esc = BD_ENET_RX_INT; + bdp->cbd_prot = 0; + bdp->cbd_bdu = 0; +#endif /* Update BD pointer to next entry */ if (status & BD_ENET_RX_WRAP) @@ -921,6 +961,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_FEC_1588 + bdp->cbd_esc = BD_ENET_RX_INT; +#endif bdp++; } @@ -934,6 +977,9 @@ static int fec_enet_alloc_buffers(struct net_device *dev) bdp->cbd_sc = 0; bdp->cbd_bufaddr = 0; +#ifdef CONFIG_FEC_1588 + bdp->cbd_esc = BD_ENET_TX_INT; +#endif bdp++; } @@ -1199,6 +1245,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]; #ifdef CONFIG_ARCH_MXS unsigned long reg; @@ -1304,12 +1351,25 @@ 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; + reg = 0x0; + } else + reg = 0x00000010; + } else + reg = 0x0; + /* 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 */ - writel(FEC_ENET_TXF | FEC_ENET_RXF, fep->hwp + FEC_IMASK); + writel(FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_TS_AVAIL | + FEC_ENET_TS_TIMER, fep->hwp + FEC_IMASK); } static void @@ -1333,6 +1393,9 @@ fec_stop(struct net_device *dev) 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); } static int __devinit @@ -1421,6 +1484,18 @@ fec_probe(struct platform_device *pdev) if (fec_enet_mii_probe(ndev) != 0) goto failed_mii_probe; + fep->ptp_priv = kmalloc(sizeof(struct fec_ptp_private), GFP_KERNEL); + if (fep->ptp_priv) { + fep->ptp_priv->hwp = fep->hwp; + ret = fec_ptp_init(fep->ptp_priv); + 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; @@ -1437,6 +1512,9 @@ fec_probe(struct platform_device *pdev) failed_mii_probe: 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); @@ -1472,6 +1550,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 5f04d4c00c4..d20d71eff12 100644 --- a/drivers/net/fec.h +++ b/drivers/net/fec.h @@ -12,6 +12,8 @@ #ifndef FEC_H #define FEC_H /****************************************************************************/ +#include +#include #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ defined(CONFIG_M520x) || defined(CONFIG_M532x) || \ @@ -47,6 +49,15 @@ #define FEC_MIIGSK_CFGR 0x300 /* MIIGSK config register */ #define FEC_MIIGSK_ENR 0x308 /* MIIGSK enable register */ +/* 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 */ @@ -77,7 +88,6 @@ #endif /* CONFIG_M5272 */ - /* * Define the buffer descriptor structure. */ @@ -86,7 +96,15 @@ struct bufdesc { unsigned short cbd_datlen; /* Data length */ unsigned short cbd_sc; /* Control and status info */ unsigned long cbd_bufaddr; /* Buffer address */ +#ifdef CONFIG_FEC_1588 + unsigned long cbd_esc; + unsigned long cbd_prot; + unsigned long cbd_bdu; + unsigned long ts; + unsigned short res0[4]; +#endif }; + #else struct bufdesc { unsigned short cbd_sc; /* Control and status info */ @@ -128,6 +146,8 @@ 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 + /* Buffer descriptor control/status used by Ethernet transmit. */ #define BD_ENET_TX_READY ((ushort)0x8000) @@ -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 /****************************************************************************/ #endif /* FEC_H */ diff --git a/drivers/net/fec_1588.c b/drivers/net/fec_1588.c new file mode 100644 index 00000000000..5babcc29de7 --- /dev/null +++ b/drivers/net/fec_1588.c @@ -0,0 +1,498 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#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; + +/* 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_data_t *tmp; + + if (fec_ptp_is_full(ptp_buf)) + return 1; + + spin_lock(&ptp_private->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(&ptp_private->ptp_lock); + + return 0; +} + +static int fec_ptp_find_and_remove(struct circ_buf *ptp_buf, + int key, struct fec_ptp_data_t *data) +{ + 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(&ptp_private->ptp_lock, flags); + if (i == end) { + ptp_buf->head = end; + spin_unlock_irqrestore(&ptp_private->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(&ptp_private->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); + writel(FEC_T_CTRL_PERIOD_RST, fpp->hwp + FEC_ATIME_CTRL); + /* start counter */ + writel(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) +{ + 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); + if (readl(priv->hwp + FEC_ATIME) < curr_time->rtc_time.nsec) + curr_time->rtc_time.sec++; +} + +/* 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(&ptp_private->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(&ptp_private->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->protocol != FEC_PACKET_TYPE_UDP) + return 0; + + udph = udp_hdr(skb); + if (udph->source == 319) + return 1; + } + + return 0; +} + +void fec_ptp_store_txstamp(struct fec_ptp_private *priv) +{ + struct fec_ptp_private *fpp = priv; + unsigned int reg; + + reg = 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 (udph->source != 319) + return; + + seq_id = *((u16 *)(skb->data + FEC_PTP_SEQ_ID_OFFS)); + control = *((u8 *)(skb->data + FEC_PTP_CTRL_OFFS)); + + tmp_rx_time.key = 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); + break; + + case PTP_MSG_DEL_REQ: + fec_ptp_insert(&(priv->rx_time_del_req), &tmp_rx_time); + 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); + break; + case PTP_MSG_P_DEL_RESP: + fec_ptp_insert(&(priv->rx_time_pdel_resp), + &tmp_rx_time); + 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); + break; + case PTP_MSG_DEL_REQ: + flag = fec_ptp_find_and_remove(&(priv->rx_time_del_req), + key, &tmp); + break; + + case PTP_MSG_P_DEL_REQ: + flag = fec_ptp_find_and_remove(&(priv->rx_time_pdel_req), + key, &tmp); + break; + case PTP_MSG_P_DEL_RESP: + flag = fec_ptp_find_and_remove(&(priv->rx_time_pdel_resp), + key, &tmp); + 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); + break; + case PTP_MSG_DEL_REQ: + flag = fec_ptp_find_and_remove( + &(priv->rx_time_del_req), key, &tmp); + break; + case PTP_MSG_P_DEL_REQ: + flag = fec_ptp_find_and_remove( + &(priv->rx_time_pdel_req), key, &tmp); + break; + case PTP_MSG_P_DEL_RESP: + flag = fec_ptp_find_and_remove( + &(priv->rx_time_pdel_resp), key, &tmp); + break; + } + + if (flag == 0) { + rx_time->sec = tmp.ts_time.sec; + rx_time->nsec = tmp.ts_time.nsec; + return 0; + } + + return -1; + } +} + +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 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_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: + /* TBD */ + break; + case PTP_GET_ORIG_COMP: + /* TBD */ + 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) +{ + 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; + 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 new file mode 100644 index 00000000000..55b1a8c995b --- /dev/null +++ b/drivers/net/fec_1588.h @@ -0,0 +1,157 @@ +/* + * 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 + +/* 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_40MHZ 20 + +#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 0x2 +#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 0xE +#define FEC_PTP_UDP_OFFS 0x22 +#define FEC_PTP_MSG_TYPE_OFFS 0x2A +#define FEC_PTP_SEQ_ID_OFFS 0x48 +#define FEC_PTP_CTRL_OFFS 0x4A +#define FEC_PACKET_TYPE_UDP 0x11 + +/* 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; +}; + +/* 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 +extern int fec_ptp_init(struct fec_ptp_private *priv); +extern void fec_ptp_cleanup(struct fec_ptp_private *priv); +extern int fec_ptp_start(struct fec_ptp_private *priv); +extern void fec_ptp_stop(struct fec_ptp_private *priv); +extern int fec_ptp_do_txstamp(struct sk_buff *skb); +extern void fec_ptp_store_txstamp(struct fec_ptp_private *priv); +extern void fec_ptp_store_rxstamp(struct fec_ptp_private *priv, + struct sk_buff *skb, + struct bufdesc *bdp); +#else +static inline int fec_ptp_init(struct fec_ptp_private *priv) +{ + 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