lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <1479466028-29914-1-git-send-email-rafalo@cadence.com>
Date:   Fri, 18 Nov 2016 10:47:08 +0000
From:   Rafal Ozieblo <rafalo@...ence.com>
To:     Nicolas Ferre <nicolas.ferre@...el.com>,
        Harini Katakam <harinikatakamlinux@...il.com>,
        Andrei Pistirica <Andrei.Pistirica@...rochip.com>,
        <netdev@...r.kernel.org>, <linux-kernel@...r.kernel.org>
CC:     Rafal Ozieblo <rafalo@...ence.com>
Subject: [PATCH net-next] cadence: Add hardware PTP support.

Signed-off-by: Rafal Ozieblo <rafalo@...ence.com>
---
 Documentation/devicetree/bindings/net/macb.txt |   1 +
 drivers/net/ethernet/cadence/macb.c            | 742 ++++++++++++++++++++++++-
 drivers/net/ethernet/cadence/macb.h            | 217 +++++++-
 3 files changed, 950 insertions(+), 10 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt
index 1506e94..27966ae 100644
--- a/Documentation/devicetree/bindings/net/macb.txt
+++ b/Documentation/devicetree/bindings/net/macb.txt
@@ -22,6 +22,7 @@ Required properties:
 	Required elements: 'pclk', 'hclk'
 	Optional elements: 'tx_clk'
 	Optional elements: 'rx_clk' applies to cdns,zynqmp-gem
+	Optional elements: 'tsu_clk'
 - clocks: Phandles to input clocks.
 
 Optional properties for PHY child node:
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index e1847ce..8481e4a 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -32,7 +32,10 @@
 #include <linux/of_gpio.h>
 #include <linux/of_mdio.h>
 #include <linux/of_net.h>
-
+#include <linux/ptp_clock_kernel.h>
+#include <linux/clocksource.h>
+#include <linux/net_tstamp.h>
+#include <linux/timecounter.h>
 #include "macb.h"
 
 #define MACB_RX_BUFFER_SIZE	128
@@ -665,6 +668,81 @@ static void macb_tx_error_task(struct work_struct *work)
 	spin_unlock_irqrestore(&bp->lock, flags);
 }
 
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+static int macb_hw_timestamp(struct macb *bp, u32 dma_desc_ts_1, u32 dma_desc_ts_2, struct timespec64 *ts)
+{
+	struct timespec64 tsu;
+
+	ts->tv_sec = (MACB_BFEXT(DMA_TS_MSB_SEC, dma_desc_ts_2) << MACB_DMA_TS_LSB_SEC_SIZE) |
+			MACB_BFEXT(DMA_TS_LSB_SEC, dma_desc_ts_1);
+	ts->tv_nsec = MACB_BFEXT(DMA_TS_NSEC, dma_desc_ts_1);
+
+	/* TSU overlaping workaround
+	 * The timestamp only contains lower few bits of seconds,
+	 * so add value from 1588 timer
+	 */
+	macb_ptp_time_get(bp, &tsu);
+
+	/* If the top bit is set in the timestamp,
+	 * but not in 1588 timer, it has rolled over,
+	 * so subtract max size
+	 */
+	if ((ts->tv_sec & (MACB_DMA_TS_SEC_TOP >> 1)) && 
+		!(tsu.tv_sec & (MACB_DMA_TS_SEC_TOP >> 1)))
+		ts->tv_sec -= MACB_DMA_TS_SEC_TOP;
+
+	ts->tv_sec += ((~MACB_DMA_TS_SEC_MASK) & (tsu.tv_sec));
+
+	return 0;
+}
+
+static void macb_tstamp_tx(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc)
+{
+	struct skb_shared_hwtstamps shhwtstamps;
+	struct timespec64 ts;
+
+	if(MACB_BFEXT(DMA_TX_TS_VALID, desc->ctrl)) {
+		macb_hw_timestamp(bp, desc->dma_desc_ts_1, desc->dma_desc_ts_2, &ts);
+		memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+		shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
+		skb_tstamp_tx(skb, &shhwtstamps);
+	}
+}
+
+static void macb_tx_timestamp_flush(struct work_struct *work)
+{
+	struct macb_queue *queue = container_of(work, struct macb_queue, tx_timestamp_task);
+	struct macb_tx_timestamp *tx_timestamp;
+	unsigned long head = smp_load_acquire(&queue->tx_tstamp_head);
+	unsigned long tail = queue->tx_tstamp_tail;
+
+	while (CIRC_CNT(head, tail, PTP_TS_BUFFER_SIZE)) {
+		tx_timestamp = &queue->tx_timestamps[tail];
+		macb_tstamp_tx(queue->bp, tx_timestamp->skb, &tx_timestamp->desc);
+		/* cleanup */
+		dev_kfree_skb_any(tx_timestamp->skb);
+		smp_store_release(&queue->tx_tstamp_tail, (tail + 1) & (PTP_TS_BUFFER_SIZE - 1));
+		tail = queue->tx_tstamp_tail;
+	}
+}
+
+static int macb_tx_timestamp_add(struct macb_queue *queue, struct sk_buff *skb, struct macb_dma_desc *desc)
+{
+	struct macb_tx_timestamp *tx_timestamp;
+	unsigned long head = queue->tx_tstamp_head;
+	unsigned long tail = ACCESS_ONCE(queue->tx_tstamp_tail);
+
+	if (CIRC_SPACE(head, tail, PTP_TS_BUFFER_SIZE) == 0)
+		return -ENOMEM;
+
+	tx_timestamp = &queue->tx_timestamps[head];
+	tx_timestamp->skb = skb;
+	memcpy(&tx_timestamp->desc, desc, sizeof(tx_timestamp->desc));
+	smp_store_release(&queue->tx_tstamp_head, (head + 1) & (PTP_TS_BUFFER_SIZE - 1));
+	return 0;
+}
+#endif
+
 static void macb_tx_interrupt(struct macb_queue *queue)
 {
 	unsigned int tail;
@@ -712,6 +790,16 @@ static void macb_tx_interrupt(struct macb_queue *queue)
 				netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n",
 					    macb_tx_ring_wrap(bp, tail),
 					    skb->data);
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+				if (bp->ptp_hw_support)
+					if (macb_tx_timestamp_add(queue, skb, desc) == 0) {
+						 /* skb now belongs to timestamp buffer
+						 * and will be removed later
+						 */
+						tx_skb->skb = NULL;
+						schedule_work(&queue->tx_timestamp_task);
+					}
+#endif
 				bp->stats.tx_packets++;
 				bp->stats.tx_bytes += skb->len;
 			}
@@ -876,6 +964,17 @@ static int gem_rx(struct macb *bp, int budget)
 		bp->stats.rx_packets++;
 		bp->stats.rx_bytes += skb->len;
 
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+		if (bp->ptp_hw_support) {
+			struct timespec64 ts;
+
+			if (MACB_BFEXT(DMA_RX_TS_VALID, desc->addr)) {
+				macb_hw_timestamp(bp, desc->dma_desc_ts_1, desc->dma_desc_ts_2, &ts);
+				skb_hwtstamps(skb)->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
+			}
+		}
+#endif
+
 #if defined(DEBUG) && defined(VERBOSE_DEBUG)
 		netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n",
 			    skb->len, skb->csum);
@@ -1103,6 +1202,9 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
 	struct macb *bp = queue->bp;
 	struct net_device *dev = bp->dev;
 	u32 status, ctrl;
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+	struct timespec64 ts;
+#endif
 
 	status = queue_readl(queue, ISR);
 
@@ -1195,6 +1297,87 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
 				queue_writel(queue, ISR, MACB_BIT(HRESP));
 		}
 
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+		if (status & MACB_BIT(PTP_DELAY_REQ_FRAME_RECEIVED)) {
+			if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+				queue_writel(queue, ISR, MACB_BIT(PTP_DELAY_REQ_FRAME_RECEIVED));
+			if (macb_ptp_time_frame_rx_get(bp, &ts) != 0) {
+				ts.tv_sec = 0;
+				ts.tv_nsec = 0;
+			}
+			macb_ptp_event(bp, &ts);
+		}
+
+		if (status & MACB_BIT(PTP_SYNC_FRAME_RECEIVED)) {
+			if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+				queue_writel(queue, ISR, MACB_BIT(PTP_SYNC_FRAME_RECEIVED));
+			if (macb_ptp_time_frame_rx_get(bp, &ts) != 0) {
+				ts.tv_sec = 0;
+				ts.tv_nsec = 0;
+			}
+			macb_ptp_event(bp, &ts);
+		}
+
+		if (status & MACB_BIT(PTP_DELAY_REQ_FRAME_TRANSMITTED)) {
+			if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+				queue_writel(queue, ISR, MACB_BIT(PTP_DELAY_REQ_FRAME_TRANSMITTED));
+			if (macb_ptp_time_frame_tx_get(bp, &ts) != 0) {
+				ts.tv_sec = 0;
+				ts.tv_nsec = 0;
+			}
+			macb_ptp_event(bp, &ts);
+		}
+
+		if (status & MACB_BIT(PTP_SYNC_FRAME_TRANSMITTED)) {
+			if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+				queue_writel(queue, ISR, MACB_BIT(PTP_SYNC_FRAME_TRANSMITTED));
+			if (macb_ptp_time_frame_tx_get(bp, &ts) != 0) {
+				ts.tv_sec = 0;
+				ts.tv_nsec = 0;
+			}
+			macb_ptp_event(bp, &ts);
+		}
+
+		if (status & MACB_BIT(PTP_PDELAY_REQ_FRAME_RECEIVED)) {
+			if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+				queue_writel(queue, ISR, MACB_BIT(PTP_PDELAY_REQ_FRAME_RECEIVED));
+			if (macb_ptp_time_peer_frame_rx_get(bp, &ts) != 0) {
+				ts.tv_sec = 0;
+				ts.tv_nsec = 0;
+			}
+			macb_ptp_event(bp, &ts);
+		}
+
+		if (status & MACB_BIT(PTP_PDELAY_RESP_FRAME_RECEIVED)) {
+			if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+				queue_writel(queue, ISR, MACB_BIT(PTP_PDELAY_RESP_FRAME_RECEIVED));
+			if (macb_ptp_time_peer_frame_rx_get(bp, &ts) != 0) {
+				ts.tv_sec = 0;
+				ts.tv_nsec = 0;
+			}
+			macb_ptp_event(bp, &ts);
+		}
+
+		if (status & MACB_BIT(PTP_PDELAY_REQ_FRAME_TRANSMITTED)) {
+			if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+				queue_writel(queue, ISR, MACB_BIT(PTP_PDELAY_REQ_FRAME_TRANSMITTED));
+			if (macb_ptp_time_peer_frame_tx_get(bp, &ts) != 0) {
+				ts.tv_sec = 0;
+				ts.tv_nsec = 0;
+			}
+			macb_ptp_event(bp, &ts);
+		}
+
+		if (status & MACB_BIT(PTP_PDELAY_RESP_FRAME_TRANSMITTED)) {
+			if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+				queue_writel(queue, ISR, MACB_BIT(PTP_PDELAY_RESP_FRAME_TRANSMITTED));
+			if (macb_ptp_time_peer_frame_tx_get(bp, &ts) != 0) {
+				ts.tv_sec = 0;
+				ts.tv_nsec = 0;
+			}
+			macb_ptp_event(bp, &ts);
+		}
+#endif
 		status = queue_readl(queue, ISR);
 	}
 
@@ -1422,7 +1605,10 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	/* Make newly initialized descriptor visible to hardware */
 	wmb();
 
-	skb_tx_timestamp(skb);
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+	if (!bp->ptp_hw_support)
+#endif
+		skb_tx_timestamp(skb);
 
 	macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
 
@@ -1752,6 +1938,9 @@ static void macb_configure_dma(struct macb *bp)
 #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
 		dmacfg |= GEM_BIT(ADDR64);
 #endif
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+		dmacfg |= GEM_BIT(RX_EXTENDED_MODE) | GEM_BIT(TX_EXTENDED_MODE);
+#endif
 		netdev_dbg(bp->dev, "Cadence configure DMA with 0x%08x\n",
 			   dmacfg);
 		gem_writel(bp, DMACFG, dmacfg);
@@ -1763,7 +1952,7 @@ static void macb_init_hw(struct macb *bp)
 	struct macb_queue *queue;
 	unsigned int q;
 
-	u32 config;
+	u32 config, ier;
 
 	macb_reset_hw(bp);
 	macb_set_hwaddr(bp);
@@ -1808,10 +1997,21 @@ static void macb_init_hw(struct macb *bp)
 #endif
 
 		/* Enable interrupts */
-		queue_writel(queue, IER,
-			     MACB_RX_INT_FLAGS |
-			     MACB_TX_INT_FLAGS |
-			     MACB_BIT(HRESP));
+		ier = MACB_RX_INT_FLAGS |
+			MACB_TX_INT_FLAGS |
+			MACB_BIT(HRESP);
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+		if (bp->ptp_hw_support)
+			ier |= MACB_BIT(PTP_DELAY_REQ_FRAME_TRANSMITTED) |
+				MACB_BIT(PTP_SYNC_FRAME_TRANSMITTED) |
+				MACB_BIT(PTP_PDELAY_REQ_FRAME_TRANSMITTED) |
+				MACB_BIT(PTP_PDELAY_RESP_FRAME_TRANSMITTED) |
+				MACB_BIT(PTP_DELAY_REQ_FRAME_RECEIVED) |
+				MACB_BIT(PTP_SYNC_FRAME_RECEIVED) |
+				MACB_BIT(PTP_PDELAY_REQ_FRAME_RECEIVED) |
+				MACB_BIT(PTP_PDELAY_RESP_FRAME_RECEIVED);
+#endif
+		queue_writel(queue, IER, ier);
 	}
 
 	/* Enable TX and RX */
@@ -2183,6 +2383,34 @@ static void macb_get_regs(struct net_device *dev, struct ethtool_regs *regs,
 		regs_buff[13] = gem_readl(bp, DMACFG);
 }
 
+int macb_get_ts_info(struct net_device *dev, struct ethtool_ts_info *ts_info)
+{
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+	struct macb *bp = netdev_priv(dev);
+
+	if (bp->ptp_hw_support) {
+		ts_info->phc_index = ptp_clock_index(bp->ptp_clock);
+		ts_info->so_timestamping =
+			SOF_TIMESTAMPING_TX_SOFTWARE |
+			SOF_TIMESTAMPING_RX_SOFTWARE |
+			SOF_TIMESTAMPING_TX_HARDWARE |
+			SOF_TIMESTAMPING_RX_HARDWARE |
+			SOF_TIMESTAMPING_RAW_HARDWARE |
+			SOF_TIMESTAMPING_SOFTWARE;
+		ts_info->tx_types =
+			(1 << HWTSTAMP_TX_ONESTEP_SYNC) |
+			(1 << HWTSTAMP_TX_OFF) |
+			(1 << HWTSTAMP_TX_ON);
+		ts_info->rx_filters =
+			(1 << HWTSTAMP_FILTER_NONE) |
+			(1 << HWTSTAMP_FILTER_ALL);
+		return 0;
+	}
+	else
+#endif
+		return ethtool_op_get_ts_info(dev, ts_info);
+}
+
 static void macb_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
 {
 	struct macb *bp = netdev_priv(netdev);
@@ -2283,7 +2511,7 @@ static const struct ethtool_ops gem_ethtool_ops = {
 	.get_regs_len		= macb_get_regs_len,
 	.get_regs		= macb_get_regs,
 	.get_link		= ethtool_op_get_link,
-	.get_ts_info		= ethtool_op_get_ts_info,
+	.get_ts_info		= macb_get_ts_info,
 	.get_ethtool_stats	= gem_get_ethtool_stats,
 	.get_strings		= gem_get_ethtool_strings,
 	.get_sset_count		= gem_get_sset_count,
@@ -2293,6 +2521,100 @@ static const struct ethtool_ops gem_ethtool_ops = {
 	.set_ringparam		= macb_set_ringparam,
 };
 
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+int macb_get_ts(struct net_device *dev, struct ifreq *rq)
+{
+	struct macb *bp = netdev_priv(dev);
+	struct hwtstamp_config *tstamp_config = &bp->tstamp_config;
+
+	if (!bp->ptp_hw_support)
+		return -EFAULT;
+	if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config)))
+		return -EFAULT;
+	else
+		return 0;
+}
+
+int macb_set_ts(struct net_device *dev, struct ifreq *rq)
+{
+	struct macb *bp = netdev_priv(dev);
+	struct hwtstamp_config *tstamp_config = &bp->tstamp_config;
+
+	struct timespec64 ts;
+	struct incrementspec incr_spec;
+
+	enum macb_bd_control tx_bd_control;
+	enum macb_bd_control rx_bd_control;
+
+	if (!bp->ptp_hw_support)
+		return -EFAULT;
+
+	if (copy_from_user(tstamp_config, rq->ifr_data, sizeof(*tstamp_config)))
+		return -EFAULT;
+
+	switch (tstamp_config->tx_type) {
+	case HWTSTAMP_TX_OFF:
+		break;
+	case HWTSTAMP_TX_ONESTEP_SYNC:
+		if (macb_ptp_set_one_step_sync(bp, 1) != 0)
+			return -ERANGE;
+	case HWTSTAMP_TX_ON:
+		tx_bd_control = TSTAMP_ALL_FRAMES;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	switch (tstamp_config->rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+		rx_bd_control =  TSTAMP_ALL_PTP_FRAMES;
+		tstamp_config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+	case HWTSTAMP_FILTER_ALL:
+		rx_bd_control = TSTAMP_ALL_FRAMES;
+		tstamp_config->rx_filter = HWTSTAMP_FILTER_ALL;
+		break;
+	default:
+		tstamp_config->rx_filter = HWTSTAMP_FILTER_NONE;
+		return -ERANGE;
+	}
+
+	/* 1. get current system time */
+	ts = ns_to_timespec64(ktime_to_ns(ktime_get_real()));
+
+	/* 2. set ptp timer */
+	macb_ptp_time_set(bp, &ts);
+
+	/* 3. set PTP timer increment value to BASE_INCREMENT */
+	incr_spec.sub_ns = bp->tsu_incr.sub_ns;
+	incr_spec.ns = bp->tsu_incr.ns;
+	macb_ptp_increment_set(bp, &incr_spec);
+
+	if (macb_ptp_set_tstamp_mode(bp, tx_bd_control, rx_bd_control) != 0)
+		return -ERANGE;
+
+	if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config)))
+		return -EFAULT;
+	else
+		return 0;
+}
+#endif
+
 static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 	struct phy_device *phydev = dev->phydev;
@@ -2303,6 +2625,13 @@ static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 	if (!phydev)
 		return -ENODEV;
 
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+	if (cmd == SIOCGHWTSTAMP)
+		return macb_get_ts(dev, rq);
+
+	if (cmd == SIOCSHWTSTAMP)
+		return macb_set_ts(dev, rq);
+#endif
 	return phy_mii_ioctl(phydev, rq, cmd);
 }
 
@@ -3147,6 +3476,17 @@ static int macb_probe(struct platform_device *pdev)
 		    macb_is_gem(bp) ? "GEM" : "MACB", macb_readl(bp, MID),
 		    dev->base_addr, dev->irq, dev->dev_addr);
 
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+	/* Enable PTP */
+	bp->ptp_hw_support = false;
+	if (GEM_BFEXT(TSU, gem_readl(bp, DCFG5))) {
+		err = macb_ptp_init(pdev);
+		if (err)
+			dev_err(&pdev->dev, "Cannot initialize ptp.\n");
+		else
+			bp->ptp_hw_support = true;
+	}
+#endif
 	return 0;
 
 err_out_unregister_mdio:
@@ -3245,6 +3585,392 @@ static int __maybe_unused macb_resume(struct device *dev)
 	return 0;
 }
 
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+int macb_ptp_init(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	struct macb *bp = netdev_priv(dev);
+	u64 tsu_f, temp;
+	unsigned int q;
+	struct macb_queue *queue;
+
+	for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
+		queue->tx_tstamp_head = 0;
+		queue->tx_tstamp_tail = 0;
+		INIT_WORK(&queue->tx_timestamp_task, macb_tx_timestamp_flush);
+	}
+
+	snprintf(bp->ptp_clock_info.name, 16, "macb_ptp");
+	bp->ptp_clock_info.max_adj   = (64E6);
+	bp->ptp_clock_info.n_alarm   = 1;
+	bp->ptp_clock_info.n_ext_ts  = 1;
+	bp->ptp_clock_info.n_per_out = 0;
+	bp->ptp_clock_info.n_pins    = 0;
+	bp->ptp_clock_info.pps	     = 1;
+	bp->ptp_clock_info.adjfreq   = macb_ptp_adjfreq;
+	bp->ptp_clock_info.adjtime   = macb_ptp_adjtime;
+	bp->ptp_clock_info.gettime64 = macb_ptp_gettime;
+	bp->ptp_clock_info.settime64 = macb_ptp_settime;
+	bp->ptp_clock_info.enable    = macb_ptp_enable;
+
+	bp->ptp_clock = ptp_clock_register(&bp->ptp_clock_info, &pdev->dev);
+
+	bp->tsu_clk = devm_clk_get(&pdev->dev, "tsu_clk");
+	if (!IS_ERR(bp->tsu_clk)) {
+		tsu_f = clk_get_rate(bp->tsu_clk);
+	}
+	/* try pclk instead */
+	else if (!IS_ERR(bp->pclk)) {
+		bp->tsu_clk = bp->pclk;
+		tsu_f = clk_get_rate(bp->tsu_clk);
+	} else
+		return -ENOTSUPP;
+
+	bp->tsu_incr.ns = div_u64(NSEC_PER_SEC, tsu_f);
+	temp = (NSEC_PER_SEC - ((u64)bp->tsu_incr.ns * tsu_f));
+	temp *= (1 << MACB_SUB_NS_INCR_SIZE);
+	bp->tsu_incr.sub_ns = div_u64(temp, tsu_f);
+
+	return 0;
+}
+
+void macb_ptp_stop(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	struct macb *bp = netdev_priv(dev);
+
+	if (bp->ptp_clock)
+		ptp_clock_unregister(bp->ptp_clock);
+}
+
+/* Interface of Linux PTP Framework */
+
+int macb_ptp_adjfreq(struct ptp_clock_info *ptp_clock_info, s32 ppb)
+{
+	struct incrementspec incr_spec;
+	struct macb *bp = container_of(ptp_clock_info, struct macb, ptp_clock_info);
+	u64 period, temp;
+	bool neg_adj = false;
+	unsigned long flags;
+
+	if (!ptp_clock_info)
+		return -EINVAL;
+
+	if (ppb < 0) {
+		neg_adj = true;
+		ppb = -ppb;
+	}
+
+	/* Adjustment is relative to base frequency */
+	incr_spec.sub_ns = bp->tsu_incr.sub_ns;
+	incr_spec.ns = bp->tsu_incr.ns;
+
+	/* scaling */
+	period = ((u64) incr_spec.ns << MACB_SUB_NS_INCR_SIZE) + incr_spec.sub_ns;
+	temp = (u64)ppb * period;
+	/* Divide with rounding, equivalent to floating dividing:
+	 * (temp / NSEC_PER_SEC) + 0.5
+	 */
+	temp = div_u64(temp + (NSEC_PER_SEC >> 1), NSEC_PER_SEC);
+
+	period = neg_adj ? (period - temp) : (period + temp);
+
+	incr_spec.ns = (period >> MACB_SUB_NS_INCR_SIZE) & ((1 << MACB_NUM_INCS_SIZE) - 1);
+	incr_spec.sub_ns = period & ((1 << MACB_SUB_NS_INCR_SIZE) - 1);
+
+	spin_lock_irqsave(&bp->lock, flags);
+	macb_ptp_increment_set(bp, &incr_spec);
+
+	spin_unlock_irqrestore(&bp->lock, flags);
+	return 0;
+}
+
+int macb_ptp_adjtime(struct ptp_clock_info *ptp_clock_info, s64 delta)
+{
+
+	u64 ts_nsec;
+	struct timespec64 ts;
+	struct macb *bp = container_of(ptp_clock_info, struct macb, ptp_clock_info);
+
+	if (!ptp_clock_info)
+		return -EINVAL;
+
+	if (delta >= -TSU_NSEC_MAX_VAL && delta <= TSU_NSEC_MAX_VAL) {
+		macb_ptp_time_adjust(bp, (s32)delta);
+	} else {
+		macb_ptp_time_get(bp, &ts);
+
+		ts_nsec = ts.tv_nsec + ts.tv_sec * NSEC_PER_SEC;
+		ts_nsec += delta;
+
+		ts.tv_sec = div_u64(ts_nsec, NSEC_PER_SEC);
+		ts.tv_nsec = ts_nsec - ts.tv_sec * NSEC_PER_SEC;
+
+		macb_ptp_time_set(bp, &ts);
+	}
+
+	return 0;
+}
+
+
+int macb_ptp_gettime(struct ptp_clock_info *ptp_clock_info, struct timespec64 *ts)
+{
+	struct macb *bp = container_of(ptp_clock_info, struct macb, ptp_clock_info);
+
+	if (!ptp_clock_info || !ts)
+		return -EINVAL;
+
+	macb_ptp_time_get(bp, ts);
+	return 0;
+}
+
+int macb_ptp_settime(struct ptp_clock_info *ptp_clock_info, const struct timespec64 *ts)
+{
+	struct macb *bp = container_of(ptp_clock_info, struct macb, ptp_clock_info);
+
+	if (!ptp_clock_info || !ts)
+		return -EINVAL;
+
+	macb_ptp_time_set(bp, ts);
+	return 0;
+}
+
+int macb_ptp_enable(struct ptp_clock_info *ptp_clock_info,
+		    struct ptp_clock_request *request, int on)
+{
+	struct macb *bp = container_of(ptp_clock_info, struct macb, ptp_clock_info);
+	u32 mask;
+
+	if (!ptp_clock_info || !request)
+		return -EINVAL;
+
+	switch (request->type) {
+	case PTP_CLK_REQ_EXTTS:	/* Toggle TSU match interrupt */
+		mask = macb_readl(bp, IMR) | (1 << MACB_TSU_TIMER_COMPARISON_INTERRUPT_OFFSET);
+		if (on)
+			macb_writel(bp, IER, mask);
+		else
+			macb_writel(bp, IDR, mask);
+		break;
+	case PTP_CLK_REQ_PEROUT: /* Toggle Periodic output */
+		return -EOPNOTSUPP;
+		/* break; */
+	case PTP_CLK_REQ_PPS:	/* Toggle TSU periodic (second) interrupt */
+		mask = macb_readl(bp, IMR) | (1 << MACB_TSU_SECONDS_REGISTER_INCREMENT_OFFSET);
+		if (on)
+			macb_writel(bp, IER, mask);
+		else
+			macb_writel(bp, IDR, mask);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+
+/* End of Interface of Linux PTP Framework */
+
+int macb_ptp_increment_get(struct macb *bp, struct incrementspec *incr_spec)
+{
+	u32 sub_ns_reg;
+	u32 ns_reg;
+
+	if (!bp || !incr_spec)
+		return -EINVAL;
+
+	sub_ns_reg = macb_readl(bp, TSU_TIMER_INCR_SUB_NSEC);
+	ns_reg = macb_readl(bp, TSU_TIMER_INCR);
+
+	incr_spec->sub_ns = MACB_BFEXT(SUB_NS_INCR_MSB, sub_ns_reg) << MACB_SUB_NS_INCR_LSB_SIZE | MACB_BFEXT(SUB_NS_INCR_LSB, sub_ns_reg);
+	incr_spec->ns = MACB_BFEXT(NS_INCREMENT, ns_reg);
+
+	return 0;
+}
+
+int macb_ptp_increment_set(struct macb *bp, struct incrementspec *incr_spec)
+{
+	u32 sub_ns_reg;
+	u32 ns_reg;
+
+	if (!bp || !incr_spec)
+		return -EINVAL;
+
+	sub_ns_reg = MACB_BF(SUB_NS_INCR_LSB, incr_spec->sub_ns)
+			| (((incr_spec->sub_ns & ~((1 << MACB_SUB_NS_INCR_LSB_SIZE) - 1)) >> MACB_SUB_NS_INCR_LSB_SIZE));
+	ns_reg = incr_spec->ns;
+
+	/* tsu_timer_incr register must be written after the tsu_timer_incr_sub_ns register
+	 * and the write operation will cause the value written to the tsu_timer_incr_sub_ns
+	 * register to take effect.
+	 */
+	macb_writel(bp, TSU_TIMER_INCR_SUB_NSEC, sub_ns_reg);
+	macb_writel(bp, TSU_TIMER_INCR, ns_reg);
+
+	return 0;
+}
+
+int macb_ptp_time_adjust(struct macb *bp, s32 delta)
+{
+	bool subtract = 0;
+	u32 val = 0;
+
+	if (!bp)
+		return -EINVAL;
+
+	if ((delta < -TSU_NSEC_MAX_VAL) || (delta > TSU_NSEC_MAX_VAL))
+		return -EINVAL;
+
+	if (delta < 0) {
+		subtract = 1;
+		delta = -delta;
+	}
+
+	val = (subtract << MACB_ADD_SUBTRACT_OFFSET) | delta;
+
+	macb_writel(bp, TSU_TIMER_ADJUST, val);
+
+	return 0;
+}
+
+int macb_ptp_time_get(struct macb *bp, struct timespec64 *ts)
+{
+	long first, second;
+	u32 sec, msb_sec;
+
+	if (!bp || !ts)
+		return -EINVAL;
+
+	first = macb_readl(bp, TSU_TMR_NSEC);
+	sec = macb_readl(bp, TSU_TMR_SEC);
+	msb_sec = macb_readl(bp, TSU_TMR_MSB_SEC);
+	second = macb_readl(bp, TSU_TMR_NSEC);
+
+	/* test for nsec rollover */
+	if (first > second) {
+		/* if so, use later read & re-read seconds
+		 * (assume all done within 1s)
+		 */
+		ts->tv_nsec = macb_readl(bp, TSU_TMR_NSEC);
+		sec = macb_readl(bp, TSU_TMR_SEC);
+		msb_sec = macb_readl(bp, TSU_TMR_MSB_SEC);
+	} else
+		ts->tv_nsec = first;
+
+	ts->tv_sec = (((u64)msb_sec << MACB_TIMER_LSB_SEC_SIZE) | sec)
+			& TSU_SEC_MAX_VAL;
+
+	return 0;
+}
+
+int macb_ptp_time_set(struct macb *bp, const struct timespec64 *ts)
+{
+	if (!bp || !ts)
+		return -EINVAL;
+
+	macb_writel(bp, TSU_TMR_MSB_SEC, (ts->tv_sec >> MACB_TIMER_LSB_SEC_SIZE)
+			& ((1 << MACB_TIMER_MSB_SEC_SIZE) - 1));
+	/* write lower bits 2nd, for synchronised secs update */
+	macb_writel(bp, TSU_TMR_SEC, ts->tv_sec & (((u64)1 << MACB_TIMER_LSB_SEC_SIZE) - 1));
+	macb_writel(bp, TSU_TMR_NSEC, ts->tv_nsec);
+
+	return 0;
+}
+
+int macb_ptp_time_peer_frame_tx_get(struct macb *bp, struct timespec64 *ts)
+{
+	if (!bp || !ts)
+		return -EINVAL;
+
+	ts->tv_sec = (((u64)macb_readl(bp, TSU_PEER_TX_MSB_SEC) << 32) |
+		macb_readl(bp, TSU_PEER_TX_SEC)) & TSU_SEC_MAX_VAL;
+	ts->tv_nsec = macb_readl(bp, TSU_PEER_TX_NSEC);
+
+	return 0;
+}
+
+int macb_ptp_time_peer_frame_rx_get(struct macb *bp, struct timespec64 *ts)
+{
+	if (!bp || !ts)
+		return -EINVAL;
+
+	ts->tv_sec = (((u64)macb_readl(bp, TSU_PEER_RX_MSB_SEC) << 32) |
+		macb_readl(bp, TSU_PEER_RX_SEC)) & TSU_SEC_MAX_VAL;
+	ts->tv_nsec = macb_readl(bp, TSU_PEER_RX_NSEC);
+
+	return 0;
+}
+
+int macb_ptp_time_frame_tx_get(struct macb *bp, struct timespec64 *ts)
+{
+	if (!bp || !ts)
+		return -EINVAL;
+
+	ts->tv_sec = (((u64)macb_readl(bp, TSU_PTP_TX_MSB_SEC) << 32) |
+		macb_readl(bp, TSU_PTP_TX_SEC)) & TSU_SEC_MAX_VAL;
+	ts->tv_nsec = macb_readl(bp, TSU_PTP_TX_NSEC);
+
+	return 0;
+}
+
+int macb_ptp_time_frame_rx_get(struct macb *bp, struct timespec64 *ts)
+{
+	if (!bp || !ts)
+		return -EINVAL;
+
+	ts->tv_sec = (((u64)macb_readl(bp, TSU_PTP_RX_MSB_SEC) << 32) |
+		      macb_readl(bp, TSU_PTP_RX_SEC)) & TSU_SEC_MAX_VAL;
+	ts->tv_nsec = macb_readl(bp, TSU_PTP_RX_NSEC);
+
+	return 0;
+}
+
+int macb_ptp_event(struct macb *bp, struct timespec64 *ts)
+{
+	struct ptp_clock_event event;
+
+	event.type = PTP_CLOCK_EXTTS;
+	event.index = 0;
+	event.timestamp = ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
+
+	ptp_clock_event(bp->ptp_clock, &event);
+
+	return 0;
+}
+
+int macb_ptp_set_one_step_sync(struct macb *bp, u8 enable)
+{
+	u32 reg_val;
+
+	if (!bp)
+		return -EINVAL;
+	if (enable < 0 || enable > 1)
+		return -EINVAL;
+
+	reg_val = macb_readl(bp, NCR);
+
+	if (enable)
+		macb_writel(bp, NCR, reg_val | MACB_BIT(ONE_STEP_SYNC_MODE));
+	else
+		macb_writel(bp, NCR, reg_val & ~MACB_BIT(ONE_STEP_SYNC_MODE));
+
+	return 0;
+}
+
+int macb_ptp_set_tstamp_mode(struct macb *bp,
+			     enum macb_bd_control tx_bd_control,
+			     enum macb_bd_control rx_bd_control)
+{
+	if (!bp)
+		return -EINVAL;
+
+	macb_writel(bp, TX_BD_CONTROL, MACB_BF(TX_BD_TS_MODE, tx_bd_control));
+	macb_writel(bp, RX_BD_CONTROL, MACB_BF(RX_BD_TS_MODE, rx_bd_control));
+
+	return 0;
+}
+#endif
+
 static SIMPLE_DEV_PM_OPS(macb_pm_ops, macb_suspend, macb_resume);
 
 static struct platform_driver macb_driver = {
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 1216950..1381f0a 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -66,8 +66,32 @@
 #define MACB_USRIO		0x00c0
 #define MACB_WOL		0x00c4
 #define MACB_MID		0x00fc
-#define MACB_TBQPH		0x04C8
-#define MACB_RBQPH		0x04D4
+
+#define MACB_TSU_PTP_TX_MSB_SEC		0x00e8	/* PTP Event Frame Transmitted Seconds Register 47:32 */
+#define MACB_TSU_PTP_RX_MSB_SEC		0x00ec	/* PTP Event Frame Received Seconds Register 47:32 */
+#define MACB_TSU_PEER_TX_MSB_SEC	0x00f0	/* PTP Peer Event Frame Transmitted Seconds Register 47:32 */
+#define MACB_TSU_PEER_RX_MSB_SEC	0x00f4	/* PTP Peer Event Frame Received Seconds Register 47:32 */
+#define MACB_TSU_TIMER_INCR_SUB_NSEC	0x01bc	/* Sub-nanoseconds increment */
+#define MACB_TSU_TIMER_INCR		0x01dc	/* Nanoseconds increment */
+#define MACB_TSU_TMR_MSB_SEC		0x01c0	/* Upper second count of Time Stamp Unit */
+#define MACB_TSU_TMR_SEC		0x01d0	/* Lower second count of Time Stamp Unit */
+#define MACB_TSU_TMR_NSEC		0x01d4	/* Nanosecond count of Time Stamp Unit */
+#define MACB_TSU_TIMER_ADJUST		0x01d8	/* Nanosecond Adjust Register */
+#define MACB_TSU_TIMER_INCR_SUB_NSEC	0x01bc	/* Sub-nanosecond Adjust Register */
+#define MACB_TSU_TIMER_INCR		0x01dc	/* Nanosecond Adjust Register */
+#define MACB_TSU_PTP_TX_SEC		0x01e0	/* PTP Event Frame Transmitted Seconds Register 31:0 */
+#define MACB_TSU_PTP_TX_NSEC		0x01e4	/* PTP Event Frame Transmitted Nanoseconds Register */
+#define MACB_TSU_PTP_RX_SEC		0x01e8	/* PTP Event Frame Received Seconds Register 31:0 */
+#define MACB_TSU_PTP_RX_NSEC		0x01ec	/* PTP Event Frame Received Nanoseconds Register */
+#define MACB_TSU_PEER_TX_SEC		0x01f0	/* PTP Peer Event Frame Transmitted Seconds Register 31:0 */
+#define MACB_TSU_PEER_TX_NSEC		0x01f4	/* PTP Peer Event Frame Transmitted Nanoseconds Register */
+#define MACB_TSU_PEER_RX_SEC		0x01f8	/* PTP Peer Event Frame Received Seconds Register 31:0 */
+#define MACB_TSU_PEER_RX_NSEC		0x01fc	/* PTP Peer Event Frame Received Nanoseconds Register */
+
+#define MACB_TX_BD_CONTROL 	0x04cc /* TX Buffer Descriptor control register */
+#define MACB_RX_BD_CONTROL 	0x04d0 /* RX Buffer Descriptor control register */
+#define MACB_TBQPH		0x04c8
+#define MACB_RBQPH		0x04d4
 
 /* GEM register offsets. */
 #define GEM_NCFGR		0x0004 /* Network Config */
@@ -174,6 +198,8 @@
 #define MACB_NCR_TPF_SIZE	1
 #define MACB_TZQ_OFFSET		12 /* Transmit zero quantum pause frame */
 #define MACB_TZQ_SIZE		1
+#define MACB_ONE_STEP_SYNC_MODE_OFFSET 24 /* Enable One Step Synchro Mode */
+#define MACB_ONE_STEP_SYNC_MODE_SIZE 1
 
 /* Bitfields in NCFGR */
 #define MACB_SPD_OFFSET		0 /* Speed */
@@ -252,6 +278,10 @@
 #define GEM_RXBS_SIZE		8
 #define GEM_DDRP_OFFSET		24 /* disc_when_no_ahb */
 #define GEM_DDRP_SIZE		1
+#define GEM_RX_EXTENDED_MODE_OFFSET	28 /* RX extended Buffer Descriptor mode */
+#define GEM_RX_EXTENDED_MODE_SIZE	1
+#define GEM_TX_EXTENDED_MODE_OFFSET	29 /* TX extended Buffer Descriptor mode */
+#define GEM_TX_EXTENDED_MODE_SIZE	1
 #define GEM_ADDR64_OFFSET	30 /* Address bus width - 64b or 32b */
 #define GEM_ADDR64_SIZE		1
 
@@ -319,6 +349,26 @@
 #define MACB_PTZ_SIZE		1
 #define MACB_WOL_OFFSET		14 /* Enable wake-on-lan interrupt */
 #define MACB_WOL_SIZE		1
+#define MACB_PTP_DELAY_REQ_FRAME_RECEIVED_OFFSET	18	/* PTP delay_req frame received */
+#define MACB_PTP_DELAY_REQ_FRAME_RECEIVED_SIZE		1
+#define MACB_PTP_SYNC_FRAME_RECEIVED_OFFSET		19	/* PTP sync frame received */
+#define MACB_PTP_SYNC_FRAME_RECEIVED_SIZE		1
+#define MACB_PTP_DELAY_REQ_FRAME_TRANSMITTED_OFFSET	20	/* PTP delay_req frame transmitted */
+#define MACB_PTP_DELAY_REQ_FRAME_TRANSMITTED_SIZE	1
+#define MACB_PTP_SYNC_FRAME_TRANSMITTED_OFFSET		21	/* PTP sync frame transmitted */
+#define MACB_PTP_SYNC_FRAME_TRANSMITTED_SIZE		1
+#define MACB_PTP_PDELAY_REQ_FRAME_RECEIVED_OFFSET	22	/* PTP pdelay_req frame received */
+#define MACB_PTP_PDELAY_REQ_FRAME_RECEIVED_SIZE		1
+#define MACB_PTP_PDELAY_RESP_FRAME_RECEIVED_OFFSET	23	/* PTP pdelay_resp frame received */
+#define MACB_PTP_PDELAY_RESP_FRAME_RECEIVED_SIZE	1
+#define MACB_PTP_PDELAY_REQ_FRAME_TRANSMITTED_OFFSET	24	/* PTP pdelay_req frame transmitted */
+#define MACB_PTP_PDELAY_REQ_FRAME_TRANSMITTED_SIZE	1
+#define MACB_PTP_PDELAY_RESP_FRAME_TRANSMITTED_OFFSET	25	/* PTP pdelay_resp frame transmitted */
+#define MACB_PTP_PDELAY_RESP_FRAME_TRANSMITTED_SIZE	1
+#define MACB_TSU_SECONDS_REGISTER_INCREMENT_OFFSET	26	/* TSU periodic (second) interrup */
+#define MACB_TSU_SECONDS_REGISTER_INCREMENT_SIZE	1
+#define MACB_TSU_TIMER_COMPARISON_INTERRUPT_OFFSET	29	/* TSU match interrupt */
+#define MACB_TSU_TIMER_COMPARISON_INTERRUPT_SIZE	1
 
 /* Bitfields in MAN */
 #define MACB_DATA_OFFSET	0 /* data */
@@ -382,6 +432,10 @@
 #define GEM_TX_PKT_BUFF_OFFSET			21
 #define GEM_TX_PKT_BUFF_SIZE			1
 
+/* Bitfields in DCFG5. */
+#define GEM_TSU_OFFSET				8
+#define GEM_TSU_SIZE				1
+
 /* Constants for CLK */
 #define MACB_CLK_DIV8				0
 #define MACB_CLK_DIV16				1
@@ -402,6 +456,74 @@
 #define MACB_MAN_READ				2
 #define MACB_MAN_CODE				2
 
+/* Constants for TSU */
+/* MACB_TSU_TIMER_INCR_SUB_NSEC */
+#define MACB_SUB_NS_INCR_MSB_OFFSET	0 /* sub-ns MSB [23:8] which the 1588 timer will be incremented each clock cycle */
+#define MACB_SUB_NS_INCR_MSB_SIZE	16
+#define MACB_SUB_NS_INCR_LSB_OFFSET	24 /* sub-ns MSB [7:0] which the 1588 timer will be incremented each clock cycle */
+#define MACB_SUB_NS_INCR_LSB_SIZE	8
+#define MACB_SUB_NS_INCR_SIZE 		(MACB_SUB_NS_INCR_MSB_SIZE + MACB_SUB_NS_INCR_LSB_SIZE)
+
+/* MACB_TSU_TIMER_INCR */
+#define MACB_NS_INCREMENT_OFFSET	0 /* ns [7:0] which the 1588 timer will be incremented each clock cycle */
+#define MACB_NS_INCREMENT_SIZE		8
+#define MACB_ALT_NS_INCR_OFFSET		8 /* Alternative nanoseconds count */
+#define MACB_ALT_NS_INCR_SIZE		8
+#define MACB_NUM_INCS_OFFSET		16 /* Number of incs before alt inc */
+#define MACB_NUM_INCS_SIZE		8
+
+/* MACB_TSU_TIMER_ADJUST */
+#define MACB_INCREMENT_VALUE_OFFSET	0 /* Timer increment value */
+#define MACB_INCREMENT_VALUE_SIZE	30
+#define MACB_ADD_SUBTRACT_OFFSET	31 /* Write as one to subtract from the 1588 timer */
+#define MACB_ADD_SUBTRACT_SIZE		1
+
+/* MACB_TSU_TIMER_MSB_SEC */
+#define MACB_TIMER_MSB_SEC_OFFSET	0 /* TSU timer value (s). MSB [47:32] of seconds timer count */
+#define MACB_TIMER_MSB_SEC_SIZE		16
+
+/* MACB_TSU_TIMER_SEC */
+#define MACB_TIMER_LSB_SEC_OFFSET	0 /* TSU timer value (s). LSB [31:0] of seconds timer count */
+#define MACB_TIMER_LSB_SEC_SIZE		32
+
+/* MACB_TSU_TIMER_NSEC */
+#define MACB_TIMER_NSEC_OFFSET		0 /* TSU timer value (ns) */
+#define MACB_TIMER_NSEC_SIZE		30
+
+/* Transmit DMA buffer descriptor Word 1 */
+#define MACB_DMA_TX_TS_VALID_OFFSET	23 /* timestamp has been captured in the Buffer Descriptor */
+#define MACB_DMA_TX_TS_VALID_SIZE	1
+
+/* Receive DMA buffer descriptor Word 0 */
+#define MACB_DMA_RX_TS_VALID_OFFSET	2 /* indicates a valid timestamp in the Buffer Descriptor */
+#define MACB_DMA_RX_TS_VALID_SIZE	1
+
+/* DMA buffer descriptor Word 2 (32 bit addressing) or Word 4 (64 bit addressing) */
+#define MACB_DMA_TS_LSB_SEC_OFFSET	30 /* Timestamp seconds[1:0]  */
+#define MACB_DMA_TS_LSB_SEC_SIZE	2
+#define MACB_DMA_TS_NSEC_OFFSET		0 /* Timestamp nanosecs [29:0] */
+#define MACB_DMA_TS_NSEC_SIZE		30
+
+/* DMA buffer descriptor Word 3 (32 bit addressing) or Word 5 (64 bit addressing) */
+
+/* New hardware supports 12 bit precision of timestamp in DMA buffer descriptor.
+ * Old hardware supports only 6 bit precision but it is enough for PTP.
+ * Less accuracy is used always instead of checking hardware version.
+ */
+#define MACB_DMA_TS_MSB_SEC_OFFSET	0 /* Timestamp seconds[5:2] */
+#define MACB_DMA_TS_MSB_SEC_SIZE	4
+#define MACB_DMA_TS_SEC_WIDTH 		(MACB_DMA_TS_MSB_SEC_SIZE + MACB_DMA_TS_LSB_SEC_SIZE)
+#define MACB_DMA_TS_SEC_TOP 		(1 << MACB_DMA_TS_SEC_WIDTH)
+#define MACB_DMA_TS_SEC_MASK 		(MACB_DMA_TS_SEC_TOP - 1)
+
+/* Constants for TX_BD_CONTROL */
+#define MACB_TX_BD_TS_MODE_OFFSET	4 /* TX Descriptor Timestamp Insertion mode */
+#define MACB_TX_BD_TS_MODE_SIZE		2
+
+/* Constants for RX_BD_CONTROL */
+#define MACB_RX_BD_TS_MODE_OFFSET 	4 /* RX Descriptor Timestamp Insertion mode */
+#define MACB_RX_BD_TS_MODE_SIZE 	2
+
 /* Capability mask bits */
 #define MACB_CAPS_ISR_CLEAR_ON_WRITE		0x00000001
 #define MACB_CAPS_USRIO_HAS_CLKEN		0x00000002
@@ -449,6 +571,8 @@
 #define queue_readl(queue, reg)		(queue)->bp->macb_reg_readl((queue)->bp, (queue)->reg)
 #define queue_writel(queue, reg, value)	(queue)->bp->macb_reg_writel((queue)->bp, (queue)->reg, (value))
 
+#define PTP_TS_BUFFER_SIZE		128 /* must be power of 2 */
+
 /* Conditional GEM/MACB macros.  These perform the operation to the correct
  * register dependent on whether the device is a GEM or a MACB.  For registers
  * and bitfields that are common across both devices, use macb_{read,write}l
@@ -483,7 +607,18 @@ struct macb_dma_desc {
 	u32     addrh;
 	u32     resvd;
 #endif
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+	u32	dma_desc_ts_1;
+	u32	dma_desc_ts_2;
+#endif
+};
+
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+struct macb_tx_timestamp {
+	struct sk_buff *skb;
+	struct macb_dma_desc desc;
 };
+#endif
 
 /* DMA descriptor bitfields */
 #define MACB_RX_USED_OFFSET			0
@@ -794,8 +929,22 @@ struct macb_queue {
 	struct macb_tx_skb	*tx_skb;
 	dma_addr_t		tx_ring_dma;
 	struct work_struct	tx_error_task;
+
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+	/* PTP support */
+	struct work_struct 	tx_timestamp_task;
+	unsigned int 		tx_tstamp_head, tx_tstamp_tail;
+	struct macb_tx_timestamp tx_timestamps[PTP_TS_BUFFER_SIZE];
+#endif
 };
 
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+struct incrementspec {
+	u32 sub_ns;
+	u32 ns;
+};
+#endif
+
 struct macb {
 	void __iomem		*regs;
 	bool			native_io;
@@ -860,8 +1009,72 @@ struct macb {
 	unsigned int		jumbo_max_len;
 
 	u32			wol;
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+	/* PTP support */
+	bool ptp_hw_support;
+	struct ptp_clock_info ptp_clock_info;
+	struct ptp_clock *ptp_clock;
+	struct clk *tsu_clk;
+	struct incrementspec tsu_incr;
+	struct hwtstamp_config tstamp_config;
+#endif
+};
+
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+#define MACB_TIMER_SEC_SIZE  (MACB_TIMER_MSB_SEC_SIZE + MACB_TIMER_LSB_SEC_SIZE)
+#define TSU_SEC_MAX_VAL (((u64)1 << MACB_TIMER_SEC_SIZE) - 1)
+#define TSU_NSEC_MAX_VAL ((1 << MACB_TIMER_NSEC_SIZE) - 1)
+
+enum macb_bd_control {
+	TSTAMP_DISABLED,
+	TSTAMP_FRAME_PTP_EVENT_ONLY,
+	TSTAMP_ALL_PTP_FRAMES,
+	TSTAMP_ALL_FRAMES,
 };
 
+int macb_ptp_init(struct platform_device *pdev);
+void macb_ptp_stop(struct platform_device *pdev);
+
+int macb_ptp_verify(struct ptp_clock_info *ptp_clock_info, unsigned int pin,
+		    enum ptp_pin_function func, unsigned int chan);
+
+/* Following functions implement interface functions of Linux PTP
+ * Framework.
+ */
+int macb_ptp_adjfreq(struct ptp_clock_info *ptp_clock_info, s32 ppb);
+int macb_ptp_adjtime(struct ptp_clock_info *ptp_clock_info, s64 delta);
+int macb_ptp_gettime(struct ptp_clock_info *ptp_clock_info,
+		     struct timespec64 *ts);
+int macb_ptp_settime(struct ptp_clock_info *ptp_clock_info,
+		     const struct timespec64 *ts);
+int macb_ptp_enable(struct ptp_clock_info *ptp_clock_info,
+		    struct ptp_clock_request *request, int on);
+
+/* Low level functions used by above interface. */
+int macb_ptp_time_get(struct macb *bp, struct timespec64 *ts);
+int macb_ptp_time_set(struct macb *bp, const struct timespec64 *ts);
+
+int macb_ptp_increment_get(struct macb *bp, struct incrementspec *incr_spec);
+int macb_ptp_increment_set(struct macb *bp, struct incrementspec *incr_spec);
+int macb_ptp_time_adjust(struct macb *bp, s32 delta);
+
+/* Timestamp reading functions */
+int macb_ptp_time_peer_frame_tx_get(struct macb *bp, struct timespec64 *ts);
+int macb_ptp_time_peer_frame_rx_get(struct macb *bp, struct timespec64 *ts);
+int macb_ptp_time_frame_tx_get(struct macb *bp, struct timespec64 *ts);
+int macb_ptp_time_frame_rx_get(struct macb *bp, struct timespec64 *ts);
+
+/* Event reporting functions */
+int macb_ptp_event(struct macb *bp, struct timespec64 *ts);
+
+/* Hardware configuratio functions */
+int macb_ptp_set_one_step_sync(struct macb *bp, u8 enable);
+int macb_ptp_set_tstamp_mode(struct macb *bp,
+			     enum macb_bd_control tx_bd_control,
+			     enum macb_bd_control rx_bd_control);
+
+#endif
+
 static inline bool macb_is_gem(struct macb *bp)
 {
 	return !!(bp->caps & MACB_CAPS_MACB_IS_GEM);
-- 
2.4.5

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ