Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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.
+3 −0
Original line number Diff line number Diff line
@@ -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 {
+100 −0
Original line number Diff line number Diff line
@@ -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);
}
+2 −1
Original line number Diff line number Diff line
@@ -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;