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