mirror of
https://github.com/genesi/linux-legacy.git
synced 2026-09-19 21:24:14 +00:00
ENGR00122832-2 MX28: Support for IEEE 1588 interface functionality
Supply the 1588 support for MX28 FEC. Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
*
|
||||
* Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
|
||||
* Copyright (c) 2004-2006 Macq Electronique SA.
|
||||
*
|
||||
* Support for FEC IEEE 1588.
|
||||
* Copyright (C) 2010 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@@ -54,6 +57,7 @@
|
||||
#endif
|
||||
|
||||
#include "fec.h"
|
||||
#include "fec_1588.h"
|
||||
|
||||
#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_MXS)
|
||||
#include <mach/hardware.h>
|
||||
@@ -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;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#ifndef FEC_H
|
||||
#define FEC_H
|
||||
/****************************************************************************/
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#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 */
|
||||
|
||||
498
drivers/net/fec_1588.c
Normal file
498
drivers/net/fec_1588.c
Normal file
@@ -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 <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;
|
||||
|
||||
/* 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);
|
||||
157
drivers/net/fec_1588.h
Normal file
157
drivers/net/fec_1588.h
Normal file
@@ -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 <linux/circ_buf.h>
|
||||
|
||||
/* 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
|
||||
Reference in New Issue
Block a user