1ae904beaSFeras Daoud /*
2ae904beaSFeras Daoud  * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
3ae904beaSFeras Daoud  *
4ae904beaSFeras Daoud  * This software is available to you under a choice of one of two
5ae904beaSFeras Daoud  * licenses.  You may choose to be licensed under the terms of the GNU
6ae904beaSFeras Daoud  * General Public License (GPL) Version 2, available from the file
7ae904beaSFeras Daoud  * COPYING in the main directory of this source tree, or the
8ae904beaSFeras Daoud  * OpenIB.org BSD license below:
9ae904beaSFeras Daoud  *
10ae904beaSFeras Daoud  *     Redistribution and use in source and binary forms, with or
11ae904beaSFeras Daoud  *     without modification, are permitted provided that the following
12ae904beaSFeras Daoud  *     conditions are met:
13ae904beaSFeras Daoud  *
14ae904beaSFeras Daoud  *      - Redistributions of source code must retain the above
15ae904beaSFeras Daoud  *        copyright notice, this list of conditions and the following
16ae904beaSFeras Daoud  *        disclaimer.
17ae904beaSFeras Daoud  *
18ae904beaSFeras Daoud  *      - Redistributions in binary form must reproduce the above
19ae904beaSFeras Daoud  *        copyright notice, this list of conditions and the following
20ae904beaSFeras Daoud  *        disclaimer in the documentation and/or other materials
21ae904beaSFeras Daoud  *        provided with the distribution.
22ae904beaSFeras Daoud  *
23ae904beaSFeras Daoud  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24ae904beaSFeras Daoud  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25ae904beaSFeras Daoud  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26ae904beaSFeras Daoud  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27ae904beaSFeras Daoud  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28ae904beaSFeras Daoud  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29ae904beaSFeras Daoud  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30ae904beaSFeras Daoud  * SOFTWARE.
31ae904beaSFeras Daoud  */
32ae904beaSFeras Daoud 
33ae904beaSFeras Daoud #include <linux/clocksource.h>
3424d33d2cSFeras Daoud #include <linux/highmem.h>
3584a58e60SRahul Rameshbabu #include <linux/log2.h>
3690bf1c8dSEran Ben Elisha #include <linux/ptp_clock_kernel.h>
3724d33d2cSFeras Daoud #include <rdma/mlx5-abi.h>
3841069256SSaeed Mahameed #include "lib/eq.h"
39ae904beaSFeras Daoud #include "en.h"
409afe9a53SOr Gerlitz #include "clock.h"
41ae904beaSFeras Daoud 
42ae904beaSFeras Daoud enum {
437c39afb3SFeras Daoud 	MLX5_PIN_MODE_IN		= 0x0,
447c39afb3SFeras Daoud 	MLX5_PIN_MODE_OUT		= 0x1,
45ae904beaSFeras Daoud };
46ae904beaSFeras Daoud 
47ae904beaSFeras Daoud enum {
487c39afb3SFeras Daoud 	MLX5_OUT_PATTERN_PULSE		= 0x0,
497c39afb3SFeras Daoud 	MLX5_OUT_PATTERN_PERIODIC	= 0x1,
50ae904beaSFeras Daoud };
51ae904beaSFeras Daoud 
52ae904beaSFeras Daoud enum {
537c39afb3SFeras Daoud 	MLX5_EVENT_MODE_DISABLE	= 0x0,
547c39afb3SFeras Daoud 	MLX5_EVENT_MODE_REPETETIVE	= 0x1,
557c39afb3SFeras Daoud 	MLX5_EVENT_MODE_ONCE_TILL_ARM	= 0x2,
56ae904beaSFeras Daoud };
57ae904beaSFeras Daoud 
58ae904beaSFeras Daoud enum {
597c39afb3SFeras Daoud 	MLX5_MTPPS_FS_ENABLE			= BIT(0x0),
607c39afb3SFeras Daoud 	MLX5_MTPPS_FS_PATTERN			= BIT(0x2),
617c39afb3SFeras Daoud 	MLX5_MTPPS_FS_PIN_MODE			= BIT(0x3),
627c39afb3SFeras Daoud 	MLX5_MTPPS_FS_TIME_STAMP		= BIT(0x4),
637c39afb3SFeras Daoud 	MLX5_MTPPS_FS_OUT_PULSE_DURATION	= BIT(0x5),
647c39afb3SFeras Daoud 	MLX5_MTPPS_FS_ENH_OUT_PER_ADJ		= BIT(0x7),
65f0462bc3SAya Levin 	MLX5_MTPPS_FS_NPPS_PERIOD               = BIT(0x9),
66f0462bc3SAya Levin 	MLX5_MTPPS_FS_OUT_PULSE_DURATION_NS     = BIT(0xa),
67ae904beaSFeras Daoud };
68ae904beaSFeras Daoud 
69d3c8a33aSRahul Rameshbabu enum {
70d3c8a33aSRahul Rameshbabu 	MLX5_MTUTC_OPERATION_ADJUST_TIME_MIN          = S16_MIN,
71d3c8a33aSRahul Rameshbabu 	MLX5_MTUTC_OPERATION_ADJUST_TIME_MAX          = S16_MAX,
72d3c8a33aSRahul Rameshbabu 	MLX5_MTUTC_OPERATION_ADJUST_TIME_EXTENDED_MIN = -200000,
73d3c8a33aSRahul Rameshbabu 	MLX5_MTUTC_OPERATION_ADJUST_TIME_EXTENDED_MAX = 200000,
74d3c8a33aSRahul Rameshbabu };
75d3c8a33aSRahul Rameshbabu 
mlx5_real_time_mode(struct mlx5_core_dev * mdev)76432119deSAya Levin static bool mlx5_real_time_mode(struct mlx5_core_dev *mdev)
77432119deSAya Levin {
78432119deSAya Levin 	return (mlx5_is_real_time_rq(mdev) || mlx5_is_real_time_sq(mdev));
79432119deSAya Levin }
80432119deSAya Levin 
mlx5_npps_real_time_supported(struct mlx5_core_dev * mdev)81f0462bc3SAya Levin static bool mlx5_npps_real_time_supported(struct mlx5_core_dev *mdev)
82f0462bc3SAya Levin {
83f0462bc3SAya Levin 	return (mlx5_real_time_mode(mdev) &&
84f0462bc3SAya Levin 		MLX5_CAP_MCAM_FEATURE(mdev, npps_period) &&
85f0462bc3SAya Levin 		MLX5_CAP_MCAM_FEATURE(mdev, out_pulse_duration_ns));
86f0462bc3SAya Levin }
87f0462bc3SAya Levin 
mlx5_modify_mtutc_allowed(struct mlx5_core_dev * mdev)88432119deSAya Levin static bool mlx5_modify_mtutc_allowed(struct mlx5_core_dev *mdev)
89432119deSAya Levin {
90432119deSAya Levin 	return MLX5_CAP_MCAM_FEATURE(mdev, ptpcyc2realtime_modify);
91432119deSAya Levin }
92432119deSAya Levin 
mlx5_ptp_shift_constant(u32 dev_freq_khz)9384a58e60SRahul Rameshbabu static u32 mlx5_ptp_shift_constant(u32 dev_freq_khz)
9484a58e60SRahul Rameshbabu {
9584a58e60SRahul Rameshbabu 	/* Optimal shift constant leads to corrections above just 1 scaled ppm.
9684a58e60SRahul Rameshbabu 	 *
9784a58e60SRahul Rameshbabu 	 * Two sets of equations are needed to derive the optimal shift
9884a58e60SRahul Rameshbabu 	 * constant for the cyclecounter.
9984a58e60SRahul Rameshbabu 	 *
10084a58e60SRahul Rameshbabu 	 *    dev_freq_khz * 1000 / 2^shift_constant = 1 scaled_ppm
10184a58e60SRahul Rameshbabu 	 *    ppb = scaled_ppm * 1000 / 2^16
10284a58e60SRahul Rameshbabu 	 *
10384a58e60SRahul Rameshbabu 	 * Using the two equations together
10484a58e60SRahul Rameshbabu 	 *
10584a58e60SRahul Rameshbabu 	 *    dev_freq_khz * 1000 / 1 scaled_ppm = 2^shift_constant
10684a58e60SRahul Rameshbabu 	 *    dev_freq_khz * 2^16 / 1 ppb = 2^shift_constant
10784a58e60SRahul Rameshbabu 	 *    dev_freq_khz = 2^(shift_constant - 16)
10884a58e60SRahul Rameshbabu 	 *
10984a58e60SRahul Rameshbabu 	 * then yields
11084a58e60SRahul Rameshbabu 	 *
11184a58e60SRahul Rameshbabu 	 *    shift_constant = ilog2(dev_freq_khz) + 16
11284a58e60SRahul Rameshbabu 	 */
11384a58e60SRahul Rameshbabu 
11484a58e60SRahul Rameshbabu 	return min(ilog2(dev_freq_khz) + 16,
11584a58e60SRahul Rameshbabu 		   ilog2((U32_MAX / NSEC_PER_MSEC) * dev_freq_khz));
11684a58e60SRahul Rameshbabu }
11784a58e60SRahul Rameshbabu 
mlx5_ptp_getmaxphase(struct ptp_clock_info * ptp)11867ac72a5SRahul Rameshbabu static s32 mlx5_ptp_getmaxphase(struct ptp_clock_info *ptp)
119d3c8a33aSRahul Rameshbabu {
12067ac72a5SRahul Rameshbabu 	struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info);
12167ac72a5SRahul Rameshbabu 	struct mlx5_core_dev *mdev;
122d3c8a33aSRahul Rameshbabu 
12367ac72a5SRahul Rameshbabu 	mdev = container_of(clock, struct mlx5_core_dev, clock);
12467ac72a5SRahul Rameshbabu 
12567ac72a5SRahul Rameshbabu 	return MLX5_CAP_MCAM_FEATURE(mdev, mtutc_time_adjustment_extended_range) ?
12667ac72a5SRahul Rameshbabu 		       MLX5_MTUTC_OPERATION_ADJUST_TIME_EXTENDED_MAX :
12767ac72a5SRahul Rameshbabu 			     MLX5_MTUTC_OPERATION_ADJUST_TIME_MAX;
128d3c8a33aSRahul Rameshbabu }
129d3c8a33aSRahul Rameshbabu 
mlx5_is_mtutc_time_adj_cap(struct mlx5_core_dev * mdev,s64 delta)13067ac72a5SRahul Rameshbabu static bool mlx5_is_mtutc_time_adj_cap(struct mlx5_core_dev *mdev, s64 delta)
13167ac72a5SRahul Rameshbabu {
13267ac72a5SRahul Rameshbabu 	s64 max = mlx5_ptp_getmaxphase(&mdev->clock.ptp_info);
13367ac72a5SRahul Rameshbabu 
13467ac72a5SRahul Rameshbabu 	if (delta < -max || delta > max)
135d3c8a33aSRahul Rameshbabu 		return false;
136d3c8a33aSRahul Rameshbabu 
137d3c8a33aSRahul Rameshbabu 	return true;
138d3c8a33aSRahul Rameshbabu }
139d3c8a33aSRahul Rameshbabu 
mlx5_set_mtutc(struct mlx5_core_dev * dev,u32 * mtutc,u32 size)140432119deSAya Levin static int mlx5_set_mtutc(struct mlx5_core_dev *dev, u32 *mtutc, u32 size)
141432119deSAya Levin {
142432119deSAya Levin 	u32 out[MLX5_ST_SZ_DW(mtutc_reg)] = {};
143432119deSAya Levin 
144432119deSAya Levin 	if (!MLX5_CAP_MCAM_REG(dev, mtutc))
145432119deSAya Levin 		return -EOPNOTSUPP;
146432119deSAya Levin 
147432119deSAya Levin 	return mlx5_core_access_reg(dev, mtutc, size, out, sizeof(out),
148432119deSAya Levin 				    MLX5_REG_MTUTC, 0, 1);
149432119deSAya Levin }
150432119deSAya Levin 
mlx5_read_time(struct mlx5_core_dev * dev,struct ptp_system_timestamp * sts,bool real_time)151432119deSAya Levin static u64 mlx5_read_time(struct mlx5_core_dev *dev,
152432119deSAya Levin 			  struct ptp_system_timestamp *sts,
153432119deSAya Levin 			  bool real_time)
15490bf1c8dSEran Ben Elisha {
15590bf1c8dSEran Ben Elisha 	u32 timer_h, timer_h1, timer_l;
15690bf1c8dSEran Ben Elisha 
157432119deSAya Levin 	timer_h = ioread32be(real_time ? &dev->iseg->real_time_h :
158432119deSAya Levin 			     &dev->iseg->internal_timer_h);
15990bf1c8dSEran Ben Elisha 	ptp_read_system_prets(sts);
160432119deSAya Levin 	timer_l = ioread32be(real_time ? &dev->iseg->real_time_l :
161432119deSAya Levin 			     &dev->iseg->internal_timer_l);
16290bf1c8dSEran Ben Elisha 	ptp_read_system_postts(sts);
163432119deSAya Levin 	timer_h1 = ioread32be(real_time ? &dev->iseg->real_time_h :
164432119deSAya Levin 			      &dev->iseg->internal_timer_h);
16590bf1c8dSEran Ben Elisha 	if (timer_h != timer_h1) {
16690bf1c8dSEran Ben Elisha 		/* wrap around */
16790bf1c8dSEran Ben Elisha 		ptp_read_system_prets(sts);
168432119deSAya Levin 		timer_l = ioread32be(real_time ? &dev->iseg->real_time_l :
169432119deSAya Levin 				     &dev->iseg->internal_timer_l);
17090bf1c8dSEran Ben Elisha 		ptp_read_system_postts(sts);
17190bf1c8dSEran Ben Elisha 	}
17290bf1c8dSEran Ben Elisha 
173432119deSAya Levin 	return real_time ? REAL_TIME_TO_NS(timer_h1, timer_l) :
174432119deSAya Levin 			   (u64)timer_l | (u64)timer_h1 << 32;
17590bf1c8dSEran Ben Elisha }
17690bf1c8dSEran Ben Elisha 
read_internal_timer(const struct cyclecounter * cc)1777c39afb3SFeras Daoud static u64 read_internal_timer(const struct cyclecounter *cc)
178ae904beaSFeras Daoud {
179d6f3dc8fSEran Ben Elisha 	struct mlx5_timer *timer = container_of(cc, struct mlx5_timer, cycles);
180d6f3dc8fSEran Ben Elisha 	struct mlx5_clock *clock = container_of(timer, struct mlx5_clock, timer);
1817c39afb3SFeras Daoud 	struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev,
1827c39afb3SFeras Daoud 						  clock);
183ae904beaSFeras Daoud 
184432119deSAya Levin 	return mlx5_read_time(mdev, NULL, false) & cc->mask;
185ae904beaSFeras Daoud }
186ae904beaSFeras Daoud 
mlx5_update_clock_info_page(struct mlx5_core_dev * mdev)18724d33d2cSFeras Daoud static void mlx5_update_clock_info_page(struct mlx5_core_dev *mdev)
18824d33d2cSFeras Daoud {
18924d33d2cSFeras Daoud 	struct mlx5_ib_clock_info *clock_info = mdev->clock_info;
19024d33d2cSFeras Daoud 	struct mlx5_clock *clock = &mdev->clock;
191d6f3dc8fSEran Ben Elisha 	struct mlx5_timer *timer;
19224d33d2cSFeras Daoud 	u32 sign;
19324d33d2cSFeras Daoud 
19424d33d2cSFeras Daoud 	if (!clock_info)
19524d33d2cSFeras Daoud 		return;
19624d33d2cSFeras Daoud 
19724d33d2cSFeras Daoud 	sign = smp_load_acquire(&clock_info->sign);
19824d33d2cSFeras Daoud 	smp_store_mb(clock_info->sign,
19924d33d2cSFeras Daoud 		     sign | MLX5_IB_CLOCK_INFO_KERNEL_UPDATING);
20024d33d2cSFeras Daoud 
201d6f3dc8fSEran Ben Elisha 	timer = &clock->timer;
202d6f3dc8fSEran Ben Elisha 	clock_info->cycles = timer->tc.cycle_last;
203d6f3dc8fSEran Ben Elisha 	clock_info->mult   = timer->cycles.mult;
204d6f3dc8fSEran Ben Elisha 	clock_info->nsec   = timer->tc.nsec;
205d6f3dc8fSEran Ben Elisha 	clock_info->frac   = timer->tc.frac;
20624d33d2cSFeras Daoud 
20724d33d2cSFeras Daoud 	smp_store_release(&clock_info->sign,
20824d33d2cSFeras Daoud 			  sign + MLX5_IB_CLOCK_INFO_KERNEL_UPDATING * 2);
20924d33d2cSFeras Daoud }
21024d33d2cSFeras Daoud 
mlx5_pps_out(struct work_struct * work)2117c39afb3SFeras Daoud static void mlx5_pps_out(struct work_struct *work)
212ae904beaSFeras Daoud {
2137c39afb3SFeras Daoud 	struct mlx5_pps *pps_info = container_of(work, struct mlx5_pps,
214ae904beaSFeras Daoud 						 out_work);
2157c39afb3SFeras Daoud 	struct mlx5_clock *clock = container_of(pps_info, struct mlx5_clock,
216ae904beaSFeras Daoud 						pps_info);
2177c39afb3SFeras Daoud 	struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev,
2187c39afb3SFeras Daoud 						  clock);
219ae904beaSFeras Daoud 	u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0};
220ae904beaSFeras Daoud 	unsigned long flags;
221ae904beaSFeras Daoud 	int i;
222ae904beaSFeras Daoud 
2237c39afb3SFeras Daoud 	for (i = 0; i < clock->ptp_info.n_pins; i++) {
224ae904beaSFeras Daoud 		u64 tstart;
225ae904beaSFeras Daoud 
22664109f1dSShay Agroskin 		write_seqlock_irqsave(&clock->lock, flags);
2277c39afb3SFeras Daoud 		tstart = clock->pps_info.start[i];
2287c39afb3SFeras Daoud 		clock->pps_info.start[i] = 0;
22964109f1dSShay Agroskin 		write_sequnlock_irqrestore(&clock->lock, flags);
230ae904beaSFeras Daoud 		if (!tstart)
231ae904beaSFeras Daoud 			continue;
232ae904beaSFeras Daoud 
233ae904beaSFeras Daoud 		MLX5_SET(mtpps_reg, in, pin, i);
234ae904beaSFeras Daoud 		MLX5_SET64(mtpps_reg, in, time_stamp, tstart);
2357c39afb3SFeras Daoud 		MLX5_SET(mtpps_reg, in, field_select, MLX5_MTPPS_FS_TIME_STAMP);
2367c39afb3SFeras Daoud 		mlx5_set_mtpps(mdev, in, sizeof(in));
237ae904beaSFeras Daoud 	}
238ae904beaSFeras Daoud }
239ae904beaSFeras Daoud 
mlx5_timestamp_overflow(struct work_struct * work)2407c39afb3SFeras Daoud static void mlx5_timestamp_overflow(struct work_struct *work)
241ae904beaSFeras Daoud {
242ae904beaSFeras Daoud 	struct delayed_work *dwork = to_delayed_work(work);
243fb609b51SEran Ben Elisha 	struct mlx5_core_dev *mdev;
244d6f3dc8fSEran Ben Elisha 	struct mlx5_timer *timer;
245fb609b51SEran Ben Elisha 	struct mlx5_clock *clock;
246ae904beaSFeras Daoud 	unsigned long flags;
247ae904beaSFeras Daoud 
248d6f3dc8fSEran Ben Elisha 	timer = container_of(dwork, struct mlx5_timer, overflow_work);
249d6f3dc8fSEran Ben Elisha 	clock = container_of(timer, struct mlx5_clock, timer);
250fb609b51SEran Ben Elisha 	mdev = container_of(clock, struct mlx5_core_dev, clock);
251d6f3dc8fSEran Ben Elisha 
252d0062076SMoshe Shemesh 	if (mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR)
253d0062076SMoshe Shemesh 		goto out;
254d0062076SMoshe Shemesh 
25564109f1dSShay Agroskin 	write_seqlock_irqsave(&clock->lock, flags);
256d6f3dc8fSEran Ben Elisha 	timecounter_read(&timer->tc);
257fb609b51SEran Ben Elisha 	mlx5_update_clock_info_page(mdev);
25864109f1dSShay Agroskin 	write_sequnlock_irqrestore(&clock->lock, flags);
259d0062076SMoshe Shemesh 
260d0062076SMoshe Shemesh out:
261d6f3dc8fSEran Ben Elisha 	schedule_delayed_work(&timer->overflow_work, timer->overflow_period);
262ae904beaSFeras Daoud }
263ae904beaSFeras Daoud 
mlx5_ptp_settime_real_time(struct mlx5_core_dev * mdev,const struct timespec64 * ts)264432119deSAya Levin static int mlx5_ptp_settime_real_time(struct mlx5_core_dev *mdev,
265432119deSAya Levin 				      const struct timespec64 *ts)
266432119deSAya Levin {
267432119deSAya Levin 	u32 in[MLX5_ST_SZ_DW(mtutc_reg)] = {};
268432119deSAya Levin 
269432119deSAya Levin 	if (!mlx5_modify_mtutc_allowed(mdev))
270432119deSAya Levin 		return 0;
271432119deSAya Levin 
272432119deSAya Levin 	if (ts->tv_sec < 0 || ts->tv_sec > U32_MAX ||
273432119deSAya Levin 	    ts->tv_nsec < 0 || ts->tv_nsec > NSEC_PER_SEC)
274432119deSAya Levin 		return -EINVAL;
275432119deSAya Levin 
276432119deSAya Levin 	MLX5_SET(mtutc_reg, in, operation, MLX5_MTUTC_OPERATION_SET_TIME_IMMEDIATE);
277432119deSAya Levin 	MLX5_SET(mtutc_reg, in, utc_sec, ts->tv_sec);
278432119deSAya Levin 	MLX5_SET(mtutc_reg, in, utc_nsec, ts->tv_nsec);
279432119deSAya Levin 
280432119deSAya Levin 	return mlx5_set_mtutc(mdev, in, sizeof(in));
281432119deSAya Levin }
282432119deSAya Levin 
mlx5_ptp_settime(struct ptp_clock_info * ptp,const struct timespec64 * ts)283fb609b51SEran Ben Elisha static int mlx5_ptp_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts)
284ae904beaSFeras Daoud {
285fb609b51SEran Ben Elisha 	struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info);
286d6f3dc8fSEran Ben Elisha 	struct mlx5_timer *timer = &clock->timer;
287fb609b51SEran Ben Elisha 	struct mlx5_core_dev *mdev;
288ae904beaSFeras Daoud 	unsigned long flags;
289432119deSAya Levin 	int err;
290ae904beaSFeras Daoud 
291fb609b51SEran Ben Elisha 	mdev = container_of(clock, struct mlx5_core_dev, clock);
292432119deSAya Levin 	err = mlx5_ptp_settime_real_time(mdev, ts);
293432119deSAya Levin 	if (err)
294432119deSAya Levin 		return err;
295432119deSAya Levin 
29664109f1dSShay Agroskin 	write_seqlock_irqsave(&clock->lock, flags);
297432119deSAya Levin 	timecounter_init(&timer->tc, &timer->cycles, timespec64_to_ns(ts));
298fb609b51SEran Ben Elisha 	mlx5_update_clock_info_page(mdev);
29964109f1dSShay Agroskin 	write_sequnlock_irqrestore(&clock->lock, flags);
300ae904beaSFeras Daoud 
301ae904beaSFeras Daoud 	return 0;
302ae904beaSFeras Daoud }
303ae904beaSFeras Daoud 
304432119deSAya Levin static
mlx5_ptp_gettimex_real_time(struct mlx5_core_dev * mdev,struct ptp_system_timestamp * sts)305432119deSAya Levin struct timespec64 mlx5_ptp_gettimex_real_time(struct mlx5_core_dev *mdev,
306432119deSAya Levin 					      struct ptp_system_timestamp *sts)
307432119deSAya Levin {
308432119deSAya Levin 	struct timespec64 ts;
309432119deSAya Levin 	u64 time;
310432119deSAya Levin 
311432119deSAya Levin 	time = mlx5_read_time(mdev, sts, true);
312432119deSAya Levin 	ts = ns_to_timespec64(time);
313432119deSAya Levin 	return ts;
314432119deSAya Levin }
315432119deSAya Levin 
mlx5_ptp_gettimex(struct ptp_clock_info * ptp,struct timespec64 * ts,struct ptp_system_timestamp * sts)3164a0475d5SMiroslav Lichvar static int mlx5_ptp_gettimex(struct ptp_clock_info *ptp, struct timespec64 *ts,
3174a0475d5SMiroslav Lichvar 			     struct ptp_system_timestamp *sts)
318ae904beaSFeras Daoud {
319fb609b51SEran Ben Elisha 	struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info);
320d6f3dc8fSEran Ben Elisha 	struct mlx5_timer *timer = &clock->timer;
321fb609b51SEran Ben Elisha 	struct mlx5_core_dev *mdev;
322ae904beaSFeras Daoud 	unsigned long flags;
3234a0475d5SMiroslav Lichvar 	u64 cycles, ns;
324ae904beaSFeras Daoud 
325fb609b51SEran Ben Elisha 	mdev = container_of(clock, struct mlx5_core_dev, clock);
326432119deSAya Levin 	if (mlx5_real_time_mode(mdev)) {
327432119deSAya Levin 		*ts = mlx5_ptp_gettimex_real_time(mdev, sts);
328432119deSAya Levin 		goto out;
329432119deSAya Levin 	}
330432119deSAya Levin 
33164109f1dSShay Agroskin 	write_seqlock_irqsave(&clock->lock, flags);
332432119deSAya Levin 	cycles = mlx5_read_time(mdev, sts, false);
333d6f3dc8fSEran Ben Elisha 	ns = timecounter_cyc2time(&timer->tc, cycles);
33464109f1dSShay Agroskin 	write_sequnlock_irqrestore(&clock->lock, flags);
335ae904beaSFeras Daoud 	*ts = ns_to_timespec64(ns);
336432119deSAya Levin out:
337ae904beaSFeras Daoud 	return 0;
338ae904beaSFeras Daoud }
339ae904beaSFeras Daoud 
mlx5_ptp_adjtime_real_time(struct mlx5_core_dev * mdev,s64 delta)340432119deSAya Levin static int mlx5_ptp_adjtime_real_time(struct mlx5_core_dev *mdev, s64 delta)
341432119deSAya Levin {
342432119deSAya Levin 	u32 in[MLX5_ST_SZ_DW(mtutc_reg)] = {};
343432119deSAya Levin 
344432119deSAya Levin 	if (!mlx5_modify_mtutc_allowed(mdev))
345432119deSAya Levin 		return 0;
346432119deSAya Levin 
347d3c8a33aSRahul Rameshbabu 	/* HW time adjustment range is checked. If out of range, settime instead */
348d3c8a33aSRahul Rameshbabu 	if (!mlx5_is_mtutc_time_adj_cap(mdev, delta)) {
349432119deSAya Levin 		struct timespec64 ts;
350432119deSAya Levin 		s64 ns;
351432119deSAya Levin 
352432119deSAya Levin 		ts = mlx5_ptp_gettimex_real_time(mdev, NULL);
353432119deSAya Levin 		ns = timespec64_to_ns(&ts) + delta;
354432119deSAya Levin 		ts = ns_to_timespec64(ns);
355432119deSAya Levin 		return mlx5_ptp_settime_real_time(mdev, &ts);
356432119deSAya Levin 	}
357432119deSAya Levin 
358432119deSAya Levin 	MLX5_SET(mtutc_reg, in, operation, MLX5_MTUTC_OPERATION_ADJUST_TIME);
359432119deSAya Levin 	MLX5_SET(mtutc_reg, in, time_adjustment, delta);
360432119deSAya Levin 
361432119deSAya Levin 	return mlx5_set_mtutc(mdev, in, sizeof(in));
362432119deSAya Levin }
363432119deSAya Levin 
mlx5_ptp_adjtime(struct ptp_clock_info * ptp,s64 delta)3647c39afb3SFeras Daoud static int mlx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
365ae904beaSFeras Daoud {
366fb609b51SEran Ben Elisha 	struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info);
367d6f3dc8fSEran Ben Elisha 	struct mlx5_timer *timer = &clock->timer;
368fb609b51SEran Ben Elisha 	struct mlx5_core_dev *mdev;
369ae904beaSFeras Daoud 	unsigned long flags;
370432119deSAya Levin 	int err;
371ae904beaSFeras Daoud 
372fb609b51SEran Ben Elisha 	mdev = container_of(clock, struct mlx5_core_dev, clock);
373432119deSAya Levin 
374432119deSAya Levin 	err = mlx5_ptp_adjtime_real_time(mdev, delta);
375432119deSAya Levin 	if (err)
376432119deSAya Levin 		return err;
37764109f1dSShay Agroskin 	write_seqlock_irqsave(&clock->lock, flags);
378d6f3dc8fSEran Ben Elisha 	timecounter_adjtime(&timer->tc, delta);
379fb609b51SEran Ben Elisha 	mlx5_update_clock_info_page(mdev);
38064109f1dSShay Agroskin 	write_sequnlock_irqrestore(&clock->lock, flags);
381ae904beaSFeras Daoud 
382ae904beaSFeras Daoud 	return 0;
383ae904beaSFeras Daoud }
384ae904beaSFeras Daoud 
mlx5_ptp_adjphase(struct ptp_clock_info * ptp,s32 delta)3858e11a68eSRahul Rameshbabu static int mlx5_ptp_adjphase(struct ptp_clock_info *ptp, s32 delta)
3868e11a68eSRahul Rameshbabu {
387*2226665bSRahul Rameshbabu 	struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info);
388*2226665bSRahul Rameshbabu 	struct mlx5_core_dev *mdev;
389*2226665bSRahul Rameshbabu 
390*2226665bSRahul Rameshbabu 	mdev = container_of(clock, struct mlx5_core_dev, clock);
391*2226665bSRahul Rameshbabu 
392*2226665bSRahul Rameshbabu 	return mlx5_ptp_adjtime_real_time(mdev, delta);
3938e11a68eSRahul Rameshbabu }
3948e11a68eSRahul Rameshbabu 
mlx5_ptp_freq_adj_real_time(struct mlx5_core_dev * mdev,long scaled_ppm)395b63636b6SRahul Rameshbabu static int mlx5_ptp_freq_adj_real_time(struct mlx5_core_dev *mdev, long scaled_ppm)
396432119deSAya Levin {
397432119deSAya Levin 	u32 in[MLX5_ST_SZ_DW(mtutc_reg)] = {};
398432119deSAya Levin 
399432119deSAya Levin 	if (!mlx5_modify_mtutc_allowed(mdev))
400432119deSAya Levin 		return 0;
401432119deSAya Levin 
402432119deSAya Levin 	MLX5_SET(mtutc_reg, in, operation, MLX5_MTUTC_OPERATION_ADJUST_FREQ_UTC);
403b63636b6SRahul Rameshbabu 
404b63636b6SRahul Rameshbabu 	if (MLX5_CAP_MCAM_FEATURE(mdev, mtutc_freq_adj_units)) {
405b63636b6SRahul Rameshbabu 		MLX5_SET(mtutc_reg, in, freq_adj_units,
406b63636b6SRahul Rameshbabu 			 MLX5_MTUTC_FREQ_ADJ_UNITS_SCALED_PPM);
407b63636b6SRahul Rameshbabu 		MLX5_SET(mtutc_reg, in, freq_adjustment, scaled_ppm);
408b63636b6SRahul Rameshbabu 	} else {
409b63636b6SRahul Rameshbabu 		MLX5_SET(mtutc_reg, in, freq_adj_units, MLX5_MTUTC_FREQ_ADJ_UNITS_PPB);
410b63636b6SRahul Rameshbabu 		MLX5_SET(mtutc_reg, in, freq_adjustment, scaled_ppm_to_ppb(scaled_ppm));
411b63636b6SRahul Rameshbabu 	}
412432119deSAya Levin 
413432119deSAya Levin 	return mlx5_set_mtutc(mdev, in, sizeof(in));
414432119deSAya Levin }
415432119deSAya Levin 
mlx5_ptp_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)416d8aad3f3SJacob Keller static int mlx5_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
417ae904beaSFeras Daoud {
418fb609b51SEran Ben Elisha 	struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info);
419d6f3dc8fSEran Ben Elisha 	struct mlx5_timer *timer = &clock->timer;
420fb609b51SEran Ben Elisha 	struct mlx5_core_dev *mdev;
421ae904beaSFeras Daoud 	unsigned long flags;
422d8aad3f3SJacob Keller 	u32 mult;
423432119deSAya Levin 	int err;
424432119deSAya Levin 
425432119deSAya Levin 	mdev = container_of(clock, struct mlx5_core_dev, clock);
426b63636b6SRahul Rameshbabu 
427b63636b6SRahul Rameshbabu 	err = mlx5_ptp_freq_adj_real_time(mdev, scaled_ppm);
428432119deSAya Levin 	if (err)
429432119deSAya Levin 		return err;
430fb609b51SEran Ben Elisha 
431d8aad3f3SJacob Keller 	mult = (u32)adjust_by_scaled_ppm(timer->nominal_c_mult, scaled_ppm);
432ae904beaSFeras Daoud 
43364109f1dSShay Agroskin 	write_seqlock_irqsave(&clock->lock, flags);
434d6f3dc8fSEran Ben Elisha 	timecounter_read(&timer->tc);
435d8aad3f3SJacob Keller 	timer->cycles.mult = mult;
436fb609b51SEran Ben Elisha 	mlx5_update_clock_info_page(mdev);
43764109f1dSShay Agroskin 	write_sequnlock_irqrestore(&clock->lock, flags);
438ae904beaSFeras Daoud 
439ae904beaSFeras Daoud 	return 0;
440ae904beaSFeras Daoud }
441ae904beaSFeras Daoud 
mlx5_extts_configure(struct ptp_clock_info * ptp,struct ptp_clock_request * rq,int on)4427c39afb3SFeras Daoud static int mlx5_extts_configure(struct ptp_clock_info *ptp,
443ae904beaSFeras Daoud 				struct ptp_clock_request *rq,
444ae904beaSFeras Daoud 				int on)
445ae904beaSFeras Daoud {
4467c39afb3SFeras Daoud 	struct mlx5_clock *clock =
4477c39afb3SFeras Daoud 			container_of(ptp, struct mlx5_clock, ptp_info);
4487c39afb3SFeras Daoud 	struct mlx5_core_dev *mdev =
4497c39afb3SFeras Daoud 			container_of(clock, struct mlx5_core_dev, clock);
450ae904beaSFeras Daoud 	u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0};
451ae904beaSFeras Daoud 	u32 field_select = 0;
452ae904beaSFeras Daoud 	u8 pin_mode = 0;
453ae904beaSFeras Daoud 	u8 pattern = 0;
454ae904beaSFeras Daoud 	int pin = -1;
455ae904beaSFeras Daoud 	int err = 0;
456ae904beaSFeras Daoud 
4577c39afb3SFeras Daoud 	if (!MLX5_PPS_CAP(mdev))
458ae904beaSFeras Daoud 		return -EOPNOTSUPP;
459ae904beaSFeras Daoud 
4602e0645a0SJacob Keller 	/* Reject requests with unsupported flags */
4612e0645a0SJacob Keller 	if (rq->extts.flags & ~(PTP_ENABLE_FEATURE |
4622e0645a0SJacob Keller 				PTP_RISING_EDGE |
4636138e687SRichard Cochran 				PTP_FALLING_EDGE |
4646138e687SRichard Cochran 				PTP_STRICT_FLAGS))
4652e0645a0SJacob Keller 		return -EOPNOTSUPP;
4662e0645a0SJacob Keller 
467ca12cf5aSRichard Cochran 	/* Reject requests to enable time stamping on both edges. */
468ca12cf5aSRichard Cochran 	if ((rq->extts.flags & PTP_STRICT_FLAGS) &&
469ca12cf5aSRichard Cochran 	    (rq->extts.flags & PTP_ENABLE_FEATURE) &&
470ca12cf5aSRichard Cochran 	    (rq->extts.flags & PTP_EXTTS_EDGES) == PTP_EXTTS_EDGES)
471ca12cf5aSRichard Cochran 		return -EOPNOTSUPP;
472ca12cf5aSRichard Cochran 
4737c39afb3SFeras Daoud 	if (rq->extts.index >= clock->ptp_info.n_pins)
474ae904beaSFeras Daoud 		return -EINVAL;
475ae904beaSFeras Daoud 
4767c39afb3SFeras Daoud 	pin = ptp_find_pin(clock->ptp, PTP_PF_EXTTS, rq->extts.index);
477ae904beaSFeras Daoud 	if (pin < 0)
478ae904beaSFeras Daoud 		return -EBUSY;
47988c8cf92SEran Ben Elisha 
48088c8cf92SEran Ben Elisha 	if (on) {
4817c39afb3SFeras Daoud 		pin_mode = MLX5_PIN_MODE_IN;
482ae904beaSFeras Daoud 		pattern = !!(rq->extts.flags & PTP_FALLING_EDGE);
4837c39afb3SFeras Daoud 		field_select = MLX5_MTPPS_FS_PIN_MODE |
4847c39afb3SFeras Daoud 			       MLX5_MTPPS_FS_PATTERN |
4857c39afb3SFeras Daoud 			       MLX5_MTPPS_FS_ENABLE;
486ae904beaSFeras Daoud 	} else {
4877c39afb3SFeras Daoud 		field_select = MLX5_MTPPS_FS_ENABLE;
488ae904beaSFeras Daoud 	}
489ae904beaSFeras Daoud 
490ae904beaSFeras Daoud 	MLX5_SET(mtpps_reg, in, pin, pin);
491ae904beaSFeras Daoud 	MLX5_SET(mtpps_reg, in, pin_mode, pin_mode);
492ae904beaSFeras Daoud 	MLX5_SET(mtpps_reg, in, pattern, pattern);
493ae904beaSFeras Daoud 	MLX5_SET(mtpps_reg, in, enable, on);
494ae904beaSFeras Daoud 	MLX5_SET(mtpps_reg, in, field_select, field_select);
495ae904beaSFeras Daoud 
4967c39afb3SFeras Daoud 	err = mlx5_set_mtpps(mdev, in, sizeof(in));
497ae904beaSFeras Daoud 	if (err)
498ae904beaSFeras Daoud 		return err;
499ae904beaSFeras Daoud 
5007c39afb3SFeras Daoud 	return mlx5_set_mtppse(mdev, pin, 0,
5017c39afb3SFeras Daoud 			       MLX5_EVENT_MODE_REPETETIVE & on);
502ae904beaSFeras Daoud }
503ae904beaSFeras Daoud 
find_target_cycles(struct mlx5_core_dev * mdev,s64 target_ns)504de19cd6cSEran Ben Elisha static u64 find_target_cycles(struct mlx5_core_dev *mdev, s64 target_ns)
505de19cd6cSEran Ben Elisha {
506de19cd6cSEran Ben Elisha 	struct mlx5_clock *clock = &mdev->clock;
507de19cd6cSEran Ben Elisha 	u64 cycles_now, cycles_delta;
508de19cd6cSEran Ben Elisha 	u64 nsec_now, nsec_delta;
509de19cd6cSEran Ben Elisha 	struct mlx5_timer *timer;
510de19cd6cSEran Ben Elisha 	unsigned long flags;
511de19cd6cSEran Ben Elisha 
512de19cd6cSEran Ben Elisha 	timer = &clock->timer;
513de19cd6cSEran Ben Elisha 
514432119deSAya Levin 	cycles_now = mlx5_read_time(mdev, NULL, false);
515de19cd6cSEran Ben Elisha 	write_seqlock_irqsave(&clock->lock, flags);
516de19cd6cSEran Ben Elisha 	nsec_now = timecounter_cyc2time(&timer->tc, cycles_now);
517de19cd6cSEran Ben Elisha 	nsec_delta = target_ns - nsec_now;
518de19cd6cSEran Ben Elisha 	cycles_delta = div64_u64(nsec_delta << timer->cycles.shift,
519de19cd6cSEran Ben Elisha 				 timer->cycles.mult);
520de19cd6cSEran Ben Elisha 	write_sequnlock_irqrestore(&clock->lock, flags);
521de19cd6cSEran Ben Elisha 
522de19cd6cSEran Ben Elisha 	return cycles_now + cycles_delta;
523de19cd6cSEran Ben Elisha }
524de19cd6cSEran Ben Elisha 
perout_conf_internal_timer(struct mlx5_core_dev * mdev,s64 sec)52564728294SAya Levin static u64 perout_conf_internal_timer(struct mlx5_core_dev *mdev, s64 sec)
526de19cd6cSEran Ben Elisha {
52764728294SAya Levin 	struct timespec64 ts = {};
528de19cd6cSEran Ben Elisha 	s64 target_ns;
529de19cd6cSEran Ben Elisha 
530de19cd6cSEran Ben Elisha 	ts.tv_sec = sec;
531de19cd6cSEran Ben Elisha 	target_ns = timespec64_to_ns(&ts);
532de19cd6cSEran Ben Elisha 
533de19cd6cSEran Ben Elisha 	return find_target_cycles(mdev, target_ns);
534de19cd6cSEran Ben Elisha }
535de19cd6cSEran Ben Elisha 
perout_conf_real_time(s64 sec,u32 nsec)536f0462bc3SAya Levin static u64 perout_conf_real_time(s64 sec, u32 nsec)
537432119deSAya Levin {
538f0462bc3SAya Levin 	return (u64)nsec | (u64)sec << 32;
539f0462bc3SAya Levin }
540f0462bc3SAya Levin 
perout_conf_1pps(struct mlx5_core_dev * mdev,struct ptp_clock_request * rq,u64 * time_stamp,bool real_time)541f0462bc3SAya Levin static int perout_conf_1pps(struct mlx5_core_dev *mdev, struct ptp_clock_request *rq,
542f0462bc3SAya Levin 			    u64 *time_stamp, bool real_time)
543f0462bc3SAya Levin {
544f0462bc3SAya Levin 	struct timespec64 ts;
545f0462bc3SAya Levin 	s64 ns;
546f0462bc3SAya Levin 
547f0462bc3SAya Levin 	ts.tv_nsec = rq->perout.period.nsec;
548f0462bc3SAya Levin 	ts.tv_sec = rq->perout.period.sec;
549f0462bc3SAya Levin 	ns = timespec64_to_ns(&ts);
550f0462bc3SAya Levin 
551f0462bc3SAya Levin 	if ((ns >> 1) != 500000000LL)
552f0462bc3SAya Levin 		return -EINVAL;
553f0462bc3SAya Levin 
554f0462bc3SAya Levin 	*time_stamp = real_time ? perout_conf_real_time(rq->perout.start.sec, 0) :
555f0462bc3SAya Levin 		      perout_conf_internal_timer(mdev, rq->perout.start.sec);
556f0462bc3SAya Levin 
557f0462bc3SAya Levin 	return 0;
558f0462bc3SAya Levin }
559f0462bc3SAya Levin 
560f0462bc3SAya Levin #define MLX5_MAX_PULSE_DURATION (BIT(__mlx5_bit_sz(mtpps_reg, out_pulse_duration_ns)) - 1)
mlx5_perout_conf_out_pulse_duration(struct mlx5_core_dev * mdev,struct ptp_clock_request * rq,u32 * out_pulse_duration_ns)561f0462bc3SAya Levin static int mlx5_perout_conf_out_pulse_duration(struct mlx5_core_dev *mdev,
562f0462bc3SAya Levin 					       struct ptp_clock_request *rq,
563f0462bc3SAya Levin 					       u32 *out_pulse_duration_ns)
564f0462bc3SAya Levin {
565f0462bc3SAya Levin 	struct mlx5_pps *pps_info = &mdev->clock.pps_info;
566f0462bc3SAya Levin 	u32 out_pulse_duration;
567f0462bc3SAya Levin 	struct timespec64 ts;
568f0462bc3SAya Levin 
569f0462bc3SAya Levin 	if (rq->perout.flags & PTP_PEROUT_DUTY_CYCLE) {
570f0462bc3SAya Levin 		ts.tv_sec = rq->perout.on.sec;
571f0462bc3SAya Levin 		ts.tv_nsec = rq->perout.on.nsec;
572f0462bc3SAya Levin 		out_pulse_duration = (u32)timespec64_to_ns(&ts);
573f0462bc3SAya Levin 	} else {
574f0462bc3SAya Levin 		/* out_pulse_duration_ns should be up to 50% of the
575f0462bc3SAya Levin 		 * pulse period as default
576f0462bc3SAya Levin 		 */
577f0462bc3SAya Levin 		ts.tv_sec = rq->perout.period.sec;
578f0462bc3SAya Levin 		ts.tv_nsec = rq->perout.period.nsec;
579f0462bc3SAya Levin 		out_pulse_duration = (u32)timespec64_to_ns(&ts) >> 1;
580f0462bc3SAya Levin 	}
581f0462bc3SAya Levin 
582f0462bc3SAya Levin 	if (out_pulse_duration < pps_info->min_out_pulse_duration_ns ||
583f0462bc3SAya Levin 	    out_pulse_duration > MLX5_MAX_PULSE_DURATION) {
584f0462bc3SAya Levin 		mlx5_core_err(mdev, "NPPS pulse duration %u is not in [%llu, %lu]\n",
585f0462bc3SAya Levin 			      out_pulse_duration, pps_info->min_out_pulse_duration_ns,
586f0462bc3SAya Levin 			      MLX5_MAX_PULSE_DURATION);
587f0462bc3SAya Levin 		return -EINVAL;
588f0462bc3SAya Levin 	}
589f0462bc3SAya Levin 	*out_pulse_duration_ns = out_pulse_duration;
590f0462bc3SAya Levin 
591f0462bc3SAya Levin 	return 0;
592f0462bc3SAya Levin }
593f0462bc3SAya Levin 
perout_conf_npps_real_time(struct mlx5_core_dev * mdev,struct ptp_clock_request * rq,u32 * field_select,u32 * out_pulse_duration_ns,u64 * period,u64 * time_stamp)594f0462bc3SAya Levin static int perout_conf_npps_real_time(struct mlx5_core_dev *mdev, struct ptp_clock_request *rq,
595f0462bc3SAya Levin 				      u32 *field_select, u32 *out_pulse_duration_ns,
596f0462bc3SAya Levin 				      u64 *period, u64 *time_stamp)
597f0462bc3SAya Levin {
598f0462bc3SAya Levin 	struct mlx5_pps *pps_info = &mdev->clock.pps_info;
599f0462bc3SAya Levin 	struct ptp_clock_time *time = &rq->perout.start;
600f0462bc3SAya Levin 	struct timespec64 ts;
601f0462bc3SAya Levin 
602f0462bc3SAya Levin 	ts.tv_sec = rq->perout.period.sec;
603f0462bc3SAya Levin 	ts.tv_nsec = rq->perout.period.nsec;
604f0462bc3SAya Levin 	if (timespec64_to_ns(&ts) < pps_info->min_npps_period) {
605f0462bc3SAya Levin 		mlx5_core_err(mdev, "NPPS period is lower than minimal npps period %llu\n",
606f0462bc3SAya Levin 			      pps_info->min_npps_period);
607f0462bc3SAya Levin 		return -EINVAL;
608f0462bc3SAya Levin 	}
609f0462bc3SAya Levin 	*period = perout_conf_real_time(rq->perout.period.sec, rq->perout.period.nsec);
610f0462bc3SAya Levin 
611f0462bc3SAya Levin 	if (mlx5_perout_conf_out_pulse_duration(mdev, rq, out_pulse_duration_ns))
612f0462bc3SAya Levin 		return -EINVAL;
613f0462bc3SAya Levin 
614f0462bc3SAya Levin 	*time_stamp = perout_conf_real_time(time->sec, time->nsec);
615f0462bc3SAya Levin 	*field_select |= MLX5_MTPPS_FS_NPPS_PERIOD |
616f0462bc3SAya Levin 			 MLX5_MTPPS_FS_OUT_PULSE_DURATION_NS;
617f0462bc3SAya Levin 
618f0462bc3SAya Levin 	return 0;
619f0462bc3SAya Levin }
620f0462bc3SAya Levin 
mlx5_perout_verify_flags(struct mlx5_core_dev * mdev,unsigned int flags)621f0462bc3SAya Levin static bool mlx5_perout_verify_flags(struct mlx5_core_dev *mdev, unsigned int flags)
622f0462bc3SAya Levin {
623f0462bc3SAya Levin 	return ((!mlx5_npps_real_time_supported(mdev) && flags) ||
624f0462bc3SAya Levin 		(mlx5_npps_real_time_supported(mdev) && flags & ~PTP_PEROUT_DUTY_CYCLE));
625432119deSAya Levin }
626432119deSAya Levin 
mlx5_perout_configure(struct ptp_clock_info * ptp,struct ptp_clock_request * rq,int on)6277c39afb3SFeras Daoud static int mlx5_perout_configure(struct ptp_clock_info *ptp,
628ae904beaSFeras Daoud 				 struct ptp_clock_request *rq,
629ae904beaSFeras Daoud 				 int on)
630ae904beaSFeras Daoud {
6317c39afb3SFeras Daoud 	struct mlx5_clock *clock =
6327c39afb3SFeras Daoud 			container_of(ptp, struct mlx5_clock, ptp_info);
6337c39afb3SFeras Daoud 	struct mlx5_core_dev *mdev =
6347c39afb3SFeras Daoud 			container_of(clock, struct mlx5_core_dev, clock);
63599b9a678SAya Levin 	bool rt_mode = mlx5_real_time_mode(mdev);
636ae904beaSFeras Daoud 	u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0};
637f0462bc3SAya Levin 	u32 out_pulse_duration_ns = 0;
638ae904beaSFeras Daoud 	u32 field_select = 0;
639f0462bc3SAya Levin 	u64 npps_period = 0;
640de19cd6cSEran Ben Elisha 	u64 time_stamp = 0;
641ae904beaSFeras Daoud 	u8 pin_mode = 0;
642ae904beaSFeras Daoud 	u8 pattern = 0;
643ae904beaSFeras Daoud 	int pin = -1;
644ae904beaSFeras Daoud 	int err = 0;
645ae904beaSFeras Daoud 
6467c39afb3SFeras Daoud 	if (!MLX5_PPS_CAP(mdev))
647ae904beaSFeras Daoud 		return -EOPNOTSUPP;
648ae904beaSFeras Daoud 
6497f9048f1SJacob Keller 	/* Reject requests with unsupported flags */
650f0462bc3SAya Levin 	if (mlx5_perout_verify_flags(mdev, rq->perout.flags))
6517f9048f1SJacob Keller 		return -EOPNOTSUPP;
6527f9048f1SJacob Keller 
6537c39afb3SFeras Daoud 	if (rq->perout.index >= clock->ptp_info.n_pins)
654ae904beaSFeras Daoud 		return -EINVAL;
655ae904beaSFeras Daoud 
656de19cd6cSEran Ben Elisha 	field_select = MLX5_MTPPS_FS_ENABLE;
65755affa97SAya Levin 	pin = ptp_find_pin(clock->ptp, PTP_PF_PEROUT, rq->perout.index);
65855affa97SAya Levin 	if (pin < 0)
65955affa97SAya Levin 		return -EBUSY;
66055affa97SAya Levin 
661de19cd6cSEran Ben Elisha 	if (on) {
662432119deSAya Levin 		bool rt_mode = mlx5_real_time_mode(mdev);
663de19cd6cSEran Ben Elisha 
6647c39afb3SFeras Daoud 		pin_mode = MLX5_PIN_MODE_OUT;
6657c39afb3SFeras Daoud 		pattern = MLX5_OUT_PATTERN_PERIODIC;
666ae904beaSFeras Daoud 
667f0462bc3SAya Levin 		if (rt_mode &&  rq->perout.start.sec > U32_MAX)
668ae904beaSFeras Daoud 			return -EINVAL;
669ae904beaSFeras Daoud 
670de19cd6cSEran Ben Elisha 		field_select |= MLX5_MTPPS_FS_PIN_MODE |
6717c39afb3SFeras Daoud 				MLX5_MTPPS_FS_PATTERN |
6727c39afb3SFeras Daoud 				MLX5_MTPPS_FS_TIME_STAMP;
673f0462bc3SAya Levin 
674f0462bc3SAya Levin 		if (mlx5_npps_real_time_supported(mdev))
675f0462bc3SAya Levin 			err = perout_conf_npps_real_time(mdev, rq, &field_select,
676f0462bc3SAya Levin 							 &out_pulse_duration_ns, &npps_period,
677f0462bc3SAya Levin 							 &time_stamp);
678f0462bc3SAya Levin 		else
679f0462bc3SAya Levin 			err = perout_conf_1pps(mdev, rq, &time_stamp, rt_mode);
680f0462bc3SAya Levin 		if (err)
681f0462bc3SAya Levin 			return err;
682ae904beaSFeras Daoud 	}
683ae904beaSFeras Daoud 
684ae904beaSFeras Daoud 	MLX5_SET(mtpps_reg, in, pin, pin);
685ae904beaSFeras Daoud 	MLX5_SET(mtpps_reg, in, pin_mode, pin_mode);
686ae904beaSFeras Daoud 	MLX5_SET(mtpps_reg, in, pattern, pattern);
687ae904beaSFeras Daoud 	MLX5_SET(mtpps_reg, in, enable, on);
688ae904beaSFeras Daoud 	MLX5_SET64(mtpps_reg, in, time_stamp, time_stamp);
689ae904beaSFeras Daoud 	MLX5_SET(mtpps_reg, in, field_select, field_select);
690f0462bc3SAya Levin 	MLX5_SET64(mtpps_reg, in, npps_period, npps_period);
691f0462bc3SAya Levin 	MLX5_SET(mtpps_reg, in, out_pulse_duration_ns, out_pulse_duration_ns);
6927c39afb3SFeras Daoud 	err = mlx5_set_mtpps(mdev, in, sizeof(in));
693ae904beaSFeras Daoud 	if (err)
694ae904beaSFeras Daoud 		return err;
695ae904beaSFeras Daoud 
69699b9a678SAya Levin 	if (rt_mode)
69799b9a678SAya Levin 		return 0;
69899b9a678SAya Levin 
6997c39afb3SFeras Daoud 	return mlx5_set_mtppse(mdev, pin, 0,
7007c39afb3SFeras Daoud 			       MLX5_EVENT_MODE_REPETETIVE & on);
701ae904beaSFeras Daoud }
702ae904beaSFeras Daoud 
mlx5_pps_configure(struct ptp_clock_info * ptp,struct ptp_clock_request * rq,int on)7037c39afb3SFeras Daoud static int mlx5_pps_configure(struct ptp_clock_info *ptp,
704ae904beaSFeras Daoud 			      struct ptp_clock_request *rq,
705ae904beaSFeras Daoud 			      int on)
706ae904beaSFeras Daoud {
7077c39afb3SFeras Daoud 	struct mlx5_clock *clock =
7087c39afb3SFeras Daoud 			container_of(ptp, struct mlx5_clock, ptp_info);
709ae904beaSFeras Daoud 
7107c39afb3SFeras Daoud 	clock->pps_info.enabled = !!on;
711ae904beaSFeras Daoud 	return 0;
712ae904beaSFeras Daoud }
713ae904beaSFeras Daoud 
mlx5_ptp_enable(struct ptp_clock_info * ptp,struct ptp_clock_request * rq,int on)7147c39afb3SFeras Daoud static int mlx5_ptp_enable(struct ptp_clock_info *ptp,
715ae904beaSFeras Daoud 			   struct ptp_clock_request *rq,
716ae904beaSFeras Daoud 			   int on)
717ae904beaSFeras Daoud {
718ae904beaSFeras Daoud 	switch (rq->type) {
719ae904beaSFeras Daoud 	case PTP_CLK_REQ_EXTTS:
7207c39afb3SFeras Daoud 		return mlx5_extts_configure(ptp, rq, on);
721ae904beaSFeras Daoud 	case PTP_CLK_REQ_PEROUT:
7227c39afb3SFeras Daoud 		return mlx5_perout_configure(ptp, rq, on);
723ae904beaSFeras Daoud 	case PTP_CLK_REQ_PPS:
7247c39afb3SFeras Daoud 		return mlx5_pps_configure(ptp, rq, on);
725ae904beaSFeras Daoud 	default:
726ae904beaSFeras Daoud 		return -EOPNOTSUPP;
727ae904beaSFeras Daoud 	}
728ae904beaSFeras Daoud 	return 0;
729ae904beaSFeras Daoud }
730ae904beaSFeras Daoud 
731071995c8SEran Ben Elisha enum {
732071995c8SEran Ben Elisha 	MLX5_MTPPS_REG_CAP_PIN_X_MODE_SUPPORT_PPS_IN = BIT(0),
733071995c8SEran Ben Elisha 	MLX5_MTPPS_REG_CAP_PIN_X_MODE_SUPPORT_PPS_OUT = BIT(1),
734071995c8SEran Ben Elisha };
735071995c8SEran Ben Elisha 
mlx5_ptp_verify(struct ptp_clock_info * ptp,unsigned int pin,enum ptp_pin_function func,unsigned int chan)7367c39afb3SFeras Daoud static int mlx5_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
737ae904beaSFeras Daoud 			   enum ptp_pin_function func, unsigned int chan)
738ae904beaSFeras Daoud {
739071995c8SEran Ben Elisha 	struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock,
740071995c8SEran Ben Elisha 						ptp_info);
741071995c8SEran Ben Elisha 
742071995c8SEran Ben Elisha 	switch (func) {
743071995c8SEran Ben Elisha 	case PTP_PF_NONE:
744071995c8SEran Ben Elisha 		return 0;
745071995c8SEran Ben Elisha 	case PTP_PF_EXTTS:
746071995c8SEran Ben Elisha 		return !(clock->pps_info.pin_caps[pin] &
747071995c8SEran Ben Elisha 			 MLX5_MTPPS_REG_CAP_PIN_X_MODE_SUPPORT_PPS_IN);
748071995c8SEran Ben Elisha 	case PTP_PF_PEROUT:
749071995c8SEran Ben Elisha 		return !(clock->pps_info.pin_caps[pin] &
750071995c8SEran Ben Elisha 			 MLX5_MTPPS_REG_CAP_PIN_X_MODE_SUPPORT_PPS_OUT);
751071995c8SEran Ben Elisha 	default:
752071995c8SEran Ben Elisha 		return -EOPNOTSUPP;
753071995c8SEran Ben Elisha 	}
754ae904beaSFeras Daoud }
755ae904beaSFeras Daoud 
7567c39afb3SFeras Daoud static const struct ptp_clock_info mlx5_ptp_clock_info = {
757ae904beaSFeras Daoud 	.owner		= THIS_MODULE,
758aac2df7fSEran Ben Elisha 	.name		= "mlx5_ptp",
759fe91d572SRahul Rameshbabu 	.max_adj	= 50000000,
760ae904beaSFeras Daoud 	.n_alarm	= 0,
761ae904beaSFeras Daoud 	.n_ext_ts	= 0,
762ae904beaSFeras Daoud 	.n_per_out	= 0,
763ae904beaSFeras Daoud 	.n_pins		= 0,
764ae904beaSFeras Daoud 	.pps		= 0,
765d8aad3f3SJacob Keller 	.adjfine	= mlx5_ptp_adjfine,
7668e11a68eSRahul Rameshbabu 	.adjphase	= mlx5_ptp_adjphase,
76767ac72a5SRahul Rameshbabu 	.getmaxphase    = mlx5_ptp_getmaxphase,
7687c39afb3SFeras Daoud 	.adjtime	= mlx5_ptp_adjtime,
7694a0475d5SMiroslav Lichvar 	.gettimex64	= mlx5_ptp_gettimex,
7707c39afb3SFeras Daoud 	.settime64	= mlx5_ptp_settime,
771ae904beaSFeras Daoud 	.enable		= NULL,
772ae904beaSFeras Daoud 	.verify		= NULL,
773ae904beaSFeras Daoud };
774ae904beaSFeras Daoud 
mlx5_query_mtpps_pin_mode(struct mlx5_core_dev * mdev,u8 pin,u32 * mtpps,u32 mtpps_size)775ed56d749SEran Ben Elisha static int mlx5_query_mtpps_pin_mode(struct mlx5_core_dev *mdev, u8 pin,
776ed56d749SEran Ben Elisha 				     u32 *mtpps, u32 mtpps_size)
777ed56d749SEran Ben Elisha {
778ed56d749SEran Ben Elisha 	u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {};
779ed56d749SEran Ben Elisha 
780ed56d749SEran Ben Elisha 	MLX5_SET(mtpps_reg, in, pin, pin);
781ed56d749SEran Ben Elisha 
782ed56d749SEran Ben Elisha 	return mlx5_core_access_reg(mdev, in, sizeof(in), mtpps,
783ed56d749SEran Ben Elisha 				    mtpps_size, MLX5_REG_MTPPS, 0, 0);
784ed56d749SEran Ben Elisha }
785ed56d749SEran Ben Elisha 
mlx5_get_pps_pin_mode(struct mlx5_clock * clock,u8 pin)786ed56d749SEran Ben Elisha static int mlx5_get_pps_pin_mode(struct mlx5_clock *clock, u8 pin)
787ed56d749SEran Ben Elisha {
788fb609b51SEran Ben Elisha 	struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev, clock);
789fb609b51SEran Ben Elisha 
790ed56d749SEran Ben Elisha 	u32 out[MLX5_ST_SZ_DW(mtpps_reg)] = {};
791ed56d749SEran Ben Elisha 	u8 mode;
792ed56d749SEran Ben Elisha 	int err;
793ed56d749SEran Ben Elisha 
794ed56d749SEran Ben Elisha 	err = mlx5_query_mtpps_pin_mode(mdev, pin, out, sizeof(out));
795ed56d749SEran Ben Elisha 	if (err || !MLX5_GET(mtpps_reg, out, enable))
796ed56d749SEran Ben Elisha 		return PTP_PF_NONE;
797ed56d749SEran Ben Elisha 
798ed56d749SEran Ben Elisha 	mode = MLX5_GET(mtpps_reg, out, pin_mode);
799ed56d749SEran Ben Elisha 
800ed56d749SEran Ben Elisha 	if (mode == MLX5_PIN_MODE_IN)
801ed56d749SEran Ben Elisha 		return PTP_PF_EXTTS;
802ed56d749SEran Ben Elisha 	else if (mode == MLX5_PIN_MODE_OUT)
803ed56d749SEran Ben Elisha 		return PTP_PF_PEROUT;
804ed56d749SEran Ben Elisha 
805ed56d749SEran Ben Elisha 	return PTP_PF_NONE;
806ed56d749SEran Ben Elisha }
807ed56d749SEran Ben Elisha 
mlx5_init_pin_config(struct mlx5_clock * clock)808302522e6SAya Levin static void mlx5_init_pin_config(struct mlx5_clock *clock)
809ae904beaSFeras Daoud {
810ae904beaSFeras Daoud 	int i;
811ae904beaSFeras Daoud 
812302522e6SAya Levin 	if (!clock->ptp_info.n_pins)
813302522e6SAya Levin 		return;
814302522e6SAya Levin 
8157c39afb3SFeras Daoud 	clock->ptp_info.pin_config =
8166396bb22SKees Cook 			kcalloc(clock->ptp_info.n_pins,
8176396bb22SKees Cook 				sizeof(*clock->ptp_info.pin_config),
8186396bb22SKees Cook 				GFP_KERNEL);
8197c39afb3SFeras Daoud 	if (!clock->ptp_info.pin_config)
820302522e6SAya Levin 		return;
8217c39afb3SFeras Daoud 	clock->ptp_info.enable = mlx5_ptp_enable;
8227c39afb3SFeras Daoud 	clock->ptp_info.verify = mlx5_ptp_verify;
8237c39afb3SFeras Daoud 	clock->ptp_info.pps = 1;
824ae904beaSFeras Daoud 
8257c39afb3SFeras Daoud 	for (i = 0; i < clock->ptp_info.n_pins; i++) {
8267c39afb3SFeras Daoud 		snprintf(clock->ptp_info.pin_config[i].name,
8277c39afb3SFeras Daoud 			 sizeof(clock->ptp_info.pin_config[i].name),
828ae904beaSFeras Daoud 			 "mlx5_pps%d", i);
8297c39afb3SFeras Daoud 		clock->ptp_info.pin_config[i].index = i;
830ed56d749SEran Ben Elisha 		clock->ptp_info.pin_config[i].func = mlx5_get_pps_pin_mode(clock, i);
83188c8cf92SEran Ben Elisha 		clock->ptp_info.pin_config[i].chan = 0;
832ae904beaSFeras Daoud 	}
833ae904beaSFeras Daoud }
834ae904beaSFeras Daoud 
mlx5_get_pps_caps(struct mlx5_core_dev * mdev)8357c39afb3SFeras Daoud static void mlx5_get_pps_caps(struct mlx5_core_dev *mdev)
836ae904beaSFeras Daoud {
8377c39afb3SFeras Daoud 	struct mlx5_clock *clock = &mdev->clock;
838ae904beaSFeras Daoud 	u32 out[MLX5_ST_SZ_DW(mtpps_reg)] = {0};
839ae904beaSFeras Daoud 
8407c39afb3SFeras Daoud 	mlx5_query_mtpps(mdev, out, sizeof(out));
841ae904beaSFeras Daoud 
8427c39afb3SFeras Daoud 	clock->ptp_info.n_pins = MLX5_GET(mtpps_reg, out,
843ae904beaSFeras Daoud 					  cap_number_of_pps_pins);
8447c39afb3SFeras Daoud 	clock->ptp_info.n_ext_ts = MLX5_GET(mtpps_reg, out,
845ae904beaSFeras Daoud 					    cap_max_num_of_pps_in_pins);
8467c39afb3SFeras Daoud 	clock->ptp_info.n_per_out = MLX5_GET(mtpps_reg, out,
847ae904beaSFeras Daoud 					     cap_max_num_of_pps_out_pins);
848ae904beaSFeras Daoud 
849f0462bc3SAya Levin 	if (MLX5_CAP_MCAM_FEATURE(mdev, npps_period))
850f0462bc3SAya Levin 		clock->pps_info.min_npps_period = 1 << MLX5_GET(mtpps_reg, out,
851f0462bc3SAya Levin 								cap_log_min_npps_period);
852f0462bc3SAya Levin 	if (MLX5_CAP_MCAM_FEATURE(mdev, out_pulse_duration_ns))
853f0462bc3SAya Levin 		clock->pps_info.min_out_pulse_duration_ns = 1 << MLX5_GET(mtpps_reg, out,
854f0462bc3SAya Levin 								cap_log_min_out_pulse_duration_ns);
855f0462bc3SAya Levin 
8567c39afb3SFeras Daoud 	clock->pps_info.pin_caps[0] = MLX5_GET(mtpps_reg, out, cap_pin_0_mode);
8577c39afb3SFeras Daoud 	clock->pps_info.pin_caps[1] = MLX5_GET(mtpps_reg, out, cap_pin_1_mode);
8587c39afb3SFeras Daoud 	clock->pps_info.pin_caps[2] = MLX5_GET(mtpps_reg, out, cap_pin_2_mode);
8597c39afb3SFeras Daoud 	clock->pps_info.pin_caps[3] = MLX5_GET(mtpps_reg, out, cap_pin_3_mode);
8607c39afb3SFeras Daoud 	clock->pps_info.pin_caps[4] = MLX5_GET(mtpps_reg, out, cap_pin_4_mode);
8617c39afb3SFeras Daoud 	clock->pps_info.pin_caps[5] = MLX5_GET(mtpps_reg, out, cap_pin_5_mode);
8627c39afb3SFeras Daoud 	clock->pps_info.pin_caps[6] = MLX5_GET(mtpps_reg, out, cap_pin_6_mode);
8637c39afb3SFeras Daoud 	clock->pps_info.pin_caps[7] = MLX5_GET(mtpps_reg, out, cap_pin_7_mode);
864ae904beaSFeras Daoud }
865ae904beaSFeras Daoud 
ts_next_sec(struct timespec64 * ts)866de19cd6cSEran Ben Elisha static void ts_next_sec(struct timespec64 *ts)
867de19cd6cSEran Ben Elisha {
868de19cd6cSEran Ben Elisha 	ts->tv_sec += 1;
869de19cd6cSEran Ben Elisha 	ts->tv_nsec = 0;
870de19cd6cSEran Ben Elisha }
871de19cd6cSEran Ben Elisha 
perout_conf_next_event_timer(struct mlx5_core_dev * mdev,struct mlx5_clock * clock)872432119deSAya Levin static u64 perout_conf_next_event_timer(struct mlx5_core_dev *mdev,
873de19cd6cSEran Ben Elisha 					struct mlx5_clock *clock)
874de19cd6cSEran Ben Elisha {
875de19cd6cSEran Ben Elisha 	struct timespec64 ts;
876de19cd6cSEran Ben Elisha 	s64 target_ns;
877de19cd6cSEran Ben Elisha 
878de19cd6cSEran Ben Elisha 	mlx5_ptp_gettimex(&clock->ptp_info, &ts, NULL);
879de19cd6cSEran Ben Elisha 	ts_next_sec(&ts);
880de19cd6cSEran Ben Elisha 	target_ns = timespec64_to_ns(&ts);
881de19cd6cSEran Ben Elisha 
88299b9a678SAya Levin 	return find_target_cycles(mdev, target_ns);
883de19cd6cSEran Ben Elisha }
884de19cd6cSEran Ben Elisha 
mlx5_pps_event(struct notifier_block * nb,unsigned long type,void * data)88541069256SSaeed Mahameed static int mlx5_pps_event(struct notifier_block *nb,
88641069256SSaeed Mahameed 			  unsigned long type, void *data)
887ae904beaSFeras Daoud {
88841069256SSaeed Mahameed 	struct mlx5_clock *clock = mlx5_nb_cof(nb, struct mlx5_clock, pps_nb);
8897c39afb3SFeras Daoud 	struct ptp_clock_event ptp_event;
89041069256SSaeed Mahameed 	struct mlx5_eqe *eqe = data;
8917c39afb3SFeras Daoud 	int pin = eqe->data.pps.pin;
892fb609b51SEran Ben Elisha 	struct mlx5_core_dev *mdev;
893ae904beaSFeras Daoud 	unsigned long flags;
894de19cd6cSEran Ben Elisha 	u64 ns;
895ae904beaSFeras Daoud 
896fb609b51SEran Ben Elisha 	mdev = container_of(clock, struct mlx5_core_dev, clock);
897fb609b51SEran Ben Elisha 
8987c39afb3SFeras Daoud 	switch (clock->ptp_info.pin_config[pin].func) {
899ae904beaSFeras Daoud 	case PTP_PF_EXTTS:
900afc98a0bSFeras Daoud 		ptp_event.index = pin;
901432119deSAya Levin 		ptp_event.timestamp = mlx5_real_time_mode(mdev) ?
902432119deSAya Levin 			mlx5_real_time_cyc2time(clock,
903432119deSAya Levin 						be64_to_cpu(eqe->data.pps.time_stamp)) :
9040d2ffdc8SEran Ben Elisha 			mlx5_timecounter_cyc2time(clock,
905afc98a0bSFeras Daoud 						  be64_to_cpu(eqe->data.pps.time_stamp));
9067c39afb3SFeras Daoud 		if (clock->pps_info.enabled) {
9077c39afb3SFeras Daoud 			ptp_event.type = PTP_CLOCK_PPSUSR;
908afc98a0bSFeras Daoud 			ptp_event.pps_times.ts_real =
909afc98a0bSFeras Daoud 					ns_to_timespec64(ptp_event.timestamp);
910ae904beaSFeras Daoud 		} else {
9117c39afb3SFeras Daoud 			ptp_event.type = PTP_CLOCK_EXTTS;
912ae904beaSFeras Daoud 		}
91339c538d6SCai Huoqing 		/* TODOL clock->ptp can be NULL if ptp_clock_register fails */
9147c39afb3SFeras Daoud 		ptp_clock_event(clock->ptp, &ptp_event);
915ae904beaSFeras Daoud 		break;
916ae904beaSFeras Daoud 	case PTP_PF_PEROUT:
917432119deSAya Levin 		ns = perout_conf_next_event_timer(mdev, clock);
91864109f1dSShay Agroskin 		write_seqlock_irqsave(&clock->lock, flags);
919de19cd6cSEran Ben Elisha 		clock->pps_info.start[pin] = ns;
92064109f1dSShay Agroskin 		write_sequnlock_irqrestore(&clock->lock, flags);
92187f3495cSEran Ben Elisha 		schedule_work(&clock->pps_info.out_work);
922ae904beaSFeras Daoud 		break;
923ae904beaSFeras Daoud 	default:
92441069256SSaeed Mahameed 		mlx5_core_err(mdev, " Unhandled clock PPS event, func %d\n",
92541069256SSaeed Mahameed 			      clock->ptp_info.pin_config[pin].func);
926ae904beaSFeras Daoud 	}
92741069256SSaeed Mahameed 
92841069256SSaeed Mahameed 	return NOTIFY_OK;
929ae904beaSFeras Daoud }
930ae904beaSFeras Daoud 
mlx5_timecounter_init(struct mlx5_core_dev * mdev)9311436de0bSEran Ben Elisha static void mlx5_timecounter_init(struct mlx5_core_dev *mdev)
932ae904beaSFeras Daoud {
9337c39afb3SFeras Daoud 	struct mlx5_clock *clock = &mdev->clock;
934d6f3dc8fSEran Ben Elisha 	struct mlx5_timer *timer = &clock->timer;
935ae904beaSFeras Daoud 	u32 dev_freq;
936ae904beaSFeras Daoud 
9377c39afb3SFeras Daoud 	dev_freq = MLX5_CAP_GEN(mdev, device_frequency_khz);
938d6f3dc8fSEran Ben Elisha 	timer->cycles.read = read_internal_timer;
93984a58e60SRahul Rameshbabu 	timer->cycles.shift = mlx5_ptp_shift_constant(dev_freq);
940d6f3dc8fSEran Ben Elisha 	timer->cycles.mult = clocksource_khz2mult(dev_freq,
941d6f3dc8fSEran Ben Elisha 						  timer->cycles.shift);
942d6f3dc8fSEran Ben Elisha 	timer->nominal_c_mult = timer->cycles.mult;
943d6f3dc8fSEran Ben Elisha 	timer->cycles.mask = CLOCKSOURCE_MASK(41);
944ae904beaSFeras Daoud 
945d6f3dc8fSEran Ben Elisha 	timecounter_init(&timer->tc, &timer->cycles,
946ae904beaSFeras Daoud 			 ktime_to_ns(ktime_get_real()));
9471436de0bSEran Ben Elisha }
9481436de0bSEran Ben Elisha 
mlx5_init_overflow_period(struct mlx5_clock * clock)9491436de0bSEran Ben Elisha static void mlx5_init_overflow_period(struct mlx5_clock *clock)
9501436de0bSEran Ben Elisha {
9511436de0bSEran Ben Elisha 	struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev, clock);
9521436de0bSEran Ben Elisha 	struct mlx5_ib_clock_info *clock_info = mdev->clock_info;
953d6f3dc8fSEran Ben Elisha 	struct mlx5_timer *timer = &clock->timer;
9541436de0bSEran Ben Elisha 	u64 overflow_cycles;
9551436de0bSEran Ben Elisha 	u64 frac = 0;
9561436de0bSEran Ben Elisha 	u64 ns;
957ae904beaSFeras Daoud 
958ae904beaSFeras Daoud 	/* Calculate period in seconds to call the overflow watchdog - to make
9595d867836SMiroslav Lichvar 	 * sure counter is checked at least twice every wrap around.
96033180beeSAriel Levkovich 	 * The period is calculated as the minimum between max HW cycles count
96133180beeSAriel Levkovich 	 * (The clock source mask) and max amount of cycles that can be
96233180beeSAriel Levkovich 	 * multiplied by clock multiplier where the result doesn't exceed
96333180beeSAriel Levkovich 	 * 64bits.
964ae904beaSFeras Daoud 	 */
965d6f3dc8fSEran Ben Elisha 	overflow_cycles = div64_u64(~0ULL >> 1, timer->cycles.mult);
966d6f3dc8fSEran Ben Elisha 	overflow_cycles = min(overflow_cycles, div_u64(timer->cycles.mask, 3));
96733180beeSAriel Levkovich 
968d6f3dc8fSEran Ben Elisha 	ns = cyclecounter_cyc2ns(&timer->cycles, overflow_cycles,
969ae904beaSFeras Daoud 				 frac, &frac);
97033180beeSAriel Levkovich 	do_div(ns, NSEC_PER_SEC / HZ);
971d6f3dc8fSEran Ben Elisha 	timer->overflow_period = ns;
972ae904beaSFeras Daoud 
973d6f3dc8fSEran Ben Elisha 	INIT_DELAYED_WORK(&timer->overflow_work, mlx5_timestamp_overflow);
974d6f3dc8fSEran Ben Elisha 	if (timer->overflow_period)
975d6f3dc8fSEran Ben Elisha 		schedule_delayed_work(&timer->overflow_work, 0);
976ae904beaSFeras Daoud 	else
9771436de0bSEran Ben Elisha 		mlx5_core_warn(mdev,
9781436de0bSEran Ben Elisha 			       "invalid overflow period, overflow_work is not scheduled\n");
9791436de0bSEran Ben Elisha 
9801436de0bSEran Ben Elisha 	if (clock_info)
981d6f3dc8fSEran Ben Elisha 		clock_info->overflow_period = timer->overflow_period;
9821436de0bSEran Ben Elisha }
9831436de0bSEran Ben Elisha 
mlx5_init_clock_info(struct mlx5_core_dev * mdev)9841436de0bSEran Ben Elisha static void mlx5_init_clock_info(struct mlx5_core_dev *mdev)
9851436de0bSEran Ben Elisha {
9861436de0bSEran Ben Elisha 	struct mlx5_clock *clock = &mdev->clock;
9871436de0bSEran Ben Elisha 	struct mlx5_ib_clock_info *info;
988d6f3dc8fSEran Ben Elisha 	struct mlx5_timer *timer;
9891436de0bSEran Ben Elisha 
9901436de0bSEran Ben Elisha 	mdev->clock_info = (struct mlx5_ib_clock_info *)get_zeroed_page(GFP_KERNEL);
9911436de0bSEran Ben Elisha 	if (!mdev->clock_info) {
9921436de0bSEran Ben Elisha 		mlx5_core_warn(mdev, "Failed to allocate IB clock info page\n");
9931436de0bSEran Ben Elisha 		return;
9941436de0bSEran Ben Elisha 	}
9951436de0bSEran Ben Elisha 
9961436de0bSEran Ben Elisha 	info = mdev->clock_info;
997d6f3dc8fSEran Ben Elisha 	timer = &clock->timer;
9981436de0bSEran Ben Elisha 
999d6f3dc8fSEran Ben Elisha 	info->nsec = timer->tc.nsec;
1000d6f3dc8fSEran Ben Elisha 	info->cycles = timer->tc.cycle_last;
1001d6f3dc8fSEran Ben Elisha 	info->mask = timer->cycles.mask;
1002d6f3dc8fSEran Ben Elisha 	info->mult = timer->nominal_c_mult;
1003d6f3dc8fSEran Ben Elisha 	info->shift = timer->cycles.shift;
1004d6f3dc8fSEran Ben Elisha 	info->frac = timer->tc.frac;
10051436de0bSEran Ben Elisha }
10061436de0bSEran Ben Elisha 
mlx5_init_timer_clock(struct mlx5_core_dev * mdev)1007432119deSAya Levin static void mlx5_init_timer_clock(struct mlx5_core_dev *mdev)
1008432119deSAya Levin {
1009432119deSAya Levin 	struct mlx5_clock *clock = &mdev->clock;
1010432119deSAya Levin 
1011432119deSAya Levin 	mlx5_timecounter_init(mdev);
1012432119deSAya Levin 	mlx5_init_clock_info(mdev);
1013432119deSAya Levin 	mlx5_init_overflow_period(clock);
1014432119deSAya Levin 	clock->ptp_info = mlx5_ptp_clock_info;
1015432119deSAya Levin 
1016432119deSAya Levin 	if (mlx5_real_time_mode(mdev)) {
1017432119deSAya Levin 		struct timespec64 ts;
1018432119deSAya Levin 
1019432119deSAya Levin 		ktime_get_real_ts64(&ts);
1020432119deSAya Levin 		mlx5_ptp_settime(&clock->ptp_info, &ts);
1021432119deSAya Levin 	}
1022432119deSAya Levin }
1023432119deSAya Levin 
mlx5_init_pps(struct mlx5_core_dev * mdev)1024302522e6SAya Levin static void mlx5_init_pps(struct mlx5_core_dev *mdev)
1025302522e6SAya Levin {
1026302522e6SAya Levin 	struct mlx5_clock *clock = &mdev->clock;
1027302522e6SAya Levin 
1028302522e6SAya Levin 	if (!MLX5_PPS_CAP(mdev))
1029302522e6SAya Levin 		return;
1030302522e6SAya Levin 
1031302522e6SAya Levin 	mlx5_get_pps_caps(mdev);
1032302522e6SAya Levin 	mlx5_init_pin_config(clock);
1033302522e6SAya Levin }
1034302522e6SAya Levin 
mlx5_init_clock(struct mlx5_core_dev * mdev)10351436de0bSEran Ben Elisha void mlx5_init_clock(struct mlx5_core_dev *mdev)
10361436de0bSEran Ben Elisha {
10371436de0bSEran Ben Elisha 	struct mlx5_clock *clock = &mdev->clock;
10381436de0bSEran Ben Elisha 
10391436de0bSEran Ben Elisha 	if (!MLX5_CAP_GEN(mdev, device_frequency_khz)) {
10401436de0bSEran Ben Elisha 		mlx5_core_warn(mdev, "invalid device_frequency_khz, aborting HW clock init\n");
10411436de0bSEran Ben Elisha 		return;
10421436de0bSEran Ben Elisha 	}
10431436de0bSEran Ben Elisha 
10441436de0bSEran Ben Elisha 	seqlock_init(&clock->lock);
1045432119deSAya Levin 	mlx5_init_timer_clock(mdev);
10461436de0bSEran Ben Elisha 	INIT_WORK(&clock->pps_info.out_work, mlx5_pps_out);
1047ae904beaSFeras Daoud 
1048ae904beaSFeras Daoud 	/* Configure the PHC */
10497c39afb3SFeras Daoud 	clock->ptp_info = mlx5_ptp_clock_info;
1050ae904beaSFeras Daoud 
1051ae904beaSFeras Daoud 	/* Initialize 1PPS data structures */
1052302522e6SAya Levin 	mlx5_init_pps(mdev);
1053ae904beaSFeras Daoud 
10547c39afb3SFeras Daoud 	clock->ptp = ptp_clock_register(&clock->ptp_info,
10557c39afb3SFeras Daoud 					&mdev->pdev->dev);
10567c39afb3SFeras Daoud 	if (IS_ERR(clock->ptp)) {
10577c39afb3SFeras Daoud 		mlx5_core_warn(mdev, "ptp_clock_register failed %ld\n",
10587c39afb3SFeras Daoud 			       PTR_ERR(clock->ptp));
10597c39afb3SFeras Daoud 		clock->ptp = NULL;
1060ae904beaSFeras Daoud 	}
106141069256SSaeed Mahameed 
106241069256SSaeed Mahameed 	MLX5_NB_INIT(&clock->pps_nb, mlx5_pps_event, PPS_EVENT);
106341069256SSaeed Mahameed 	mlx5_eq_notifier_register(mdev, &clock->pps_nb);
1064ae904beaSFeras Daoud }
1065ae904beaSFeras Daoud 
mlx5_cleanup_clock(struct mlx5_core_dev * mdev)10667c39afb3SFeras Daoud void mlx5_cleanup_clock(struct mlx5_core_dev *mdev)
1067ae904beaSFeras Daoud {
10687c39afb3SFeras Daoud 	struct mlx5_clock *clock = &mdev->clock;
1069ae904beaSFeras Daoud 
10707c39afb3SFeras Daoud 	if (!MLX5_CAP_GEN(mdev, device_frequency_khz))
1071ae904beaSFeras Daoud 		return;
1072ae904beaSFeras Daoud 
107341069256SSaeed Mahameed 	mlx5_eq_notifier_unregister(mdev, &clock->pps_nb);
10747c39afb3SFeras Daoud 	if (clock->ptp) {
10757c39afb3SFeras Daoud 		ptp_clock_unregister(clock->ptp);
10767c39afb3SFeras Daoud 		clock->ptp = NULL;
1077ae904beaSFeras Daoud 	}
1078ae904beaSFeras Daoud 
10797c39afb3SFeras Daoud 	cancel_work_sync(&clock->pps_info.out_work);
1080d6f3dc8fSEran Ben Elisha 	cancel_delayed_work_sync(&clock->timer.overflow_work);
108124d33d2cSFeras Daoud 
108224d33d2cSFeras Daoud 	if (mdev->clock_info) {
1083ddcdc368SJason Gunthorpe 		free_page((unsigned long)mdev->clock_info);
108424d33d2cSFeras Daoud 		mdev->clock_info = NULL;
108524d33d2cSFeras Daoud 	}
108624d33d2cSFeras Daoud 
10877c39afb3SFeras Daoud 	kfree(clock->ptp_info.pin_config);
1088ae904beaSFeras Daoud }
1089