diff mbox series

[v5,08/10] net: eth: altera: add support for ptp and timestamping

Message ID 20200727092157.115937-9-joyce.ooi@intel.com
State Changes Requested
Delegated to: David Miller
Headers show
Series net: eth: altera: tse: Add PTP and mSGDMA prefetcher | expand

Commit Message

Joyce Ooi July 27, 2020, 9:21 a.m. UTC
From: Dalon Westergreen <dalon.westergreen@intel.com>

Add support for the ptp clock used with the tse, and update
the driver to support timestamping when enabled.  We also
enable debugfs entries for the ptp clock to allow some user
control and interaction with the ptp clock.

Cc: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: Dalon Westergreen <dalon.westergreen@intel.com>
Signed-off-by: Joyce Ooi <joyce.ooi@intel.com>
---
v2: this patch is added in patch version 2
v3: no change
v4: no change
v5: rename 'ptp_enable' variable to 'has_ptp', initialize hardware clock
    to 0, and minor suggested edits
---
 drivers/net/ethernet/altera/Kconfig              |   1 +
 drivers/net/ethernet/altera/Makefile             |   3 +-
 drivers/net/ethernet/altera/altera_tse.h         |   8 +
 drivers/net/ethernet/altera/altera_tse_ethtool.c |  29 ++
 drivers/net/ethernet/altera/altera_tse_main.c    | 115 +++++++-
 drivers/net/ethernet/altera/intel_fpga_tod.c     | 341 +++++++++++++++++++++++
 drivers/net/ethernet/altera/intel_fpga_tod.h     |  56 ++++
 7 files changed, 551 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/ethernet/altera/intel_fpga_tod.c
 create mode 100644 drivers/net/ethernet/altera/intel_fpga_tod.h

Comments

Richard Cochran July 27, 2020, 2:29 p.m. UTC | #1
On Mon, Jul 27, 2020 at 05:21:55PM +0800, Ooi, Joyce wrote:

> +/* ioctl to configure timestamping */
> +static int tse_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
> +{
> +	struct altera_tse_private *priv = netdev_priv(dev);
> +	struct hwtstamp_config config;
> +
> +	if (!netif_running(dev))
> +		return -EINVAL;
> +
> +	if (!priv->has_ptp) {
> +		netdev_alert(priv->dev, "Timestamping not supported");
> +		return -EOPNOTSUPP;
> +	}

The user might well have a PHY that supports time stamping.  The code
must pass the ioctl through to the PHY even when !priv->has_ptp.

> +
> +	if (!dev->phydev)
> +		return -EINVAL;
> +
> +	if (!phy_has_hwtstamp(dev->phydev)) {
> +		if (cmd == SIOCSHWTSTAMP) {
> +			if (copy_from_user(&config, ifr->ifr_data,
> +					   sizeof(struct hwtstamp_config)))
> +				return -EFAULT;
> +
> +			if (config.flags)
> +				return -EINVAL;
> +
> +			switch (config.tx_type) {
> +			case HWTSTAMP_TX_OFF:
> +				priv->hwts_tx_en = 0;
> +				break;
> +			case HWTSTAMP_TX_ON:
> +				priv->hwts_tx_en = 1;
> +				break;
> +			default:
> +				return -ERANGE;
> +			}
> +
> +			switch (config.rx_filter) {
> +			case HWTSTAMP_FILTER_NONE:
> +				priv->hwts_rx_en = 0;
> +				config.rx_filter = HWTSTAMP_FILTER_NONE;
> +				break;
> +			default:
> +				priv->hwts_rx_en = 1;
> +				config.rx_filter = HWTSTAMP_FILTER_ALL;
> +				break;
> +			}
> +
> +			if (copy_to_user(ifr->ifr_data, &config,
> +					 sizeof(struct hwtstamp_config)))
> +				return -EFAULT;
> +			else
> +				return 0;
> +		}
> +
> +		if (cmd == SIOCGHWTSTAMP) {
> +			config.flags = 0;
> +
> +			if (priv->hwts_tx_en)
> +				config.tx_type = HWTSTAMP_TX_ON;
> +			else
> +				config.tx_type = HWTSTAMP_TX_OFF;
> +
> +			if (priv->hwts_rx_en)
> +				config.rx_filter = HWTSTAMP_FILTER_ALL;
> +			else
> +				config.rx_filter = HWTSTAMP_FILTER_NONE;
> +
> +			if (copy_to_user(ifr->ifr_data, &config,
> +					 sizeof(struct hwtstamp_config)))
> +				return -EFAULT;
> +			else
> +				return 0;
> +		}
> +	}
> +
> +	return phy_mii_ioctl(dev->phydev, ifr, cmd);
> +}

Thanks,
Richard
Jakub Kicinski July 27, 2020, 3:54 p.m. UTC | #2
On Mon, 27 Jul 2020 17:21:55 +0800 Ooi, Joyce wrote:
> From: Dalon Westergreen <dalon.westergreen@intel.com>
> 
> Add support for the ptp clock used with the tse, and update
> the driver to support timestamping when enabled.  We also
> enable debugfs entries for the ptp clock to allow some user
> control and interaction with the ptp clock.
> 
> Cc: Richard Cochran <richardcochran@gmail.com>
> Signed-off-by: Dalon Westergreen <dalon.westergreen@intel.com>
> Signed-off-by: Joyce Ooi <joyce.ooi@intel.com>

W=1 build reveals a warnings, please fix:

drivers/net/ethernet/altera/intel_fpga_tod.c:334:55: warning: Using plain integer as NULL 
pointer
kernel test robot July 27, 2020, 4:50 p.m. UTC | #3
Hi Joyce",

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]
[also build test WARNING on net/master robh/for-next linus/master v5.8-rc7 next-20200727]
[cannot apply to sparc-next/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Ooi-Joyce/net-eth-altera-tse-Add-PTP-and-mSGDMA-prefetcher/20200727-173217
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git a57066b1a01977a646145f4ce8dfb4538b08368a
config: nios2-randconfig-s032-20200727 (attached as .config)
compiler: nios2-linux-gcc (GCC) 9.3.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # apt-get install sparse
        # sparse version: v0.6.2-94-geb6779f6-dirty
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=nios2 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>


sparse warnings: (new ones prefixed by >>)

>> drivers/net/ethernet/altera/intel_fpga_tod.c:334:55: sparse: sparse: Using plain integer as NULL pointer
   drivers/net/ethernet/altera/intel_fpga_tod.c: note: in included file (through arch/nios2/include/asm/io.h, include/linux/io.h, arch/nios2/include/asm/pgtable.h, ...):
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:179:15: sparse: sparse: cast to restricted __le32
   include/asm-generic/io.h:179:15: sparse: sparse: cast to restricted __le32
   include/asm-generic/io.h:179:15: sparse: sparse: cast to restricted __le32
   include/asm-generic/io.h:179:15: sparse: sparse: cast to restricted __le32
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:179:15: sparse: sparse: cast to restricted __le32
   include/asm-generic/io.h:179:15: sparse: sparse: cast to restricted __le32
   include/asm-generic/io.h:179:15: sparse: sparse: cast to restricted __le32
   include/asm-generic/io.h:179:15: sparse: sparse: cast to restricted __le32
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]
   include/asm-generic/io.h:225:22: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned int [usertype] value @@     got restricted __le32 [usertype] @@
   include/asm-generic/io.h:225:22: sparse:     expected unsigned int [usertype] value
   include/asm-generic/io.h:225:22: sparse:     got restricted __le32 [usertype]

vim +334 drivers/net/ethernet/altera/intel_fpga_tod.c

   306	
   307	/* Common PTP probe function */
   308	int intel_fpga_tod_probe(struct platform_device *pdev,
   309				 struct intel_fpga_tod_private *priv)
   310	{
   311		struct resource *ptp_res;
   312		int ret = -ENODEV;
   313	
   314		priv->dev = (struct net_device *)platform_get_drvdata(pdev);
   315	
   316		/* Time-of-Day (ToD) Clock address space */
   317		ret = request_and_map(pdev, "tod_ctrl", &ptp_res,
   318				      (void __iomem **)&priv->tod_ctrl);
   319		if (ret)
   320			goto err;
   321	
   322		dev_info(&pdev->dev, "\tTOD Ctrl at 0x%08lx\n",
   323			 (unsigned long)ptp_res->start);
   324	
   325		/* Time-of-Day (ToD) Clock period clock */
   326		priv->tod_clk = devm_clk_get(&pdev->dev, "tod_clk");
   327		if (IS_ERR(priv->tod_clk)) {
   328			dev_err(&pdev->dev, "cannot obtain ToD period clock\n");
   329			ret = -ENXIO;
   330			goto err;
   331		}
   332	
   333		/* Initialize the hardware clock to zero */
 > 334		intel_fpga_tod_set_time(&priv->ptp_clock_ops, 0);
   335	
   336		spin_lock_init(&priv->tod_lock);
   337	err:
   338		return ret;
   339	}
   340	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Joyce Ooi Aug. 7, 2020, 8 a.m. UTC | #4
> -----Original Message-----
> From: Richard Cochran <richardcochran@gmail.com>
> Sent: Monday, July 27, 2020 10:29 PM
> To: Ooi, Joyce <joyce.ooi@intel.com>
> Cc: Thor Thayer <thor.thayer@linux.intel.com>; David S . Miller
> <davem@davemloft.net>; Jakub Kicinski <kuba@kernel.org>;
> netdev@vger.kernel.org; linux-kernel@vger.kernel.org; Dalon Westergreen
> <dalon.westergreen@linux.intel.com>; Tan, Ley Foon
> <ley.foon.tan@intel.com>; See, Chin Liang <chin.liang.see@intel.com>;
> Nguyen, Dinh <dinh.nguyen@intel.com>; Westergreen, Dalon
> <dalon.westergreen@intel.com>
> Subject: Re: [PATCH v5 08/10] net: eth: altera: add support for ptp and
> timestamping
> 
> On Mon, Jul 27, 2020 at 05:21:55PM +0800, Ooi, Joyce wrote:
> 
> > +/* ioctl to configure timestamping */ static int tse_do_ioctl(struct
> > +net_device *dev, struct ifreq *ifr, int cmd) {
> > +	struct altera_tse_private *priv = netdev_priv(dev);
> > +	struct hwtstamp_config config;
> > +
> > +	if (!netif_running(dev))
> > +		return -EINVAL;
> > +
> > +	if (!priv->has_ptp) {
> > +		netdev_alert(priv->dev, "Timestamping not supported");
> > +		return -EOPNOTSUPP;
> > +	}
> 
> The user might well have a PHY that supports time stamping.  The code must
> pass the ioctl through to the PHY even when !priv->has_ptp.
Ok, I'll remove 'return -EOPNOTSUPP;' to allow those that have PHY with timestamping support to go pass through ioctl.

>
> > +
> > +	if (!dev->phydev)
> > +		return -EINVAL;
> > +
> > +	if (!phy_has_hwtstamp(dev->phydev)) {
> > +		if (cmd == SIOCSHWTSTAMP) {
> > +			if (copy_from_user(&config, ifr->ifr_data,
> > +					   sizeof(struct hwtstamp_config)))
> > +				return -EFAULT;
> > +
> > +			if (config.flags)
> > +				return -EINVAL;
> > +
> > +			switch (config.tx_type) {
> > +			case HWTSTAMP_TX_OFF:
> > +				priv->hwts_tx_en = 0;
> > +				break;
> > +			case HWTSTAMP_TX_ON:
> > +				priv->hwts_tx_en = 1;
> > +				break;
> > +			default:
> > +				return -ERANGE;
> > +			}
> > +
> > +			switch (config.rx_filter) {
> > +			case HWTSTAMP_FILTER_NONE:
> > +				priv->hwts_rx_en = 0;
> > +				config.rx_filter = HWTSTAMP_FILTER_NONE;
> > +				break;
> > +			default:
> > +				priv->hwts_rx_en = 1;
> > +				config.rx_filter = HWTSTAMP_FILTER_ALL;
> > +				break;
> > +			}
> > +
> > +			if (copy_to_user(ifr->ifr_data, &config,
> > +					 sizeof(struct hwtstamp_config)))
> > +				return -EFAULT;
> > +			else
> > +				return 0;
> > +		}
> > +
> > +		if (cmd == SIOCGHWTSTAMP) {
> > +			config.flags = 0;
> > +
> > +			if (priv->hwts_tx_en)
> > +				config.tx_type = HWTSTAMP_TX_ON;
> > +			else
> > +				config.tx_type = HWTSTAMP_TX_OFF;
> > +
> > +			if (priv->hwts_rx_en)
> > +				config.rx_filter = HWTSTAMP_FILTER_ALL;
> > +			else
> > +				config.rx_filter = HWTSTAMP_FILTER_NONE;
> > +
> > +			if (copy_to_user(ifr->ifr_data, &config,
> > +					 sizeof(struct hwtstamp_config)))
> > +				return -EFAULT;
> > +			else
> > +				return 0;
> > +		}
> > +	}
> > +
> > +	return phy_mii_ioctl(dev->phydev, ifr, cmd); }
> 
> Thanks,
> Richard
diff mbox series

Patch

diff --git a/drivers/net/ethernet/altera/Kconfig b/drivers/net/ethernet/altera/Kconfig
index 914e56b91467..c24374f532f2 100644
--- a/drivers/net/ethernet/altera/Kconfig
+++ b/drivers/net/ethernet/altera/Kconfig
@@ -3,6 +3,7 @@  config ALTERA_TSE
 	tristate "Altera Triple-Speed Ethernet MAC support"
 	depends on HAS_DMA
 	select PHYLIB
+	imply PTP_1588_CLOCK
 	help
 	  This driver supports the Altera Triple-Speed (TSE) Ethernet MAC.
 
diff --git a/drivers/net/ethernet/altera/Makefile b/drivers/net/ethernet/altera/Makefile
index a52db80aee9f..fc2e460926b3 100644
--- a/drivers/net/ethernet/altera/Makefile
+++ b/drivers/net/ethernet/altera/Makefile
@@ -5,4 +5,5 @@ 
 
 obj-$(CONFIG_ALTERA_TSE) += altera_tse.o
 altera_tse-objs := altera_tse_main.o altera_tse_ethtool.o \
-altera_msgdma.o altera_sgdma.o altera_utils.o
+		   altera_msgdma.o altera_sgdma.o altera_utils.o \
+		   intel_fpga_tod.o
diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h
index 79d02748c89d..87faecee80d2 100644
--- a/drivers/net/ethernet/altera/altera_tse.h
+++ b/drivers/net/ethernet/altera/altera_tse.h
@@ -28,6 +28,8 @@ 
 #include <linux/netdevice.h>
 #include <linux/phy.h>
 
+#include "intel_fpga_tod.h"
+
 #define ALTERA_TSE_SW_RESET_WATCHDOG_CNTR	10000
 #define ALTERA_TSE_MAC_FIFO_WIDTH		4	/* TX/RX FIFO width in
 							 * bytes
@@ -417,6 +419,12 @@  struct altera_tse_private {
 	/* TSE Revision */
 	u32	revision;
 
+	/* Shared PTP structure */
+	struct intel_fpga_tod_private ptp_priv;
+	int hwts_tx_en;
+	int hwts_rx_en;
+	u32 has_ptp;
+
 	/* mSGDMA Rx Dispatcher address space */
 	void __iomem *rx_dma_csr;
 	void __iomem *rx_dma_desc;
diff --git a/drivers/net/ethernet/altera/altera_tse_ethtool.c b/drivers/net/ethernet/altera/altera_tse_ethtool.c
index 420d77f00eab..b4bdb520c96a 100644
--- a/drivers/net/ethernet/altera/altera_tse_ethtool.c
+++ b/drivers/net/ethernet/altera/altera_tse_ethtool.c
@@ -19,6 +19,7 @@ 
 #include <linux/ethtool.h>
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
+#include <linux/net_tstamp.h>
 #include <linux/phy.h>
 
 #include "altera_tse.h"
@@ -222,6 +223,33 @@  static void tse_get_regs(struct net_device *dev, struct ethtool_regs *regs,
 		buf[i] = csrrd32(priv->mac_dev, i * 4);
 }
 
+static int tse_get_ts_info(struct net_device *dev,
+			   struct ethtool_ts_info *info)
+{
+	struct altera_tse_private *priv = netdev_priv(dev);
+
+	if (priv->has_ptp) {
+		if (priv->ptp_priv.ptp_clock)
+			info->phc_index =
+				ptp_clock_index(priv->ptp_priv.ptp_clock);
+		else
+			info->phc_index = -1;
+
+		info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+					SOF_TIMESTAMPING_RX_HARDWARE |
+					SOF_TIMESTAMPING_RAW_HARDWARE;
+
+		info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON);
+
+		info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
+				   (1 << HWTSTAMP_FILTER_ALL);
+
+		return 0;
+	}
+
+	return ethtool_op_get_ts_info(dev, info);
+}
+
 static const struct ethtool_ops tse_ethtool_ops = {
 	.get_drvinfo = tse_get_drvinfo,
 	.get_regs_len = tse_reglen,
@@ -234,6 +262,7 @@  static const struct ethtool_ops tse_ethtool_ops = {
 	.set_msglevel = tse_set_msglevel,
 	.get_link_ksettings = phy_ethtool_get_link_ksettings,
 	.set_link_ksettings = phy_ethtool_set_link_ksettings,
+	.get_ts_info = tse_get_ts_info,
 };
 
 void altera_tse_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index c9100ce24b0a..07531d99e911 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -18,14 +18,17 @@ 
  */
 
 #include <linux/atomic.h>
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
+#include <linux/if_ether.h>
 #include <linux/if_vlan.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mii.h>
+#include <linux/net_tstamp.h>
 #include <linux/netdevice.h>
 #include <linux/of_device.h>
 #include <linux/of_mdio.h>
@@ -40,6 +43,7 @@ 
 #include "altera_tse.h"
 #include "altera_sgdma.h"
 #include "altera_msgdma.h"
+#include "intel_fpga_tod.h"
 
 static atomic_t instance_count = ATOMIC_INIT(~0);
 /* Module parameters */
@@ -598,7 +602,11 @@  static netdev_tx_t tse_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	if (ret)
 		goto out;
 
-	skb_tx_timestamp(skb);
+	if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+		     priv->hwts_tx_en))
+		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+	else
+		skb_tx_timestamp(skb);
 
 	priv->tx_prod++;
 	dev->stats.tx_bytes += skb->len;
@@ -1238,6 +1246,9 @@  static int tse_open(struct net_device *dev)
 	if (dev->phydev)
 		phy_start(dev->phydev);
 
+	priv->hwts_tx_en = 0;
+	priv->hwts_rx_en = 0;
+
 	napi_enable(&priv->napi);
 	netif_start_queue(dev);
 
@@ -1309,6 +1320,85 @@  static int tse_shutdown(struct net_device *dev)
 	return 0;
 }
 
+/* ioctl to configure timestamping */
+static int tse_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct altera_tse_private *priv = netdev_priv(dev);
+	struct hwtstamp_config config;
+
+	if (!netif_running(dev))
+		return -EINVAL;
+
+	if (!priv->has_ptp) {
+		netdev_alert(priv->dev, "Timestamping not supported");
+		return -EOPNOTSUPP;
+	}
+
+	if (!dev->phydev)
+		return -EINVAL;
+
+	if (!phy_has_hwtstamp(dev->phydev)) {
+		if (cmd == SIOCSHWTSTAMP) {
+			if (copy_from_user(&config, ifr->ifr_data,
+					   sizeof(struct hwtstamp_config)))
+				return -EFAULT;
+
+			if (config.flags)
+				return -EINVAL;
+
+			switch (config.tx_type) {
+			case HWTSTAMP_TX_OFF:
+				priv->hwts_tx_en = 0;
+				break;
+			case HWTSTAMP_TX_ON:
+				priv->hwts_tx_en = 1;
+				break;
+			default:
+				return -ERANGE;
+			}
+
+			switch (config.rx_filter) {
+			case HWTSTAMP_FILTER_NONE:
+				priv->hwts_rx_en = 0;
+				config.rx_filter = HWTSTAMP_FILTER_NONE;
+				break;
+			default:
+				priv->hwts_rx_en = 1;
+				config.rx_filter = HWTSTAMP_FILTER_ALL;
+				break;
+			}
+
+			if (copy_to_user(ifr->ifr_data, &config,
+					 sizeof(struct hwtstamp_config)))
+				return -EFAULT;
+			else
+				return 0;
+		}
+
+		if (cmd == SIOCGHWTSTAMP) {
+			config.flags = 0;
+
+			if (priv->hwts_tx_en)
+				config.tx_type = HWTSTAMP_TX_ON;
+			else
+				config.tx_type = HWTSTAMP_TX_OFF;
+
+			if (priv->hwts_rx_en)
+				config.rx_filter = HWTSTAMP_FILTER_ALL;
+			else
+				config.rx_filter = HWTSTAMP_FILTER_NONE;
+
+			if (copy_to_user(ifr->ifr_data, &config,
+					 sizeof(struct hwtstamp_config)))
+				return -EFAULT;
+			else
+				return 0;
+		}
+	}
+
+	return phy_mii_ioctl(dev->phydev, ifr, cmd);
+}
+
 static struct net_device_ops altera_tse_netdev_ops = {
 	.ndo_open		= tse_open,
 	.ndo_stop		= tse_shutdown,
@@ -1317,6 +1407,7 @@  static struct net_device_ops altera_tse_netdev_ops = {
 	.ndo_set_rx_mode	= tse_set_rx_mode,
 	.ndo_change_mtu		= tse_change_mtu,
 	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_do_ioctl		= tse_do_ioctl,
 };
 
 /* Probe Altera TSE MAC device
@@ -1568,6 +1659,26 @@  static int altera_tse_probe(struct platform_device *pdev)
 		netdev_err(ndev, "Cannot attach to PHY (error: %d)\n", ret);
 		goto err_init_phy;
 	}
+
+	priv->has_ptp = of_property_read_bool(pdev->dev.of_node, "altr,has-ptp");
+	dev_info(&pdev->dev, "PTP Enable: %d\n", priv->has_ptp);
+
+	if (priv->has_ptp) {
+		/* MAP PTP */
+		ret = intel_fpga_tod_probe(pdev, &priv->ptp_priv);
+		if (ret) {
+			dev_err(&pdev->dev, "cannot map PTP\n");
+			goto err_init_phy;
+		}
+		ret = intel_fpga_tod_register(&priv->ptp_priv,
+					      priv->device);
+		if (ret) {
+			dev_err(&pdev->dev, "Failed to register PTP clock\n");
+			ret = -ENXIO;
+			goto err_init_phy;
+		}
+	}
+
 	return 0;
 
 err_init_phy:
@@ -1595,6 +1706,8 @@  static int altera_tse_remove(struct platform_device *pdev)
 	}
 
 	platform_set_drvdata(pdev, NULL);
+	if (priv->has_ptp)
+		intel_fpga_tod_unregister(&priv->ptp_priv);
 	altera_tse_mdio_destroy(ndev);
 	unregister_netdev(ndev);
 	free_netdev(ndev);
diff --git a/drivers/net/ethernet/altera/intel_fpga_tod.c b/drivers/net/ethernet/altera/intel_fpga_tod.c
new file mode 100644
index 000000000000..77dba91e8a38
--- /dev/null
+++ b/drivers/net/ethernet/altera/intel_fpga_tod.c
@@ -0,0 +1,341 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Intel FPGA ToD PTP Hardware Clock (PHC) Linux driver
+ * Copyright (C) 2015-2016 Altera Corporation. All rights reserved.
+ * Copyright (C) 2017-2020 Intel Corporation. All rights reserved.
+ *
+ * Author(s):
+ *	Dalon Westergreen <dalon.westergreen@intel.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gcd.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+#include <linux/net_tstamp.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "altera_utils.h"
+#include "intel_fpga_tod.h"
+
+#define NOMINAL_PPB			1000000000ULL
+#define TOD_PERIOD_MAX			0xfffff
+#define TOD_PERIOD_MIN			0
+#define TOD_DRIFT_ADJUST_FNS_MAX	0xffff
+#define TOD_DRIFT_ADJUST_RATE_MAX	0xffff
+#define TOD_ADJUST_COUNT_MAX		0xfffff
+#define TOD_ADJUST_MS_MAX		(((((TOD_PERIOD_MAX) >> 16) + 1) * \
+					  ((TOD_ADJUST_COUNT_MAX) + 1)) /  \
+					 1000000UL)
+
+/* A fine ToD HW clock offset adjustment.
+ * To perform the fine offset adjustment the AdjustPeriod register is used
+ * to replace the Period register for AdjustCount clock cycles in hardware.
+ */
+static int fine_adjust_tod_clock(struct intel_fpga_tod_private *priv,
+				 u32 adjust_period, u32 adjust_count)
+{
+	int limit;
+
+	csrwr32(adjust_period, priv->tod_ctrl, tod_csroffs(adjust_period));
+	csrwr32(adjust_count, priv->tod_ctrl, tod_csroffs(adjust_count));
+
+	/* Wait for present offset adjustment update to complete */
+	limit = TOD_ADJUST_MS_MAX;
+	while (limit--) {
+		if (!csrrd32(priv->tod_ctrl, tod_csroffs(adjust_count)))
+			break;
+		mdelay(1);
+	}
+	if (limit < 0)
+		return -EBUSY;
+
+	return 0;
+}
+
+/* A coarse ToD HW clock offset adjustment.
+ * The coarse time adjustment performs by adding or subtracting the delta value
+ * from the current ToD HW clock time.
+ */
+static int coarse_adjust_tod_clock(struct intel_fpga_tod_private *priv,
+				   s64 delta)
+{
+	u32 seconds_msb, seconds_lsb, nanosec;
+	u64 seconds, now;
+
+	if (delta == 0)
+		goto out;
+
+	/* Get current time */
+	nanosec = csrrd32(priv->tod_ctrl, tod_csroffs(nanosec));
+	seconds_lsb = csrrd32(priv->tod_ctrl, tod_csroffs(seconds_lsb));
+	seconds_msb = csrrd32(priv->tod_ctrl, tod_csroffs(seconds_msb));
+
+	/* Calculate new time */
+	seconds = (((u64)(seconds_msb & 0x0000ffff)) << 32) | seconds_lsb;
+	now = seconds * NSEC_PER_SEC + nanosec + delta;
+
+	seconds = div_u64_rem(now, NSEC_PER_SEC, &nanosec);
+	seconds_msb = upper_32_bits(seconds) & 0x0000ffff;
+	seconds_lsb = lower_32_bits(seconds);
+
+	/* Set corrected time */
+	csrwr32(seconds_msb, priv->tod_ctrl, tod_csroffs(seconds_msb));
+	csrwr32(seconds_lsb, priv->tod_ctrl, tod_csroffs(seconds_lsb));
+	csrwr32(nanosec, priv->tod_ctrl, tod_csroffs(nanosec));
+
+out:
+	return 0;
+}
+
+static int intel_fpga_tod_adjust_fine(struct ptp_clock_info *ptp,
+				      long scaled_ppm)
+{
+	struct intel_fpga_tod_private *priv =
+		container_of(ptp, struct intel_fpga_tod_private, ptp_clock_ops);
+	u32 tod_period, tod_rem, tod_drift_adjust_fns, tod_drift_adjust_rate;
+	unsigned long flags;
+	unsigned long rate;
+	int ret = 0;
+	u64 ppb;
+
+	rate = clk_get_rate(priv->tod_clk);
+
+	/* From scaled_ppm_to_ppb */
+	ppb = 1 + scaled_ppm;
+	ppb *= 125;
+	ppb >>= 13;
+
+	ppb += NOMINAL_PPB;
+
+	tod_period = div_u64_rem(ppb << 16, rate, &tod_rem);
+	if (tod_period > TOD_PERIOD_MAX) {
+		ret = -ERANGE;
+		goto out;
+	}
+
+	/* The drift of ToD adjusted periodically by adding a drift_adjust_fns
+	 * correction value every drift_adjust_rate count of clock cycles.
+	 */
+	tod_drift_adjust_fns = tod_rem / gcd(tod_rem, rate);
+	tod_drift_adjust_rate = rate / gcd(tod_rem, rate);
+
+	while ((tod_drift_adjust_fns > TOD_DRIFT_ADJUST_FNS_MAX) |
+		(tod_drift_adjust_rate > TOD_DRIFT_ADJUST_RATE_MAX)) {
+		tod_drift_adjust_fns = tod_drift_adjust_fns >> 1;
+		tod_drift_adjust_rate = tod_drift_adjust_rate >> 1;
+	}
+
+	if (tod_drift_adjust_fns == 0)
+		tod_drift_adjust_rate = 0;
+
+	spin_lock_irqsave(&priv->tod_lock, flags);
+	csrwr32(tod_period, priv->tod_ctrl, tod_csroffs(period));
+	csrwr32(0, priv->tod_ctrl, tod_csroffs(adjust_period));
+	csrwr32(0, priv->tod_ctrl, tod_csroffs(adjust_count));
+	csrwr32(tod_drift_adjust_fns, priv->tod_ctrl,
+		tod_csroffs(drift_adjust));
+	csrwr32(tod_drift_adjust_rate, priv->tod_ctrl,
+		tod_csroffs(drift_adjust_rate));
+	spin_unlock_irqrestore(&priv->tod_lock, flags);
+
+out:
+	return ret;
+}
+
+static int intel_fpga_tod_adjust_time(struct ptp_clock_info *ptp, s64 delta)
+{
+	struct intel_fpga_tod_private *priv =
+		container_of(ptp, struct intel_fpga_tod_private, ptp_clock_ops);
+	u32 period, diff, rem, rem_period, adj_period;
+	int neg_adj = 0, ret = 0;
+	unsigned long flags;
+	u64 count;
+
+	if (delta < 0) {
+		neg_adj = 1;
+		delta = -delta;
+	}
+
+	spin_lock_irqsave(&priv->tod_lock, flags);
+
+	/* Get the maximum possible value of the Period register offset
+	 * adjustment in nanoseconds scale. This depends on the current
+	 * Period register setting and the maximum and minimum possible
+	 * values of the Period register.
+	 */
+	period = csrrd32(priv->tod_ctrl, tod_csroffs(period));
+
+	if (neg_adj)
+		diff = (period - TOD_PERIOD_MIN) >> 16;
+	else
+		diff = (TOD_PERIOD_MAX - period) >> 16;
+
+	/* Find the number of cycles required for the
+	 * time adjustment
+	 */
+	count = div_u64_rem(delta, diff, &rem);
+
+	if (neg_adj) {
+		adj_period = period - (diff << 16);
+		rem_period = period - (rem << 16);
+	} else {
+		adj_period = period + (diff << 16);
+		rem_period = period + (rem << 16);
+	}
+
+	/* If count is larger than the maximum count,
+	 * just set the time.
+	 */
+	if (count > TOD_ADJUST_COUNT_MAX) {
+		/* Perform the coarse time offset adjustment */
+		ret = coarse_adjust_tod_clock(priv, delta);
+	} else {
+		/* Adjust the period for count cycles to adjust the time */
+		if (count)
+			ret = fine_adjust_tod_clock(priv, adj_period, count);
+
+		/* If there is a remainder, adjust the period for an
+		 * additional cycle
+		 */
+		if (rem)
+			ret = fine_adjust_tod_clock(priv, rem_period, 1);
+	}
+
+	spin_unlock_irqrestore(&priv->tod_lock, flags);
+
+	return ret;
+}
+
+static int intel_fpga_tod_get_time(struct ptp_clock_info *ptp,
+				   struct timespec64 *ts)
+{
+	struct intel_fpga_tod_private *priv =
+		container_of(ptp, struct intel_fpga_tod_private, ptp_clock_ops);
+	u32 seconds_msb, seconds_lsb, nanosec;
+	unsigned long flags;
+	u64 seconds;
+
+	spin_lock_irqsave(&priv->tod_lock, flags);
+	nanosec = csrrd32(priv->tod_ctrl, tod_csroffs(nanosec));
+	seconds_lsb = csrrd32(priv->tod_ctrl, tod_csroffs(seconds_lsb));
+	seconds_msb = csrrd32(priv->tod_ctrl, tod_csroffs(seconds_msb));
+	spin_unlock_irqrestore(&priv->tod_lock, flags);
+
+	seconds = (((u64)(seconds_msb & 0x0000ffff)) << 32) | seconds_lsb;
+
+	ts->tv_nsec = nanosec;
+	ts->tv_sec = (__kernel_old_time_t)seconds;
+
+	return 0;
+}
+
+static int intel_fpga_tod_set_time(struct ptp_clock_info *ptp,
+				   const struct timespec64 *ts)
+{
+	struct intel_fpga_tod_private *priv =
+		container_of(ptp, struct intel_fpga_tod_private, ptp_clock_ops);
+	u32 seconds_msb = upper_32_bits(ts->tv_sec) & 0x0000ffff;
+	u32 seconds_lsb = lower_32_bits(ts->tv_sec);
+	u32 nanosec = lower_32_bits(ts->tv_nsec);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->tod_lock, flags);
+	csrwr32(seconds_msb, priv->tod_ctrl, tod_csroffs(seconds_msb));
+	csrwr32(seconds_lsb, priv->tod_ctrl, tod_csroffs(seconds_lsb));
+	csrwr32(nanosec, priv->tod_ctrl, tod_csroffs(nanosec));
+	spin_unlock_irqrestore(&priv->tod_lock, flags);
+
+	return 0;
+}
+
+static int intel_fpga_tod_enable_feature(struct ptp_clock_info *ptp,
+					 struct ptp_clock_request *request,
+					 int on)
+{
+	return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info intel_fpga_tod_clock_ops = {
+	.owner = THIS_MODULE,
+	.name = "intel_fpga_tod",
+	.max_adj = 500000000,
+	.n_alarm = 0,
+	.n_ext_ts = 0,
+	.n_per_out = 0,
+	.pps = 0,
+	.adjfine = intel_fpga_tod_adjust_fine,
+	.adjtime = intel_fpga_tod_adjust_time,
+	.gettime64 = intel_fpga_tod_get_time,
+	.settime64 = intel_fpga_tod_set_time,
+	.enable = intel_fpga_tod_enable_feature,
+};
+
+/* Register the PTP clock driver to kernel */
+int intel_fpga_tod_register(struct intel_fpga_tod_private *priv,
+			    struct device *device)
+{
+	int ret = 0;
+
+	priv->ptp_clock_ops = intel_fpga_tod_clock_ops;
+
+	priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops, device);
+	if (IS_ERR(priv->ptp_clock)) {
+		priv->ptp_clock = NULL;
+		ret = -ENODEV;
+	}
+
+	if (priv->tod_clk)
+		ret = clk_prepare_enable(priv->tod_clk);
+
+	return ret;
+}
+
+/* Remove/unregister the ptp clock driver from the kernel */
+void intel_fpga_tod_unregister(struct intel_fpga_tod_private *priv)
+{
+	if (priv->ptp_clock) {
+		ptp_clock_unregister(priv->ptp_clock);
+		priv->ptp_clock = NULL;
+	}
+
+	if (priv->tod_clk)
+		clk_disable_unprepare(priv->tod_clk);
+}
+
+/* Common PTP probe function */
+int intel_fpga_tod_probe(struct platform_device *pdev,
+			 struct intel_fpga_tod_private *priv)
+{
+	struct resource *ptp_res;
+	int ret = -ENODEV;
+
+	priv->dev = (struct net_device *)platform_get_drvdata(pdev);
+
+	/* Time-of-Day (ToD) Clock address space */
+	ret = request_and_map(pdev, "tod_ctrl", &ptp_res,
+			      (void __iomem **)&priv->tod_ctrl);
+	if (ret)
+		goto err;
+
+	dev_info(&pdev->dev, "\tTOD Ctrl at 0x%08lx\n",
+		 (unsigned long)ptp_res->start);
+
+	/* Time-of-Day (ToD) Clock period clock */
+	priv->tod_clk = devm_clk_get(&pdev->dev, "tod_clk");
+	if (IS_ERR(priv->tod_clk)) {
+		dev_err(&pdev->dev, "cannot obtain ToD period clock\n");
+		ret = -ENXIO;
+		goto err;
+	}
+
+	/* Initialize the hardware clock to zero */
+	intel_fpga_tod_set_time(&priv->ptp_clock_ops, 0);
+
+	spin_lock_init(&priv->tod_lock);
+err:
+	return ret;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/altera/intel_fpga_tod.h b/drivers/net/ethernet/altera/intel_fpga_tod.h
new file mode 100644
index 000000000000..414f0deb36b6
--- /dev/null
+++ b/drivers/net/ethernet/altera/intel_fpga_tod.h
@@ -0,0 +1,56 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Altera PTP Hardware Clock (PHC) Linux driver
+ * Copyright (C) 2015-2016 Altera Corporation. All rights reserved.
+ * Copyright (C) 2017-2020 Intel Corporation. All rights reserved.
+ *
+ * Author(s):
+ *	Dalon Westergreen <dalon.westergreen@intel.com>
+ */
+
+#ifndef __INTEL_FPGA_TOD_H__
+#define __INTEL_FPGA_TOD_H__
+
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/platform_device.h>
+
+/* Altera Time-of-Day (ToD) clock register space. */
+struct intel_fpga_tod {
+	u32 seconds_msb;
+	u32 seconds_lsb;
+	u32 nanosec;
+	u32 reserved1[0x1];
+	u32 period;
+	u32 adjust_period;
+	u32 adjust_count;
+	u32 drift_adjust;
+	u32 drift_adjust_rate;
+};
+
+#define tod_csroffs(a)	(offsetof(struct intel_fpga_tod, a))
+
+struct intel_fpga_tod_private {
+	struct net_device *dev;
+
+	struct ptp_clock_info ptp_clock_ops;
+	struct ptp_clock *ptp_clock;
+
+	/* Time-of-Day (ToD) Clock address space */
+	struct intel_fpga_tod __iomem *tod_ctrl;
+	struct clk *tod_clk;
+
+	/* ToD clock registers protection */
+	spinlock_t tod_lock;
+};
+
+int intel_fpga_tod_init(struct intel_fpga_tod_private *priv);
+void intel_fpga_tod_uinit(struct intel_fpga_tod_private *priv);
+int intel_fpga_tod_register(struct intel_fpga_tod_private *priv,
+			    struct device *device);
+void intel_fpga_tod_unregister(struct intel_fpga_tod_private *priv);
+int intel_fpga_tod_probe(struct platform_device *pdev,
+			 struct intel_fpga_tod_private *priv);
+
+#endif /* __INTEL_FPGA_TOD_H__ */