diff options
author | Eran Ben Elisha <eranbe@mellanox.com> | 2015-12-29 12:58:32 (GMT) |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-01-05 19:11:50 (GMT) |
commit | 3d8c38af149309feb2541b995b3a45df170d6da3 (patch) | |
tree | da57592cc23e5b7ff6a640a0173a30f373a6e5ba /drivers/net/ethernet/mellanox/mlx5/core/en_clock.c | |
parent | ef9814deafd0c83a358d49e3709c3e16cc352118 (diff) | |
download | linux-3d8c38af149309feb2541b995b3a45df170d6da3.tar.xz |
net/mlx5e: Add PTP Hardware Clock (PHC) support
Add a PHC support to the mlx5_en driver. Use reader/writer spinlocks to
protect the timecounter since every packet received needs to call
timecounter_cycle2time() when timestamping is enabled. This can become
a performance bottleneck with RSS and multiple receive queues if normal
spinlocks are used.
The driver has been tested with both Documentation/ptp/testptp and the
linuxptp project (http://linuxptp.sourceforge.net/) on a Mellanox
ConnectX-4 card.
Signed-off-by: Eran Ben Elisha <eranbe@mellanox.com>
Cc: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlx5/core/en_clock.c')
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/en_clock.c | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c index 49a8238..be65435 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c @@ -130,6 +130,89 @@ int mlx5e_hwstamp_get(struct net_device *dev, struct ifreq *ifr) return copy_to_user(ifr->ifr_data, cfg, sizeof(*cfg)) ? -EFAULT : 0; } +static int mlx5e_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, + ptp_info); + u64 ns = timespec64_to_ns(ts); + + write_lock(&tstamp->lock); + timecounter_init(&tstamp->clock, &tstamp->cycles, ns); + write_unlock(&tstamp->lock); + + return 0; +} + +static int mlx5e_ptp_gettime(struct ptp_clock_info *ptp, + struct timespec64 *ts) +{ + struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, + ptp_info); + u64 ns; + + write_lock(&tstamp->lock); + ns = timecounter_read(&tstamp->clock); + write_unlock(&tstamp->lock); + + *ts = ns_to_timespec64(ns); + + return 0; +} + +static int mlx5e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, + ptp_info); + + write_lock(&tstamp->lock); + timecounter_adjtime(&tstamp->clock, delta); + write_unlock(&tstamp->lock); + + return 0; +} + +static int mlx5e_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) +{ + u64 adj; + u32 diff; + int neg_adj = 0; + struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, + ptp_info); + + if (delta < 0) { + neg_adj = 1; + delta = -delta; + } + + adj = tstamp->nominal_c_mult; + adj *= delta; + diff = div_u64(adj, 1000000000ULL); + + write_lock(&tstamp->lock); + timecounter_read(&tstamp->clock); + tstamp->cycles.mult = neg_adj ? tstamp->nominal_c_mult - diff : + tstamp->nominal_c_mult + diff; + write_unlock(&tstamp->lock); + + return 0; +} + +static const struct ptp_clock_info mlx5e_ptp_clock_info = { + .owner = THIS_MODULE, + .max_adj = 100000000, + .n_alarm = 0, + .n_ext_ts = 0, + .n_per_out = 0, + .n_pins = 0, + .pps = 0, + .adjfreq = mlx5e_ptp_adjfreq, + .adjtime = mlx5e_ptp_adjtime, + .gettime64 = mlx5e_ptp_gettime, + .settime64 = mlx5e_ptp_settime, + .enable = NULL, +}; + static void mlx5e_timestamp_init_config(struct mlx5e_tstamp *tstamp) { tstamp->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF; @@ -174,6 +257,18 @@ void mlx5e_timestamp_init(struct mlx5e_priv *priv) schedule_delayed_work(&tstamp->overflow_work, 0); else mlx5_core_warn(priv->mdev, "invalid overflow period, overflow_work is not scheduled\n"); + + /* Configure the PHC */ + tstamp->ptp_info = mlx5e_ptp_clock_info; + snprintf(tstamp->ptp_info.name, 16, "mlx5 ptp"); + + tstamp->ptp = ptp_clock_register(&tstamp->ptp_info, + &priv->mdev->pdev->dev); + if (IS_ERR_OR_NULL(tstamp->ptp)) { + mlx5_core_warn(priv->mdev, "ptp_clock_register failed %ld\n", + PTR_ERR(tstamp->ptp)); + tstamp->ptp = NULL; + } } void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv) @@ -183,5 +278,10 @@ void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv) if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) return; + if (priv->tstamp.ptp) { + ptp_clock_unregister(priv->tstamp.ptp); + priv->tstamp.ptp = NULL; + } + cancel_delayed_work_sync(&tstamp->overflow_work); } |