diff --git a/doc/guides/nics/octeontx2.rst b/doc/guides/nics/octeontx2.rst
index 20281b030..41eb3c7b9 100644
--- a/doc/guides/nics/octeontx2.rst
+++ b/doc/guides/nics/octeontx2.rst
@@ -27,6 +27,7 @@ Features of the OCTEON TX2 Ethdev PMD are:
 - Link state information
 - Link flow control
 - Debug utilities - Context dump and error interrupt support
+- IEEE1588 timestamping
 
 Prerequisites
 -------------
diff --git a/drivers/net/octeontx2/Makefile b/drivers/net/octeontx2/Makefile
index 582857459..f950fca14 100644
--- a/drivers/net/octeontx2/Makefile
+++ b/drivers/net/octeontx2/Makefile
@@ -34,6 +34,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_OCTEONTX2_PMD) += \
 	otx2_tm.c	\
 	otx2_rss.c	\
 	otx2_mac.c	\
+	otx2_ptp.c	\
 	otx2_link.c	\
 	otx2_stats.c	\
 	otx2_lookup.c	\
diff --git a/drivers/net/octeontx2/meson.build b/drivers/net/octeontx2/meson.build
index 4b56f4461..2cac57d2b 100644
--- a/drivers/net/octeontx2/meson.build
+++ b/drivers/net/octeontx2/meson.build
@@ -6,6 +6,7 @@ sources = files(
 		'otx2_tm.c',
 		'otx2_rss.c',
 		'otx2_mac.c',
+		'otx2_ptp.c',
 		'otx2_link.c',
 		'otx2_stats.c',
 		'otx2_lookup.c',
diff --git a/drivers/net/octeontx2/otx2_ethdev.c b/drivers/net/octeontx2/otx2_ethdev.c
index 25469c5f9..6ab8ed79d 100644
--- a/drivers/net/octeontx2/otx2_ethdev.c
+++ b/drivers/net/octeontx2/otx2_ethdev.c
@@ -336,9 +336,7 @@ nix_cq_rq_uninit(struct rte_eth_dev *eth_dev, struct otx2_eth_rxq *rxq)
 static inline int
 nix_get_data_off(struct otx2_eth_dev *dev)
 {
-	RTE_SET_USED(dev);
-
-	return 0;
+	return otx2_ethdev_is_ptp_en(dev) ? NIX_TIMESYNC_RX_OFFSET : 0;
 }
 
 uint64_t
@@ -450,6 +448,7 @@ otx2_nix_rx_queue_setup(struct rte_eth_dev *eth_dev, uint16_t rq,
 	rxq->qlen = nix_qsize_to_val(qsize);
 	rxq->qsize = qsize;
 	rxq->lookup_mem = otx2_nix_fastpath_lookup_mem_get();
+	rxq->tstamp = &dev->tstamp;
 
 	/* Alloc completion queue */
 	rc = nix_cq_rq_init(eth_dev, dev, rq, rxq, mp);
@@ -736,6 +735,7 @@ otx2_nix_form_default_desc(struct otx2_eth_txq *txq)
 			send_mem->dsz = 0x0;
 			send_mem->wmem = 0x1;
 			send_mem->alg = NIX_SENDMEMALG_SETTSTMP;
+			send_mem->addr = txq->dev->tstamp.tx_tstamp_iova;
 		}
 		sg = (union nix_send_sg_s *)&txq->cmd[4];
 	} else {
@@ -1160,6 +1160,16 @@ otx2_nix_configure(struct rte_eth_dev *eth_dev)
 		goto free_nix_lf;
 	}
 
+	/* Enable PTP if it was requested by the app or if it is already
+	 * enabled in PF owning this VF
+	 */
+	memset(&dev->tstamp, 0, sizeof(struct otx2_timesync_info));
+	if ((dev->rx_offloads & DEV_RX_OFFLOAD_TIMESTAMP) ||
+	    otx2_ethdev_is_ptp_en(dev))
+		otx2_nix_timesync_enable(eth_dev);
+	else
+		otx2_nix_timesync_disable(eth_dev);
+
 	/*
 	 * Restore queue config when reconfigure followed by
 	 * reconfigure and no queue configure invoked from application case.
@@ -1338,6 +1348,8 @@ static const struct eth_dev_ops otx2_eth_dev_ops = {
 	.get_module_eeprom        = otx2_nix_get_module_eeprom,
 	.flow_ctrl_get            = otx2_nix_flow_ctrl_get,
 	.flow_ctrl_set            = otx2_nix_flow_ctrl_set,
+	.timesync_enable          = otx2_nix_timesync_enable,
+	.timesync_disable         = otx2_nix_timesync_disable,
 };
 
 static inline int
@@ -1542,6 +1554,10 @@ otx2_eth_dev_uninit(struct rte_eth_dev *eth_dev, bool mbox_close)
 	/* Disable nix bpid config */
 	otx2_nix_rxchan_bpid_cfg(eth_dev, false);
 
+	/* Disable PTP if already enabled */
+	if (otx2_ethdev_is_ptp_en(dev))
+		otx2_nix_timesync_disable(eth_dev);
+
 	/* Free up SQs */
 	for (i = 0; i < eth_dev->data->nb_tx_queues; i++) {
 		otx2_nix_tx_queue_release(eth_dev->data->tx_queues[i]);
diff --git a/drivers/net/octeontx2/otx2_ethdev.h b/drivers/net/octeontx2/otx2_ethdev.h
index 03ecd32ec..1ca28add4 100644
--- a/drivers/net/octeontx2/otx2_ethdev.h
+++ b/drivers/net/octeontx2/otx2_ethdev.h
@@ -13,6 +13,7 @@
 #include <rte_mbuf.h>
 #include <rte_mempool.h>
 #include <rte_string_fns.h>
+#include <rte_time.h>
 
 #include "otx2_common.h"
 #include "otx2_dev.h"
@@ -128,6 +129,12 @@
 #define NIX_DEFAULT_RSS_CTX_GROUP  0
 #define NIX_DEFAULT_RSS_MCAM_IDX  -1
 
+#define otx2_ethdev_is_ptp_en(dev)	((dev)->ptp_en)
+
+#define NIX_TIMESYNC_TX_CMD_LEN		8
+/* Additional timesync values. */
+#define OTX2_CYCLECOUNTER_MASK   0xffffffffffffffffULL
+
 enum nix_q_size_e {
 	nix_q_size_16,	/* 16 entries */
 	nix_q_size_64,	/* 64 entries */
@@ -234,6 +241,12 @@ struct otx2_eth_dev {
 	struct otx2_eth_qconf *tx_qconf;
 	struct otx2_eth_qconf *rx_qconf;
 	struct rte_eth_dev *eth_dev;
+	/* PTP counters */
+	bool ptp_en;
+	struct otx2_timesync_info tstamp;
+	struct rte_timecounter  systime_tc;
+	struct rte_timecounter  rx_tstamp_tc;
+	struct rte_timecounter  tx_tstamp_tc;
 } __rte_cache_aligned;
 
 struct otx2_eth_txq {
@@ -414,4 +427,8 @@ int otx2_ethdev_parse_devargs(struct rte_devargs *devargs,
 /* Rx and Tx routines */
 void otx2_nix_form_default_desc(struct otx2_eth_txq *txq);
 
+/* Timesync - PTP routines */
+int otx2_nix_timesync_enable(struct rte_eth_dev *eth_dev);
+int otx2_nix_timesync_disable(struct rte_eth_dev *eth_dev);
+
 #endif /* __OTX2_ETHDEV_H__ */
diff --git a/drivers/net/octeontx2/otx2_ptp.c b/drivers/net/octeontx2/otx2_ptp.c
new file mode 100644
index 000000000..105067949
--- /dev/null
+++ b/drivers/net/octeontx2/otx2_ptp.c
@@ -0,0 +1,135 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2019 Marvell International Ltd.
+ */
+
+#include <rte_ethdev_driver.h>
+
+#include "otx2_ethdev.h"
+
+#define PTP_FREQ_ADJUST (1 << 9)
+
+static void
+nix_start_timecounters(struct rte_eth_dev *eth_dev)
+{
+	struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+
+	memset(&dev->systime_tc, 0, sizeof(struct rte_timecounter));
+	memset(&dev->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
+	memset(&dev->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
+
+	dev->systime_tc.cc_mask = OTX2_CYCLECOUNTER_MASK;
+	dev->rx_tstamp_tc.cc_mask = OTX2_CYCLECOUNTER_MASK;
+	dev->tx_tstamp_tc.cc_mask = OTX2_CYCLECOUNTER_MASK;
+}
+
+static int
+nix_ptp_config(struct rte_eth_dev *eth_dev, int en)
+{
+	struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+	struct otx2_mbox *mbox = dev->mbox;
+	uint8_t rc = 0;
+
+	if (otx2_dev_is_vf(dev))
+		return rc;
+
+	if (en) {
+		/* Enable time stamping of sent PTP packets. */
+		otx2_mbox_alloc_msg_nix_lf_ptp_tx_enable(mbox);
+		rc = otx2_mbox_process(mbox);
+		if (rc) {
+			otx2_err("MBOX ptp tx conf enable failed: err %d", rc);
+			return rc;
+		}
+		/* Enable time stamping of received PTP packets. */
+		otx2_mbox_alloc_msg_cgx_ptp_rx_enable(mbox);
+	} else {
+		/* Disable time stamping of sent PTP packets. */
+		otx2_mbox_alloc_msg_nix_lf_ptp_tx_disable(mbox);
+		rc = otx2_mbox_process(mbox);
+		if (rc) {
+			otx2_err("MBOX ptp tx conf disable failed: err %d", rc);
+			return rc;
+		}
+		/* Disable time stamping of received PTP packets. */
+		otx2_mbox_alloc_msg_cgx_ptp_rx_disable(mbox);
+	}
+
+	return otx2_mbox_process(mbox);
+}
+
+int
+otx2_nix_timesync_enable(struct rte_eth_dev *eth_dev)
+{
+	struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+	int i, rc = 0;
+
+	if (otx2_ethdev_is_ptp_en(dev)) {
+		otx2_info("PTP mode is already enabled ");
+		return -EINVAL;
+	}
+
+	/* If we are VF, no further action can be taken */
+	if (otx2_dev_is_vf(dev))
+		return -EINVAL;
+
+	if (!(dev->rx_offload_flags & NIX_RX_OFFLOAD_PTYPE_F)) {
+		otx2_err("Ptype offload is disabled, it should be enabled");
+		return -EINVAL;
+	}
+
+	/* Allocating a iova address for tx tstamp */
+	const struct rte_memzone *ts;
+	ts = rte_eth_dma_zone_reserve(eth_dev, "otx2_ts",
+				      0, OTX2_ALIGN, OTX2_ALIGN,
+				      dev->node);
+	if (ts == NULL)
+		otx2_err("Failed to allocate mem for tx tstamp addr");
+
+	dev->tstamp.tx_tstamp_iova = ts->iova;
+	dev->tstamp.tx_tstamp = ts->addr;
+
+	/* System time should be already on by default */
+	nix_start_timecounters(eth_dev);
+
+	dev->rx_offloads |= DEV_RX_OFFLOAD_TIMESTAMP;
+	dev->rx_offload_flags |= NIX_RX_OFFLOAD_TSTAMP_F;
+	dev->tx_offload_flags |= NIX_TX_OFFLOAD_TSTAMP_F;
+
+	rc = nix_ptp_config(eth_dev, 1);
+	if (!rc) {
+		for (i = 0; i < eth_dev->data->nb_tx_queues; i++) {
+			struct otx2_eth_txq *txq = eth_dev->data->tx_queues[i];
+			otx2_nix_form_default_desc(txq);
+		}
+	}
+	return rc;
+}
+
+int
+otx2_nix_timesync_disable(struct rte_eth_dev *eth_dev)
+{
+	struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+	int i, rc = 0;
+
+	if (!otx2_ethdev_is_ptp_en(dev)) {
+		otx2_nix_dbg("PTP mode is disabled");
+		return -EINVAL;
+	}
+
+	/* If we are VF, nothing else can be done */
+	if (otx2_dev_is_vf(dev))
+		return -EINVAL;
+
+	dev->rx_offloads &= ~DEV_RX_OFFLOAD_TIMESTAMP;
+	dev->rx_offload_flags &= ~NIX_RX_OFFLOAD_TSTAMP_F;
+	dev->tx_offload_flags &= ~NIX_TX_OFFLOAD_TSTAMP_F;
+
+	rc = nix_ptp_config(eth_dev, 0);
+	if (!rc) {
+		for (i = 0; i < eth_dev->data->nb_tx_queues; i++) {
+			struct otx2_eth_txq *txq = eth_dev->data->tx_queues[i];
+			otx2_nix_form_default_desc(txq);
+		}
+	}
+	return rc;
+}
diff --git a/drivers/net/octeontx2/otx2_rx.h b/drivers/net/octeontx2/otx2_rx.h
index 1283fdf37..0c3627c12 100644
--- a/drivers/net/octeontx2/otx2_rx.h
+++ b/drivers/net/octeontx2/otx2_rx.h
@@ -13,5 +13,16 @@
 					 sizeof(uint16_t))
 
 #define NIX_RX_OFFLOAD_PTYPE_F         BIT(1)
+#define NIX_RX_OFFLOAD_TSTAMP_F        BIT(5)
+
+#define NIX_TIMESYNC_RX_OFFSET		8
+
+struct otx2_timesync_info {
+	uint64_t	rx_tstamp;
+	rte_iova_t	tx_tstamp_iova;
+	uint64_t	*tx_tstamp;
+	uint8_t		tx_ready;
+	uint8_t		rx_ready;
+} __rte_cache_aligned;
 
 #endif /* __OTX2_RX_H__ */
