From 67d56859d24864af530506c76523f0fc3c5cb502 Mon Sep 17 00:00:00 2001 From: Alison Wang <b18965@freescale.com> Date: Thu, 4 Aug 2011 09:59:44 +0800 Subject: [PATCH 20/52] Add dual FEC 1588 timer support Add Modelo dual FEC 1588 function with IXXXAT statck. Signed-off-by: Alison Wang <b18965@freescale.com> --- drivers/net/Kconfig | 6 + drivers/net/Makefile | 3 + drivers/net/fec.c | 153 ++++++++++++- drivers/net/fec.h | 25 ++ drivers/net/fec_1588.c | 626 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/fec_1588.h | 195 +++++++++++++++ 6 files changed, 1006 insertions(+), 2 deletions(-) create mode 100644 drivers/net/fec_1588.c create mode 100644 drivers/net/fec_1588.h --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1958,6 +1958,12 @@ config FEC2 Say Y here if you want to use the second built-in 10/100 Fast ethernet controller on some Motorola ColdFire processors. +config FEC_1588 + bool "Enable 1588 interface(on some ColdFire designs)" + depends on M5441X && FEC + help + Say Y here if 1588 function is enabled. + config FEC_548x tristate "MCF547x/MCF548x Fast Ethernet Controller support" depends on M547X_8X --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -123,6 +123,9 @@ obj-$(CONFIG_PCMCIA_PCNET) += 8390.o obj-$(CONFIG_HP100) += hp100.o obj-$(CONFIG_SMC9194) += smc9194.o obj-$(CONFIG_FEC) += fec.o +ifeq ($(CONFIG_FEC_1588), y) +obj-$(CONFIG_FEC) += fec_1588.o +endif obj-$(CONFIG_FEC_548x) += fec_m547x.o obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -53,6 +53,7 @@ #endif #include "fec.h" +#include "fec_1588.h" #if defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28) #define FEC_ALIGNMENT 0xf @@ -135,8 +136,15 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ +#define FEC_ENET_TS_AVAIL ((uint)0x00010000) +#define FEC_ENET_TS_TIMER ((uint)0x00008000) +#if defined(CONFIG_FEC_1588) +#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII | \ + FEC_ENET_TS_AVAIL | FEC_ENET_TS_TIMER) +#else #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII) +#endif /* The FEC stores dest/src/type, data, and checksum for receive packets. */ @@ -209,6 +217,10 @@ struct fec_enet_private { int link; int full_duplex; struct completion mdio_done; +#ifdef CONFIG_FEC_1588 + struct fec_ptp_private *ptp_priv; + uint ptimer_present; +#endif }; static irqreturn_t fec_enet_interrupt(int irq, void * dev_id); @@ -252,6 +264,9 @@ fec_enet_start_xmit(struct sk_buff *skb, struct bufdesc *bdp; void *bufaddr; unsigned short status; +#ifdef CONFIG_FEC_1588 + unsigned long estatus; +#endif unsigned long flags; if (!fep->link) { @@ -293,6 +308,17 @@ fec_enet_start_xmit(struct sk_buff *skb, bufaddr = fep->tx_bounce[index]; } +#ifdef CONFIG_FEC_1588 + if (fep->ptimer_present) { + if (fec_ptp_do_txstamp(skb)) + estatus = BD_ENET_TX_TS; + else + estatus = 0; + + bdp->cbd_esc = (estatus | BD_ENET_TX_INT); + bdp->cbd_bdu = 0; + } +#endif /* * Some design made an incorrect assumption on endian mode of * the system that it's running on. As the result, driver has to @@ -357,6 +383,9 @@ fec_enet_interrupt(int irq, void * dev_i { struct net_device *dev = dev_id; struct fec_enet_private *fep = netdev_priv(dev); +#ifdef CONFIG_FEC_1588 + struct fec_ptp_private *fpp = fep->ptp_priv; +#endif uint int_events; irqreturn_t ret = IRQ_NONE; @@ -364,6 +393,10 @@ fec_enet_interrupt(int irq, void * dev_i int_events = readl(fep->hwp + FEC_IEVENT); writel(int_events, fep->hwp + FEC_IEVENT); +#ifdef CONFIG_FEC_1588 + if (__raw_readb(MCF_DTIM1_DTER) & MCF_DTIM_DTER_REF) + __raw_writeb(MCF_DTIM_DTER_REF, MCF_DTIM1_DTER); +#endif if (int_events & FEC_ENET_RXF) { ret = IRQ_HANDLED; fec_enet_rx(dev); @@ -378,6 +411,19 @@ fec_enet_interrupt(int irq, void * dev_i fec_enet_tx(dev); } +#ifdef CONFIG_FEC_1588 + if (int_events & FEC_ENET_TS_AVAIL) { + ret = IRQ_HANDLED; + fec_ptp_store_txstamp(fep->ptp_priv); + } + + if (int_events & FEC_ENET_TS_TIMER) { + ret = IRQ_HANDLED; + if (fep->ptimer_present) + fpp->prtc++; + } +#endif + if (int_events & FEC_ENET_MII) { ret = IRQ_HANDLED; complete(&fep->mdio_done); @@ -394,6 +440,9 @@ fec_enet_tx(struct net_device *dev) struct fec_enet_private *fep; struct bufdesc *bdp; unsigned short status; +#ifdef CONFIG_FEC_1588 + unsigned long estatus; +#endif struct sk_buff *skb; fep = netdev_priv(dev); @@ -437,6 +486,13 @@ fec_enet_tx(struct net_device *dev) if (status & BD_ENET_TX_DEF) dev->stats.collisions++; +#if defined(CONFIG_FEC_1588) + if (fep->ptimer_present) { + estatus = bdp->cbd_esc; + if (estatus & BD_ENET_TX_TS) + fec_ptp_store_txstamp(fep->ptp_priv); + } +#endif /* Free the sk buffer associated with this last transmit */ dev_kfree_skb_any(skb); fep->tx_skbuff[fep->skb_dirty] = NULL; @@ -470,6 +526,9 @@ static void fec_enet_rx(struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); +#ifdef CONFIG_FEC_1588 + struct fec_ptp_private *fpp = fep->ptp_priv; +#endif const struct platform_device_id *id_entry = platform_get_device_id(fep->pdev); struct bufdesc *bdp; @@ -554,6 +613,12 @@ fec_enet_rx(struct net_device *dev) skb_put(skb, pkt_len - 4); /* Make room */ skb_copy_to_linear_data(skb, data, pkt_len - 4); skb->protocol = eth_type_trans(skb, dev); + +#ifdef CONFIG_FEC_1588 + /* 1588 messeage TS handle */ + if (fep->ptimer_present) + fec_ptp_store_rxstamp(fpp, skb, bdp); +#endif netif_rx(skb); } @@ -567,6 +632,11 @@ rx_processing_done: status |= BD_ENET_RX_EMPTY; bdp->cbd_sc = status; +#ifdef CONFIG_FEC_1588 + bdp->cbd_esc = BD_ENET_RX_INT; + bdp->cbd_prot = 0; + bdp->cbd_bdu = 0; +#endif /* Update BD pointer to next entry */ if (status & BD_ENET_RX_WRAP) bdp = fep->rx_bd_base; @@ -669,8 +739,11 @@ static void fec_enet_adjust_link(struct fec_stop(dev); if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) - writel(2, fep->hwp + FEC_ECNTRL); - +#ifdef CONFIG_FEC_1588 + writel(0x00000012, fep->hwp + FEC_ECNTRL); +#else + writel(0x00000002, fep->hwp + FEC_ECNTRL); +#endif status_change = 1; } @@ -983,6 +1056,10 @@ static int fec_enet_alloc_buffers(struct bdp->cbd_bufaddr = dma_map_single(&dev->dev, skb->data, FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE); bdp->cbd_sc = BD_ENET_RX_EMPTY; + +#ifdef CONFIG_FEC_1588 + bdp->cbd_esc = BD_ENET_RX_INT; +#endif bdp++; } @@ -996,6 +1073,9 @@ static int fec_enet_alloc_buffers(struct bdp->cbd_sc = 0; bdp->cbd_bufaddr = 0; +#ifdef CONFIG_FEC_1588 + bdp->cbd_esc = BD_ENET_TX_INT; +#endif bdp++; } @@ -1256,8 +1336,12 @@ fec_restart(struct net_device *dev, int writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH); } +#ifdef CONFIG_FEC_1588 + writel(0x7fff8000, fep->hwp + FEC_IEVENT); +#else /* Clear any outstanding interrupt. */ writel(0xffc00000, fep->hwp + FEC_IEVENT); +#endif /* Reset all multicast. */ writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); @@ -1342,8 +1426,25 @@ fec_restart(struct net_device *dev, int #endif } +#ifdef CONFIG_FEC_1588 + if (fep->ptimer_present) { + int ret; + /* Set Timer count */ + ret = fec_ptp_start(fep->ptp_priv); + if (ret) { + fep->ptimer_present = 0; + writel(2, fep->hwp + FEC_ECNTRL); + } else { + val = readl(fep->hwp + FEC_ECNTRL); + val |= 0x00000012; + writel(val, fep->hwp + FEC_ECNTRL); + } + } else + writel(2, fep->hwp + FEC_ECNTRL); +#else /* And last, enable the transmit and receive processing */ writel(2, fep->hwp + FEC_ECNTRL); +#endif writel(0, fep->hwp + FEC_R_DES_ACTIVE); /* Enable interrupts we wish to service */ @@ -1367,6 +1468,10 @@ fec_stop(struct net_device *dev) writel(1, fep->hwp + FEC_ECNTRL); udelay(10); writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); +#ifdef CONFIG_FEC_1588 + if (fep->ptimer_present) + fec_ptp_stop(fep->ptp_priv); +#endif writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); } @@ -1428,6 +1533,24 @@ fec_probe(struct platform_device *pdev) } } +#ifdef CONFIG_FEC_1588 + i = (pdev->id) ? (64 + 64 + 64 + 7) : (64 + 64 + 64); + if (request_irq(i + 48, fec_enet_interrupt, IRQF_DISABLED, + "1588 TS AVAIL", ndev) != 0) + printk(KERN_ERR "FEC: Could not alloc FEC %x 1588 TS AVAIL " + "IRQ(%d)!\n", pdev->id, i + 48); + + if (pdev->id == 0) { + printk("setup TS timer interrupt through DMA timer1\n"); + __raw_writew(MCF_DTIM_DTMR_RST_RST, MCF_DTIM1_DTMR); + + if (request_irq(64 + 33, fec_enet_interrupt, IRQF_DISABLED, + "1588 TS TIMER", ndev) != 0) + printk(KERN_ERR "FEC: Could not alloc FEC %x 1588 TS" + " TIMER IRQ(%d)!\n", pdev->id, 64 + 33); + } +#endif + fep->clk = clk_get(&pdev->dev, "fec_clk"); if (IS_ERR(fep->clk)) { ret = PTR_ERR(fep->clk); @@ -1443,6 +1566,20 @@ fec_probe(struct platform_device *pdev) if (ret) goto failed_mii_init; +#ifdef CONFIG_FEC_1588 + fep->ptp_priv = kzalloc(sizeof(struct fec_ptp_private), + GFP_KERNEL); + if (fep->ptp_priv) { + fep->ptp_priv->hwp = fep->hwp; + ret = fec_ptp_init(fep->ptp_priv, pdev->id); + if (ret) + printk(KERN_ERR "IEEE1588: ptp-timer init failed\n"); + else + fep->ptimer_present = 1; + } else + printk(KERN_ERR "IEEE1588: failed to malloc memory\n"); +#endif + /* Carrier starts down, phylib will bring it up */ netif_carrier_off(ndev); @@ -1454,6 +1591,12 @@ fec_probe(struct platform_device *pdev) failed_register: fec_enet_mii_remove(fep); +#ifdef CONFIG_FEC_1588 + if (fep->ptimer_present) + fec_ptp_cleanup(fep->ptp_priv); + + kfree(fep->ptp_priv); +#endif failed_mii_init: failed_init: clk_disable(fep->clk); @@ -1485,6 +1628,12 @@ fec_drv_remove(struct platform_device *p clk_disable(fep->clk); clk_put(fep->clk); iounmap((void __iomem *)ndev->base_addr); +#ifdef CONFIG_FEC_1588 + if (fep->ptimer_present) + fec_ptp_cleanup(fep->ptp_priv); + + kfree(fep->ptp_priv); +#endif unregister_netdev(ndev); free_netdev(ndev); return 0; --- a/drivers/net/fec.h +++ b/drivers/net/fec.h @@ -50,6 +50,16 @@ #define FEC_MIIGSK_CFGR 0x300 /* MIIGSK Configuration reg */ #define FEC_MIIGSK_ENR 0x308 /* MIIGSK Enable reg */ +#if defined(CONFIG_FEC_1588) +#define FEC_ATIME_CTRL 0x400 +#define FEC_ATIME 0x404 +#define FEC_ATIME_EVT_OFFSET 0x408 +#define FEC_ATIME_EVT_PERIOD 0x40c +#define FEC_ATIME_CORR 0x410 +#define FEC_ATIME_INC 0x414 +#define FEC_TS_TIMESTAMP 0x418 +#endif + #else #define FEC_ECNTRL 0x000 /* Ethernet control reg */ @@ -78,6 +88,9 @@ #endif /* CONFIG_M5272 */ +#if defined(CONFIG_FEC_1588) +#define FEC_ENHANCED_MODE 1 +#endif /* * Define the buffer descriptor structure. @@ -93,6 +106,14 @@ struct bufdesc { unsigned short cbd_sc; /* Control and status info */ unsigned short cbd_datlen; /* Data length */ unsigned long cbd_bufaddr; /* Buffer address */ + +#ifdef FEC_ENHANCED_MODE + unsigned long cbd_esc; + unsigned long cbd_prot; + unsigned long cbd_bdu; + unsigned long ts; + unsigned short res0[4]; +#endif }; #endif @@ -128,6 +149,7 @@ struct bufdesc { #define BD_ENET_RX_OV ((ushort)0x0002) #define BD_ENET_RX_CL ((ushort)0x0001) #define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ +#define BD_ENET_RX_INT 0x00800000 /* Buffer descriptor control/status used by Ethernet transmit. */ @@ -146,6 +168,9 @@ struct bufdesc { #define BD_ENET_TX_CSL ((ushort)0x0001) #define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */ +#define BD_ENET_TX_TS 0x20000000 +#define BD_ENET_TX_INT 0x40000000 +#define BD_ENET_TX_BDU 0x80000000 /****************************************************************************/ #endif /* FEC_H */ --- /dev/null +++ b/drivers/net/fec_1588.c @@ -0,0 +1,626 @@ +/* + * drivers/net/fec_1588.c + * + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2009 IXXAT Automation, GmbH + * + * FEC Ethernet Driver -- IEEE 1588 interface functionality + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/io.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/vmalloc.h> +#include <linux/spinlock.h> +#include <linux/ip.h> +#include <linux/udp.h> +#include <asm/mcf5441x_ccm.h> +#include <asm/mcf5441x_dtim.h> +#include <asm/mcfsim.h> +#include "fec_1588.h" + +static DECLARE_WAIT_QUEUE_HEAD(ptp_rx_ts_wait); +#define PTP_GET_RX_TIMEOUT (HZ/10) +#define COLDFIRE_DTIM1_INT (64+32+1) + +static struct fec_ptp_private *ptp_private[2]; + +static void init_DTIM1_for_1588(struct fec_ptp_private *priv) +{ + printk(KERN_INFO "Initializing DTIM1 for 1588 TS timer\n"); + + __raw_writew(MCF_DTIM_DTMR_RST_RST, MCF_DTIM1_DTMR); + + /*Enable 1588*/ + + __raw_writeb(MCF_DTIM_DTXMR_1588EN, MCF_DTIM1_DTXMR); + + /*Compare to the 1588 timerbase*/ + __raw_writel(FEC_T_PERIOD_ONE_SEC - FEC_T_INC_40MHZ, MCF_DTIM1_DTRR); + + __raw_writeb(MCF_DTIM_DTER_REF, MCF_DTIM1_DTER); + + MCF_GPIO_PAR_TIMER = (MCF_GPIO_PAR_TIMER & MCF_GPIO_PAR_TIMER_T1IN_MASK) + | MCF_GPIO_PAR_TIMER_T1IN_T1OUT; +} + +static void start_DTIM1(void) +{ + __raw_writew(MCF_DTIM_DTMR_RST_EN | MCF_DTIM_DTMR_ORRI | + MCF_DTIM_DTMR_OM, MCF_DTIM1_DTMR); +} + +static void stop_DTIM1(void) +{ + __raw_writew(MCF_DTIM_DTMR_RST_RST, MCF_DTIM1_DTMR); +} + +/* Alloc the ring resource */ +static int fec_ptp_init_circ(struct circ_buf *ptp_buf) +{ + ptp_buf->buf = vmalloc(DEFAULT_PTP_RX_BUF_SZ * + sizeof(struct fec_ptp_data_t)); + + if (!ptp_buf->buf) + return 1; + ptp_buf->head = 0; + ptp_buf->tail = 0; + + return 0; +} + +static inline int fec_ptp_calc_index(int size, int curr_index, int offset) +{ + return (curr_index + offset) % size; +} + +static int fec_ptp_is_empty(struct circ_buf *buf) +{ + return (buf->head == buf->tail); +} + +static int fec_ptp_nelems(struct circ_buf *buf) +{ + const int front = buf->head; + const int end = buf->tail; + const int size = DEFAULT_PTP_RX_BUF_SZ; + int n_items; + + if (end > front) + n_items = end - front; + else if (end < front) + n_items = size - (front - end); + else + n_items = 0; + + return n_items; +} + +static int fec_ptp_is_full(struct circ_buf *buf) +{ + if (fec_ptp_nelems(buf) == + (DEFAULT_PTP_RX_BUF_SZ - 1)) + return 1; + else + return 0; +} + +static int fec_ptp_insert(struct circ_buf *ptp_buf, + struct fec_ptp_data_t *data, + struct fec_ptp_private *priv) +{ + struct fec_ptp_data_t *tmp; + + if (fec_ptp_is_full(ptp_buf)) + return 1; + + spin_lock(&priv->ptp_lock); + tmp = (struct fec_ptp_data_t *)(ptp_buf->buf) + ptp_buf->tail; + + tmp->key = data->key; + tmp->ts_time.sec = data->ts_time.sec; + tmp->ts_time.nsec = data->ts_time.nsec; + + ptp_buf->tail = fec_ptp_calc_index(DEFAULT_PTP_RX_BUF_SZ, + ptp_buf->tail, 1); + spin_unlock(&priv->ptp_lock); + + return 0; +} + +static int fec_ptp_find_and_remove(struct circ_buf *ptp_buf, + int key, + struct fec_ptp_data_t *data, + struct fec_ptp_private *priv) +{ + int i; + int size = DEFAULT_PTP_RX_BUF_SZ; + int end = ptp_buf->tail; + unsigned long flags; + struct fec_ptp_data_t *tmp; + + if (fec_ptp_is_empty(ptp_buf)) + return 1; + + i = ptp_buf->head; + while (i != end) { + tmp = (struct fec_ptp_data_t *)(ptp_buf->buf) + i; + if (tmp->key == key) + break; + i = fec_ptp_calc_index(size, i, 1); + } + + spin_lock_irqsave(&priv->ptp_lock, flags); + if (i == end) { + ptp_buf->head = end; + spin_unlock_irqrestore(&priv->ptp_lock, flags); + return 1; + } + + data->ts_time.sec = tmp->ts_time.sec; + data->ts_time.nsec = tmp->ts_time.nsec; + + ptp_buf->head = fec_ptp_calc_index(size, i, 1); + spin_unlock_irqrestore(&priv->ptp_lock, flags); + + return 0; +} + +/* 1588 Module intialization */ +int fec_ptp_start(struct fec_ptp_private *priv) +{ + struct fec_ptp_private *fpp = priv; + + MCF_CCM_MISCCR3 = 0x0000; + + init_DTIM1_for_1588(priv); + + /* Select 1588 Timer source and enable module for starting Tmr Clock */ + fec_writel(FEC_T_CTRL_RESTART, fpp->hwp + FEC_ATIME_CTRL); + fec_writel(FEC_T_INC_40MHZ << FEC_T_INC_OFFSET, + fpp->hwp + FEC_ATIME_INC); + fec_writel(FEC_T_PERIOD_ONE_SEC, fpp->hwp + FEC_ATIME_EVT_PERIOD); + /* start counter */ + fec_writel(FEC_T_CTRL_PERIOD_RST | FEC_T_CTRL_ENABLE | + FEC_T_CTRL_PINPER, fpp->hwp + FEC_ATIME_CTRL); + + start_DTIM1(); + + return 0; +} + +/* Cleanup routine for 1588 module. + * When PTP is disabled this routing is called */ +void fec_ptp_stop(struct fec_ptp_private *priv) +{ + struct fec_ptp_private *fpp = priv; + + fec_writel(0, fpp->hwp + FEC_ATIME_CTRL); + fec_writel(FEC_T_CTRL_RESTART, fpp->hwp + FEC_ATIME_CTRL); + stop_DTIM1(); +} + +static void fec_get_curr_cnt(struct fec_ptp_private *priv, + struct ptp_rtc_time *curr_time) +{ + u32 tempval; + + fec_writel(FEC_T_CTRL_CAPTURE, priv->hwp + FEC_ATIME_CTRL); + fec_writel(FEC_T_CTRL_CAPTURE, priv->hwp + FEC_ATIME_CTRL); + curr_time->rtc_time.nsec = fec_readl(priv->hwp + FEC_ATIME); + curr_time->rtc_time.sec = priv->prtc; + + fec_writel(FEC_T_CTRL_CAPTURE, priv->hwp + FEC_ATIME_CTRL); + tempval = fec_readl(priv->hwp + FEC_ATIME); + if (tempval < curr_time->rtc_time.nsec) { + curr_time->rtc_time.nsec = tempval; + curr_time->rtc_time.sec = priv->prtc; + } +} + +/* Set the 1588 timer counter registers */ +static void fec_set_1588cnt(struct fec_ptp_private *priv, + struct ptp_rtc_time *fec_time) +{ + u32 tempval; + unsigned long flags; + + spin_lock_irqsave(&priv->cnt_lock, flags); + + priv->prtc = fec_time->rtc_time.sec; + + tempval = fec_time->rtc_time.nsec; + fec_writel(tempval, priv->hwp + FEC_ATIME); + spin_unlock_irqrestore(&priv->cnt_lock, flags); +} + +/* Set the BD to ptp */ +int fec_ptp_do_txstamp(struct sk_buff *skb) +{ + struct iphdr *iph; + struct udphdr *udph; + + if (skb->len > 44) { + /* Check if port is 319 for PTP Event, and check for UDP */ + iph = ip_hdr(skb); + if (iph == NULL || iph->protocol != FEC_PACKET_TYPE_UDP) + return 0; + + udph = udp_hdr(skb); + if (udph != NULL && ntohs(udph->source) == 319) + return 1; + } + + return 0; +} + +void fec_ptp_store_txstamp(struct fec_ptp_private *priv) +{ + struct fec_ptp_private *fpp = priv; + unsigned int reg; + + reg = fec_readl(fpp->hwp + FEC_TS_TIMESTAMP); + fpp->txstamp.nsec = reg; + fpp->txstamp.sec = fpp->prtc; +} + +void fec_ptp_store_rxstamp(struct fec_ptp_private *priv, + struct sk_buff *skb, + struct bufdesc *bdp) +{ + int msg_type, seq_id, control; + struct fec_ptp_data_t tmp_rx_time; + struct fec_ptp_private *fpp = priv; + struct iphdr *iph; + struct udphdr *udph; + + /* Check for UDP, and Check if port is 319 for PTP Event */ + iph = (struct iphdr *)(skb->data + FEC_PTP_IP_OFFS); + if (iph->protocol != FEC_PACKET_TYPE_UDP) + return; + + udph = (struct udphdr *)(skb->data + FEC_PTP_UDP_OFFS); + if (ntohs(udph->source) != 319) + return; + + seq_id = *((u16 *)(skb->data + FEC_PTP_SEQ_ID_OFFS)); + control = *((u8 *)(skb->data + FEC_PTP_CTRL_OFFS)); + + tmp_rx_time.key = ntohs(seq_id); + tmp_rx_time.ts_time.sec = fpp->prtc; + tmp_rx_time.ts_time.nsec = bdp->ts; + + switch (control) { + + case PTP_MSG_SYNC: + fec_ptp_insert(&(priv->rx_time_sync), &tmp_rx_time, priv); + break; + + case PTP_MSG_DEL_REQ: + fec_ptp_insert(&(priv->rx_time_del_req), &tmp_rx_time, priv); + break; + + /* clear transportSpecific field*/ + case PTP_MSG_ALL_OTHER: + msg_type = (*((u8 *)(skb->data + + FEC_PTP_MSG_TYPE_OFFS))) & 0x0F; + switch (msg_type) { + case PTP_MSG_P_DEL_REQ: + fec_ptp_insert(&(priv->rx_time_pdel_req), + &tmp_rx_time, priv); + break; + case PTP_MSG_P_DEL_RESP: + fec_ptp_insert(&(priv->rx_time_pdel_resp), + &tmp_rx_time, priv); + break; + default: + break; + } + break; + default: + break; + } + + wake_up_interruptible(&ptp_rx_ts_wait); +} + +static void fec_get_tx_timestamp(struct fec_ptp_private *priv, + struct ptp_time *tx_time) +{ + tx_time->sec = priv->txstamp.sec; + tx_time->nsec = priv->txstamp.nsec; +} + +static uint8_t fec_get_rx_time(struct fec_ptp_private *priv, + struct ptp_ts_data *pts, + struct ptp_time *rx_time) +{ + struct fec_ptp_data_t tmp; + int key, flag; + u8 mode; + + key = pts->seq_id; + mode = pts->message_type; + switch (mode) { + case PTP_MSG_SYNC: + flag = fec_ptp_find_and_remove(&(priv->rx_time_sync), + key, &tmp, priv); + break; + case PTP_MSG_DEL_REQ: + flag = fec_ptp_find_and_remove(&(priv->rx_time_del_req), + key, &tmp, priv); + break; + + case PTP_MSG_P_DEL_REQ: + flag = fec_ptp_find_and_remove(&(priv->rx_time_pdel_req), + key, &tmp, priv); + break; + case PTP_MSG_P_DEL_RESP: + flag = fec_ptp_find_and_remove(&(priv->rx_time_pdel_resp), + key, &tmp, priv); + break; + + default: + flag = 1; + printk(KERN_ERR "ERROR\n"); + break; + } + + if (!flag) { + rx_time->sec = tmp.ts_time.sec; + rx_time->nsec = tmp.ts_time.nsec; + return 0; + } else { + wait_event_interruptible_timeout(ptp_rx_ts_wait, 0, + PTP_GET_RX_TIMEOUT); + + switch (mode) { + case PTP_MSG_SYNC: + flag = fec_ptp_find_and_remove(&(priv->rx_time_sync), + key, &tmp, priv); + break; + case PTP_MSG_DEL_REQ: + flag = fec_ptp_find_and_remove( + &(priv->rx_time_del_req), key, &tmp, priv); + break; + case PTP_MSG_P_DEL_REQ: + flag = fec_ptp_find_and_remove( + &(priv->rx_time_pdel_req), key, &tmp, priv); + break; + case PTP_MSG_P_DEL_RESP: + flag = fec_ptp_find_and_remove( + &(priv->rx_time_pdel_resp), key, &tmp, priv); + break; + } + + if (flag == 0) { + rx_time->sec = tmp.ts_time.sec; + rx_time->nsec = tmp.ts_time.nsec; + return 0; + } + + return -1; + } +} + +static void fec_handle_ptpdrift( + struct ptp_set_comp *comp, + struct ptp_time_correct *ptc) +{ + u32 ndrift; + u32 i; + u32 tmp, tmp_ns, tmp_prid; + u32 min_ns, min_prid, miss_ns; + + ndrift = comp->drift; + if (ndrift == 0) { + ptc->corr_inc = 0; + ptc->corr_period = 0; + return; + } + + if (ndrift >= FEC_ATIME_40MHZ) { + ptc->corr_inc = (u32)(ndrift / FEC_ATIME_40MHZ); + ptc->corr_period = 1; + return; + } + + min_ns = 1; + tmp = FEC_ATIME_40MHZ % ndrift; + tmp_prid = (u32)(FEC_ATIME_40MHZ / ndrift); + min_prid = tmp_prid; + miss_ns = tmp / tmp_prid; + for (i = 2; i <= FEC_T_INC_40MHZ; i++) { + tmp = (FEC_ATIME_40MHZ * i) % ndrift; + tmp_prid = (FEC_ATIME_40MHZ * i) / ndrift; + tmp_ns = tmp / tmp_prid; + if (tmp_ns <= 10) { + min_ns = i; + min_prid = tmp_prid; + break; + } + + if (tmp_ns < miss_ns) { + min_ns = i; + min_prid = tmp_prid; + miss_ns = tmp_ns; + } + } + + ptc->corr_inc = min_ns; + ptc->corr_period = min_prid; +} + +static void fec_set_drift(struct fec_ptp_private *priv, + struct ptp_set_comp *comp) +{ + struct ptp_time_correct tc; + struct fec_ptp_private *fpp = priv; + u32 tmp, corr_ns; + + fec_handle_ptpdrift(comp, &tc); + if (tc.corr_inc == 0) + return; + + if (comp->o_ops == TRUE) + corr_ns = FEC_T_INC_40MHZ + tc.corr_inc; + else + corr_ns = FEC_T_INC_40MHZ - tc.corr_inc; + + tmp = fec_readl(fpp->hwp + FEC_ATIME_INC) & FEC_T_INC_MASK; + tmp |= corr_ns << FEC_T_INC_CORR_OFFSET; + fec_writel(tmp, fpp->hwp + FEC_ATIME_INC); + + fec_writel(tc.corr_period, fpp->hwp + FEC_ATIME_CORR); +} + +static int ptp_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int ptp_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static long ptp_unlocked_ioctl( + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct ptp_rtc_time *cnt; + struct ptp_rtc_time curr_time; + struct ptp_time rx_time, tx_time; + struct ptp_ts_data *p_ts; + struct ptp_set_comp *p_comp; + struct fec_ptp_private *priv; + struct inode *inode = file->f_mapping->host; + unsigned int minor = MINOR(inode->i_rdev); + long retval = 0; + + priv = (struct fec_ptp_private *) ptp_private[minor]; + switch (cmd) { + case PTP_GET_RX_TIMESTAMP: + p_ts = (struct ptp_ts_data *)arg; + retval = fec_get_rx_time(priv, p_ts, &rx_time); + if (retval == 0) + copy_to_user((void __user *)(&(p_ts->ts)), &rx_time, + sizeof(rx_time)); + break; + case PTP_GET_TX_TIMESTAMP: + p_ts = (struct ptp_ts_data *)arg; + fec_get_tx_timestamp(priv, &tx_time); + copy_to_user((void __user *)(&(p_ts->ts)), &tx_time, + sizeof(tx_time)); + break; + case PTP_GET_CURRENT_TIME: + fec_get_curr_cnt(priv, &curr_time); + copy_to_user((void __user *)arg, &curr_time, sizeof(curr_time)); + break; + case PTP_SET_RTC_TIME: + cnt = (struct ptp_rtc_time *)arg; + fec_set_1588cnt(priv, cnt); + break; + case PTP_FLUSH_TIMESTAMP: + /* reset sync buffer */ + priv->rx_time_sync.head = 0; + priv->rx_time_sync.tail = 0; + /* reset delay_req buffer */ + priv->rx_time_del_req.head = 0; + priv->rx_time_del_req.tail = 0; + /* reset pdelay_req buffer */ + priv->rx_time_pdel_req.head = 0; + priv->rx_time_pdel_req.tail = 0; + /* reset pdelay_resp buffer */ + priv->rx_time_pdel_resp.head = 0; + priv->rx_time_pdel_resp.tail = 0; + break; + case PTP_SET_COMPENSATION: + p_comp = (struct ptp_set_comp *)arg; + fec_set_drift(priv, p_comp); + break; + case PTP_GET_ORIG_COMP: + ((struct ptp_get_comp *)arg)->dw_origcomp = FEC_PTP_ORIG_COMP; + break; + default: + return -EINVAL; + } + return retval; +} + +static const struct file_operations ptp_fops = { + .owner = THIS_MODULE, + .llseek = NULL, + .read = NULL, + .write = NULL, + .unlocked_ioctl = ptp_unlocked_ioctl, + .open = ptp_open, + .release = ptp_release, +}; + +static int init_ptp(void) +{ + if (register_chrdev(PTP_MAJOR, "ptp", &ptp_fops)) + printk(KERN_ERR "Unable to register PTP deivce as char\n"); + + return 0; +} + +static void ptp_free(void) +{ + /*unregister the PTP device*/ + unregister_chrdev(PTP_MAJOR, "ptp"); +} + + + +/* + * Resource required for accessing 1588 Timer Registers. + */ +int fec_ptp_init(struct fec_ptp_private *priv, int id) +{ + fec_ptp_init_circ(&(priv->rx_time_sync)); + fec_ptp_init_circ(&(priv->rx_time_del_req)); + fec_ptp_init_circ(&(priv->rx_time_pdel_req)); + fec_ptp_init_circ(&(priv->rx_time_pdel_resp)); + + spin_lock_init(&priv->ptp_lock); + spin_lock_init(&priv->cnt_lock); + ptp_private[id] = priv; + if (id == 0) + init_ptp(); + return 0; +} +EXPORT_SYMBOL(fec_ptp_init); + +void fec_ptp_cleanup(struct fec_ptp_private *priv) +{ + + if (priv->rx_time_sync.buf) + vfree(priv->rx_time_sync.buf); + if (priv->rx_time_del_req.buf) + vfree(priv->rx_time_del_req.buf); + if (priv->rx_time_pdel_req.buf) + vfree(priv->rx_time_pdel_req.buf); + if (priv->rx_time_pdel_resp.buf) + vfree(priv->rx_time_pdel_resp.buf); + + ptp_free(); +} +EXPORT_SYMBOL(fec_ptp_cleanup); --- /dev/null +++ b/drivers/net/fec_1588.h @@ -0,0 +1,195 @@ +/* + * drivers/net/fec_1588.h + * + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef FEC_1588_H +#define FEC_1588_H + +#include <linux/circ_buf.h> +#include "fec.h" + +#define fec_readl(addr) \ + ({ unsigned int __v = (*(volatile unsigned int *) (addr)); __v; }) + +#define fec_writel(b, addr) (void)((*(volatile unsigned int *) (addr)) = (b)) + +#define FALSE 0 +#define TRUE 1 + +/* FEC 1588 register bits */ +#define FEC_T_CTRL_CAPTURE 0x00000800 +#define FEC_T_CTRL_RESTART 0x00000200 +#define FEC_T_CTRL_PERIOD_RST 0x00000010 +#define FEC_T_CTRL_PINPER 0x00000080 +#define FEC_T_CTRL_ENABLE 0x00000001 + +#define FEC_T_INC_MASK 0x0000007f +#define FEC_T_INC_OFFSET 0 +#define FEC_T_INC_CORR_MASK 0x00007f00 +#define FEC_T_INC_CORR_OFFSET 8 + +#define FEC_T_INC_40MHZ 8 +#define FEC_ATIME_40MHZ 125000000 + +#define FEC_T_PERIOD_ONE_SEC 0x3B9ACA00 + +/* IEEE 1588 definition */ +#define FEC_ECNTRL_TS_EN 0x10 +#define PTP_MAJOR 232 /*the temporary major number + *used by PTP driver, the major + *number 232~239 is unassigned*/ + +#define DEFAULT_PTP_RX_BUF_SZ 2048 +#define PTP_MSG_SYNC 0x0 +#define PTP_MSG_DEL_REQ 0x1 +#define PTP_MSG_P_DEL_REQ 0x2 +#define PTP_MSG_P_DEL_RESP 0x3 +#define PTP_MSG_DEL_RESP 0x4 +#define PTP_MSG_ALL_OTHER 0x5 + +#define PTP_GET_TX_TIMESTAMP 0x1 +#define PTP_GET_RX_TIMESTAMP 0x9 +#define PTP_SET_RTC_TIME 0x3 +#define PTP_SET_COMPENSATION 0x4 +#define PTP_GET_CURRENT_TIME 0x5 +#define PTP_FLUSH_TIMESTAMP 0x6 +#define PTP_ADJ_ADDEND 0x7 +#define PTP_GET_ORIG_COMP 0x8 +#define PTP_GET_ADDEND 0xB +#define PTP_GET_RX_TIMESTAMP_PDELAY_REQ 0xC +#define PTP_GET_RX_TIMESTAMP_PDELAY_RESP 0xD + +#define FEC_PTP_DOMAIN_DLFT 0xe0000181 +#define FEC_PTP_IP_OFFS 0x0 +#define FEC_PTP_UDP_OFFS 0x14 +#define FEC_PTP_MSG_TYPE_OFFS 0x1C +#define FEC_PTP_SEQ_ID_OFFS 0x3A +#define FEC_PTP_COR_NS 0x24 +#define FEC_PTP_CTRL_OFFS 0x3C +#define FEC_PACKET_TYPE_UDP 0x11 + +#define FEC_PTP_ORIG_COMP 0x15555 + +/* PTP standard time representation structure */ +struct ptp_time { + u64 sec; /* seconds */ + u32 nsec; /* nanoseconds */ +}; + +/* Structure for PTP Time Stamp */ +struct fec_ptp_data_t { + int key; + struct ptp_time ts_time; +}; + +/* interface for PTP driver command GET_TX_TIME */ +struct ptp_ts_data { + /* PTP version */ + u8 version; + /* PTP source port ID */ + u8 spid[10]; + /* PTP sequence ID */ + u16 seq_id; + /* PTP message type */ + u8 message_type; + /* PTP timestamp */ + struct ptp_time ts; +}; + +/* interface for PTP driver command SET_RTC_TIME/GET_CURRENT_TIME */ +struct ptp_rtc_time { + struct ptp_time rtc_time; +}; + +/* interface for PTP driver command SET_COMPENSATION */ +struct ptp_set_comp { + u32 drift; + u32 o_ops; +}; + +/* interface for PTP driver command GET_ORIG_COMP */ +struct ptp_get_comp { + /* the initial compensation value */ + u32 dw_origcomp; + /* the minimum compensation value */ + u32 dw_mincomp; + /*the max compensation value*/ + u32 dw_maxcomp; + /*the min drift applying min compensation value in ppm*/ + u32 dw_mindrift; + /*the max drift applying max compensation value in ppm*/ + u32 dw_maxdrift; +}; + +struct ptp_time_correct { + u32 corr_period; + u32 corr_inc; +}; + +/* PTP message version */ +#define PTP_1588_MSG_VER_1 1 +#define PTP_1588_MSG_VER_2 2 + +struct fec_ptp_private { + void __iomem *hwp; + + struct circ_buf rx_time_sync; + struct circ_buf rx_time_del_req; + struct circ_buf rx_time_pdel_req; + struct circ_buf rx_time_pdel_resp; + spinlock_t ptp_lock; + spinlock_t cnt_lock; + + u64 prtc; + struct ptp_time txstamp; +}; + +#ifdef CONFIG_FEC_1588 +extern int fec_ptp_init(struct fec_ptp_private *priv, int id); +extern void fec_ptp_cleanup(struct fec_ptp_private *priv); +extern int fec_ptp_start(struct fec_ptp_private *priv); +extern void fec_ptp_stop(struct fec_ptp_private *priv); +extern int fec_ptp_do_txstamp(struct sk_buff *skb); +extern void fec_ptp_store_txstamp(struct fec_ptp_private *priv); +extern void fec_ptp_store_rxstamp(struct fec_ptp_private *priv, + struct sk_buff *skb, + struct bufdesc *bdp); +#else +static inline int fec_ptp_init(struct fec_ptp_private *priv, int id) +{ + return 1; +} +static inline void fec_ptp_cleanup(struct fec_ptp_private *priv) { } +static inline int fec_ptp_start(struct fec_ptp_private *priv) +{ + return 1; +} +static inline void fec_ptp_stop(struct fec_ptp_private *priv) {} +static inline int fec_ptp_do_txstamp(struct sk_buff *skb) +{ + return 0; +} +static inline void fec_ptp_store_txstamp(struct fec_ptp_private *priv) {} +static inline void fec_ptp_store_rxstamp(struct fec_ptp_private *priv, + struct sk_buff *skb, + struct bufdesc *bdp) {} +#endif /* 1588 */ + +#endif