18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2012 Mellanox Technologies. All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
58c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
68c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
78c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the
88c2ecf20Sopenharmony_ci * OpenIB.org BSD license below:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
118c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
128c2ecf20Sopenharmony_ci *     conditions are met:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *      - Redistributions of source code must retain the above
158c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
168c2ecf20Sopenharmony_ci *        disclaimer.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
198c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
208c2ecf20Sopenharmony_ci *        disclaimer in the documentation and/or other materials
218c2ecf20Sopenharmony_ci *        provided with the distribution.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
248c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
258c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
268c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
278c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
288c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
298c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
308c2ecf20Sopenharmony_ci * SOFTWARE.
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <linux/mlx4/device.h>
358c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "mlx4_en.h"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* mlx4_en_read_clock - read raw cycle counter (to be used by time counter)
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_cistatic u64 mlx4_en_read_clock(const struct cyclecounter *tc)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct mlx4_en_dev *mdev =
448c2ecf20Sopenharmony_ci		container_of(tc, struct mlx4_en_dev, cycles);
458c2ecf20Sopenharmony_ci	struct mlx4_dev *dev = mdev->dev;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	return mlx4_read_clock(dev) & tc->mask;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ciu64 mlx4_en_get_cqe_ts(struct mlx4_cqe *cqe)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	u64 hi, lo;
538c2ecf20Sopenharmony_ci	struct mlx4_ts_cqe *ts_cqe = (struct mlx4_ts_cqe *)cqe;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	lo = (u64)be16_to_cpu(ts_cqe->timestamp_lo);
568c2ecf20Sopenharmony_ci	hi = ((u64)be32_to_cpu(ts_cqe->timestamp_hi) + !lo) << 16;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return hi | lo;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_civoid mlx4_en_fill_hwtstamps(struct mlx4_en_dev *mdev,
628c2ecf20Sopenharmony_ci			    struct skb_shared_hwtstamps *hwts,
638c2ecf20Sopenharmony_ci			    u64 timestamp)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	unsigned int seq;
668c2ecf20Sopenharmony_ci	u64 nsec;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	do {
698c2ecf20Sopenharmony_ci		seq = read_seqbegin(&mdev->clock_lock);
708c2ecf20Sopenharmony_ci		nsec = timecounter_cyc2time(&mdev->clock, timestamp);
718c2ecf20Sopenharmony_ci	} while (read_seqretry(&mdev->clock_lock, seq));
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	memset(hwts, 0, sizeof(struct skb_shared_hwtstamps));
748c2ecf20Sopenharmony_ci	hwts->hwtstamp = ns_to_ktime(nsec);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/**
788c2ecf20Sopenharmony_ci * mlx4_en_remove_timestamp - disable PTP device
798c2ecf20Sopenharmony_ci * @mdev: board private structure
808c2ecf20Sopenharmony_ci *
818c2ecf20Sopenharmony_ci * Stop the PTP support.
828c2ecf20Sopenharmony_ci **/
838c2ecf20Sopenharmony_civoid mlx4_en_remove_timestamp(struct mlx4_en_dev *mdev)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	if (mdev->ptp_clock) {
868c2ecf20Sopenharmony_ci		ptp_clock_unregister(mdev->ptp_clock);
878c2ecf20Sopenharmony_ci		mdev->ptp_clock = NULL;
888c2ecf20Sopenharmony_ci		mlx4_info(mdev, "removed PHC\n");
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define MLX4_EN_WRAP_AROUND_SEC	10UL
938c2ecf20Sopenharmony_ci/* By scheduling the overflow check every 5 seconds, we have a reasonably
948c2ecf20Sopenharmony_ci * good chance we wont miss a wrap around.
958c2ecf20Sopenharmony_ci * TOTO: Use a timer instead of a work queue to increase the guarantee.
968c2ecf20Sopenharmony_ci */
978c2ecf20Sopenharmony_ci#define MLX4_EN_OVERFLOW_PERIOD (MLX4_EN_WRAP_AROUND_SEC * HZ / 2)
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_civoid mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	bool timeout = time_is_before_jiffies(mdev->last_overflow_check +
1028c2ecf20Sopenharmony_ci					      MLX4_EN_OVERFLOW_PERIOD);
1038c2ecf20Sopenharmony_ci	unsigned long flags;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (timeout) {
1068c2ecf20Sopenharmony_ci		write_seqlock_irqsave(&mdev->clock_lock, flags);
1078c2ecf20Sopenharmony_ci		timecounter_read(&mdev->clock);
1088c2ecf20Sopenharmony_ci		write_sequnlock_irqrestore(&mdev->clock_lock, flags);
1098c2ecf20Sopenharmony_ci		mdev->last_overflow_check = jiffies;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/**
1148c2ecf20Sopenharmony_ci * mlx4_en_phc_adjfreq - adjust the frequency of the hardware clock
1158c2ecf20Sopenharmony_ci * @ptp: ptp clock structure
1168c2ecf20Sopenharmony_ci * @delta: Desired frequency change in parts per billion
1178c2ecf20Sopenharmony_ci *
1188c2ecf20Sopenharmony_ci * Adjust the frequency of the PHC cycle counter by the indicated delta from
1198c2ecf20Sopenharmony_ci * the base frequency.
1208c2ecf20Sopenharmony_ci **/
1218c2ecf20Sopenharmony_cistatic int mlx4_en_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	u64 adj;
1248c2ecf20Sopenharmony_ci	u32 diff, mult;
1258c2ecf20Sopenharmony_ci	int neg_adj = 0;
1268c2ecf20Sopenharmony_ci	unsigned long flags;
1278c2ecf20Sopenharmony_ci	struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
1288c2ecf20Sopenharmony_ci						ptp_clock_info);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (delta < 0) {
1318c2ecf20Sopenharmony_ci		neg_adj = 1;
1328c2ecf20Sopenharmony_ci		delta = -delta;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci	mult = mdev->nominal_c_mult;
1358c2ecf20Sopenharmony_ci	adj = mult;
1368c2ecf20Sopenharmony_ci	adj *= delta;
1378c2ecf20Sopenharmony_ci	diff = div_u64(adj, 1000000000ULL);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	write_seqlock_irqsave(&mdev->clock_lock, flags);
1408c2ecf20Sopenharmony_ci	timecounter_read(&mdev->clock);
1418c2ecf20Sopenharmony_ci	mdev->cycles.mult = neg_adj ? mult - diff : mult + diff;
1428c2ecf20Sopenharmony_ci	write_sequnlock_irqrestore(&mdev->clock_lock, flags);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/**
1488c2ecf20Sopenharmony_ci * mlx4_en_phc_adjtime - Shift the time of the hardware clock
1498c2ecf20Sopenharmony_ci * @ptp: ptp clock structure
1508c2ecf20Sopenharmony_ci * @delta: Desired change in nanoseconds
1518c2ecf20Sopenharmony_ci *
1528c2ecf20Sopenharmony_ci * Adjust the timer by resetting the timecounter structure.
1538c2ecf20Sopenharmony_ci **/
1548c2ecf20Sopenharmony_cistatic int mlx4_en_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
1578c2ecf20Sopenharmony_ci						ptp_clock_info);
1588c2ecf20Sopenharmony_ci	unsigned long flags;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	write_seqlock_irqsave(&mdev->clock_lock, flags);
1618c2ecf20Sopenharmony_ci	timecounter_adjtime(&mdev->clock, delta);
1628c2ecf20Sopenharmony_ci	write_sequnlock_irqrestore(&mdev->clock_lock, flags);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/**
1688c2ecf20Sopenharmony_ci * mlx4_en_phc_gettime - Reads the current time from the hardware clock
1698c2ecf20Sopenharmony_ci * @ptp: ptp clock structure
1708c2ecf20Sopenharmony_ci * @ts: timespec structure to hold the current time value
1718c2ecf20Sopenharmony_ci *
1728c2ecf20Sopenharmony_ci * Read the timecounter and return the correct value in ns after converting
1738c2ecf20Sopenharmony_ci * it into a struct timespec.
1748c2ecf20Sopenharmony_ci **/
1758c2ecf20Sopenharmony_cistatic int mlx4_en_phc_gettime(struct ptp_clock_info *ptp,
1768c2ecf20Sopenharmony_ci			       struct timespec64 *ts)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
1798c2ecf20Sopenharmony_ci						ptp_clock_info);
1808c2ecf20Sopenharmony_ci	unsigned long flags;
1818c2ecf20Sopenharmony_ci	u64 ns;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	write_seqlock_irqsave(&mdev->clock_lock, flags);
1848c2ecf20Sopenharmony_ci	ns = timecounter_read(&mdev->clock);
1858c2ecf20Sopenharmony_ci	write_sequnlock_irqrestore(&mdev->clock_lock, flags);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	*ts = ns_to_timespec64(ns);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return 0;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/**
1938c2ecf20Sopenharmony_ci * mlx4_en_phc_settime - Set the current time on the hardware clock
1948c2ecf20Sopenharmony_ci * @ptp: ptp clock structure
1958c2ecf20Sopenharmony_ci * @ts: timespec containing the new time for the cycle counter
1968c2ecf20Sopenharmony_ci *
1978c2ecf20Sopenharmony_ci * Reset the timecounter to use a new base value instead of the kernel
1988c2ecf20Sopenharmony_ci * wall timer value.
1998c2ecf20Sopenharmony_ci **/
2008c2ecf20Sopenharmony_cistatic int mlx4_en_phc_settime(struct ptp_clock_info *ptp,
2018c2ecf20Sopenharmony_ci			       const struct timespec64 *ts)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
2048c2ecf20Sopenharmony_ci						ptp_clock_info);
2058c2ecf20Sopenharmony_ci	u64 ns = timespec64_to_ns(ts);
2068c2ecf20Sopenharmony_ci	unsigned long flags;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* reset the timecounter */
2098c2ecf20Sopenharmony_ci	write_seqlock_irqsave(&mdev->clock_lock, flags);
2108c2ecf20Sopenharmony_ci	timecounter_init(&mdev->clock, &mdev->cycles, ns);
2118c2ecf20Sopenharmony_ci	write_sequnlock_irqrestore(&mdev->clock_lock, flags);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return 0;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/**
2178c2ecf20Sopenharmony_ci * mlx4_en_phc_enable - enable or disable an ancillary feature
2188c2ecf20Sopenharmony_ci * @ptp: ptp clock structure
2198c2ecf20Sopenharmony_ci * @request: Desired resource to enable or disable
2208c2ecf20Sopenharmony_ci * @on: Caller passes one to enable or zero to disable
2218c2ecf20Sopenharmony_ci *
2228c2ecf20Sopenharmony_ci * Enable (or disable) ancillary features of the PHC subsystem.
2238c2ecf20Sopenharmony_ci * Currently, no ancillary features are supported.
2248c2ecf20Sopenharmony_ci **/
2258c2ecf20Sopenharmony_cistatic int mlx4_en_phc_enable(struct ptp_clock_info __always_unused *ptp,
2268c2ecf20Sopenharmony_ci			      struct ptp_clock_request __always_unused *request,
2278c2ecf20Sopenharmony_ci			      int __always_unused on)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic const struct ptp_clock_info mlx4_en_ptp_clock_info = {
2338c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2348c2ecf20Sopenharmony_ci	.max_adj	= 100000000,
2358c2ecf20Sopenharmony_ci	.n_alarm	= 0,
2368c2ecf20Sopenharmony_ci	.n_ext_ts	= 0,
2378c2ecf20Sopenharmony_ci	.n_per_out	= 0,
2388c2ecf20Sopenharmony_ci	.n_pins		= 0,
2398c2ecf20Sopenharmony_ci	.pps		= 0,
2408c2ecf20Sopenharmony_ci	.adjfreq	= mlx4_en_phc_adjfreq,
2418c2ecf20Sopenharmony_ci	.adjtime	= mlx4_en_phc_adjtime,
2428c2ecf20Sopenharmony_ci	.gettime64	= mlx4_en_phc_gettime,
2438c2ecf20Sopenharmony_ci	.settime64	= mlx4_en_phc_settime,
2448c2ecf20Sopenharmony_ci	.enable		= mlx4_en_phc_enable,
2458c2ecf20Sopenharmony_ci};
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci/* This function calculates the max shift that enables the user range
2498c2ecf20Sopenharmony_ci * of MLX4_EN_WRAP_AROUND_SEC values in the cycles register.
2508c2ecf20Sopenharmony_ci */
2518c2ecf20Sopenharmony_cistatic u32 freq_to_shift(u16 freq)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	u32 freq_khz = freq * 1000;
2548c2ecf20Sopenharmony_ci	u64 max_val_cycles = freq_khz * 1000 * MLX4_EN_WRAP_AROUND_SEC;
2558c2ecf20Sopenharmony_ci	u64 max_val_cycles_rounded = 1ULL << fls64(max_val_cycles - 1);
2568c2ecf20Sopenharmony_ci	/* calculate max possible multiplier in order to fit in 64bit */
2578c2ecf20Sopenharmony_ci	u64 max_mul = div64_u64(ULLONG_MAX, max_val_cycles_rounded);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/* This comes from the reverse of clocksource_khz2mult */
2608c2ecf20Sopenharmony_ci	return ilog2(div_u64(max_mul * freq_khz, 1000000));
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_civoid mlx4_en_init_timestamp(struct mlx4_en_dev *mdev)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct mlx4_dev *dev = mdev->dev;
2668c2ecf20Sopenharmony_ci	unsigned long flags;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/* mlx4_en_init_timestamp is called for each netdev.
2698c2ecf20Sopenharmony_ci	 * mdev->ptp_clock is common for all ports, skip initialization if
2708c2ecf20Sopenharmony_ci	 * was done for other port.
2718c2ecf20Sopenharmony_ci	 */
2728c2ecf20Sopenharmony_ci	if (mdev->ptp_clock)
2738c2ecf20Sopenharmony_ci		return;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	seqlock_init(&mdev->clock_lock);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	memset(&mdev->cycles, 0, sizeof(mdev->cycles));
2788c2ecf20Sopenharmony_ci	mdev->cycles.read = mlx4_en_read_clock;
2798c2ecf20Sopenharmony_ci	mdev->cycles.mask = CLOCKSOURCE_MASK(48);
2808c2ecf20Sopenharmony_ci	mdev->cycles.shift = freq_to_shift(dev->caps.hca_core_clock);
2818c2ecf20Sopenharmony_ci	mdev->cycles.mult =
2828c2ecf20Sopenharmony_ci		clocksource_khz2mult(1000 * dev->caps.hca_core_clock, mdev->cycles.shift);
2838c2ecf20Sopenharmony_ci	mdev->nominal_c_mult = mdev->cycles.mult;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	write_seqlock_irqsave(&mdev->clock_lock, flags);
2868c2ecf20Sopenharmony_ci	timecounter_init(&mdev->clock, &mdev->cycles,
2878c2ecf20Sopenharmony_ci			 ktime_to_ns(ktime_get_real()));
2888c2ecf20Sopenharmony_ci	write_sequnlock_irqrestore(&mdev->clock_lock, flags);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* Configure the PHC */
2918c2ecf20Sopenharmony_ci	mdev->ptp_clock_info = mlx4_en_ptp_clock_info;
2928c2ecf20Sopenharmony_ci	snprintf(mdev->ptp_clock_info.name, 16, "mlx4 ptp");
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	mdev->ptp_clock = ptp_clock_register(&mdev->ptp_clock_info,
2958c2ecf20Sopenharmony_ci					     &mdev->pdev->dev);
2968c2ecf20Sopenharmony_ci	if (IS_ERR(mdev->ptp_clock)) {
2978c2ecf20Sopenharmony_ci		mdev->ptp_clock = NULL;
2988c2ecf20Sopenharmony_ci		mlx4_err(mdev, "ptp_clock_register failed\n");
2998c2ecf20Sopenharmony_ci	} else if (mdev->ptp_clock) {
3008c2ecf20Sopenharmony_ci		mlx4_info(mdev, "registered PHC clock\n");
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci}
304