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> 3590bf1c8dSEran Ben Elisha #include <linux/ptp_clock_kernel.h> 3624d33d2cSFeras Daoud #include <rdma/mlx5-abi.h> 3741069256SSaeed Mahameed #include "lib/eq.h" 38ae904beaSFeras Daoud #include "en.h" 399afe9a53SOr Gerlitz #include "clock.h" 40ae904beaSFeras Daoud 41ae904beaSFeras Daoud enum { 426a401092SRahul Rameshbabu MLX5_CYCLES_SHIFT = 31 43ae904beaSFeras Daoud }; 44ae904beaSFeras Daoud 45ae904beaSFeras Daoud enum { 467c39afb3SFeras Daoud MLX5_PIN_MODE_IN = 0x0, 477c39afb3SFeras Daoud MLX5_PIN_MODE_OUT = 0x1, 48ae904beaSFeras Daoud }; 49ae904beaSFeras Daoud 50ae904beaSFeras Daoud enum { 517c39afb3SFeras Daoud MLX5_OUT_PATTERN_PULSE = 0x0, 527c39afb3SFeras Daoud MLX5_OUT_PATTERN_PERIODIC = 0x1, 53ae904beaSFeras Daoud }; 54ae904beaSFeras Daoud 55ae904beaSFeras Daoud enum { 567c39afb3SFeras Daoud MLX5_EVENT_MODE_DISABLE = 0x0, 577c39afb3SFeras Daoud MLX5_EVENT_MODE_REPETETIVE = 0x1, 587c39afb3SFeras Daoud MLX5_EVENT_MODE_ONCE_TILL_ARM = 0x2, 59ae904beaSFeras Daoud }; 60ae904beaSFeras Daoud 61ae904beaSFeras Daoud enum { 627c39afb3SFeras Daoud MLX5_MTPPS_FS_ENABLE = BIT(0x0), 637c39afb3SFeras Daoud MLX5_MTPPS_FS_PATTERN = BIT(0x2), 647c39afb3SFeras Daoud MLX5_MTPPS_FS_PIN_MODE = BIT(0x3), 657c39afb3SFeras Daoud MLX5_MTPPS_FS_TIME_STAMP = BIT(0x4), 667c39afb3SFeras Daoud MLX5_MTPPS_FS_OUT_PULSE_DURATION = BIT(0x5), 677c39afb3SFeras Daoud MLX5_MTPPS_FS_ENH_OUT_PER_ADJ = BIT(0x7), 68f0462bc3SAya Levin MLX5_MTPPS_FS_NPPS_PERIOD = BIT(0x9), 69f0462bc3SAya Levin MLX5_MTPPS_FS_OUT_PULSE_DURATION_NS = BIT(0xa), 70ae904beaSFeras Daoud }; 71ae904beaSFeras Daoud 72d3c8a33aSRahul Rameshbabu enum { 73d3c8a33aSRahul Rameshbabu MLX5_MTUTC_OPERATION_ADJUST_TIME_MIN = S16_MIN, 74d3c8a33aSRahul Rameshbabu MLX5_MTUTC_OPERATION_ADJUST_TIME_MAX = S16_MAX, 75d3c8a33aSRahul Rameshbabu MLX5_MTUTC_OPERATION_ADJUST_TIME_EXTENDED_MIN = -200000, 76d3c8a33aSRahul Rameshbabu MLX5_MTUTC_OPERATION_ADJUST_TIME_EXTENDED_MAX = 200000, 77d3c8a33aSRahul Rameshbabu }; 78d3c8a33aSRahul Rameshbabu 79432119deSAya Levin static bool mlx5_real_time_mode(struct mlx5_core_dev *mdev) 80432119deSAya Levin { 81432119deSAya Levin return (mlx5_is_real_time_rq(mdev) || mlx5_is_real_time_sq(mdev)); 82432119deSAya Levin } 83432119deSAya Levin 84f0462bc3SAya Levin static bool mlx5_npps_real_time_supported(struct mlx5_core_dev *mdev) 85f0462bc3SAya Levin { 86f0462bc3SAya Levin return (mlx5_real_time_mode(mdev) && 87f0462bc3SAya Levin MLX5_CAP_MCAM_FEATURE(mdev, npps_period) && 88f0462bc3SAya Levin MLX5_CAP_MCAM_FEATURE(mdev, out_pulse_duration_ns)); 89f0462bc3SAya Levin } 90f0462bc3SAya Levin 91432119deSAya Levin static bool mlx5_modify_mtutc_allowed(struct mlx5_core_dev *mdev) 92432119deSAya Levin { 93432119deSAya Levin return MLX5_CAP_MCAM_FEATURE(mdev, ptpcyc2realtime_modify); 94432119deSAya Levin } 95432119deSAya Levin 96*67ac72a5SRahul Rameshbabu static s32 mlx5_ptp_getmaxphase(struct ptp_clock_info *ptp) 97d3c8a33aSRahul Rameshbabu { 98*67ac72a5SRahul Rameshbabu struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info); 99*67ac72a5SRahul Rameshbabu struct mlx5_core_dev *mdev; 100d3c8a33aSRahul Rameshbabu 101*67ac72a5SRahul Rameshbabu mdev = container_of(clock, struct mlx5_core_dev, clock); 102*67ac72a5SRahul Rameshbabu 103*67ac72a5SRahul Rameshbabu return MLX5_CAP_MCAM_FEATURE(mdev, mtutc_time_adjustment_extended_range) ? 104*67ac72a5SRahul Rameshbabu MLX5_MTUTC_OPERATION_ADJUST_TIME_EXTENDED_MAX : 105*67ac72a5SRahul Rameshbabu MLX5_MTUTC_OPERATION_ADJUST_TIME_MAX; 106d3c8a33aSRahul Rameshbabu } 107d3c8a33aSRahul Rameshbabu 108*67ac72a5SRahul Rameshbabu static bool mlx5_is_mtutc_time_adj_cap(struct mlx5_core_dev *mdev, s64 delta) 109*67ac72a5SRahul Rameshbabu { 110*67ac72a5SRahul Rameshbabu s64 max = mlx5_ptp_getmaxphase(&mdev->clock.ptp_info); 111*67ac72a5SRahul Rameshbabu 112*67ac72a5SRahul Rameshbabu if (delta < -max || delta > max) 113d3c8a33aSRahul Rameshbabu return false; 114d3c8a33aSRahul Rameshbabu 115d3c8a33aSRahul Rameshbabu return true; 116d3c8a33aSRahul Rameshbabu } 117d3c8a33aSRahul Rameshbabu 118432119deSAya Levin static int mlx5_set_mtutc(struct mlx5_core_dev *dev, u32 *mtutc, u32 size) 119432119deSAya Levin { 120432119deSAya Levin u32 out[MLX5_ST_SZ_DW(mtutc_reg)] = {}; 121432119deSAya Levin 122432119deSAya Levin if (!MLX5_CAP_MCAM_REG(dev, mtutc)) 123432119deSAya Levin return -EOPNOTSUPP; 124432119deSAya Levin 125432119deSAya Levin return mlx5_core_access_reg(dev, mtutc, size, out, sizeof(out), 126432119deSAya Levin MLX5_REG_MTUTC, 0, 1); 127432119deSAya Levin } 128432119deSAya Levin 129432119deSAya Levin static u64 mlx5_read_time(struct mlx5_core_dev *dev, 130432119deSAya Levin struct ptp_system_timestamp *sts, 131432119deSAya Levin bool real_time) 13290bf1c8dSEran Ben Elisha { 13390bf1c8dSEran Ben Elisha u32 timer_h, timer_h1, timer_l; 13490bf1c8dSEran Ben Elisha 135432119deSAya Levin timer_h = ioread32be(real_time ? &dev->iseg->real_time_h : 136432119deSAya Levin &dev->iseg->internal_timer_h); 13790bf1c8dSEran Ben Elisha ptp_read_system_prets(sts); 138432119deSAya Levin timer_l = ioread32be(real_time ? &dev->iseg->real_time_l : 139432119deSAya Levin &dev->iseg->internal_timer_l); 14090bf1c8dSEran Ben Elisha ptp_read_system_postts(sts); 141432119deSAya Levin timer_h1 = ioread32be(real_time ? &dev->iseg->real_time_h : 142432119deSAya Levin &dev->iseg->internal_timer_h); 14390bf1c8dSEran Ben Elisha if (timer_h != timer_h1) { 14490bf1c8dSEran Ben Elisha /* wrap around */ 14590bf1c8dSEran Ben Elisha ptp_read_system_prets(sts); 146432119deSAya Levin timer_l = ioread32be(real_time ? &dev->iseg->real_time_l : 147432119deSAya Levin &dev->iseg->internal_timer_l); 14890bf1c8dSEran Ben Elisha ptp_read_system_postts(sts); 14990bf1c8dSEran Ben Elisha } 15090bf1c8dSEran Ben Elisha 151432119deSAya Levin return real_time ? REAL_TIME_TO_NS(timer_h1, timer_l) : 152432119deSAya Levin (u64)timer_l | (u64)timer_h1 << 32; 15390bf1c8dSEran Ben Elisha } 15490bf1c8dSEran Ben Elisha 1557c39afb3SFeras Daoud static u64 read_internal_timer(const struct cyclecounter *cc) 156ae904beaSFeras Daoud { 157d6f3dc8fSEran Ben Elisha struct mlx5_timer *timer = container_of(cc, struct mlx5_timer, cycles); 158d6f3dc8fSEran Ben Elisha struct mlx5_clock *clock = container_of(timer, struct mlx5_clock, timer); 1597c39afb3SFeras Daoud struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev, 1607c39afb3SFeras Daoud clock); 161ae904beaSFeras Daoud 162432119deSAya Levin return mlx5_read_time(mdev, NULL, false) & cc->mask; 163ae904beaSFeras Daoud } 164ae904beaSFeras Daoud 16524d33d2cSFeras Daoud static void mlx5_update_clock_info_page(struct mlx5_core_dev *mdev) 16624d33d2cSFeras Daoud { 16724d33d2cSFeras Daoud struct mlx5_ib_clock_info *clock_info = mdev->clock_info; 16824d33d2cSFeras Daoud struct mlx5_clock *clock = &mdev->clock; 169d6f3dc8fSEran Ben Elisha struct mlx5_timer *timer; 17024d33d2cSFeras Daoud u32 sign; 17124d33d2cSFeras Daoud 17224d33d2cSFeras Daoud if (!clock_info) 17324d33d2cSFeras Daoud return; 17424d33d2cSFeras Daoud 17524d33d2cSFeras Daoud sign = smp_load_acquire(&clock_info->sign); 17624d33d2cSFeras Daoud smp_store_mb(clock_info->sign, 17724d33d2cSFeras Daoud sign | MLX5_IB_CLOCK_INFO_KERNEL_UPDATING); 17824d33d2cSFeras Daoud 179d6f3dc8fSEran Ben Elisha timer = &clock->timer; 180d6f3dc8fSEran Ben Elisha clock_info->cycles = timer->tc.cycle_last; 181d6f3dc8fSEran Ben Elisha clock_info->mult = timer->cycles.mult; 182d6f3dc8fSEran Ben Elisha clock_info->nsec = timer->tc.nsec; 183d6f3dc8fSEran Ben Elisha clock_info->frac = timer->tc.frac; 18424d33d2cSFeras Daoud 18524d33d2cSFeras Daoud smp_store_release(&clock_info->sign, 18624d33d2cSFeras Daoud sign + MLX5_IB_CLOCK_INFO_KERNEL_UPDATING * 2); 18724d33d2cSFeras Daoud } 18824d33d2cSFeras Daoud 1897c39afb3SFeras Daoud static void mlx5_pps_out(struct work_struct *work) 190ae904beaSFeras Daoud { 1917c39afb3SFeras Daoud struct mlx5_pps *pps_info = container_of(work, struct mlx5_pps, 192ae904beaSFeras Daoud out_work); 1937c39afb3SFeras Daoud struct mlx5_clock *clock = container_of(pps_info, struct mlx5_clock, 194ae904beaSFeras Daoud pps_info); 1957c39afb3SFeras Daoud struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev, 1967c39afb3SFeras Daoud clock); 197ae904beaSFeras Daoud u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; 198ae904beaSFeras Daoud unsigned long flags; 199ae904beaSFeras Daoud int i; 200ae904beaSFeras Daoud 2017c39afb3SFeras Daoud for (i = 0; i < clock->ptp_info.n_pins; i++) { 202ae904beaSFeras Daoud u64 tstart; 203ae904beaSFeras Daoud 20464109f1dSShay Agroskin write_seqlock_irqsave(&clock->lock, flags); 2057c39afb3SFeras Daoud tstart = clock->pps_info.start[i]; 2067c39afb3SFeras Daoud clock->pps_info.start[i] = 0; 20764109f1dSShay Agroskin write_sequnlock_irqrestore(&clock->lock, flags); 208ae904beaSFeras Daoud if (!tstart) 209ae904beaSFeras Daoud continue; 210ae904beaSFeras Daoud 211ae904beaSFeras Daoud MLX5_SET(mtpps_reg, in, pin, i); 212ae904beaSFeras Daoud MLX5_SET64(mtpps_reg, in, time_stamp, tstart); 2137c39afb3SFeras Daoud MLX5_SET(mtpps_reg, in, field_select, MLX5_MTPPS_FS_TIME_STAMP); 2147c39afb3SFeras Daoud mlx5_set_mtpps(mdev, in, sizeof(in)); 215ae904beaSFeras Daoud } 216ae904beaSFeras Daoud } 217ae904beaSFeras Daoud 2187c39afb3SFeras Daoud static void mlx5_timestamp_overflow(struct work_struct *work) 219ae904beaSFeras Daoud { 220ae904beaSFeras Daoud struct delayed_work *dwork = to_delayed_work(work); 221fb609b51SEran Ben Elisha struct mlx5_core_dev *mdev; 222d6f3dc8fSEran Ben Elisha struct mlx5_timer *timer; 223fb609b51SEran Ben Elisha struct mlx5_clock *clock; 224ae904beaSFeras Daoud unsigned long flags; 225ae904beaSFeras Daoud 226d6f3dc8fSEran Ben Elisha timer = container_of(dwork, struct mlx5_timer, overflow_work); 227d6f3dc8fSEran Ben Elisha clock = container_of(timer, struct mlx5_clock, timer); 228fb609b51SEran Ben Elisha mdev = container_of(clock, struct mlx5_core_dev, clock); 229d6f3dc8fSEran Ben Elisha 23064109f1dSShay Agroskin write_seqlock_irqsave(&clock->lock, flags); 231d6f3dc8fSEran Ben Elisha timecounter_read(&timer->tc); 232fb609b51SEran Ben Elisha mlx5_update_clock_info_page(mdev); 23364109f1dSShay Agroskin write_sequnlock_irqrestore(&clock->lock, flags); 234d6f3dc8fSEran Ben Elisha schedule_delayed_work(&timer->overflow_work, timer->overflow_period); 235ae904beaSFeras Daoud } 236ae904beaSFeras Daoud 237432119deSAya Levin static int mlx5_ptp_settime_real_time(struct mlx5_core_dev *mdev, 238432119deSAya Levin const struct timespec64 *ts) 239432119deSAya Levin { 240432119deSAya Levin u32 in[MLX5_ST_SZ_DW(mtutc_reg)] = {}; 241432119deSAya Levin 242432119deSAya Levin if (!mlx5_modify_mtutc_allowed(mdev)) 243432119deSAya Levin return 0; 244432119deSAya Levin 245432119deSAya Levin if (ts->tv_sec < 0 || ts->tv_sec > U32_MAX || 246432119deSAya Levin ts->tv_nsec < 0 || ts->tv_nsec > NSEC_PER_SEC) 247432119deSAya Levin return -EINVAL; 248432119deSAya Levin 249432119deSAya Levin MLX5_SET(mtutc_reg, in, operation, MLX5_MTUTC_OPERATION_SET_TIME_IMMEDIATE); 250432119deSAya Levin MLX5_SET(mtutc_reg, in, utc_sec, ts->tv_sec); 251432119deSAya Levin MLX5_SET(mtutc_reg, in, utc_nsec, ts->tv_nsec); 252432119deSAya Levin 253432119deSAya Levin return mlx5_set_mtutc(mdev, in, sizeof(in)); 254432119deSAya Levin } 255432119deSAya Levin 256fb609b51SEran Ben Elisha static int mlx5_ptp_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts) 257ae904beaSFeras Daoud { 258fb609b51SEran Ben Elisha struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info); 259d6f3dc8fSEran Ben Elisha struct mlx5_timer *timer = &clock->timer; 260fb609b51SEran Ben Elisha struct mlx5_core_dev *mdev; 261ae904beaSFeras Daoud unsigned long flags; 262432119deSAya Levin int err; 263ae904beaSFeras Daoud 264fb609b51SEran Ben Elisha mdev = container_of(clock, struct mlx5_core_dev, clock); 265432119deSAya Levin err = mlx5_ptp_settime_real_time(mdev, ts); 266432119deSAya Levin if (err) 267432119deSAya Levin return err; 268432119deSAya Levin 26964109f1dSShay Agroskin write_seqlock_irqsave(&clock->lock, flags); 270432119deSAya Levin timecounter_init(&timer->tc, &timer->cycles, timespec64_to_ns(ts)); 271fb609b51SEran Ben Elisha mlx5_update_clock_info_page(mdev); 27264109f1dSShay Agroskin write_sequnlock_irqrestore(&clock->lock, flags); 273ae904beaSFeras Daoud 274ae904beaSFeras Daoud return 0; 275ae904beaSFeras Daoud } 276ae904beaSFeras Daoud 277432119deSAya Levin static 278432119deSAya Levin struct timespec64 mlx5_ptp_gettimex_real_time(struct mlx5_core_dev *mdev, 279432119deSAya Levin struct ptp_system_timestamp *sts) 280432119deSAya Levin { 281432119deSAya Levin struct timespec64 ts; 282432119deSAya Levin u64 time; 283432119deSAya Levin 284432119deSAya Levin time = mlx5_read_time(mdev, sts, true); 285432119deSAya Levin ts = ns_to_timespec64(time); 286432119deSAya Levin return ts; 287432119deSAya Levin } 288432119deSAya Levin 2894a0475d5SMiroslav Lichvar static int mlx5_ptp_gettimex(struct ptp_clock_info *ptp, struct timespec64 *ts, 2904a0475d5SMiroslav Lichvar struct ptp_system_timestamp *sts) 291ae904beaSFeras Daoud { 292fb609b51SEran Ben Elisha struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info); 293d6f3dc8fSEran Ben Elisha struct mlx5_timer *timer = &clock->timer; 294fb609b51SEran Ben Elisha struct mlx5_core_dev *mdev; 295ae904beaSFeras Daoud unsigned long flags; 2964a0475d5SMiroslav Lichvar u64 cycles, ns; 297ae904beaSFeras Daoud 298fb609b51SEran Ben Elisha mdev = container_of(clock, struct mlx5_core_dev, clock); 299432119deSAya Levin if (mlx5_real_time_mode(mdev)) { 300432119deSAya Levin *ts = mlx5_ptp_gettimex_real_time(mdev, sts); 301432119deSAya Levin goto out; 302432119deSAya Levin } 303432119deSAya Levin 30464109f1dSShay Agroskin write_seqlock_irqsave(&clock->lock, flags); 305432119deSAya Levin cycles = mlx5_read_time(mdev, sts, false); 306d6f3dc8fSEran Ben Elisha ns = timecounter_cyc2time(&timer->tc, cycles); 30764109f1dSShay Agroskin write_sequnlock_irqrestore(&clock->lock, flags); 308ae904beaSFeras Daoud *ts = ns_to_timespec64(ns); 309432119deSAya Levin out: 310ae904beaSFeras Daoud return 0; 311ae904beaSFeras Daoud } 312ae904beaSFeras Daoud 313432119deSAya Levin static int mlx5_ptp_adjtime_real_time(struct mlx5_core_dev *mdev, s64 delta) 314432119deSAya Levin { 315432119deSAya Levin u32 in[MLX5_ST_SZ_DW(mtutc_reg)] = {}; 316432119deSAya Levin 317432119deSAya Levin if (!mlx5_modify_mtutc_allowed(mdev)) 318432119deSAya Levin return 0; 319432119deSAya Levin 320d3c8a33aSRahul Rameshbabu /* HW time adjustment range is checked. If out of range, settime instead */ 321d3c8a33aSRahul Rameshbabu if (!mlx5_is_mtutc_time_adj_cap(mdev, delta)) { 322432119deSAya Levin struct timespec64 ts; 323432119deSAya Levin s64 ns; 324432119deSAya Levin 325432119deSAya Levin ts = mlx5_ptp_gettimex_real_time(mdev, NULL); 326432119deSAya Levin ns = timespec64_to_ns(&ts) + delta; 327432119deSAya Levin ts = ns_to_timespec64(ns); 328432119deSAya Levin return mlx5_ptp_settime_real_time(mdev, &ts); 329432119deSAya Levin } 330432119deSAya Levin 331432119deSAya Levin MLX5_SET(mtutc_reg, in, operation, MLX5_MTUTC_OPERATION_ADJUST_TIME); 332432119deSAya Levin MLX5_SET(mtutc_reg, in, time_adjustment, delta); 333432119deSAya Levin 334432119deSAya Levin return mlx5_set_mtutc(mdev, in, sizeof(in)); 335432119deSAya Levin } 336432119deSAya Levin 3377c39afb3SFeras Daoud static int mlx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 338ae904beaSFeras Daoud { 339fb609b51SEran Ben Elisha struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info); 340d6f3dc8fSEran Ben Elisha struct mlx5_timer *timer = &clock->timer; 341fb609b51SEran Ben Elisha struct mlx5_core_dev *mdev; 342ae904beaSFeras Daoud unsigned long flags; 343432119deSAya Levin int err; 344ae904beaSFeras Daoud 345fb609b51SEran Ben Elisha mdev = container_of(clock, struct mlx5_core_dev, clock); 346432119deSAya Levin 347432119deSAya Levin err = mlx5_ptp_adjtime_real_time(mdev, delta); 348432119deSAya Levin if (err) 349432119deSAya Levin return err; 35064109f1dSShay Agroskin write_seqlock_irqsave(&clock->lock, flags); 351d6f3dc8fSEran Ben Elisha timecounter_adjtime(&timer->tc, delta); 352fb609b51SEran Ben Elisha mlx5_update_clock_info_page(mdev); 35364109f1dSShay Agroskin write_sequnlock_irqrestore(&clock->lock, flags); 354ae904beaSFeras Daoud 355ae904beaSFeras Daoud return 0; 356ae904beaSFeras Daoud } 357ae904beaSFeras Daoud 3588e11a68eSRahul Rameshbabu static int mlx5_ptp_adjphase(struct ptp_clock_info *ptp, s32 delta) 3598e11a68eSRahul Rameshbabu { 3608e11a68eSRahul Rameshbabu return mlx5_ptp_adjtime(ptp, delta); 3618e11a68eSRahul Rameshbabu } 3628e11a68eSRahul Rameshbabu 363b63636b6SRahul Rameshbabu static int mlx5_ptp_freq_adj_real_time(struct mlx5_core_dev *mdev, long scaled_ppm) 364432119deSAya Levin { 365432119deSAya Levin u32 in[MLX5_ST_SZ_DW(mtutc_reg)] = {}; 366432119deSAya Levin 367432119deSAya Levin if (!mlx5_modify_mtutc_allowed(mdev)) 368432119deSAya Levin return 0; 369432119deSAya Levin 370432119deSAya Levin MLX5_SET(mtutc_reg, in, operation, MLX5_MTUTC_OPERATION_ADJUST_FREQ_UTC); 371b63636b6SRahul Rameshbabu 372b63636b6SRahul Rameshbabu if (MLX5_CAP_MCAM_FEATURE(mdev, mtutc_freq_adj_units)) { 373b63636b6SRahul Rameshbabu MLX5_SET(mtutc_reg, in, freq_adj_units, 374b63636b6SRahul Rameshbabu MLX5_MTUTC_FREQ_ADJ_UNITS_SCALED_PPM); 375b63636b6SRahul Rameshbabu MLX5_SET(mtutc_reg, in, freq_adjustment, scaled_ppm); 376b63636b6SRahul Rameshbabu } else { 377b63636b6SRahul Rameshbabu MLX5_SET(mtutc_reg, in, freq_adj_units, MLX5_MTUTC_FREQ_ADJ_UNITS_PPB); 378b63636b6SRahul Rameshbabu MLX5_SET(mtutc_reg, in, freq_adjustment, scaled_ppm_to_ppb(scaled_ppm)); 379b63636b6SRahul Rameshbabu } 380432119deSAya Levin 381432119deSAya Levin return mlx5_set_mtutc(mdev, in, sizeof(in)); 382432119deSAya Levin } 383432119deSAya Levin 384d8aad3f3SJacob Keller static int mlx5_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 385ae904beaSFeras Daoud { 386fb609b51SEran Ben Elisha struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info); 387d6f3dc8fSEran Ben Elisha struct mlx5_timer *timer = &clock->timer; 388fb609b51SEran Ben Elisha struct mlx5_core_dev *mdev; 389ae904beaSFeras Daoud unsigned long flags; 390d8aad3f3SJacob Keller u32 mult; 391432119deSAya Levin int err; 392432119deSAya Levin 393432119deSAya Levin mdev = container_of(clock, struct mlx5_core_dev, clock); 394b63636b6SRahul Rameshbabu 395b63636b6SRahul Rameshbabu err = mlx5_ptp_freq_adj_real_time(mdev, scaled_ppm); 396432119deSAya Levin if (err) 397432119deSAya Levin return err; 398fb609b51SEran Ben Elisha 399d8aad3f3SJacob Keller mult = (u32)adjust_by_scaled_ppm(timer->nominal_c_mult, scaled_ppm); 400ae904beaSFeras Daoud 40164109f1dSShay Agroskin write_seqlock_irqsave(&clock->lock, flags); 402d6f3dc8fSEran Ben Elisha timecounter_read(&timer->tc); 403d8aad3f3SJacob Keller timer->cycles.mult = mult; 404fb609b51SEran Ben Elisha mlx5_update_clock_info_page(mdev); 40564109f1dSShay Agroskin write_sequnlock_irqrestore(&clock->lock, flags); 406ae904beaSFeras Daoud 407ae904beaSFeras Daoud return 0; 408ae904beaSFeras Daoud } 409ae904beaSFeras Daoud 4107c39afb3SFeras Daoud static int mlx5_extts_configure(struct ptp_clock_info *ptp, 411ae904beaSFeras Daoud struct ptp_clock_request *rq, 412ae904beaSFeras Daoud int on) 413ae904beaSFeras Daoud { 4147c39afb3SFeras Daoud struct mlx5_clock *clock = 4157c39afb3SFeras Daoud container_of(ptp, struct mlx5_clock, ptp_info); 4167c39afb3SFeras Daoud struct mlx5_core_dev *mdev = 4177c39afb3SFeras Daoud container_of(clock, struct mlx5_core_dev, clock); 418ae904beaSFeras Daoud u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; 419ae904beaSFeras Daoud u32 field_select = 0; 420ae904beaSFeras Daoud u8 pin_mode = 0; 421ae904beaSFeras Daoud u8 pattern = 0; 422ae904beaSFeras Daoud int pin = -1; 423ae904beaSFeras Daoud int err = 0; 424ae904beaSFeras Daoud 4257c39afb3SFeras Daoud if (!MLX5_PPS_CAP(mdev)) 426ae904beaSFeras Daoud return -EOPNOTSUPP; 427ae904beaSFeras Daoud 4282e0645a0SJacob Keller /* Reject requests with unsupported flags */ 4292e0645a0SJacob Keller if (rq->extts.flags & ~(PTP_ENABLE_FEATURE | 4302e0645a0SJacob Keller PTP_RISING_EDGE | 4316138e687SRichard Cochran PTP_FALLING_EDGE | 4326138e687SRichard Cochran PTP_STRICT_FLAGS)) 4332e0645a0SJacob Keller return -EOPNOTSUPP; 4342e0645a0SJacob Keller 435ca12cf5aSRichard Cochran /* Reject requests to enable time stamping on both edges. */ 436ca12cf5aSRichard Cochran if ((rq->extts.flags & PTP_STRICT_FLAGS) && 437ca12cf5aSRichard Cochran (rq->extts.flags & PTP_ENABLE_FEATURE) && 438ca12cf5aSRichard Cochran (rq->extts.flags & PTP_EXTTS_EDGES) == PTP_EXTTS_EDGES) 439ca12cf5aSRichard Cochran return -EOPNOTSUPP; 440ca12cf5aSRichard Cochran 4417c39afb3SFeras Daoud if (rq->extts.index >= clock->ptp_info.n_pins) 442ae904beaSFeras Daoud return -EINVAL; 443ae904beaSFeras Daoud 4447c39afb3SFeras Daoud pin = ptp_find_pin(clock->ptp, PTP_PF_EXTTS, rq->extts.index); 445ae904beaSFeras Daoud if (pin < 0) 446ae904beaSFeras Daoud return -EBUSY; 44788c8cf92SEran Ben Elisha 44888c8cf92SEran Ben Elisha if (on) { 4497c39afb3SFeras Daoud pin_mode = MLX5_PIN_MODE_IN; 450ae904beaSFeras Daoud pattern = !!(rq->extts.flags & PTP_FALLING_EDGE); 4517c39afb3SFeras Daoud field_select = MLX5_MTPPS_FS_PIN_MODE | 4527c39afb3SFeras Daoud MLX5_MTPPS_FS_PATTERN | 4537c39afb3SFeras Daoud MLX5_MTPPS_FS_ENABLE; 454ae904beaSFeras Daoud } else { 4557c39afb3SFeras Daoud field_select = MLX5_MTPPS_FS_ENABLE; 456ae904beaSFeras Daoud } 457ae904beaSFeras Daoud 458ae904beaSFeras Daoud MLX5_SET(mtpps_reg, in, pin, pin); 459ae904beaSFeras Daoud MLX5_SET(mtpps_reg, in, pin_mode, pin_mode); 460ae904beaSFeras Daoud MLX5_SET(mtpps_reg, in, pattern, pattern); 461ae904beaSFeras Daoud MLX5_SET(mtpps_reg, in, enable, on); 462ae904beaSFeras Daoud MLX5_SET(mtpps_reg, in, field_select, field_select); 463ae904beaSFeras Daoud 4647c39afb3SFeras Daoud err = mlx5_set_mtpps(mdev, in, sizeof(in)); 465ae904beaSFeras Daoud if (err) 466ae904beaSFeras Daoud return err; 467ae904beaSFeras Daoud 4687c39afb3SFeras Daoud return mlx5_set_mtppse(mdev, pin, 0, 4697c39afb3SFeras Daoud MLX5_EVENT_MODE_REPETETIVE & on); 470ae904beaSFeras Daoud } 471ae904beaSFeras Daoud 472de19cd6cSEran Ben Elisha static u64 find_target_cycles(struct mlx5_core_dev *mdev, s64 target_ns) 473de19cd6cSEran Ben Elisha { 474de19cd6cSEran Ben Elisha struct mlx5_clock *clock = &mdev->clock; 475de19cd6cSEran Ben Elisha u64 cycles_now, cycles_delta; 476de19cd6cSEran Ben Elisha u64 nsec_now, nsec_delta; 477de19cd6cSEran Ben Elisha struct mlx5_timer *timer; 478de19cd6cSEran Ben Elisha unsigned long flags; 479de19cd6cSEran Ben Elisha 480de19cd6cSEran Ben Elisha timer = &clock->timer; 481de19cd6cSEran Ben Elisha 482432119deSAya Levin cycles_now = mlx5_read_time(mdev, NULL, false); 483de19cd6cSEran Ben Elisha write_seqlock_irqsave(&clock->lock, flags); 484de19cd6cSEran Ben Elisha nsec_now = timecounter_cyc2time(&timer->tc, cycles_now); 485de19cd6cSEran Ben Elisha nsec_delta = target_ns - nsec_now; 486de19cd6cSEran Ben Elisha cycles_delta = div64_u64(nsec_delta << timer->cycles.shift, 487de19cd6cSEran Ben Elisha timer->cycles.mult); 488de19cd6cSEran Ben Elisha write_sequnlock_irqrestore(&clock->lock, flags); 489de19cd6cSEran Ben Elisha 490de19cd6cSEran Ben Elisha return cycles_now + cycles_delta; 491de19cd6cSEran Ben Elisha } 492de19cd6cSEran Ben Elisha 49364728294SAya Levin static u64 perout_conf_internal_timer(struct mlx5_core_dev *mdev, s64 sec) 494de19cd6cSEran Ben Elisha { 49564728294SAya Levin struct timespec64 ts = {}; 496de19cd6cSEran Ben Elisha s64 target_ns; 497de19cd6cSEran Ben Elisha 498de19cd6cSEran Ben Elisha ts.tv_sec = sec; 499de19cd6cSEran Ben Elisha target_ns = timespec64_to_ns(&ts); 500de19cd6cSEran Ben Elisha 501de19cd6cSEran Ben Elisha return find_target_cycles(mdev, target_ns); 502de19cd6cSEran Ben Elisha } 503de19cd6cSEran Ben Elisha 504f0462bc3SAya Levin static u64 perout_conf_real_time(s64 sec, u32 nsec) 505432119deSAya Levin { 506f0462bc3SAya Levin return (u64)nsec | (u64)sec << 32; 507f0462bc3SAya Levin } 508f0462bc3SAya Levin 509f0462bc3SAya Levin static int perout_conf_1pps(struct mlx5_core_dev *mdev, struct ptp_clock_request *rq, 510f0462bc3SAya Levin u64 *time_stamp, bool real_time) 511f0462bc3SAya Levin { 512f0462bc3SAya Levin struct timespec64 ts; 513f0462bc3SAya Levin s64 ns; 514f0462bc3SAya Levin 515f0462bc3SAya Levin ts.tv_nsec = rq->perout.period.nsec; 516f0462bc3SAya Levin ts.tv_sec = rq->perout.period.sec; 517f0462bc3SAya Levin ns = timespec64_to_ns(&ts); 518f0462bc3SAya Levin 519f0462bc3SAya Levin if ((ns >> 1) != 500000000LL) 520f0462bc3SAya Levin return -EINVAL; 521f0462bc3SAya Levin 522f0462bc3SAya Levin *time_stamp = real_time ? perout_conf_real_time(rq->perout.start.sec, 0) : 523f0462bc3SAya Levin perout_conf_internal_timer(mdev, rq->perout.start.sec); 524f0462bc3SAya Levin 525f0462bc3SAya Levin return 0; 526f0462bc3SAya Levin } 527f0462bc3SAya Levin 528f0462bc3SAya Levin #define MLX5_MAX_PULSE_DURATION (BIT(__mlx5_bit_sz(mtpps_reg, out_pulse_duration_ns)) - 1) 529f0462bc3SAya Levin static int mlx5_perout_conf_out_pulse_duration(struct mlx5_core_dev *mdev, 530f0462bc3SAya Levin struct ptp_clock_request *rq, 531f0462bc3SAya Levin u32 *out_pulse_duration_ns) 532f0462bc3SAya Levin { 533f0462bc3SAya Levin struct mlx5_pps *pps_info = &mdev->clock.pps_info; 534f0462bc3SAya Levin u32 out_pulse_duration; 535f0462bc3SAya Levin struct timespec64 ts; 536f0462bc3SAya Levin 537f0462bc3SAya Levin if (rq->perout.flags & PTP_PEROUT_DUTY_CYCLE) { 538f0462bc3SAya Levin ts.tv_sec = rq->perout.on.sec; 539f0462bc3SAya Levin ts.tv_nsec = rq->perout.on.nsec; 540f0462bc3SAya Levin out_pulse_duration = (u32)timespec64_to_ns(&ts); 541f0462bc3SAya Levin } else { 542f0462bc3SAya Levin /* out_pulse_duration_ns should be up to 50% of the 543f0462bc3SAya Levin * pulse period as default 544f0462bc3SAya Levin */ 545f0462bc3SAya Levin ts.tv_sec = rq->perout.period.sec; 546f0462bc3SAya Levin ts.tv_nsec = rq->perout.period.nsec; 547f0462bc3SAya Levin out_pulse_duration = (u32)timespec64_to_ns(&ts) >> 1; 548f0462bc3SAya Levin } 549f0462bc3SAya Levin 550f0462bc3SAya Levin if (out_pulse_duration < pps_info->min_out_pulse_duration_ns || 551f0462bc3SAya Levin out_pulse_duration > MLX5_MAX_PULSE_DURATION) { 552f0462bc3SAya Levin mlx5_core_err(mdev, "NPPS pulse duration %u is not in [%llu, %lu]\n", 553f0462bc3SAya Levin out_pulse_duration, pps_info->min_out_pulse_duration_ns, 554f0462bc3SAya Levin MLX5_MAX_PULSE_DURATION); 555f0462bc3SAya Levin return -EINVAL; 556f0462bc3SAya Levin } 557f0462bc3SAya Levin *out_pulse_duration_ns = out_pulse_duration; 558f0462bc3SAya Levin 559f0462bc3SAya Levin return 0; 560f0462bc3SAya Levin } 561f0462bc3SAya Levin 562f0462bc3SAya Levin static int perout_conf_npps_real_time(struct mlx5_core_dev *mdev, struct ptp_clock_request *rq, 563f0462bc3SAya Levin u32 *field_select, u32 *out_pulse_duration_ns, 564f0462bc3SAya Levin u64 *period, u64 *time_stamp) 565f0462bc3SAya Levin { 566f0462bc3SAya Levin struct mlx5_pps *pps_info = &mdev->clock.pps_info; 567f0462bc3SAya Levin struct ptp_clock_time *time = &rq->perout.start; 568f0462bc3SAya Levin struct timespec64 ts; 569f0462bc3SAya Levin 570f0462bc3SAya Levin ts.tv_sec = rq->perout.period.sec; 571f0462bc3SAya Levin ts.tv_nsec = rq->perout.period.nsec; 572f0462bc3SAya Levin if (timespec64_to_ns(&ts) < pps_info->min_npps_period) { 573f0462bc3SAya Levin mlx5_core_err(mdev, "NPPS period is lower than minimal npps period %llu\n", 574f0462bc3SAya Levin pps_info->min_npps_period); 575f0462bc3SAya Levin return -EINVAL; 576f0462bc3SAya Levin } 577f0462bc3SAya Levin *period = perout_conf_real_time(rq->perout.period.sec, rq->perout.period.nsec); 578f0462bc3SAya Levin 579f0462bc3SAya Levin if (mlx5_perout_conf_out_pulse_duration(mdev, rq, out_pulse_duration_ns)) 580f0462bc3SAya Levin return -EINVAL; 581f0462bc3SAya Levin 582f0462bc3SAya Levin *time_stamp = perout_conf_real_time(time->sec, time->nsec); 583f0462bc3SAya Levin *field_select |= MLX5_MTPPS_FS_NPPS_PERIOD | 584f0462bc3SAya Levin MLX5_MTPPS_FS_OUT_PULSE_DURATION_NS; 585f0462bc3SAya Levin 586f0462bc3SAya Levin return 0; 587f0462bc3SAya Levin } 588f0462bc3SAya Levin 589f0462bc3SAya Levin static bool mlx5_perout_verify_flags(struct mlx5_core_dev *mdev, unsigned int flags) 590f0462bc3SAya Levin { 591f0462bc3SAya Levin return ((!mlx5_npps_real_time_supported(mdev) && flags) || 592f0462bc3SAya Levin (mlx5_npps_real_time_supported(mdev) && flags & ~PTP_PEROUT_DUTY_CYCLE)); 593432119deSAya Levin } 594432119deSAya Levin 5957c39afb3SFeras Daoud static int mlx5_perout_configure(struct ptp_clock_info *ptp, 596ae904beaSFeras Daoud struct ptp_clock_request *rq, 597ae904beaSFeras Daoud int on) 598ae904beaSFeras Daoud { 5997c39afb3SFeras Daoud struct mlx5_clock *clock = 6007c39afb3SFeras Daoud container_of(ptp, struct mlx5_clock, ptp_info); 6017c39afb3SFeras Daoud struct mlx5_core_dev *mdev = 6027c39afb3SFeras Daoud container_of(clock, struct mlx5_core_dev, clock); 60399b9a678SAya Levin bool rt_mode = mlx5_real_time_mode(mdev); 604ae904beaSFeras Daoud u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; 605f0462bc3SAya Levin u32 out_pulse_duration_ns = 0; 606ae904beaSFeras Daoud u32 field_select = 0; 607f0462bc3SAya Levin u64 npps_period = 0; 608de19cd6cSEran Ben Elisha u64 time_stamp = 0; 609ae904beaSFeras Daoud u8 pin_mode = 0; 610ae904beaSFeras Daoud u8 pattern = 0; 611ae904beaSFeras Daoud int pin = -1; 612ae904beaSFeras Daoud int err = 0; 613ae904beaSFeras Daoud 6147c39afb3SFeras Daoud if (!MLX5_PPS_CAP(mdev)) 615ae904beaSFeras Daoud return -EOPNOTSUPP; 616ae904beaSFeras Daoud 6177f9048f1SJacob Keller /* Reject requests with unsupported flags */ 618f0462bc3SAya Levin if (mlx5_perout_verify_flags(mdev, rq->perout.flags)) 6197f9048f1SJacob Keller return -EOPNOTSUPP; 6207f9048f1SJacob Keller 6217c39afb3SFeras Daoud if (rq->perout.index >= clock->ptp_info.n_pins) 622ae904beaSFeras Daoud return -EINVAL; 623ae904beaSFeras Daoud 624de19cd6cSEran Ben Elisha field_select = MLX5_MTPPS_FS_ENABLE; 62555affa97SAya Levin pin = ptp_find_pin(clock->ptp, PTP_PF_PEROUT, rq->perout.index); 62655affa97SAya Levin if (pin < 0) 62755affa97SAya Levin return -EBUSY; 62855affa97SAya Levin 629de19cd6cSEran Ben Elisha if (on) { 630432119deSAya Levin bool rt_mode = mlx5_real_time_mode(mdev); 631de19cd6cSEran Ben Elisha 6327c39afb3SFeras Daoud pin_mode = MLX5_PIN_MODE_OUT; 6337c39afb3SFeras Daoud pattern = MLX5_OUT_PATTERN_PERIODIC; 634ae904beaSFeras Daoud 635f0462bc3SAya Levin if (rt_mode && rq->perout.start.sec > U32_MAX) 636ae904beaSFeras Daoud return -EINVAL; 637ae904beaSFeras Daoud 638de19cd6cSEran Ben Elisha field_select |= MLX5_MTPPS_FS_PIN_MODE | 6397c39afb3SFeras Daoud MLX5_MTPPS_FS_PATTERN | 6407c39afb3SFeras Daoud MLX5_MTPPS_FS_TIME_STAMP; 641f0462bc3SAya Levin 642f0462bc3SAya Levin if (mlx5_npps_real_time_supported(mdev)) 643f0462bc3SAya Levin err = perout_conf_npps_real_time(mdev, rq, &field_select, 644f0462bc3SAya Levin &out_pulse_duration_ns, &npps_period, 645f0462bc3SAya Levin &time_stamp); 646f0462bc3SAya Levin else 647f0462bc3SAya Levin err = perout_conf_1pps(mdev, rq, &time_stamp, rt_mode); 648f0462bc3SAya Levin if (err) 649f0462bc3SAya Levin return err; 650ae904beaSFeras Daoud } 651ae904beaSFeras Daoud 652ae904beaSFeras Daoud MLX5_SET(mtpps_reg, in, pin, pin); 653ae904beaSFeras Daoud MLX5_SET(mtpps_reg, in, pin_mode, pin_mode); 654ae904beaSFeras Daoud MLX5_SET(mtpps_reg, in, pattern, pattern); 655ae904beaSFeras Daoud MLX5_SET(mtpps_reg, in, enable, on); 656ae904beaSFeras Daoud MLX5_SET64(mtpps_reg, in, time_stamp, time_stamp); 657ae904beaSFeras Daoud MLX5_SET(mtpps_reg, in, field_select, field_select); 658f0462bc3SAya Levin MLX5_SET64(mtpps_reg, in, npps_period, npps_period); 659f0462bc3SAya Levin MLX5_SET(mtpps_reg, in, out_pulse_duration_ns, out_pulse_duration_ns); 6607c39afb3SFeras Daoud err = mlx5_set_mtpps(mdev, in, sizeof(in)); 661ae904beaSFeras Daoud if (err) 662ae904beaSFeras Daoud return err; 663ae904beaSFeras Daoud 66499b9a678SAya Levin if (rt_mode) 66599b9a678SAya Levin return 0; 66699b9a678SAya Levin 6677c39afb3SFeras Daoud return mlx5_set_mtppse(mdev, pin, 0, 6687c39afb3SFeras Daoud MLX5_EVENT_MODE_REPETETIVE & on); 669ae904beaSFeras Daoud } 670ae904beaSFeras Daoud 6717c39afb3SFeras Daoud static int mlx5_pps_configure(struct ptp_clock_info *ptp, 672ae904beaSFeras Daoud struct ptp_clock_request *rq, 673ae904beaSFeras Daoud int on) 674ae904beaSFeras Daoud { 6757c39afb3SFeras Daoud struct mlx5_clock *clock = 6767c39afb3SFeras Daoud container_of(ptp, struct mlx5_clock, ptp_info); 677ae904beaSFeras Daoud 6787c39afb3SFeras Daoud clock->pps_info.enabled = !!on; 679ae904beaSFeras Daoud return 0; 680ae904beaSFeras Daoud } 681ae904beaSFeras Daoud 6827c39afb3SFeras Daoud static int mlx5_ptp_enable(struct ptp_clock_info *ptp, 683ae904beaSFeras Daoud struct ptp_clock_request *rq, 684ae904beaSFeras Daoud int on) 685ae904beaSFeras Daoud { 686ae904beaSFeras Daoud switch (rq->type) { 687ae904beaSFeras Daoud case PTP_CLK_REQ_EXTTS: 6887c39afb3SFeras Daoud return mlx5_extts_configure(ptp, rq, on); 689ae904beaSFeras Daoud case PTP_CLK_REQ_PEROUT: 6907c39afb3SFeras Daoud return mlx5_perout_configure(ptp, rq, on); 691ae904beaSFeras Daoud case PTP_CLK_REQ_PPS: 6927c39afb3SFeras Daoud return mlx5_pps_configure(ptp, rq, on); 693ae904beaSFeras Daoud default: 694ae904beaSFeras Daoud return -EOPNOTSUPP; 695ae904beaSFeras Daoud } 696ae904beaSFeras Daoud return 0; 697ae904beaSFeras Daoud } 698ae904beaSFeras Daoud 699071995c8SEran Ben Elisha enum { 700071995c8SEran Ben Elisha MLX5_MTPPS_REG_CAP_PIN_X_MODE_SUPPORT_PPS_IN = BIT(0), 701071995c8SEran Ben Elisha MLX5_MTPPS_REG_CAP_PIN_X_MODE_SUPPORT_PPS_OUT = BIT(1), 702071995c8SEran Ben Elisha }; 703071995c8SEran Ben Elisha 7047c39afb3SFeras Daoud static int mlx5_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, 705ae904beaSFeras Daoud enum ptp_pin_function func, unsigned int chan) 706ae904beaSFeras Daoud { 707071995c8SEran Ben Elisha struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, 708071995c8SEran Ben Elisha ptp_info); 709071995c8SEran Ben Elisha 710071995c8SEran Ben Elisha switch (func) { 711071995c8SEran Ben Elisha case PTP_PF_NONE: 712071995c8SEran Ben Elisha return 0; 713071995c8SEran Ben Elisha case PTP_PF_EXTTS: 714071995c8SEran Ben Elisha return !(clock->pps_info.pin_caps[pin] & 715071995c8SEran Ben Elisha MLX5_MTPPS_REG_CAP_PIN_X_MODE_SUPPORT_PPS_IN); 716071995c8SEran Ben Elisha case PTP_PF_PEROUT: 717071995c8SEran Ben Elisha return !(clock->pps_info.pin_caps[pin] & 718071995c8SEran Ben Elisha MLX5_MTPPS_REG_CAP_PIN_X_MODE_SUPPORT_PPS_OUT); 719071995c8SEran Ben Elisha default: 720071995c8SEran Ben Elisha return -EOPNOTSUPP; 721071995c8SEran Ben Elisha } 722ae904beaSFeras Daoud } 723ae904beaSFeras Daoud 7247c39afb3SFeras Daoud static const struct ptp_clock_info mlx5_ptp_clock_info = { 725ae904beaSFeras Daoud .owner = THIS_MODULE, 726aac2df7fSEran Ben Elisha .name = "mlx5_ptp", 727fe91d572SRahul Rameshbabu .max_adj = 50000000, 728ae904beaSFeras Daoud .n_alarm = 0, 729ae904beaSFeras Daoud .n_ext_ts = 0, 730ae904beaSFeras Daoud .n_per_out = 0, 731ae904beaSFeras Daoud .n_pins = 0, 732ae904beaSFeras Daoud .pps = 0, 733d8aad3f3SJacob Keller .adjfine = mlx5_ptp_adjfine, 7348e11a68eSRahul Rameshbabu .adjphase = mlx5_ptp_adjphase, 735*67ac72a5SRahul Rameshbabu .getmaxphase = mlx5_ptp_getmaxphase, 7367c39afb3SFeras Daoud .adjtime = mlx5_ptp_adjtime, 7374a0475d5SMiroslav Lichvar .gettimex64 = mlx5_ptp_gettimex, 7387c39afb3SFeras Daoud .settime64 = mlx5_ptp_settime, 739ae904beaSFeras Daoud .enable = NULL, 740ae904beaSFeras Daoud .verify = NULL, 741ae904beaSFeras Daoud }; 742ae904beaSFeras Daoud 743ed56d749SEran Ben Elisha static int mlx5_query_mtpps_pin_mode(struct mlx5_core_dev *mdev, u8 pin, 744ed56d749SEran Ben Elisha u32 *mtpps, u32 mtpps_size) 745ed56d749SEran Ben Elisha { 746ed56d749SEran Ben Elisha u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {}; 747ed56d749SEran Ben Elisha 748ed56d749SEran Ben Elisha MLX5_SET(mtpps_reg, in, pin, pin); 749ed56d749SEran Ben Elisha 750ed56d749SEran Ben Elisha return mlx5_core_access_reg(mdev, in, sizeof(in), mtpps, 751ed56d749SEran Ben Elisha mtpps_size, MLX5_REG_MTPPS, 0, 0); 752ed56d749SEran Ben Elisha } 753ed56d749SEran Ben Elisha 754ed56d749SEran Ben Elisha static int mlx5_get_pps_pin_mode(struct mlx5_clock *clock, u8 pin) 755ed56d749SEran Ben Elisha { 756fb609b51SEran Ben Elisha struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev, clock); 757fb609b51SEran Ben Elisha 758ed56d749SEran Ben Elisha u32 out[MLX5_ST_SZ_DW(mtpps_reg)] = {}; 759ed56d749SEran Ben Elisha u8 mode; 760ed56d749SEran Ben Elisha int err; 761ed56d749SEran Ben Elisha 762ed56d749SEran Ben Elisha err = mlx5_query_mtpps_pin_mode(mdev, pin, out, sizeof(out)); 763ed56d749SEran Ben Elisha if (err || !MLX5_GET(mtpps_reg, out, enable)) 764ed56d749SEran Ben Elisha return PTP_PF_NONE; 765ed56d749SEran Ben Elisha 766ed56d749SEran Ben Elisha mode = MLX5_GET(mtpps_reg, out, pin_mode); 767ed56d749SEran Ben Elisha 768ed56d749SEran Ben Elisha if (mode == MLX5_PIN_MODE_IN) 769ed56d749SEran Ben Elisha return PTP_PF_EXTTS; 770ed56d749SEran Ben Elisha else if (mode == MLX5_PIN_MODE_OUT) 771ed56d749SEran Ben Elisha return PTP_PF_PEROUT; 772ed56d749SEran Ben Elisha 773ed56d749SEran Ben Elisha return PTP_PF_NONE; 774ed56d749SEran Ben Elisha } 775ed56d749SEran Ben Elisha 776302522e6SAya Levin static void mlx5_init_pin_config(struct mlx5_clock *clock) 777ae904beaSFeras Daoud { 778ae904beaSFeras Daoud int i; 779ae904beaSFeras Daoud 780302522e6SAya Levin if (!clock->ptp_info.n_pins) 781302522e6SAya Levin return; 782302522e6SAya Levin 7837c39afb3SFeras Daoud clock->ptp_info.pin_config = 7846396bb22SKees Cook kcalloc(clock->ptp_info.n_pins, 7856396bb22SKees Cook sizeof(*clock->ptp_info.pin_config), 7866396bb22SKees Cook GFP_KERNEL); 7877c39afb3SFeras Daoud if (!clock->ptp_info.pin_config) 788302522e6SAya Levin return; 7897c39afb3SFeras Daoud clock->ptp_info.enable = mlx5_ptp_enable; 7907c39afb3SFeras Daoud clock->ptp_info.verify = mlx5_ptp_verify; 7917c39afb3SFeras Daoud clock->ptp_info.pps = 1; 792ae904beaSFeras Daoud 7937c39afb3SFeras Daoud for (i = 0; i < clock->ptp_info.n_pins; i++) { 7947c39afb3SFeras Daoud snprintf(clock->ptp_info.pin_config[i].name, 7957c39afb3SFeras Daoud sizeof(clock->ptp_info.pin_config[i].name), 796ae904beaSFeras Daoud "mlx5_pps%d", i); 7977c39afb3SFeras Daoud clock->ptp_info.pin_config[i].index = i; 798ed56d749SEran Ben Elisha clock->ptp_info.pin_config[i].func = mlx5_get_pps_pin_mode(clock, i); 79988c8cf92SEran Ben Elisha clock->ptp_info.pin_config[i].chan = 0; 800ae904beaSFeras Daoud } 801ae904beaSFeras Daoud } 802ae904beaSFeras Daoud 8037c39afb3SFeras Daoud static void mlx5_get_pps_caps(struct mlx5_core_dev *mdev) 804ae904beaSFeras Daoud { 8057c39afb3SFeras Daoud struct mlx5_clock *clock = &mdev->clock; 806ae904beaSFeras Daoud u32 out[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; 807ae904beaSFeras Daoud 8087c39afb3SFeras Daoud mlx5_query_mtpps(mdev, out, sizeof(out)); 809ae904beaSFeras Daoud 8107c39afb3SFeras Daoud clock->ptp_info.n_pins = MLX5_GET(mtpps_reg, out, 811ae904beaSFeras Daoud cap_number_of_pps_pins); 8127c39afb3SFeras Daoud clock->ptp_info.n_ext_ts = MLX5_GET(mtpps_reg, out, 813ae904beaSFeras Daoud cap_max_num_of_pps_in_pins); 8147c39afb3SFeras Daoud clock->ptp_info.n_per_out = MLX5_GET(mtpps_reg, out, 815ae904beaSFeras Daoud cap_max_num_of_pps_out_pins); 816ae904beaSFeras Daoud 817f0462bc3SAya Levin if (MLX5_CAP_MCAM_FEATURE(mdev, npps_period)) 818f0462bc3SAya Levin clock->pps_info.min_npps_period = 1 << MLX5_GET(mtpps_reg, out, 819f0462bc3SAya Levin cap_log_min_npps_period); 820f0462bc3SAya Levin if (MLX5_CAP_MCAM_FEATURE(mdev, out_pulse_duration_ns)) 821f0462bc3SAya Levin clock->pps_info.min_out_pulse_duration_ns = 1 << MLX5_GET(mtpps_reg, out, 822f0462bc3SAya Levin cap_log_min_out_pulse_duration_ns); 823f0462bc3SAya Levin 8247c39afb3SFeras Daoud clock->pps_info.pin_caps[0] = MLX5_GET(mtpps_reg, out, cap_pin_0_mode); 8257c39afb3SFeras Daoud clock->pps_info.pin_caps[1] = MLX5_GET(mtpps_reg, out, cap_pin_1_mode); 8267c39afb3SFeras Daoud clock->pps_info.pin_caps[2] = MLX5_GET(mtpps_reg, out, cap_pin_2_mode); 8277c39afb3SFeras Daoud clock->pps_info.pin_caps[3] = MLX5_GET(mtpps_reg, out, cap_pin_3_mode); 8287c39afb3SFeras Daoud clock->pps_info.pin_caps[4] = MLX5_GET(mtpps_reg, out, cap_pin_4_mode); 8297c39afb3SFeras Daoud clock->pps_info.pin_caps[5] = MLX5_GET(mtpps_reg, out, cap_pin_5_mode); 8307c39afb3SFeras Daoud clock->pps_info.pin_caps[6] = MLX5_GET(mtpps_reg, out, cap_pin_6_mode); 8317c39afb3SFeras Daoud clock->pps_info.pin_caps[7] = MLX5_GET(mtpps_reg, out, cap_pin_7_mode); 832ae904beaSFeras Daoud } 833ae904beaSFeras Daoud 834de19cd6cSEran Ben Elisha static void ts_next_sec(struct timespec64 *ts) 835de19cd6cSEran Ben Elisha { 836de19cd6cSEran Ben Elisha ts->tv_sec += 1; 837de19cd6cSEran Ben Elisha ts->tv_nsec = 0; 838de19cd6cSEran Ben Elisha } 839de19cd6cSEran Ben Elisha 840432119deSAya Levin static u64 perout_conf_next_event_timer(struct mlx5_core_dev *mdev, 841de19cd6cSEran Ben Elisha struct mlx5_clock *clock) 842de19cd6cSEran Ben Elisha { 843de19cd6cSEran Ben Elisha struct timespec64 ts; 844de19cd6cSEran Ben Elisha s64 target_ns; 845de19cd6cSEran Ben Elisha 846de19cd6cSEran Ben Elisha mlx5_ptp_gettimex(&clock->ptp_info, &ts, NULL); 847de19cd6cSEran Ben Elisha ts_next_sec(&ts); 848de19cd6cSEran Ben Elisha target_ns = timespec64_to_ns(&ts); 849de19cd6cSEran Ben Elisha 85099b9a678SAya Levin return find_target_cycles(mdev, target_ns); 851de19cd6cSEran Ben Elisha } 852de19cd6cSEran Ben Elisha 85341069256SSaeed Mahameed static int mlx5_pps_event(struct notifier_block *nb, 85441069256SSaeed Mahameed unsigned long type, void *data) 855ae904beaSFeras Daoud { 85641069256SSaeed Mahameed struct mlx5_clock *clock = mlx5_nb_cof(nb, struct mlx5_clock, pps_nb); 8577c39afb3SFeras Daoud struct ptp_clock_event ptp_event; 85841069256SSaeed Mahameed struct mlx5_eqe *eqe = data; 8597c39afb3SFeras Daoud int pin = eqe->data.pps.pin; 860fb609b51SEran Ben Elisha struct mlx5_core_dev *mdev; 861ae904beaSFeras Daoud unsigned long flags; 862de19cd6cSEran Ben Elisha u64 ns; 863ae904beaSFeras Daoud 864fb609b51SEran Ben Elisha mdev = container_of(clock, struct mlx5_core_dev, clock); 865fb609b51SEran Ben Elisha 8667c39afb3SFeras Daoud switch (clock->ptp_info.pin_config[pin].func) { 867ae904beaSFeras Daoud case PTP_PF_EXTTS: 868afc98a0bSFeras Daoud ptp_event.index = pin; 869432119deSAya Levin ptp_event.timestamp = mlx5_real_time_mode(mdev) ? 870432119deSAya Levin mlx5_real_time_cyc2time(clock, 871432119deSAya Levin be64_to_cpu(eqe->data.pps.time_stamp)) : 8720d2ffdc8SEran Ben Elisha mlx5_timecounter_cyc2time(clock, 873afc98a0bSFeras Daoud be64_to_cpu(eqe->data.pps.time_stamp)); 8747c39afb3SFeras Daoud if (clock->pps_info.enabled) { 8757c39afb3SFeras Daoud ptp_event.type = PTP_CLOCK_PPSUSR; 876afc98a0bSFeras Daoud ptp_event.pps_times.ts_real = 877afc98a0bSFeras Daoud ns_to_timespec64(ptp_event.timestamp); 878ae904beaSFeras Daoud } else { 8797c39afb3SFeras Daoud ptp_event.type = PTP_CLOCK_EXTTS; 880ae904beaSFeras Daoud } 88139c538d6SCai Huoqing /* TODOL clock->ptp can be NULL if ptp_clock_register fails */ 8827c39afb3SFeras Daoud ptp_clock_event(clock->ptp, &ptp_event); 883ae904beaSFeras Daoud break; 884ae904beaSFeras Daoud case PTP_PF_PEROUT: 885432119deSAya Levin ns = perout_conf_next_event_timer(mdev, clock); 88664109f1dSShay Agroskin write_seqlock_irqsave(&clock->lock, flags); 887de19cd6cSEran Ben Elisha clock->pps_info.start[pin] = ns; 88864109f1dSShay Agroskin write_sequnlock_irqrestore(&clock->lock, flags); 88987f3495cSEran Ben Elisha schedule_work(&clock->pps_info.out_work); 890ae904beaSFeras Daoud break; 891ae904beaSFeras Daoud default: 89241069256SSaeed Mahameed mlx5_core_err(mdev, " Unhandled clock PPS event, func %d\n", 89341069256SSaeed Mahameed clock->ptp_info.pin_config[pin].func); 894ae904beaSFeras Daoud } 89541069256SSaeed Mahameed 89641069256SSaeed Mahameed return NOTIFY_OK; 897ae904beaSFeras Daoud } 898ae904beaSFeras Daoud 8991436de0bSEran Ben Elisha static void mlx5_timecounter_init(struct mlx5_core_dev *mdev) 900ae904beaSFeras Daoud { 9017c39afb3SFeras Daoud struct mlx5_clock *clock = &mdev->clock; 902d6f3dc8fSEran Ben Elisha struct mlx5_timer *timer = &clock->timer; 903ae904beaSFeras Daoud u32 dev_freq; 904ae904beaSFeras Daoud 9057c39afb3SFeras Daoud dev_freq = MLX5_CAP_GEN(mdev, device_frequency_khz); 906d6f3dc8fSEran Ben Elisha timer->cycles.read = read_internal_timer; 907d6f3dc8fSEran Ben Elisha timer->cycles.shift = MLX5_CYCLES_SHIFT; 908d6f3dc8fSEran Ben Elisha timer->cycles.mult = clocksource_khz2mult(dev_freq, 909d6f3dc8fSEran Ben Elisha timer->cycles.shift); 910d6f3dc8fSEran Ben Elisha timer->nominal_c_mult = timer->cycles.mult; 911d6f3dc8fSEran Ben Elisha timer->cycles.mask = CLOCKSOURCE_MASK(41); 912ae904beaSFeras Daoud 913d6f3dc8fSEran Ben Elisha timecounter_init(&timer->tc, &timer->cycles, 914ae904beaSFeras Daoud ktime_to_ns(ktime_get_real())); 9151436de0bSEran Ben Elisha } 9161436de0bSEran Ben Elisha 9171436de0bSEran Ben Elisha static void mlx5_init_overflow_period(struct mlx5_clock *clock) 9181436de0bSEran Ben Elisha { 9191436de0bSEran Ben Elisha struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev, clock); 9201436de0bSEran Ben Elisha struct mlx5_ib_clock_info *clock_info = mdev->clock_info; 921d6f3dc8fSEran Ben Elisha struct mlx5_timer *timer = &clock->timer; 9221436de0bSEran Ben Elisha u64 overflow_cycles; 9231436de0bSEran Ben Elisha u64 frac = 0; 9241436de0bSEran Ben Elisha u64 ns; 925ae904beaSFeras Daoud 926ae904beaSFeras Daoud /* Calculate period in seconds to call the overflow watchdog - to make 9275d867836SMiroslav Lichvar * sure counter is checked at least twice every wrap around. 92833180beeSAriel Levkovich * The period is calculated as the minimum between max HW cycles count 92933180beeSAriel Levkovich * (The clock source mask) and max amount of cycles that can be 93033180beeSAriel Levkovich * multiplied by clock multiplier where the result doesn't exceed 93133180beeSAriel Levkovich * 64bits. 932ae904beaSFeras Daoud */ 933d6f3dc8fSEran Ben Elisha overflow_cycles = div64_u64(~0ULL >> 1, timer->cycles.mult); 934d6f3dc8fSEran Ben Elisha overflow_cycles = min(overflow_cycles, div_u64(timer->cycles.mask, 3)); 93533180beeSAriel Levkovich 936d6f3dc8fSEran Ben Elisha ns = cyclecounter_cyc2ns(&timer->cycles, overflow_cycles, 937ae904beaSFeras Daoud frac, &frac); 93833180beeSAriel Levkovich do_div(ns, NSEC_PER_SEC / HZ); 939d6f3dc8fSEran Ben Elisha timer->overflow_period = ns; 940ae904beaSFeras Daoud 941d6f3dc8fSEran Ben Elisha INIT_DELAYED_WORK(&timer->overflow_work, mlx5_timestamp_overflow); 942d6f3dc8fSEran Ben Elisha if (timer->overflow_period) 943d6f3dc8fSEran Ben Elisha schedule_delayed_work(&timer->overflow_work, 0); 944ae904beaSFeras Daoud else 9451436de0bSEran Ben Elisha mlx5_core_warn(mdev, 9461436de0bSEran Ben Elisha "invalid overflow period, overflow_work is not scheduled\n"); 9471436de0bSEran Ben Elisha 9481436de0bSEran Ben Elisha if (clock_info) 949d6f3dc8fSEran Ben Elisha clock_info->overflow_period = timer->overflow_period; 9501436de0bSEran Ben Elisha } 9511436de0bSEran Ben Elisha 9521436de0bSEran Ben Elisha static void mlx5_init_clock_info(struct mlx5_core_dev *mdev) 9531436de0bSEran Ben Elisha { 9541436de0bSEran Ben Elisha struct mlx5_clock *clock = &mdev->clock; 9551436de0bSEran Ben Elisha struct mlx5_ib_clock_info *info; 956d6f3dc8fSEran Ben Elisha struct mlx5_timer *timer; 9571436de0bSEran Ben Elisha 9581436de0bSEran Ben Elisha mdev->clock_info = (struct mlx5_ib_clock_info *)get_zeroed_page(GFP_KERNEL); 9591436de0bSEran Ben Elisha if (!mdev->clock_info) { 9601436de0bSEran Ben Elisha mlx5_core_warn(mdev, "Failed to allocate IB clock info page\n"); 9611436de0bSEran Ben Elisha return; 9621436de0bSEran Ben Elisha } 9631436de0bSEran Ben Elisha 9641436de0bSEran Ben Elisha info = mdev->clock_info; 965d6f3dc8fSEran Ben Elisha timer = &clock->timer; 9661436de0bSEran Ben Elisha 967d6f3dc8fSEran Ben Elisha info->nsec = timer->tc.nsec; 968d6f3dc8fSEran Ben Elisha info->cycles = timer->tc.cycle_last; 969d6f3dc8fSEran Ben Elisha info->mask = timer->cycles.mask; 970d6f3dc8fSEran Ben Elisha info->mult = timer->nominal_c_mult; 971d6f3dc8fSEran Ben Elisha info->shift = timer->cycles.shift; 972d6f3dc8fSEran Ben Elisha info->frac = timer->tc.frac; 9731436de0bSEran Ben Elisha } 9741436de0bSEran Ben Elisha 975432119deSAya Levin static void mlx5_init_timer_clock(struct mlx5_core_dev *mdev) 976432119deSAya Levin { 977432119deSAya Levin struct mlx5_clock *clock = &mdev->clock; 978432119deSAya Levin 979432119deSAya Levin mlx5_timecounter_init(mdev); 980432119deSAya Levin mlx5_init_clock_info(mdev); 981432119deSAya Levin mlx5_init_overflow_period(clock); 982432119deSAya Levin clock->ptp_info = mlx5_ptp_clock_info; 983432119deSAya Levin 984432119deSAya Levin if (mlx5_real_time_mode(mdev)) { 985432119deSAya Levin struct timespec64 ts; 986432119deSAya Levin 987432119deSAya Levin ktime_get_real_ts64(&ts); 988432119deSAya Levin mlx5_ptp_settime(&clock->ptp_info, &ts); 989432119deSAya Levin } 990432119deSAya Levin } 991432119deSAya Levin 992302522e6SAya Levin static void mlx5_init_pps(struct mlx5_core_dev *mdev) 993302522e6SAya Levin { 994302522e6SAya Levin struct mlx5_clock *clock = &mdev->clock; 995302522e6SAya Levin 996302522e6SAya Levin if (!MLX5_PPS_CAP(mdev)) 997302522e6SAya Levin return; 998302522e6SAya Levin 999302522e6SAya Levin mlx5_get_pps_caps(mdev); 1000302522e6SAya Levin mlx5_init_pin_config(clock); 1001302522e6SAya Levin } 1002302522e6SAya Levin 10031436de0bSEran Ben Elisha void mlx5_init_clock(struct mlx5_core_dev *mdev) 10041436de0bSEran Ben Elisha { 10051436de0bSEran Ben Elisha struct mlx5_clock *clock = &mdev->clock; 10061436de0bSEran Ben Elisha 10071436de0bSEran Ben Elisha if (!MLX5_CAP_GEN(mdev, device_frequency_khz)) { 10081436de0bSEran Ben Elisha mlx5_core_warn(mdev, "invalid device_frequency_khz, aborting HW clock init\n"); 10091436de0bSEran Ben Elisha return; 10101436de0bSEran Ben Elisha } 10111436de0bSEran Ben Elisha 10121436de0bSEran Ben Elisha seqlock_init(&clock->lock); 1013432119deSAya Levin mlx5_init_timer_clock(mdev); 10141436de0bSEran Ben Elisha INIT_WORK(&clock->pps_info.out_work, mlx5_pps_out); 1015ae904beaSFeras Daoud 1016ae904beaSFeras Daoud /* Configure the PHC */ 10177c39afb3SFeras Daoud clock->ptp_info = mlx5_ptp_clock_info; 1018ae904beaSFeras Daoud 1019ae904beaSFeras Daoud /* Initialize 1PPS data structures */ 1020302522e6SAya Levin mlx5_init_pps(mdev); 1021ae904beaSFeras Daoud 10227c39afb3SFeras Daoud clock->ptp = ptp_clock_register(&clock->ptp_info, 10237c39afb3SFeras Daoud &mdev->pdev->dev); 10247c39afb3SFeras Daoud if (IS_ERR(clock->ptp)) { 10257c39afb3SFeras Daoud mlx5_core_warn(mdev, "ptp_clock_register failed %ld\n", 10267c39afb3SFeras Daoud PTR_ERR(clock->ptp)); 10277c39afb3SFeras Daoud clock->ptp = NULL; 1028ae904beaSFeras Daoud } 102941069256SSaeed Mahameed 103041069256SSaeed Mahameed MLX5_NB_INIT(&clock->pps_nb, mlx5_pps_event, PPS_EVENT); 103141069256SSaeed Mahameed mlx5_eq_notifier_register(mdev, &clock->pps_nb); 1032ae904beaSFeras Daoud } 1033ae904beaSFeras Daoud 10347c39afb3SFeras Daoud void mlx5_cleanup_clock(struct mlx5_core_dev *mdev) 1035ae904beaSFeras Daoud { 10367c39afb3SFeras Daoud struct mlx5_clock *clock = &mdev->clock; 1037ae904beaSFeras Daoud 10387c39afb3SFeras Daoud if (!MLX5_CAP_GEN(mdev, device_frequency_khz)) 1039ae904beaSFeras Daoud return; 1040ae904beaSFeras Daoud 104141069256SSaeed Mahameed mlx5_eq_notifier_unregister(mdev, &clock->pps_nb); 10427c39afb3SFeras Daoud if (clock->ptp) { 10437c39afb3SFeras Daoud ptp_clock_unregister(clock->ptp); 10447c39afb3SFeras Daoud clock->ptp = NULL; 1045ae904beaSFeras Daoud } 1046ae904beaSFeras Daoud 10477c39afb3SFeras Daoud cancel_work_sync(&clock->pps_info.out_work); 1048d6f3dc8fSEran Ben Elisha cancel_delayed_work_sync(&clock->timer.overflow_work); 104924d33d2cSFeras Daoud 105024d33d2cSFeras Daoud if (mdev->clock_info) { 1051ddcdc368SJason Gunthorpe free_page((unsigned long)mdev->clock_info); 105224d33d2cSFeras Daoud mdev->clock_info = NULL; 105324d33d2cSFeras Daoud } 105424d33d2cSFeras Daoud 10557c39afb3SFeras Daoud kfree(clock->ptp_info.pin_config); 1056ae904beaSFeras Daoud } 1057