aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-12-30 18:30:00 -0500
committerDavid S. Miller <davem@davemloft.net>2014-12-30 18:30:00 -0500
commit5115ec9654f87ba0d9508569f91fe1f444ffbf58 (patch)
treec4b176d1e40eecb2caa00952eae95ecb2b2a046b /drivers/net/ethernet
parentMerge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net (diff)
parenttimecounter: keep track of accumulated fractional nanoseconds (diff)
downloadlinux-dev-5115ec9654f87ba0d9508569f91fe1f444ffbf58.tar.xz
linux-dev-5115ec9654f87ba0d9508569f91fe1f444ffbf58.zip
Merge branch 'timecounter'
Richard Cochran says: ==================== Time Counter fixes and improvements Several PTP Hardware Clock (PHC) drivers implement the clock in software using the timecounter/cyclecounter code. This series adds one simple improvement and one more subtle fix to the shared timecounter facility. Credit for this series goes to Janusz Użycki, who pointed the issues out to me off list. Patch #1 simply move the timecounter code into its own file. When working on this series, it was really annoying to see half the kernel recompile after every tweak to the timecounter stuff. There is no reason to keep this together with the clocksource code. Patch #2 implements an improved adjtime() method, and patches 3-10 convert all of the drivers over to the new method. Patch #11 fixes a subtle but important issue with the timecounter WRT frequency adjustment. As it stands now, a timecounter based PHC will exhibit a variable frequency resolution (and variable time error) depending on how often the clock is read. In timecounter_read_delta(), the expression (delta * cc->mult) >> cc->shift; can lose resolution from the adjusted value of 'mult'. If the value of 'delta' is too small, then small changes in 'mult' have no effect. However, if the delta value is large enough, then small changes in 'mult' will have an effect. Reading the clock too often means smaller 'delta' values which in turn will spoil the fine adjustments made to 'mult'. Up until now, this effect did not show up in my testing. The following example explains why. The CPTS has an input clock of 250 MHz, and the clock source uses mult=0x80000000 and shift=29, making the ticks to nanoseconds conversion like this: ticks * 2^31 ------------ 2^29 Imagine what happens if the clock is read every 10 milliseconds. Ten milliseconds are about 2500000 ticks, which corresponds to about 21 bits. The product in the numerator has then 52 bits. After the shift operation, 23 bits are preserved. This results in a frequency adjustment resolution of about 0.1 ppm (not _too_ bad.) A frequency resolution of 1 ppm requires 20 bits. A frequency resolution of 1 ppb requires 30 bits. For the 250 MHz CPTS clock, reading every 4 seconds yields a 1 ppb resolution (which is the finest that our API allows). However, the error can be much higher if the clock is read too often or if time stamps occur close in time to read operations. In general it is really not acceptable to allow the rate of clock readings to influence the clock accuracy. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to '')
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-ptp.c8
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c6
-rw-r--r--drivers/net/ethernet/freescale/fec.h1
-rw-r--r--drivers/net/ethernet/freescale/fec_ptp.c16
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h2
-rw-r--r--drivers/net/ethernet/intel/e1000e/ptp.c5
-rw-r--r--drivers/net/ethernet/intel/igb/igb.h2
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ptp.c7
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe.h2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_clock.c9
-rw-r--r--drivers/net/ethernet/ti/cpts.c5
-rw-r--r--drivers/net/ethernet/ti/cpts.h1
15 files changed, 17 insertions, 62 deletions
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c
index a1bf9d1cdae1..f5acf4cc69bd 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c
@@ -171,15 +171,9 @@ static int xgbe_adjtime(struct ptp_clock_info *info, s64 delta)
struct xgbe_prv_data,
ptp_clock_info);
unsigned long flags;
- u64 nsec;
spin_lock_irqsave(&pdata->tstamp_lock, flags);
-
- nsec = timecounter_read(&pdata->tstamp_tc);
-
- nsec += delta;
- timecounter_init(&pdata->tstamp_tc, &pdata->tstamp_cc, nsec);
-
+ timecounter_adjtime(&pdata->tstamp_tc, delta);
spin_unlock_irqrestore(&pdata->tstamp_lock, flags);
return 0;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index f9ec762ac3f0..2af6affc35a7 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -124,7 +124,7 @@
#include <linux/if_vlan.h>
#include <linux/bitops.h>
#include <linux/ptp_clock_kernel.h>
-#include <linux/clocksource.h>
+#include <linux/timecounter.h>
#include <linux/net_tstamp.h>
#include <net/dcbnl.h>
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index c3a6072134f5..792ba72fb5c8 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -22,7 +22,7 @@
#include <linux/ptp_clock_kernel.h>
#include <linux/net_tstamp.h>
-#include <linux/clocksource.h>
+#include <linux/timecounter.h>
/* compilation time flags */
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 72eef9fc883e..2c951326a85d 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -13267,14 +13267,10 @@ static int bnx2x_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
static int bnx2x_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct bnx2x *bp = container_of(ptp, struct bnx2x, ptp_clock_info);
- u64 now;
DP(BNX2X_MSG_PTP, "PTP adjtime called, delta = %llx\n", delta);
- now = timecounter_read(&bp->timecounter);
- now += delta;
- /* Re-init the timecounter */
- timecounter_init(&bp->timecounter, &bp->cyclecounter, now);
+ timecounter_adjtime(&bp->timecounter, delta);
return 0;
}
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 469691ad4a1e..df8bbddaeb37 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -16,6 +16,7 @@
#include <linux/clocksource.h>
#include <linux/net_tstamp.h>
#include <linux/ptp_clock_kernel.h>
+#include <linux/timecounter.h>
#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
defined(CONFIG_M520x) || defined(CONFIG_M532x) || \
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c
index 992c8c3db553..1f9cf2345266 100644
--- a/drivers/net/ethernet/freescale/fec_ptp.c
+++ b/drivers/net/ethernet/freescale/fec_ptp.c
@@ -374,23 +374,9 @@ static int fec_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
struct fec_enet_private *fep =
container_of(ptp, struct fec_enet_private, ptp_caps);
unsigned long flags;
- u64 now;
- u32 counter;
spin_lock_irqsave(&fep->tmreg_lock, flags);
-
- now = timecounter_read(&fep->tc);
- now += delta;
-
- /* Get the timer value based on adjusted timestamp.
- * Update the counter with the masked value.
- */
- counter = now & fep->cc.mask;
- writel(counter, fep->hwp + FEC_ATIME);
-
- /* reset the timecounter */
- timecounter_init(&fep->tc, &fep->cc, now);
-
+ timecounter_adjtime(&fep->tc, delta);
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
return 0;
diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index 7785240a0da1..9416e5a7e0c8 100644
--- a/drivers/net/ethernet/intel/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -34,7 +34,7 @@
#include <linux/pci-aspm.h>
#include <linux/crc32.h>
#include <linux/if_vlan.h>
-#include <linux/clocksource.h>
+#include <linux/timecounter.h>
#include <linux/net_tstamp.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/ptp_classify.h>
diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c
index fb1a914a3ad4..978ef9c4a043 100644
--- a/drivers/net/ethernet/intel/e1000e/ptp.c
+++ b/drivers/net/ethernet/intel/e1000e/ptp.c
@@ -90,12 +90,9 @@ static int e1000e_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter,
ptp_clock_info);
unsigned long flags;
- s64 now;
spin_lock_irqsave(&adapter->systim_lock, flags);
- now = timecounter_read(&adapter->tc);
- now += delta;
- timecounter_init(&adapter->tc, &adapter->cc, now);
+ timecounter_adjtime(&adapter->tc, delta);
spin_unlock_irqrestore(&adapter->systim_lock, flags);
return 0;
diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
index 82d891e183b1..ee22da391474 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -29,7 +29,7 @@
#include "e1000_mac.h"
#include "e1000_82575.h"
-#include <linux/clocksource.h>
+#include <linux/timecounter.h>
#include <linux/net_tstamp.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/bitops.h>
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
index 794c139f0cc0..1d27f2d3b57c 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -256,14 +256,9 @@ static int igb_ptp_adjtime_82576(struct ptp_clock_info *ptp, s64 delta)
struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
ptp_caps);
unsigned long flags;
- s64 now;
spin_lock_irqsave(&igb->tmreg_lock, flags);
-
- now = timecounter_read(&igb->tc);
- now += delta;
- timecounter_init(&igb->tc, &igb->cc, now);
-
+ timecounter_adjtime(&igb->tc, delta);
spin_unlock_irqrestore(&igb->tmreg_lock, flags);
return 0;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index b6137be43920..38fc64cf5dca 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -38,7 +38,7 @@
#include <linux/if_vlan.h>
#include <linux/jiffies.h>
-#include <linux/clocksource.h>
+#include <linux/timecounter.h>
#include <linux/net_tstamp.h>
#include <linux/ptp_clock_kernel.h>
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
index 5fd4b5271f9a..47c29eaaa140 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
@@ -261,18 +261,9 @@ static int ixgbe_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
struct ixgbe_adapter *adapter =
container_of(ptp, struct ixgbe_adapter, ptp_caps);
unsigned long flags;
- u64 now;
spin_lock_irqsave(&adapter->tmreg_lock, flags);
-
- now = timecounter_read(&adapter->tc);
- now += delta;
-
- /* reset the timecounter */
- timecounter_init(&adapter->tc,
- &adapter->cc,
- now);
-
+ timecounter_adjtime(&adapter->tc, delta);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
ixgbe_ptp_setup_sdp(adapter);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
index 999014413b1a..e9cce4f72b24 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
@@ -147,12 +147,9 @@ static int mlx4_en_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
ptp_clock_info);
unsigned long flags;
- s64 now;
write_lock_irqsave(&mdev->clock_lock, flags);
- now = timecounter_read(&mdev->clock);
- now += delta;
- timecounter_init(&mdev->clock, &mdev->cycles, now);
+ timecounter_adjtime(&mdev->clock, delta);
write_unlock_irqrestore(&mdev->clock_lock, flags);
return 0;
@@ -243,7 +240,7 @@ void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev)
{
struct mlx4_dev *dev = mdev->dev;
unsigned long flags;
- u64 ns;
+ u64 ns, zero = 0;
rwlock_init(&mdev->clock_lock);
@@ -268,7 +265,7 @@ void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev)
/* Calculate period in seconds to call the overflow watchdog - to make
* sure counter is checked at least once every wrap around.
*/
- ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask);
+ ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask, zero, &zero);
do_div(ns, NSEC_PER_SEC / 2 / HZ);
mdev->overflow_period = ns;
diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c
index 4a4388b813ac..fbe42cb107ec 100644
--- a/drivers/net/ethernet/ti/cpts.c
+++ b/drivers/net/ethernet/ti/cpts.c
@@ -157,14 +157,11 @@ static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
- s64 now;
unsigned long flags;
struct cpts *cpts = container_of(ptp, struct cpts, info);
spin_lock_irqsave(&cpts->lock, flags);
- now = timecounter_read(&cpts->tc);
- now += delta;
- timecounter_init(&cpts->tc, &cpts->cc, now);
+ timecounter_adjtime(&cpts->tc, delta);
spin_unlock_irqrestore(&cpts->lock, flags);
return 0;
diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h
index 1a581ef7eee8..69a46b92c7d6 100644
--- a/drivers/net/ethernet/ti/cpts.h
+++ b/drivers/net/ethernet/ti/cpts.h
@@ -27,6 +27,7 @@
#include <linux/list.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/skbuff.h>
+#include <linux/timecounter.h>
struct cpsw_cpts {
u32 idver; /* Identification and version */