[dpdk-dev,v2,4/6] i40e: add additional ieee1588 support functions

Message ID 1446198204-9852-5-git-send-email-danielx.t.mrzyglod@intel.com (mailing list archive)
State Changes Requested, archived
Headers

Commit Message

Daniel Mrzyglod Oct. 30, 2015, 9:43 a.m. UTC
  From: Pablo de Lara <pablo.de.lara.guarch@intel.com>

Add additional functions to support the existing IEEE1588
functionality and to enable getting, setting and adjusting
the device time.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 214 +++++++++++++++++++++++++++++++++++++----
 drivers/net/i40e/i40e_ethdev.h |  18 ++++
 2 files changed, 212 insertions(+), 20 deletions(-)
  

Comments

Ananyev, Konstantin Oct. 30, 2015, 11:19 a.m. UTC | #1
Hi Daniel,

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Daniel Mrzyglod
> Sent: Friday, October 30, 2015 9:43 AM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support functions
> 
> From: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> 
> Add additional functions to support the existing IEEE1588
> functionality and to enable getting, setting and adjusting
> the device time.
> 
> Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
> ---
>  drivers/net/i40e/i40e_ethdev.c | 214 +++++++++++++++++++++++++++++++++++++----
>  drivers/net/i40e/i40e_ethdev.h |  18 ++++
>  2 files changed, 212 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
> index 2dd9fdc..985ab24 100644
> --- a/drivers/net/i40e/i40e_ethdev.c
> +++ b/drivers/net/i40e/i40e_ethdev.c
> @@ -107,11 +107,14 @@
>  	(1UL << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) | \
>  	(1UL << RTE_ETH_FLOW_L2_PAYLOAD))
> 
> -#define I40E_PTP_40GB_INCVAL  0x0199999999ULL
> -#define I40E_PTP_10GB_INCVAL  0x0333333333ULL
> -#define I40E_PTP_1GB_INCVAL   0x2000000000ULL
> -#define I40E_PRTTSYN_TSYNENA  0x80000000
> -#define I40E_PRTTSYN_TSYNTYPE 0x0e000000
> +/* Additional timesync values. */
> +#define NSEC_PER_SEC             1000000000L
> +#define I40E_PTP_40GB_INCVAL     0x0199999999ULL
> +#define I40E_PTP_10GB_INCVAL     0x0333333333ULL
> +#define I40E_PTP_1GB_INCVAL      0x2000000000ULL
> +#define I40E_PRTTSYN_TSYNENA     0x80000000
> +#define I40E_PRTTSYN_TSYNTYPE    0x0e000000
> +#define I40E_CYCLECOUNTER_MASK   0xffffffffffffffff
> 
>  static int eth_i40e_dev_init(struct rte_eth_dev *eth_dev);
>  static int eth_i40e_dev_uninit(struct rte_eth_dev *eth_dev);
> @@ -232,6 +235,11 @@ static int i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
>  					   uint32_t flags);
>  static int i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
>  					   struct timespec *timestamp);
> +static int i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
> +static int i40e_timesync_time_get(struct rte_eth_dev *dev,
> +		struct timespec *timestamp);
> +static int i40e_timesync_time_set(struct rte_eth_dev *dev,
> +		struct timespec *timestamp);
> 
>  static const struct rte_pci_id pci_id_i40e_map[] = {
>  #define RTE_PCI_DEV_ID_DECL_I40E(vend, dev) {RTE_PCI_DEVICE(vend, dev)},
> @@ -289,6 +297,9 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
>  	.timesync_disable             = i40e_timesync_disable,
>  	.timesync_read_rx_timestamp   = i40e_timesync_read_rx_timestamp,
>  	.timesync_read_tx_timestamp   = i40e_timesync_read_tx_timestamp,
> +	.timesync_time_adjust         = i40e_timesync_time_adjust,
> +	.timesync_time_get            = i40e_timesync_time_get,
> +	.timesync_time_set            = i40e_timesync_time_set,
>  };
> 
>  static struct eth_driver rte_i40e_pmd = {
> @@ -6175,17 +6186,116 @@ i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id)
>  	return 0;
>  }
> 
> -static int
> -i40e_timesync_enable(struct rte_eth_dev *dev)
> +static inline uint64_t
> +timespec_to_ns(const struct timespec *ts)
> +{
> +	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
> +}
> +
> +static struct timespec
> +ns_to_timespec(uint64_t nsec)
> +{
> +	struct timespec ts = {0, 0};
> +
> +	if (nsec == 0)
> +		return ts;
> +
> +	ts.tv_sec = nsec / NSEC_PER_SEC;
> +	ts.tv_nsec = nsec % NSEC_PER_SEC;
> +
> +	return ts;
> +}
> +
> +/*
> + * Adds the new cycles (in nanoseconds) to the previous time stored.
> + */
> +static uint64_t
> +timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
> +{
> +	uint64_t delta = (cycle_tstamp - tc->cycle_last);
> +	uint64_t nsec = tc->nsec;
> +
> +	nsec += delta;
> +
> +	return nsec;
> +}

These 3 functions above are redefined for each PMD in your patch.
I think it is worth to move it to the upper layer (rte_ethdev, rte_net) to avoid code duplication.
Thanks
Kinstantin

> +
> +static uint64_t
> +i40e_read_timesync_cyclecounter(struct rte_eth_dev *dev)
>  {
>  	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> -	struct rte_eth_link *link = &dev->data->dev_link;
> -	uint32_t tsync_ctl_l;
> -	uint32_t tsync_ctl_h;
> +	uint64_t systim_cycles = 0;
> +
> +	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_L);
> +	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_H)
> +			<< 32;
> +
> +	return systim_cycles;
> +}
> +
> +static uint64_t
> +timecounter_read_ns_delta(struct rte_eth_dev *dev)
> +{
> +	uint64_t cycle_now, cycle_delta;
> +	struct i40e_adapter *adapter =
> +			(struct i40e_adapter *)dev->data->dev_private;
> +
> +	/* Read cycle counter. */
> +	cycle_now = adapter->tc.cc->read(dev);
> +
> +	/* Calculate the delta since the last timecounter_read_delta(). */
> +	cycle_delta = (cycle_now - adapter->tc.cycle_last);
> +
> +	/* Update time stamp of timecounter_read_delta() call. */
> +	adapter->tc.cycle_last = cycle_now;
> +
> +	/* Delta already in nanoseconds. */
> +	return cycle_delta;
> +}
> +
> +static uint64_t
> +timecounter_read(struct rte_eth_dev *dev)
> +{
> +	uint64_t nsec;
> +	struct i40e_adapter *adapter =
> +			(struct i40e_adapter *)dev->data->dev_private;
> +
> +	/* Increment time by nanoseconds since last call. */
> +	nsec = timecounter_read_ns_delta(dev);
> +	nsec += adapter->tc.nsec;
> +	adapter->tc.nsec = nsec;
> +
> +	return nsec;
> +}
> +
> +
> +static void
> +timecounter_init(struct rte_eth_dev *dev,
> +		      uint64_t start_time)
> +{
> +	struct i40e_adapter *adapter =
> +			(struct i40e_adapter *)dev->data->dev_private;
> +	adapter->tc.cc = &adapter->cc;
> +	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
> +	adapter->tc.nsec = start_time;
> +}
> +
> +static void
> +i40e_start_cyclecounter(struct rte_eth_dev *dev)
> +{
> +	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +	struct i40e_adapter *adapter =
> +			(struct i40e_adapter *)dev->data->dev_private;
> +	struct rte_eth_link link;
>  	uint32_t tsync_inc_l;
>  	uint32_t tsync_inc_h;
> 
> -	switch (link->link_speed) {
> +	/* Get current link speed. */
> +	memset(&link, 0, sizeof(link));
> +	i40e_dev_link_update(dev, 1);
> +	rte_i40e_dev_atomic_read_link_status(dev, &link);
> +
> +	switch (link.link_speed) {
>  	case ETH_LINK_SPEED_40G:
>  		tsync_inc_l = I40E_PTP_40GB_INCVAL & 0xFFFFFFFF;
>  		tsync_inc_h = I40E_PTP_40GB_INCVAL >> 32;
> @@ -6203,6 +6313,63 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
>  		tsync_inc_h = 0x0;
>  	}
> 
> +	/* Set the timesync increment value. */
> +	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
> +	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
> +
> +	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
> +	adapter->cc.read = i40e_read_timesync_cyclecounter;
> +}
> +
> +static int
> +i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
> +{
> +	struct i40e_adapter *adapter =
> +			(struct i40e_adapter *)dev->data->dev_private;
> +
> +	adapter->tc.nsec += delta;
> +
> +	return 0;
> +}
> +
> +static int
> +i40e_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
> +{
> +	uint64_t ns;
> +
> +	ns = timespec_to_ns(ts);
> +
> +	/* Reset the timecounter. */
> +	timecounter_init(dev, ns);
> +
> +	return 0;
> +}
> +
> +static int
> +i40e_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
> +{
> +	uint64_t ns;
> +
> +	ns = timecounter_read(dev);
> +	*ts = ns_to_timespec(ns);
> +
> +	return 0;
> +}
> +
> +static int
> +i40e_timesync_enable(struct rte_eth_dev *dev)
> +{
> +	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +	uint32_t tsync_ctl_l;
> +	uint32_t tsync_ctl_h;
> +	uint64_t ns;
> +	struct timespec zerotime = {0, 0};
> +
> +	/* Set 0.0 epoch time to initialize timecounter. */
> +	ns = timespec_to_ns(&zerotime);
> +	i40e_start_cyclecounter(dev);
> +	timecounter_init(dev, ns);
> +
>  	/* Clear timesync registers. */
>  	I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
>  	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
> @@ -6212,10 +6379,6 @@ i40e_timesync_enable(struct rte_eth_dev *dev)
>  	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(3));
>  	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
> 
> -	/* Set the timesync increment value. */
> -	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
> -	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
> -
>  	/* Enable timestamping of PTP packets. */
>  	tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
>  	tsync_ctl_l |= I40E_PRTTSYN_TSYNENA;
> @@ -6247,7 +6410,7 @@ i40e_timesync_disable(struct rte_eth_dev *dev)
>  	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
>  	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
> 
> -	/* Set the timesync increment value. */
> +	/* Reset the timesync increment value. */
>  	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
>  	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
> 
> @@ -6259,10 +6422,14 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
>  				struct timespec *timestamp, uint32_t flags)
>  {
>  	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +	struct i40e_adapter *adapter =
> +		(struct i40e_adapter *)dev->data->dev_private;
> +
>  	uint32_t sync_status;
>  	uint32_t rx_stmpl;
>  	uint32_t rx_stmph;
>  	uint32_t index = flags & 0x03;
> +	uint64_t regival = 0;
> 
>  	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_1);
>  	if ((sync_status & (1 << index)) == 0)
> @@ -6270,9 +6437,11 @@ i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
> 
>  	rx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
>  	rx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index));
> +	timecounter_read(dev);
> 
> -	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
> -	timestamp->tv_nsec = 0;
> +	regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
> +	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
> +	*timestamp = ns_to_timespec(regival);
> 
>  	return  0;
>  }
> @@ -6282,9 +6451,13 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
>  				struct timespec *timestamp)
>  {
>  	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +	struct i40e_adapter *adapter =
> +		(struct i40e_adapter *)dev->data->dev_private;
> +
>  	uint32_t sync_status;
>  	uint32_t tx_stmpl;
>  	uint32_t tx_stmph;
> +	uint64_t regival = 0;
> 
>  	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
>  	if ((sync_status & I40E_PRTTSYN_STAT_0_TXTIME_MASK) == 0)
> @@ -6293,8 +6466,9 @@ i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
>  	tx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
>  	tx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
> 
> -	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
> -	timestamp->tv_nsec = 0;
> +	regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
> +	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
> +	*timestamp = ns_to_timespec(regival);
> 
>  	return  0;
>  }
> diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
> index 6185657..7f2d79b 100644
> --- a/drivers/net/i40e/i40e_ethdev.h
> +++ b/drivers/net/i40e/i40e_ethdev.h
> @@ -450,6 +450,22 @@ struct i40e_vf {
>  };
> 
>  /*
> + * Structure for cyclecounter IEEE1588 functionality.
> + */
> +struct cyclecounter {
> +	uint64_t (*read)(struct rte_eth_dev *dev);
> +};
> +
> +/*
> + * Structure to hold and calculate Unix epoch time.
> + */
> +struct timecounter {
> +	struct cyclecounter *cc;
> +	uint64_t cycle_last;
> +	uint64_t nsec;
> +};
> +
> +/*
>   * Structure to store private data for each PF/VF instance.
>   */
>  struct i40e_adapter {
> @@ -462,6 +478,8 @@ struct i40e_adapter {
>  		struct i40e_pf pf;
>  		struct i40e_vf vf;
>  	};
> +	struct cyclecounter cc;
> +	struct timecounter tc;
>  };
> 
>  int i40e_dev_switch_queues(struct i40e_pf *pf, bool on);
> --
> 2.1.0
  
De Lara Guarch, Pablo Oct. 30, 2015, 11:33 a.m. UTC | #2
Hi Konstantin,

> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ananyev,
> Konstantin
> Sent: Friday, October 30, 2015 11:20 AM
> To: Mrzyglod, DanielX T; dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support
> functions
> 
> Hi Daniel,
> 
> > -----Original Message-----
> > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Daniel Mrzyglod
> > Sent: Friday, October 30, 2015 9:43 AM
> > To: dev@dpdk.org
> > Subject: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support
> functions
> >
> > From: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> >
> > Add additional functions to support the existing IEEE1588
> > functionality and to enable getting, setting and adjusting
> > the device time.
> >
> > Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> > Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>

[...]

> 
> These 3 functions above are redefined for each PMD in your patch.
> I think it is worth to move it to the upper layer (rte_ethdev, rte_net) to avoid
> code duplication.
> Thanks
> Kinstantin

We thought about that, but we didn't know what the best place was for them.
Looking at the functions in rte_ethdev, they look quite different compared to these ones,
so we thought it wasn't the appropriate place. Do you think that is the best place?

Thanks,
Pablo
  
Thomas Monjalon Oct. 30, 2015, 11:36 a.m. UTC | #3
2015-10-30 11:33, De Lara Guarch, Pablo:
> > These 3 functions above are redefined for each PMD in your patch.
> > I think it is worth to move it to the upper layer (rte_ethdev, rte_net) to avoid
> > code duplication.
> > Thanks
> > Kinstantin
> 
> We thought about that, but we didn't know what the best place was for them.
> Looking at the functions in rte_ethdev, they look quite different compared to these ones,
> so we thought it wasn't the appropriate place. Do you think that is the best place?

You are talking about the timer conversions, right?
It looks appropriate for EAL.
  
Ananyev, Konstantin Oct. 30, 2015, 11:38 a.m. UTC | #4
> -----Original Message-----
> From: De Lara Guarch, Pablo
> Sent: Friday, October 30, 2015 11:33 AM
> To: Ananyev, Konstantin; Mrzyglod, DanielX T; dev@dpdk.org
> Subject: RE: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support functions
> 
> Hi Konstantin,
> 
> > -----Original Message-----
> > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Ananyev,
> > Konstantin
> > Sent: Friday, October 30, 2015 11:20 AM
> > To: Mrzyglod, DanielX T; dev@dpdk.org
> > Subject: Re: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support
> > functions
> >
> > Hi Daniel,
> >
> > > -----Original Message-----
> > > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Daniel Mrzyglod
> > > Sent: Friday, October 30, 2015 9:43 AM
> > > To: dev@dpdk.org
> > > Subject: [dpdk-dev] [PATCH v2 4/6] i40e: add additional ieee1588 support
> > functions
> > >
> > > From: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> > >
> > > Add additional functions to support the existing IEEE1588
> > > functionality and to enable getting, setting and adjusting
> > > the device time.
> > >
> > > Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> > > Signed-off-by: Daniel Mrzyglod <danielx.t.mrzyglod@intel.com>
> 
> [...]
> 
> >
> > These 3 functions above are redefined for each PMD in your patch.
> > I think it is worth to move it to the upper layer (rte_ethdev, rte_net) to avoid
> > code duplication.
> > Thanks
> > Kinstantin
> 
> We thought about that, but we didn't know what the best place was for them.
> Looking at the functions in rte_ethdev, they look quite different compared to these ones,
> so we thought it wasn't the appropriate place. Do you think that is the best place

Inisde librte_net might be?
They are sort of helper functions for PTP right?
So might be create a librte_net/rte_ptp.h and put it here?
Konstantin

> 
> Thanks,
> Pablo
  

Patch

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 2dd9fdc..985ab24 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -107,11 +107,14 @@ 
 	(1UL << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) | \
 	(1UL << RTE_ETH_FLOW_L2_PAYLOAD))
 
-#define I40E_PTP_40GB_INCVAL  0x0199999999ULL
-#define I40E_PTP_10GB_INCVAL  0x0333333333ULL
-#define I40E_PTP_1GB_INCVAL   0x2000000000ULL
-#define I40E_PRTTSYN_TSYNENA  0x80000000
-#define I40E_PRTTSYN_TSYNTYPE 0x0e000000
+/* Additional timesync values. */
+#define NSEC_PER_SEC             1000000000L
+#define I40E_PTP_40GB_INCVAL     0x0199999999ULL
+#define I40E_PTP_10GB_INCVAL     0x0333333333ULL
+#define I40E_PTP_1GB_INCVAL      0x2000000000ULL
+#define I40E_PRTTSYN_TSYNENA     0x80000000
+#define I40E_PRTTSYN_TSYNTYPE    0x0e000000
+#define I40E_CYCLECOUNTER_MASK   0xffffffffffffffff
 
 static int eth_i40e_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_i40e_dev_uninit(struct rte_eth_dev *eth_dev);
@@ -232,6 +235,11 @@  static int i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 					   uint32_t flags);
 static int i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					   struct timespec *timestamp);
+static int i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta);
+static int i40e_timesync_time_get(struct rte_eth_dev *dev,
+		struct timespec *timestamp);
+static int i40e_timesync_time_set(struct rte_eth_dev *dev,
+		struct timespec *timestamp);
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
 #define RTE_PCI_DEV_ID_DECL_I40E(vend, dev) {RTE_PCI_DEVICE(vend, dev)},
@@ -289,6 +297,9 @@  static const struct eth_dev_ops i40e_eth_dev_ops = {
 	.timesync_disable             = i40e_timesync_disable,
 	.timesync_read_rx_timestamp   = i40e_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = i40e_timesync_read_tx_timestamp,
+	.timesync_time_adjust         = i40e_timesync_time_adjust,
+	.timesync_time_get            = i40e_timesync_time_get,
+	.timesync_time_set            = i40e_timesync_time_set,
 };
 
 static struct eth_driver rte_i40e_pmd = {
@@ -6175,17 +6186,116 @@  i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id)
 	return 0;
 }
 
-static int
-i40e_timesync_enable(struct rte_eth_dev *dev)
+static inline uint64_t
+timespec_to_ns(const struct timespec *ts)
+{
+	return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
+}
+
+static struct timespec
+ns_to_timespec(uint64_t nsec)
+{
+	struct timespec ts = {0, 0};
+
+	if (nsec == 0)
+		return ts;
+
+	ts.tv_sec = nsec / NSEC_PER_SEC;
+	ts.tv_nsec = nsec % NSEC_PER_SEC;
+
+	return ts;
+}
+
+/*
+ * Adds the new cycles (in nanoseconds) to the previous time stored.
+ */
+static uint64_t
+timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp)
+{
+	uint64_t delta = (cycle_tstamp - tc->cycle_last);
+	uint64_t nsec = tc->nsec;
+
+	nsec += delta;
+
+	return nsec;
+}
+
+static uint64_t
+i40e_read_timesync_cyclecounter(struct rte_eth_dev *dev)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct rte_eth_link *link = &dev->data->dev_link;
-	uint32_t tsync_ctl_l;
-	uint32_t tsync_ctl_h;
+	uint64_t systim_cycles = 0;
+
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_L);
+	systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_H)
+			<< 32;
+
+	return systim_cycles;
+}
+
+static uint64_t
+timecounter_read_ns_delta(struct rte_eth_dev *dev)
+{
+	uint64_t cycle_now, cycle_delta;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	/* Read cycle counter. */
+	cycle_now = adapter->tc.cc->read(dev);
+
+	/* Calculate the delta since the last timecounter_read_delta(). */
+	cycle_delta = (cycle_now - adapter->tc.cycle_last);
+
+	/* Update time stamp of timecounter_read_delta() call. */
+	adapter->tc.cycle_last = cycle_now;
+
+	/* Delta already in nanoseconds. */
+	return cycle_delta;
+}
+
+static uint64_t
+timecounter_read(struct rte_eth_dev *dev)
+{
+	uint64_t nsec;
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	/* Increment time by nanoseconds since last call. */
+	nsec = timecounter_read_ns_delta(dev);
+	nsec += adapter->tc.nsec;
+	adapter->tc.nsec = nsec;
+
+	return nsec;
+}
+
+
+static void
+timecounter_init(struct rte_eth_dev *dev,
+		      uint64_t start_time)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	adapter->tc.cc = &adapter->cc;
+	adapter->tc.cycle_last = adapter->tc.cc->read(dev);
+	adapter->tc.nsec = start_time;
+}
+
+static void
+i40e_start_cyclecounter(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+	struct rte_eth_link link;
 	uint32_t tsync_inc_l;
 	uint32_t tsync_inc_h;
 
-	switch (link->link_speed) {
+	/* Get current link speed. */
+	memset(&link, 0, sizeof(link));
+	i40e_dev_link_update(dev, 1);
+	rte_i40e_dev_atomic_read_link_status(dev, &link);
+
+	switch (link.link_speed) {
 	case ETH_LINK_SPEED_40G:
 		tsync_inc_l = I40E_PTP_40GB_INCVAL & 0xFFFFFFFF;
 		tsync_inc_h = I40E_PTP_40GB_INCVAL >> 32;
@@ -6203,6 +6313,63 @@  i40e_timesync_enable(struct rte_eth_dev *dev)
 		tsync_inc_h = 0x0;
 	}
 
+	/* Set the timesync increment value. */
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
+	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
+
+	memset(&adapter->cc, 0, sizeof(struct cyclecounter));
+	adapter->cc.read = i40e_read_timesync_cyclecounter;
+}
+
+static int
+i40e_timesync_time_adjust(struct rte_eth_dev *dev, int64_t delta)
+{
+	struct i40e_adapter *adapter =
+			(struct i40e_adapter *)dev->data->dev_private;
+
+	adapter->tc.nsec += delta;
+
+	return 0;
+}
+
+static int
+i40e_timesync_time_set(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timespec_to_ns(ts);
+
+	/* Reset the timecounter. */
+	timecounter_init(dev, ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_time_get(struct rte_eth_dev *dev, struct timespec *ts)
+{
+	uint64_t ns;
+
+	ns = timecounter_read(dev);
+	*ts = ns_to_timespec(ns);
+
+	return 0;
+}
+
+static int
+i40e_timesync_enable(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint32_t tsync_ctl_l;
+	uint32_t tsync_ctl_h;
+	uint64_t ns;
+	struct timespec zerotime = {0, 0};
+
+	/* Set 0.0 epoch time to initialize timecounter. */
+	ns = timespec_to_ns(&zerotime);
+	i40e_start_cyclecounter(dev);
+	timecounter_init(dev, ns);
+
 	/* Clear timesync registers. */
 	I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
@@ -6212,10 +6379,6 @@  i40e_timesync_enable(struct rte_eth_dev *dev)
 	I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(3));
 	I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
 
-	/* Set the timesync increment value. */
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
-	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
-
 	/* Enable timestamping of PTP packets. */
 	tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
 	tsync_ctl_l |= I40E_PRTTSYN_TSYNENA;
@@ -6247,7 +6410,7 @@  i40e_timesync_disable(struct rte_eth_dev *dev)
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
 
-	/* Set the timesync increment value. */
+	/* Reset the timesync increment value. */
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
 	I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
 
@@ -6259,10 +6422,14 @@  i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp, uint32_t flags)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t rx_stmpl;
 	uint32_t rx_stmph;
 	uint32_t index = flags & 0x03;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_1);
 	if ((sync_status & (1 << index)) == 0)
@@ -6270,9 +6437,11 @@  i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 
 	rx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
 	rx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index));
+	timecounter_read(dev);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
@@ -6282,9 +6451,13 @@  i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 				struct timespec *timestamp)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_adapter *adapter =
+		(struct i40e_adapter *)dev->data->dev_private;
+
 	uint32_t sync_status;
 	uint32_t tx_stmpl;
 	uint32_t tx_stmph;
+	uint64_t regival = 0;
 
 	sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
 	if ((sync_status & I40E_PRTTSYN_STAT_0_TXTIME_MASK) == 0)
@@ -6293,8 +6466,9 @@  i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 	tx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
 	tx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
 
-	timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
-	timestamp->tv_nsec = 0;
+	regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+	regival = timecounter_cycles_to_ns_time(&adapter->tc, regival);
+	*timestamp = ns_to_timespec(regival);
 
 	return  0;
 }
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 6185657..7f2d79b 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -450,6 +450,22 @@  struct i40e_vf {
 };
 
 /*
+ * Structure for cyclecounter IEEE1588 functionality.
+ */
+struct cyclecounter {
+	uint64_t (*read)(struct rte_eth_dev *dev);
+};
+
+/*
+ * Structure to hold and calculate Unix epoch time.
+ */
+struct timecounter {
+	struct cyclecounter *cc;
+	uint64_t cycle_last;
+	uint64_t nsec;
+};
+
+/*
  * Structure to store private data for each PF/VF instance.
  */
 struct i40e_adapter {
@@ -462,6 +478,8 @@  struct i40e_adapter {
 		struct i40e_pf pf;
 		struct i40e_vf vf;
 	};
+	struct cyclecounter cc;
+	struct timecounter tc;
 };
 
 int i40e_dev_switch_queues(struct i40e_pf *pf, bool on);