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