commit: http://blackfin.uclinux.org/git/?p=linux-kernel;a=commitdiff;h=c114b897eb7d47616d607f04e5d9e47bc3c0e1dc
branch: http://blackfin.uclinux.org/git/?p=linux-kernel;a=shortlog;h=refs/heads/trunk

Sample code of ieee1588 implemention for stmmac.

Signed-off-by: Bob Liu <[email protected]>
---
 .../mach-bf609/include/mach/cdefBF60x_base.h       |   15 ++
 drivers/net/ethernet/stmicro/stmmac/descs.h        |   11 +
 drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c |    6 +-
 drivers/net/ethernet/stmicro/stmmac/enh_desc.c     |   11 +-
 drivers/net/ethernet/stmicro/stmmac/stmmac.h       |   22 ++
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c  |  260 +++++++++++++++++++-
 6 files changed, 320 insertions(+), 5 deletions(-)

diff --git a/arch/blackfin/mach-bf609/include/mach/cdefBF60x_base.h b/arch/blackfin/mach-bf609/include/mach/cdefBF60x_base.h
index c19e754..03982e3 100644
--- a/arch/blackfin/mach-bf609/include/mach/cdefBF60x_base.h
+++ b/arch/blackfin/mach-bf609/include/mach/cdefBF60x_base.h
@@ -49,6 +49,21 @@
 
 /* RTC Registers */
 
+/* EMAC0 Registers */
+#define bfin_read_EMAC_PTP_ADDEND()     bfin_read32(EMAC0_TM_ADDEND, val)
+#define bfin_write_EMAC_PTP_ADDEND(val) bfin_write32(EMAC0_TM_ADDEND, val)
+#define bfin_read_EMAC_PTP_CTL()    	bfin_read32(EMAC0_TM_CTL)
+#define bfin_write_EMAC_PTP_CTL(val) 	bfin_write32(EMAC0_TM_CTL, val)
+#define bfin_read_EMAC_PTP_SEC()    	bfin_read32(EMAC0_TM_SEC)
+#define bfin_write_EMAC_PTP_SEC(val) 	bfin_write32(EMAC0_TM_SEC, val)
+#define bfin_read_EMAC_PTP_SUBSEC()    	bfin_read32(EMAC0_TM_SUBSEC)
+#define bfin_write_EMAC_PTP_SUBSEC(val) bfin_write32(EMAC0_TM_SUBSEC, val)
+#define bfin_read_EMAC_PTP_NSEC()    	bfin_read32(EMAC0_TM_NSEC)
+#define bfin_write_EMAC_PTP_NSEC(val) 	bfin_write32(EMAC0_TM_NSEC, val)
+#define bfin_read_EMAC_PTP_SECUPDT()    bfin_read32(EMAC0_TM_SECUPDT)
+#define bfin_write_EMAC_PTP_SECUPDT(val) bfin_write32(EMAC0_TM_SECUPDT, val)
+#define bfin_read_EMAC_PTP_NSECUPDT()    bfin_read32(EMAC0_TM_NSECUPDT)
+#define bfin_write_EMAC_PTP_NSECUPDT(val) bfin_write32(EMAC0_TM_NSECUPDT, val)
 
 /* UART0 Registers */
 
diff --git a/drivers/net/ethernet/stmicro/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h
index 9820ec8..ffa0073 100644
--- a/drivers/net/ethernet/stmicro/stmmac/descs.h
+++ b/drivers/net/ethernet/stmicro/stmmac/descs.h
@@ -20,6 +20,11 @@
 
   Author: Giuseppe Cavallaro <[email protected]>
 *******************************************************************************/
+
+#ifdef CONFIG_BLACKFIN
+#define STMMAC_IEEE1588
+#endif
+
 struct dma_desc {
 	/* Receive descriptor */
 	union {
@@ -156,6 +161,12 @@ struct dma_desc {
 	} des01;
 	unsigned int des2;
 	unsigned int des3;
+#ifdef STMMAC_IEEE1588
+	unsigned int des4;
+	unsigned int des5;
+	unsigned int des6;
+	unsigned int des7;
+#endif
 };
 
 /* Transmit checksum insertion control */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
index d682a0b..6a10c39 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
@@ -52,11 +52,13 @@ static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, u32 dma_tx,
 
 #ifdef CONFIG_BLACKFIN
 	writel(DMA_AXI_BUS_BLEN4 | DMA_AXI_BUS_UNDEF, ioaddr + DMA_AXI_BUS);
-#endif
+	writel(DMA_BUS_MODE_DEFAULT | (pbl << DMA_BUS_MODE_PBL_SHIFT) | (1 << 7),
+	       ioaddr + DMA_BUS_MODE);
+#else
 	/* Enable Application Access by writing to DMA CSR0 */
 	writel(DMA_BUS_MODE_DEFAULT | (pbl << DMA_BUS_MODE_PBL_SHIFT),
 	       ioaddr + DMA_BUS_MODE);
-
+#endif
 	/* Mask interrupts by writing to CSR7 */
 	writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
 
diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
index ad1b627..cae3af8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
@@ -291,9 +291,16 @@ static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
 				     int csum_flag)
 {
 	p->des01.etx.first_segment = is_fs;
-
 	enh_set_tx_desc_len(p, len);
-
+#ifdef STMMAC_IEEE1588
+	p->des01.etx.time_stamp_enable = 1;
+#endif
+	if (unlikely(len > BUF_SIZE_4KiB)) {
+		p->des01.etx.buffer1_size = BUF_SIZE_4KiB;
+		p->des01.etx.buffer2_size = len - BUF_SIZE_4KiB;
+	} else {
+		p->des01.etx.buffer1_size = len;
+	}
 	if (likely(csum_flag))
 		p->des01.etx.checksum_insertion = cic_full;
 }
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index b4b095f..fc23d69 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -29,6 +29,22 @@
 #include "stmmac_timer.h"
 #endif
 
+#ifdef STMMAC_IEEE1588
+#include <linux/net_tstamp.h>
+#include <linux/clocksource.h>
+#include <linux/timecompare.h>
+#include <linux/timer.h>
+
+#define PTP_EN          (0x1)        /* Enable the PTP_TSYNC module */
+#define PTP_TSINIT      (0x4)        /* update system timer */
+#define PTP_TSENALL     (1 << 8)
+#define PTP_TSVER2ENA   (1 << 10)
+#define PTP_TSIPENA     (1 << 11)
+#define PTP_TSIPV4ENA   (1 << 13)
+#define PTP_TSMASTERENA (1 << 15)
+#define PTP_TSEVENTENA  (1 << 15)
+#endif
+
 struct stmmac_priv {
 	/* Frequently used values are kept adjacent for cache effect */
 	struct dma_desc *dma_tx ____cacheline_aligned;
@@ -81,6 +97,12 @@ struct stmmac_priv {
 	struct stmmac_counters mmc;
 	struct dma_features dma_cap;
 	int hw_cap_support;
+#ifdef STMMAC_IEEE1588
+	struct cyclecounter cycles;
+	struct timecounter clock;
+	struct timecompare compare;
+	struct hwtstamp_config stamp_cfg;
+#endif
 };
 
 extern int phyaddr;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 0d9b86b..70e4727 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -74,7 +74,12 @@
 #define TX_DBG(fmt, args...)  do { } while (0)
 #endif
 
+#ifdef CONFIG_BLACKFIN
+#define STMMAC_ALIGN(x) (x)
+#else
 #define STMMAC_ALIGN(x)	L1_CACHE_ALIGN(x)
+#endif
+
 #define JUMBO_LEN	9000
 
 /* Module parameters */
@@ -140,6 +145,249 @@ static int stmmac_init_fs(struct net_device *dev);
 static void stmmac_exit_fs(void);
 #endif
 
+#ifdef STMMAC_IEEE1588
+#define MAX_TIMEOUT_CNT	5000
+#define stmmac_hwtstamp_is_none(cfg) ((cfg) == HWTSTAMP_FILTER_NONE)
+
+static int stmmac_hwtstamp_ioctl(struct net_device *netdev,
+		struct ifreq *ifr, int cmd)
+{
+	struct hwtstamp_config config;
+	struct stmmac_priv *lp = netdev_priv(netdev);
+	u32 ptpctl;
+
+	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+		return -EFAULT;
+
+	pr_debug("%s config flag:0x%x, tx_type:0x%x, rx_filter:0x%x\n",
+			__func__, config.flags, config.tx_type, config.rx_filter);
+
+	/* reserved for future extensions */
+	if (config.flags)
+		return -EINVAL;
+
+	if ((config.tx_type != HWTSTAMP_TX_OFF) &&
+			(config.tx_type != HWTSTAMP_TX_ON))
+		return -ERANGE;
+
+	ptpctl = bfin_read_EMAC_PTP_CTL();
+	bfin_write_EMAC_PTP_CTL((1 << 16));
+	SSYNC();
+	ptpctl = bfin_read_EMAC_PTP_CTL();
+	printk("ptp init val is:  0x%x, config.rx_filter:0x%x============\n", ptpctl, config.rx_filter);
+
+	switch (config.rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		/*
+		 * Dont allow any timestamping
+		 */
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+		config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
+		ptpctl |= PTP_TSIPV4ENA;  /* udp version 1 */
+		break;
+
+	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+		config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
+		ptpctl |= (PTP_TSIPV4ENA | PTP_TSVER2ENA); /* udp version 2 */
+		break;
+
+	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+		config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
+		ptpctl |= (PTP_TSIPENA | PTP_TSVER2ENA);   /* ethernet version 2 */
+		break;
+
+	case HWTSTAMP_FILTER_PTP_V2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+		config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+		ptpctl |= (PTP_TSENALL | PTP_TSVER2ENA);   /* ethernet version 2 */
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	if (config.tx_type == HWTSTAMP_TX_OFF &&
+		stmmac_hwtstamp_is_none(config.rx_filter)) {
+		ptpctl &= ~PTP_EN;
+		bfin_write_EMAC_PTP_CTL(ptpctl);
+
+		SSYNC();
+	} else {
+		ptpctl |= PTP_EN;
+		bfin_write_EMAC_PTP_CTL(ptpctl);
+
+		bfin_write_EMAC_PTP_SECUPDT(0x0);
+		bfin_write_EMAC_PTP_NSECUPDT(0x0);
+		ptpctl |= PTP_TSINIT;
+		bfin_write_EMAC_PTP_CTL(ptpctl);
+
+		bfin_write_EMAC_PTP_SUBSEC(0x2b);
+
+		SSYNC();
+
+		lp->compare.last_update = 0;
+		timecounter_init(&lp->clock,
+				&lp->cycles,
+				ktime_to_ns(ktime_get_real()));
+		timecompare_update(&lp->compare, 0);
+		printk("sec %d, nsec %d, ptpctl 0x%x, subsec 0x%x\n", bfin_read_EMAC_PTP_SEC(),
+				bfin_read_EMAC_PTP_NSEC(), bfin_read_EMAC_PTP_CTL(), bfin_read_EMAC_PTP_SUBSEC());
+	}
+
+	lp->stamp_cfg = config;
+	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+		-EFAULT : 0;
+}
+
+static void stmmac_dump_hwtamp(char *s, ktime_t *hw, ktime_t *ts, struct timecompare *cmp)
+{
+	ktime_t sys = ktime_get_real();
+
+	printk("%s %s hardware:%d,%d transform system:%d,%d system:%d,%d, cmp:%lld, %lld\n",
+			__func__, s, hw->tv.sec, hw->tv.nsec, ts->tv.sec, ts->tv.nsec, sys.tv.sec,
+			sys.tv.nsec, cmp->offset, cmp->skew);
+}
+
+static void stmmac_tx_hwtstamp(struct stmmac_priv *lp, struct sk_buff *skb,
+				struct dma_desc *desc)
+{
+	if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
+		int timeout_cnt = MAX_TIMEOUT_CNT;
+
+		/* When doing time stamping, keep the connection to the socket
+		 * a while longer
+		 */
+		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+
+		/*
+		 * The timestamping is done at the EMAC module's MII/RMII interface
+		 * when the module sees the Start of Frame of an event message packet. This
+		 * interface is the closest possible place to the physical Ethernet transmission
+		 * medium, providing the best timing accuracy.
+		 */
+		while (!desc->des01.etx.time_stamp_status && (--timeout_cnt))
+			udelay(20);
+
+		/* work around */
+		/*
+		if (timeout_cnt == 0) {
+			printk("%d", desc->des01.etx.time_stamp_status);
+			if (desc->des01.etx.time_stamp_status)
+				timeout_cnt = 1;
+		}
+		*/
+		if (timeout_cnt == 0) {
+			printk("timestamp the TX packet failed==========\n");
+			printk("des01.first:%d, tsenable:%d, stamp_status:%d\n", desc->des01.etx.first_segment,
+						desc->des01.etx.time_stamp_enable, desc->des01.etx.time_stamp_status);
+			printk("hwsec %d, hwnsec %d\n", bfin_read_EMAC_PTP_SEC(), bfin_read_EMAC_PTP_NSEC());
+			if (!desc->des01.etx.time_stamp_status)
+				return;
+		}
+		{
+			struct skb_shared_hwtstamps shhwtstamps;
+			u64 ns;
+			u64 regval;
+
+			regval = desc->des6;
+			regval |= (u64)desc->des7 << 32;
+			printk("tx: des06: %ld, des07 %ld\n", desc->des6, desc->des7);
+			memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+			ns = timecounter_cyc2time(&lp->clock,
+					regval);
+			timecompare_update(&lp->compare, ns);
+			shhwtstamps.hwtstamp = ns_to_ktime(ns);
+			shhwtstamps.syststamp =
+				timecompare_transform(&lp->compare, ns);
+			skb_tstamp_tx(skb, &shhwtstamps);
+
+			stmmac_dump_hwtamp("TX", &shhwtstamps.hwtstamp, &shhwtstamps.syststamp, &lp->compare);
+		}
+	}
+}
+
+static void stmmac_rx_hwtstamp(struct stmmac_priv *lp, struct sk_buff *skb, struct dma_desc *desc)
+{
+	u64 regval, ns;
+	struct skb_shared_hwtstamps *shhwtstamps;
+
+	if (stmmac_hwtstamp_is_none(lp->stamp_cfg.rx_filter))
+		return;
+
+	if (!desc->des01.erx.ipc_csum_error)
+		return;
+
+	shhwtstamps = skb_hwtstamps(skb);
+
+	regval = desc->des6;
+	regval |= (u64)desc->des7 << 32;
+	printk("rx: des06: %ld, des07 %ld\n", desc->des6, desc->des7);
+	ns = timecounter_cyc2time(&lp->clock, regval);
+	timecompare_update(&lp->compare, ns);
+	memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+	shhwtstamps->hwtstamp = ns_to_ktime(ns);
+	shhwtstamps->syststamp = timecompare_transform(&lp->compare, ns);
+
+	stmmac_dump_hwtamp("RX", &shhwtstamps->hwtstamp, &shhwtstamps->syststamp, &lp->compare);
+}
+
+/*
+ * stmmac_read_clock - read raw cycle counter (to be used by time counter)
+ */
+static cycle_t stmmac_read_clock(const struct cyclecounter *tc)
+{
+	u64 ns;
+	ktime_t hw_time;
+
+	hw_time.tv.sec = bfin_read_EMAC_PTP_SEC();
+	hw_time.tv.nsec = bfin_read_EMAC_PTP_NSEC();
+	ns = ktime_to_ns(hw_time);
+	do_div(ns, 20);
+	return ns;
+}
+
+
+static void stmmac_hwtstamp_init(struct net_device *netdev)
+{
+	struct stmmac_priv *lp = netdev_priv(netdev);
+	u64 append;
+
+	*(unsigned int *)(0xFFC03404) = 0x0;
+	printk("%s===========pads is 0x%x\n", __func__, *(unsigned int *)(0xFFC03404));
+	/* Initialize hardware timer */
+
+	memset(&lp->cycles, 0, sizeof(lp->cycles));
+	lp->cycles.read = stmmac_read_clock;
+	lp->cycles.mask = CLOCKSOURCE_MASK(64);
+	lp->cycles.mult = 20;
+	lp->cycles.shift = 0;
+
+	/* Synchronize our NIC clock against system wall clock */
+	memset(&lp->compare, 0, sizeof(lp->compare));
+	lp->compare.source = &lp->clock;
+	lp->compare.target = ktime_get_real;
+	lp->compare.num_samples = 10;
+
+	/* Initialize hwstamp config */
+	lp->stamp_cfg.rx_filter = HWTSTAMP_FILTER_NONE;
+	lp->stamp_cfg.tx_type = HWTSTAMP_TX_OFF;
+}
+
+#else
+# define stmmac_hwtstamp_is_none(cfg) 0
+# define stmmac_hwtstamp_init(dev)
+# define stmmac_hwtstamp_ioctl(dev, ifr, cmd) (-EOPNOTSUPP)
+# define stmmac_rx_hwtstamp(priv, skb, desc)
+# define stmmac_tx_hwtstamp(priv, skb, desc)
+#endif
+
 /**
  * stmmac_verify_args - verify the driver parameters.
  * Description: it verifies if some wrong parameter is passed to the driver.
@@ -1200,6 +1448,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 	priv->hw->dma->enable_dma_transmission(priv->ioaddr);
 
 	spin_unlock(&priv->tx_lock);
+	stmmac_tx_hwtstamp(priv, skb, desc);
 
 	return NETDEV_TX_OK;
 }
@@ -1314,6 +1563,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
 			}
 #endif
 			skb->protocol = eth_type_trans(skb, priv->dev);
+			stmmac_rx_hwtstamp(priv, skb, p);
 
 			if (unlikely(!priv->rx_coe)) {
 				/* No RX COE for old mac10/100 devices */
@@ -1492,7 +1742,6 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
 		priv->hw->mac->host_irq_status((void __iomem *) dev->base_addr);
 
 	stmmac_dma_interrupt(priv);
-
 	return IRQ_HANDLED;
 }
 
@@ -1528,6 +1777,9 @@ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 	if (!priv->phydev)
 		return -EINVAL;
 
+	if (cmd == SIOCSHWTSTAMP)
+		return stmmac_hwtstamp_ioctl(dev, rq, cmd);
+
 	ret = phy_mii_ioctl(priv->phydev, rq, cmd);
 
 	return ret;
@@ -1785,6 +2037,12 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
 		device_set_wakeup_capable(priv->device, 1);
 	}
 
+	stmmac_hwtstamp_init(dev);
+
+	DBG(probe, DEBUG, "%s: Scatter/Gather: %s - HW checksums: %s\n",
+	    dev->name, (dev->features & NETIF_F_SG) ? "on" : "off",
+	    (dev->features & NETIF_F_IP_CSUM) ? "on" : "off");
+
 	return ret;
 }
 
_______________________________________________
Linux-kernel-commits mailing list
[email protected]
https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits

Reply via email to