Commit 3d8c38af authored by Eran Ben Elisha's avatar Eran Ben Elisha Committed by David S. Miller

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: default avatarEran Ben Elisha <eranbe@mellanox.com>
Cc: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: default avatarSaeed Mahameed <saeedm@mellanox.com>
Acked-by: default avatarRichard Cochran <richardcochran@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ef9814de
......@@ -13,6 +13,7 @@ config MLX5_CORE
config MLX5_CORE_EN
bool "Mellanox Technologies ConnectX-4 Ethernet support"
depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE
select PTP_1588_CLOCK
default n
---help---
Ethernet support in Mellanox Technologies ConnectX-4 NIC.
......
......@@ -34,6 +34,7 @@
#include <linux/etherdevice.h>
#include <linux/timecounter.h>
#include <linux/net_tstamp.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/qp.h>
#include <linux/mlx5/cq.h>
......@@ -295,6 +296,8 @@ struct mlx5e_tstamp {
unsigned long overflow_period;
struct delayed_work overflow_work;
struct mlx5_core_dev *mdev;
struct ptp_clock *ptp;
struct ptp_clock_info ptp_info;
};
enum {
......
......@@ -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);
}
......@@ -865,7 +865,8 @@ static int mlx5e_get_ts_info(struct net_device *dev,
if (ret)
return ret;
info->phc_index = -1;
info->phc_index = priv->tstamp.ptp ?
ptp_clock_index(priv->tstamp.ptp) : -1;
if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz))
return 0;
......
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