Commit 3f2bba7d authored by David S. Miller's avatar David S. Miller

Merge branch 'ptp-more-accurate-PHC-system-clock-synchronization'

Miroslav Lichvar says:

====================
More accurate PHC<->system clock synchronization

RFC->v1:
- added new patches
- separated PHC timestamp from ptp_system_timestamp
- fixed memory leak in PTP_SYS_OFFSET_EXTENDED
- changed PTP_SYS_OFFSET_EXTENDED to work with array of arrays
- fixed PTP_SYS_OFFSET_EXTENDED to break correctly from loop
- fixed timecounter updates in drivers
- split gettimex in igb driver
- fixed ptp_read_* functions to be available without
  CONFIG_PTP_1588_CLOCK

This series enables a more accurate synchronization between PTP hardware
clocks and the system clock.

The first two patches are minor cleanup/bug fixes.

The third patch adds an extended version of the PTP_SYS_OFFSET ioctl,
which returns three timestamps for each measurement. The idea is to
shorten the interval between the system timestamps to contain just the
reading of the lowest register of the PHC in order to reduce the error
in the measured offset and get a smaller upper bound on the maximum
error.

The fourth patch deprecates the original gettime function.

The remaining patches update the gettime function in order to support
the new ioctl in the e1000e, igb, ixgbe, and tg3 drivers.

Tests with few different NICs in different machines show that:
- with an I219 (e1000e) the measured delay was reduced from 2500 to 1300
  ns and the error in the measured offset, when compared to the cross
  timestamping supported by the driver, was reduced by a factor of 5
- with an I210 (igb) the delay was reduced from 5100 to 1700 ns
- with an I350 (igb) the delay was reduced from 2300 to 750 ns
- with an X550 (ixgbe) the delay was reduced from 1950 to 650 ns
- with a BCM5720 (tg3) the delay was reduced from 2400 to 1200 ns
====================
Acked-by: default avatarRichard Cochran <richardcochran@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 70e79832 6fe42e22
...@@ -6135,10 +6135,16 @@ static int tg3_setup_phy(struct tg3 *tp, bool force_reset) ...@@ -6135,10 +6135,16 @@ static int tg3_setup_phy(struct tg3 *tp, bool force_reset)
} }
/* tp->lock must be held */ /* tp->lock must be held */
static u64 tg3_refclk_read(struct tg3 *tp) static u64 tg3_refclk_read(struct tg3 *tp, struct ptp_system_timestamp *sts)
{ {
u64 stamp = tr32(TG3_EAV_REF_CLCK_LSB); u64 stamp;
return stamp | (u64)tr32(TG3_EAV_REF_CLCK_MSB) << 32;
ptp_read_system_prets(sts);
stamp = tr32(TG3_EAV_REF_CLCK_LSB);
ptp_read_system_postts(sts);
stamp |= (u64)tr32(TG3_EAV_REF_CLCK_MSB) << 32;
return stamp;
} }
/* tp->lock must be held */ /* tp->lock must be held */
...@@ -6229,13 +6235,14 @@ static int tg3_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) ...@@ -6229,13 +6235,14 @@ static int tg3_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
return 0; return 0;
} }
static int tg3_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) static int tg3_ptp_gettimex(struct ptp_clock_info *ptp, struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{ {
u64 ns; u64 ns;
struct tg3 *tp = container_of(ptp, struct tg3, ptp_info); struct tg3 *tp = container_of(ptp, struct tg3, ptp_info);
tg3_full_lock(tp, 0); tg3_full_lock(tp, 0);
ns = tg3_refclk_read(tp); ns = tg3_refclk_read(tp, sts);
ns += tp->ptp_adjust; ns += tp->ptp_adjust;
tg3_full_unlock(tp); tg3_full_unlock(tp);
...@@ -6330,7 +6337,7 @@ static const struct ptp_clock_info tg3_ptp_caps = { ...@@ -6330,7 +6337,7 @@ static const struct ptp_clock_info tg3_ptp_caps = {
.pps = 0, .pps = 0,
.adjfreq = tg3_ptp_adjfreq, .adjfreq = tg3_ptp_adjfreq,
.adjtime = tg3_ptp_adjtime, .adjtime = tg3_ptp_adjtime,
.gettime64 = tg3_ptp_gettime, .gettimex64 = tg3_ptp_gettimex,
.settime64 = tg3_ptp_settime, .settime64 = tg3_ptp_settime,
.enable = tg3_ptp_enable, .enable = tg3_ptp_enable,
}; };
......
...@@ -505,6 +505,9 @@ extern const struct e1000_info e1000_es2_info; ...@@ -505,6 +505,9 @@ extern const struct e1000_info e1000_es2_info;
void e1000e_ptp_init(struct e1000_adapter *adapter); void e1000e_ptp_init(struct e1000_adapter *adapter);
void e1000e_ptp_remove(struct e1000_adapter *adapter); void e1000e_ptp_remove(struct e1000_adapter *adapter);
u64 e1000e_read_systim(struct e1000_adapter *adapter,
struct ptp_system_timestamp *sts);
static inline s32 e1000_phy_hw_reset(struct e1000_hw *hw) static inline s32 e1000_phy_hw_reset(struct e1000_hw *hw)
{ {
return hw->phy.ops.reset(hw); return hw->phy.ops.reset(hw);
......
...@@ -4319,13 +4319,16 @@ void e1000e_reinit_locked(struct e1000_adapter *adapter) ...@@ -4319,13 +4319,16 @@ void e1000e_reinit_locked(struct e1000_adapter *adapter)
/** /**
* e1000e_sanitize_systim - sanitize raw cycle counter reads * e1000e_sanitize_systim - sanitize raw cycle counter reads
* @hw: pointer to the HW structure * @hw: pointer to the HW structure
* @systim: time value read, sanitized and returned * @systim: PHC time value read, sanitized and returned
* @sts: structure to hold system time before and after reading SYSTIML,
* may be NULL
* *
* Errata for 82574/82583 possible bad bits read from SYSTIMH/L: * Errata for 82574/82583 possible bad bits read from SYSTIMH/L:
* check to see that the time is incrementing at a reasonable * check to see that the time is incrementing at a reasonable
* rate and is a multiple of incvalue. * rate and is a multiple of incvalue.
**/ **/
static u64 e1000e_sanitize_systim(struct e1000_hw *hw, u64 systim) static u64 e1000e_sanitize_systim(struct e1000_hw *hw, u64 systim,
struct ptp_system_timestamp *sts)
{ {
u64 time_delta, rem, temp; u64 time_delta, rem, temp;
u64 systim_next; u64 systim_next;
...@@ -4335,7 +4338,9 @@ static u64 e1000e_sanitize_systim(struct e1000_hw *hw, u64 systim) ...@@ -4335,7 +4338,9 @@ static u64 e1000e_sanitize_systim(struct e1000_hw *hw, u64 systim)
incvalue = er32(TIMINCA) & E1000_TIMINCA_INCVALUE_MASK; incvalue = er32(TIMINCA) & E1000_TIMINCA_INCVALUE_MASK;
for (i = 0; i < E1000_MAX_82574_SYSTIM_REREADS; i++) { for (i = 0; i < E1000_MAX_82574_SYSTIM_REREADS; i++) {
/* latch SYSTIMH on read of SYSTIML */ /* latch SYSTIMH on read of SYSTIML */
ptp_read_system_prets(sts);
systim_next = (u64)er32(SYSTIML); systim_next = (u64)er32(SYSTIML);
ptp_read_system_postts(sts);
systim_next |= (u64)er32(SYSTIMH) << 32; systim_next |= (u64)er32(SYSTIMH) << 32;
time_delta = systim_next - systim; time_delta = systim_next - systim;
...@@ -4353,15 +4358,16 @@ static u64 e1000e_sanitize_systim(struct e1000_hw *hw, u64 systim) ...@@ -4353,15 +4358,16 @@ static u64 e1000e_sanitize_systim(struct e1000_hw *hw, u64 systim)
} }
/** /**
* e1000e_cyclecounter_read - read raw cycle counter (used by time counter) * e1000e_read_systim - read SYSTIM register
* @cc: cyclecounter structure * @adapter: board private structure
* @sts: structure which will contain system time before and after reading
* SYSTIML, may be NULL
**/ **/
static u64 e1000e_cyclecounter_read(const struct cyclecounter *cc) u64 e1000e_read_systim(struct e1000_adapter *adapter,
struct ptp_system_timestamp *sts)
{ {
struct e1000_adapter *adapter = container_of(cc, struct e1000_adapter,
cc);
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
u32 systimel, systimeh; u32 systimel, systimel_2, systimeh;
u64 systim; u64 systim;
/* SYSTIMH latching upon SYSTIML read does not work well. /* SYSTIMH latching upon SYSTIML read does not work well.
* This means that if SYSTIML overflows after we read it but before * This means that if SYSTIML overflows after we read it but before
...@@ -4369,11 +4375,15 @@ static u64 e1000e_cyclecounter_read(const struct cyclecounter *cc) ...@@ -4369,11 +4375,15 @@ static u64 e1000e_cyclecounter_read(const struct cyclecounter *cc)
* will experience a huge non linear increment in the systime value * will experience a huge non linear increment in the systime value
* to fix that we test for overflow and if true, we re-read systime. * to fix that we test for overflow and if true, we re-read systime.
*/ */
ptp_read_system_prets(sts);
systimel = er32(SYSTIML); systimel = er32(SYSTIML);
ptp_read_system_postts(sts);
systimeh = er32(SYSTIMH); systimeh = er32(SYSTIMH);
/* Is systimel is so large that overflow is possible? */ /* Is systimel is so large that overflow is possible? */
if (systimel >= (u32)0xffffffff - E1000_TIMINCA_INCVALUE_MASK) { if (systimel >= (u32)0xffffffff - E1000_TIMINCA_INCVALUE_MASK) {
u32 systimel_2 = er32(SYSTIML); ptp_read_system_prets(sts);
systimel_2 = er32(SYSTIML);
ptp_read_system_postts(sts);
if (systimel > systimel_2) { if (systimel > systimel_2) {
/* There was an overflow, read again SYSTIMH, and use /* There was an overflow, read again SYSTIMH, and use
* systimel_2 * systimel_2
...@@ -4386,11 +4396,23 @@ static u64 e1000e_cyclecounter_read(const struct cyclecounter *cc) ...@@ -4386,11 +4396,23 @@ static u64 e1000e_cyclecounter_read(const struct cyclecounter *cc)
systim |= (u64)systimeh << 32; systim |= (u64)systimeh << 32;
if (adapter->flags2 & FLAG2_CHECK_SYSTIM_OVERFLOW) if (adapter->flags2 & FLAG2_CHECK_SYSTIM_OVERFLOW)
systim = e1000e_sanitize_systim(hw, systim); systim = e1000e_sanitize_systim(hw, systim, sts);
return systim; return systim;
} }
/**
* e1000e_cyclecounter_read - read raw cycle counter (used by time counter)
* @cc: cyclecounter structure
**/
static u64 e1000e_cyclecounter_read(const struct cyclecounter *cc)
{
struct e1000_adapter *adapter = container_of(cc, struct e1000_adapter,
cc);
return e1000e_read_systim(adapter, NULL);
}
/** /**
* e1000_sw_init - Initialize general software structures (struct e1000_adapter) * e1000_sw_init - Initialize general software structures (struct e1000_adapter)
* @adapter: board private structure to initialize * @adapter: board private structure to initialize
......
...@@ -161,14 +161,18 @@ static int e1000e_phc_getcrosststamp(struct ptp_clock_info *ptp, ...@@ -161,14 +161,18 @@ static int e1000e_phc_getcrosststamp(struct ptp_clock_info *ptp,
#endif/*CONFIG_E1000E_HWTS*/ #endif/*CONFIG_E1000E_HWTS*/
/** /**
* e1000e_phc_gettime - Reads the current time from the hardware clock * e1000e_phc_gettimex - Reads the current time from the hardware clock and
* system clock
* @ptp: ptp clock structure * @ptp: ptp clock structure
* @ts: timespec structure to hold the current time value * @ts: timespec structure to hold the current PHC time
* @sts: structure to hold the current system time
* *
* Read the timecounter and return the correct value in ns after converting * Read the timecounter and return the correct value in ns after converting
* it into a struct timespec. * it into a struct timespec.
**/ **/
static int e1000e_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) static int e1000e_phc_gettimex(struct ptp_clock_info *ptp,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{ {
struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter, struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter,
ptp_clock_info); ptp_clock_info);
...@@ -177,8 +181,8 @@ static int e1000e_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) ...@@ -177,8 +181,8 @@ static int e1000e_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
spin_lock_irqsave(&adapter->systim_lock, flags); spin_lock_irqsave(&adapter->systim_lock, flags);
/* Use timecounter_cyc2time() to allow non-monotonic SYSTIM readings */ /* NOTE: Non-monotonic SYSTIM readings may be returned */
cycles = adapter->cc.read(&adapter->cc); cycles = e1000e_read_systim(adapter, sts);
ns = timecounter_cyc2time(&adapter->tc, cycles); ns = timecounter_cyc2time(&adapter->tc, cycles);
spin_unlock_irqrestore(&adapter->systim_lock, flags); spin_unlock_irqrestore(&adapter->systim_lock, flags);
...@@ -258,7 +262,7 @@ static const struct ptp_clock_info e1000e_ptp_clock_info = { ...@@ -258,7 +262,7 @@ static const struct ptp_clock_info e1000e_ptp_clock_info = {
.pps = 0, .pps = 0,
.adjfreq = e1000e_phc_adjfreq, .adjfreq = e1000e_phc_adjfreq,
.adjtime = e1000e_phc_adjtime, .adjtime = e1000e_phc_adjtime,
.gettime64 = e1000e_phc_gettime, .gettimex64 = e1000e_phc_gettimex,
.settime64 = e1000e_phc_settime, .settime64 = e1000e_phc_settime,
.enable = e1000e_phc_enable, .enable = e1000e_phc_enable,
}; };
......
...@@ -275,17 +275,53 @@ static int igb_ptp_adjtime_i210(struct ptp_clock_info *ptp, s64 delta) ...@@ -275,17 +275,53 @@ static int igb_ptp_adjtime_i210(struct ptp_clock_info *ptp, s64 delta)
return 0; return 0;
} }
static int igb_ptp_gettime_82576(struct ptp_clock_info *ptp, static int igb_ptp_gettimex_82576(struct ptp_clock_info *ptp,
struct timespec64 *ts) struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{ {
struct igb_adapter *igb = container_of(ptp, struct igb_adapter, struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
ptp_caps); ptp_caps);
struct e1000_hw *hw = &igb->hw;
unsigned long flags; unsigned long flags;
u32 lo, hi;
u64 ns; u64 ns;
spin_lock_irqsave(&igb->tmreg_lock, flags); spin_lock_irqsave(&igb->tmreg_lock, flags);
ns = timecounter_read(&igb->tc); ptp_read_system_prets(sts);
lo = rd32(E1000_SYSTIML);
ptp_read_system_postts(sts);
hi = rd32(E1000_SYSTIMH);
ns = timecounter_cyc2time(&igb->tc, ((u64)hi << 32) | lo);
spin_unlock_irqrestore(&igb->tmreg_lock, flags);
*ts = ns_to_timespec64(ns);
return 0;
}
static int igb_ptp_gettimex_82580(struct ptp_clock_info *ptp,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{
struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
ptp_caps);
struct e1000_hw *hw = &igb->hw;
unsigned long flags;
u32 lo, hi;
u64 ns;
spin_lock_irqsave(&igb->tmreg_lock, flags);
ptp_read_system_prets(sts);
rd32(E1000_SYSTIMR);
ptp_read_system_postts(sts);
lo = rd32(E1000_SYSTIML);
hi = rd32(E1000_SYSTIMH);
ns = timecounter_cyc2time(&igb->tc, ((u64)hi << 32) | lo);
spin_unlock_irqrestore(&igb->tmreg_lock, flags); spin_unlock_irqrestore(&igb->tmreg_lock, flags);
...@@ -294,16 +330,22 @@ static int igb_ptp_gettime_82576(struct ptp_clock_info *ptp, ...@@ -294,16 +330,22 @@ static int igb_ptp_gettime_82576(struct ptp_clock_info *ptp,
return 0; return 0;
} }
static int igb_ptp_gettime_i210(struct ptp_clock_info *ptp, static int igb_ptp_gettimex_i210(struct ptp_clock_info *ptp,
struct timespec64 *ts) struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{ {
struct igb_adapter *igb = container_of(ptp, struct igb_adapter, struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
ptp_caps); ptp_caps);
struct e1000_hw *hw = &igb->hw;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&igb->tmreg_lock, flags); spin_lock_irqsave(&igb->tmreg_lock, flags);
igb_ptp_read_i210(igb, ts); ptp_read_system_prets(sts);
rd32(E1000_SYSTIMR);
ptp_read_system_postts(sts);
ts->tv_nsec = rd32(E1000_SYSTIML);
ts->tv_sec = rd32(E1000_SYSTIMH);
spin_unlock_irqrestore(&igb->tmreg_lock, flags); spin_unlock_irqrestore(&igb->tmreg_lock, flags);
...@@ -656,9 +698,12 @@ static void igb_ptp_overflow_check(struct work_struct *work) ...@@ -656,9 +698,12 @@ static void igb_ptp_overflow_check(struct work_struct *work)
struct igb_adapter *igb = struct igb_adapter *igb =
container_of(work, struct igb_adapter, ptp_overflow_work.work); container_of(work, struct igb_adapter, ptp_overflow_work.work);
struct timespec64 ts; struct timespec64 ts;
u64 ns;
igb->ptp_caps.gettime64(&igb->ptp_caps, &ts); /* Update the timecounter */
ns = timecounter_read(&igb->tc);
ts = ns_to_timespec64(ns);
pr_debug("igb overflow check at %lld.%09lu\n", pr_debug("igb overflow check at %lld.%09lu\n",
(long long) ts.tv_sec, ts.tv_nsec); (long long) ts.tv_sec, ts.tv_nsec);
...@@ -1124,7 +1169,7 @@ void igb_ptp_init(struct igb_adapter *adapter) ...@@ -1124,7 +1169,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
adapter->ptp_caps.pps = 0; adapter->ptp_caps.pps = 0;
adapter->ptp_caps.adjfreq = igb_ptp_adjfreq_82576; adapter->ptp_caps.adjfreq = igb_ptp_adjfreq_82576;
adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576; adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
adapter->ptp_caps.gettime64 = igb_ptp_gettime_82576; adapter->ptp_caps.gettimex64 = igb_ptp_gettimex_82576;
adapter->ptp_caps.settime64 = igb_ptp_settime_82576; adapter->ptp_caps.settime64 = igb_ptp_settime_82576;
adapter->ptp_caps.enable = igb_ptp_feature_enable; adapter->ptp_caps.enable = igb_ptp_feature_enable;
adapter->cc.read = igb_ptp_read_82576; adapter->cc.read = igb_ptp_read_82576;
...@@ -1143,7 +1188,7 @@ void igb_ptp_init(struct igb_adapter *adapter) ...@@ -1143,7 +1188,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
adapter->ptp_caps.pps = 0; adapter->ptp_caps.pps = 0;
adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580; adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580;
adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576; adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
adapter->ptp_caps.gettime64 = igb_ptp_gettime_82576; adapter->ptp_caps.gettimex64 = igb_ptp_gettimex_82580;
adapter->ptp_caps.settime64 = igb_ptp_settime_82576; adapter->ptp_caps.settime64 = igb_ptp_settime_82576;
adapter->ptp_caps.enable = igb_ptp_feature_enable; adapter->ptp_caps.enable = igb_ptp_feature_enable;
adapter->cc.read = igb_ptp_read_82580; adapter->cc.read = igb_ptp_read_82580;
...@@ -1171,7 +1216,7 @@ void igb_ptp_init(struct igb_adapter *adapter) ...@@ -1171,7 +1216,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
adapter->ptp_caps.pin_config = adapter->sdp_config; adapter->ptp_caps.pin_config = adapter->sdp_config;
adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580; adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580;
adapter->ptp_caps.adjtime = igb_ptp_adjtime_i210; adapter->ptp_caps.adjtime = igb_ptp_adjtime_i210;
adapter->ptp_caps.gettime64 = igb_ptp_gettime_i210; adapter->ptp_caps.gettimex64 = igb_ptp_gettimex_i210;
adapter->ptp_caps.settime64 = igb_ptp_settime_i210; adapter->ptp_caps.settime64 = igb_ptp_settime_i210;
adapter->ptp_caps.enable = igb_ptp_feature_enable_i210; adapter->ptp_caps.enable = igb_ptp_feature_enable_i210;
adapter->ptp_caps.verify = igb_ptp_verify_pin; adapter->ptp_caps.verify = igb_ptp_verify_pin;
......
...@@ -443,22 +443,52 @@ static int ixgbe_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) ...@@ -443,22 +443,52 @@ static int ixgbe_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
} }
/** /**
* ixgbe_ptp_gettime * ixgbe_ptp_gettimex
* @ptp: the ptp clock structure * @ptp: the ptp clock structure
* @ts: timespec structure to hold the current time value * @ts: timespec to hold the PHC timestamp
* @sts: structure to hold the system time before and after reading the PHC
* *
* read the timecounter and return the correct value on ns, * read the timecounter and return the correct value on ns,
* after converting it into a struct timespec. * after converting it into a struct timespec.
*/ */
static int ixgbe_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) static int ixgbe_ptp_gettimex(struct ptp_clock_info *ptp,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{ {
struct ixgbe_adapter *adapter = struct ixgbe_adapter *adapter =
container_of(ptp, struct ixgbe_adapter, ptp_caps); container_of(ptp, struct ixgbe_adapter, ptp_caps);
struct ixgbe_hw *hw = &adapter->hw;
unsigned long flags; unsigned long flags;
u64 ns; u64 ns, stamp;
spin_lock_irqsave(&adapter->tmreg_lock, flags); spin_lock_irqsave(&adapter->tmreg_lock, flags);
ns = timecounter_read(&adapter->hw_tc);
switch (adapter->hw.mac.type) {
case ixgbe_mac_X550:
case ixgbe_mac_X550EM_x:
case ixgbe_mac_x550em_a:
/* Upper 32 bits represent billions of cycles, lower 32 bits
* represent cycles. However, we use timespec64_to_ns for the
* correct math even though the units haven't been corrected
* yet.
*/
ptp_read_system_prets(sts);
IXGBE_READ_REG(hw, IXGBE_SYSTIMR);
ptp_read_system_postts(sts);
ts->tv_nsec = IXGBE_READ_REG(hw, IXGBE_SYSTIML);
ts->tv_sec = IXGBE_READ_REG(hw, IXGBE_SYSTIMH);
stamp = timespec64_to_ns(ts);
break;
default:
ptp_read_system_prets(sts);
stamp = IXGBE_READ_REG(hw, IXGBE_SYSTIML);
ptp_read_system_postts(sts);
stamp |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIMH) << 32;
break;
}
ns = timecounter_cyc2time(&adapter->hw_tc, stamp);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags); spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
*ts = ns_to_timespec64(ns); *ts = ns_to_timespec64(ns);
...@@ -567,10 +597,14 @@ void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter) ...@@ -567,10 +597,14 @@ void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter)
{ {
bool timeout = time_is_before_jiffies(adapter->last_overflow_check + bool timeout = time_is_before_jiffies(adapter->last_overflow_check +
IXGBE_OVERFLOW_PERIOD); IXGBE_OVERFLOW_PERIOD);
struct timespec64 ts; unsigned long flags;
if (timeout) { if (timeout) {
ixgbe_ptp_gettime(&adapter->ptp_caps, &ts); /* Update the timecounter */
spin_lock_irqsave(&adapter->tmreg_lock, flags);
timecounter_read(&adapter->hw_tc);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
adapter->last_overflow_check = jiffies; adapter->last_overflow_check = jiffies;
} }
} }
...@@ -1216,7 +1250,7 @@ static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter) ...@@ -1216,7 +1250,7 @@ static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
adapter->ptp_caps.pps = 1; adapter->ptp_caps.pps = 1;
adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599; adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599;
adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime; adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime; adapter->ptp_caps.gettimex64 = ixgbe_ptp_gettimex;
adapter->ptp_caps.settime64 = ixgbe_ptp_settime; adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
adapter->ptp_caps.enable = ixgbe_ptp_feature_enable; adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
adapter->ptp_setup_sdp = ixgbe_ptp_setup_sdp_x540; adapter->ptp_setup_sdp = ixgbe_ptp_setup_sdp_x540;
...@@ -1233,7 +1267,7 @@ static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter) ...@@ -1233,7 +1267,7 @@ static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
adapter->ptp_caps.pps = 0; adapter->ptp_caps.pps = 0;
adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599; adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599;
adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime; adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime; adapter->ptp_caps.gettimex64 = ixgbe_ptp_gettimex;
adapter->ptp_caps.settime64 = ixgbe_ptp_settime; adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
adapter->ptp_caps.enable = ixgbe_ptp_feature_enable; adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
break; break;
...@@ -1249,7 +1283,7 @@ static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter) ...@@ -1249,7 +1283,7 @@ static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
adapter->ptp_caps.pps = 0; adapter->ptp_caps.pps = 0;
adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_X550; adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_X550;
adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime; adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime; adapter->ptp_caps.gettimex64 = ixgbe_ptp_gettimex;
adapter->ptp_caps.settime64 = ixgbe_ptp_settime; adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
adapter->ptp_caps.enable = ixgbe_ptp_feature_enable; adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
adapter->ptp_setup_sdp = NULL; adapter->ptp_setup_sdp = NULL;
......
...@@ -121,18 +121,20 @@ int ptp_open(struct posix_clock *pc, fmode_t fmode) ...@@ -121,18 +121,20 @@ int ptp_open(struct posix_clock *pc, fmode_t fmode)
long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
{ {
struct ptp_clock_caps caps;
struct ptp_clock_request req;
struct ptp_sys_offset *sysoff = NULL;
struct ptp_sys_offset_precise precise_offset;
struct ptp_pin_desc pd;
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
struct ptp_sys_offset_extended *extoff = NULL;
struct ptp_sys_offset_precise precise_offset;
struct system_device_crosststamp xtstamp;
struct ptp_clock_info *ops = ptp->info; struct ptp_clock_info *ops = ptp->info;
struct ptp_sys_offset *sysoff = NULL;
struct ptp_system_timestamp sts;
struct ptp_clock_request req;
struct ptp_clock_caps caps;
struct ptp_clock_time *pct; struct ptp_clock_time *pct;
unsigned int i, pin_index;
struct ptp_pin_desc pd;
struct timespec64 ts; struct timespec64 ts;
struct system_device_crosststamp xtstamp;
int enable, err = 0; int enable, err = 0;
unsigned int i, pin_index;
switch (cmd) { switch (cmd) {
...@@ -211,6 +213,36 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) ...@@ -211,6 +213,36 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
err = -EFAULT; err = -EFAULT;
break; break;
case PTP_SYS_OFFSET_EXTENDED:
if (!ptp->info->gettimex64) {
err = -EOPNOTSUPP;
break;
}
extoff = memdup_user((void __user *)arg, sizeof(*extoff));
if (IS_ERR(extoff)) {
err = PTR_ERR(extoff);
extoff = NULL;
break;
}
if (extoff->n_samples > PTP_MAX_SAMPLES) {
err = -EINVAL;
break;
}
for (i = 0; i < extoff->n_samples; i++) {
err = ptp->info->gettimex64(ptp->info, &ts, &sts);
if (err)
goto out;
extoff->ts[i][0].sec = sts.pre_ts.tv_sec;
extoff->ts[i][0].nsec = sts.pre_ts.tv_nsec;
extoff->ts[i][1].sec = ts.tv_sec;
extoff->ts[i][1].nsec = ts.tv_nsec;
extoff->ts[i][2].sec = sts.post_ts.tv_sec;
extoff->ts[i][2].nsec = sts.post_ts.tv_nsec;
}
if (copy_to_user((void __user *)arg, extoff, sizeof(*extoff)))
err = -EFAULT;
break;
case PTP_SYS_OFFSET: case PTP_SYS_OFFSET:
sysoff = memdup_user((void __user *)arg, sizeof(*sysoff)); sysoff = memdup_user((void __user *)arg, sizeof(*sysoff));
if (IS_ERR(sysoff)) { if (IS_ERR(sysoff)) {
...@@ -228,7 +260,12 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) ...@@ -228,7 +260,12 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
pct->sec = ts.tv_sec; pct->sec = ts.tv_sec;
pct->nsec = ts.tv_nsec; pct->nsec = ts.tv_nsec;
pct++; pct++;
ptp->info->gettime64(ptp->info, &ts); if (ops->gettimex64)
err = ops->gettimex64(ops, &ts, NULL);
else
err = ops->gettime64(ops, &ts);
if (err)
goto out;
pct->sec = ts.tv_sec; pct->sec = ts.tv_sec;
pct->nsec = ts.tv_nsec; pct->nsec = ts.tv_nsec;
pct++; pct++;
...@@ -281,6 +318,8 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) ...@@ -281,6 +318,8 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
break; break;
} }
out:
kfree(extoff);
kfree(sysoff); kfree(sysoff);
return err; return err;
} }
......
...@@ -117,7 +117,10 @@ static int ptp_clock_gettime(struct posix_clock *pc, struct timespec64 *tp) ...@@ -117,7 +117,10 @@ static int ptp_clock_gettime(struct posix_clock *pc, struct timespec64 *tp)
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
int err; int err;
err = ptp->info->gettime64(ptp->info, tp); if (ptp->info->gettimex64)
err = ptp->info->gettimex64(ptp->info, tp, NULL);
else
err = ptp->info->gettime64(ptp->info, tp);
return err; return err;
} }
......
...@@ -39,6 +39,15 @@ struct ptp_clock_request { ...@@ -39,6 +39,15 @@ struct ptp_clock_request {
}; };
struct system_device_crosststamp; struct system_device_crosststamp;
/**
* struct ptp_system_timestamp - system time corresponding to a PHC timestamp
*/
struct ptp_system_timestamp {
struct timespec64 pre_ts;
struct timespec64 post_ts;
};
/** /**
* struct ptp_clock_info - decribes a PTP hardware clock * struct ptp_clock_info - decribes a PTP hardware clock
* *
...@@ -73,8 +82,18 @@ struct system_device_crosststamp; ...@@ -73,8 +82,18 @@ struct system_device_crosststamp;
* parameter delta: Desired change in nanoseconds. * parameter delta: Desired change in nanoseconds.
* *
* @gettime64: Reads the current time from the hardware clock. * @gettime64: Reads the current time from the hardware clock.
* This method is deprecated. New drivers should implement
* the @gettimex64 method instead.
* parameter ts: Holds the result. * parameter ts: Holds the result.
* *
* @gettimex64: Reads the current time from the hardware clock and optionally
* also the system clock.
* parameter ts: Holds the PHC timestamp.
* parameter sts: If not NULL, it holds a pair of timestamps from
* the system clock. The first reading is made right before
* reading the lowest bits of the PHC timestamp and the second
* reading immediately follows that.
*
* @getcrosststamp: Reads the current time from the hardware clock and * @getcrosststamp: Reads the current time from the hardware clock and
* system clock simultaneously. * system clock simultaneously.
* parameter cts: Contains timestamp (device,system) pair, * parameter cts: Contains timestamp (device,system) pair,
...@@ -124,6 +143,8 @@ struct ptp_clock_info { ...@@ -124,6 +143,8 @@ struct ptp_clock_info {
int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta); int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta);
int (*adjtime)(struct ptp_clock_info *ptp, s64 delta); int (*adjtime)(struct ptp_clock_info *ptp, s64 delta);
int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts); int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts);
int (*gettimex64)(struct ptp_clock_info *ptp, struct timespec64 *ts,
struct ptp_system_timestamp *sts);
int (*getcrosststamp)(struct ptp_clock_info *ptp, int (*getcrosststamp)(struct ptp_clock_info *ptp,
struct system_device_crosststamp *cts); struct system_device_crosststamp *cts);
int (*settime64)(struct ptp_clock_info *p, const struct timespec64 *ts); int (*settime64)(struct ptp_clock_info *p, const struct timespec64 *ts);
...@@ -247,4 +268,16 @@ static inline int ptp_schedule_worker(struct ptp_clock *ptp, ...@@ -247,4 +268,16 @@ static inline int ptp_schedule_worker(struct ptp_clock *ptp,
#endif #endif
static inline void ptp_read_system_prets(struct ptp_system_timestamp *sts)
{
if (sts)
ktime_get_real_ts64(&sts->pre_ts);
}
static inline void ptp_read_system_postts(struct ptp_system_timestamp *sts)
{
if (sts)
ktime_get_real_ts64(&sts->post_ts);
}
#endif #endif
...@@ -84,6 +84,16 @@ struct ptp_sys_offset { ...@@ -84,6 +84,16 @@ struct ptp_sys_offset {
struct ptp_clock_time ts[2 * PTP_MAX_SAMPLES + 1]; struct ptp_clock_time ts[2 * PTP_MAX_SAMPLES + 1];
}; };
struct ptp_sys_offset_extended {
unsigned int n_samples; /* Desired number of measurements. */
unsigned int rsv[3]; /* Reserved for future use. */
/*
* Array of [system, phc, system] time stamps. The kernel will provide
* 3*n_samples time stamps.
*/
struct ptp_clock_time ts[PTP_MAX_SAMPLES][3];
};
struct ptp_sys_offset_precise { struct ptp_sys_offset_precise {
struct ptp_clock_time device; struct ptp_clock_time device;
struct ptp_clock_time sys_realtime; struct ptp_clock_time sys_realtime;
...@@ -136,6 +146,8 @@ struct ptp_pin_desc { ...@@ -136,6 +146,8 @@ struct ptp_pin_desc {
#define PTP_PIN_SETFUNC _IOW(PTP_CLK_MAGIC, 7, struct ptp_pin_desc) #define PTP_PIN_SETFUNC _IOW(PTP_CLK_MAGIC, 7, struct ptp_pin_desc)
#define PTP_SYS_OFFSET_PRECISE \ #define PTP_SYS_OFFSET_PRECISE \
_IOWR(PTP_CLK_MAGIC, 8, struct ptp_sys_offset_precise) _IOWR(PTP_CLK_MAGIC, 8, struct ptp_sys_offset_precise)
#define PTP_SYS_OFFSET_EXTENDED \
_IOW(PTP_CLK_MAGIC, 9, struct ptp_sys_offset_extended)
struct ptp_extts_event { struct ptp_extts_event {
struct ptp_clock_time t; /* Time event occured. */ struct ptp_clock_time t; /* Time event occured. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment