linux/drivers/net/ethernet/mellanox/mlx4/en_clock.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2012 Mellanox Technologies. All rights reserved.
   3 *
   4 * This software is available to you under a choice of one of two
   5 * licenses.  You may choose to be licensed under the terms of the GNU
   6 * General Public License (GPL) Version 2, available from the file
   7 * COPYING in the main directory of this source tree, or the
   8 * OpenIB.org BSD license below:
   9 *
  10 *     Redistribution and use in source and binary forms, with or
  11 *     without modification, are permitted provided that the following
  12 *     conditions are met:
  13 *
  14 *      - Redistributions of source code must retain the above
  15 *        copyright notice, this list of conditions and the following
  16 *        disclaimer.
  17 *
  18 *      - Redistributions in binary form must reproduce the above
  19 *        copyright notice, this list of conditions and the following
  20 *        disclaimer in the documentation and/or other materials
  21 *        provided with the distribution.
  22 *
  23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  30 * SOFTWARE.
  31 *
  32 */
  33
  34#include <linux/mlx4/device.h>
  35#include <linux/clocksource.h>
  36
  37#include "mlx4_en.h"
  38
  39/* mlx4_en_read_clock - read raw cycle counter (to be used by time counter)
  40 */
  41static cycle_t mlx4_en_read_clock(const struct cyclecounter *tc)
  42{
  43        struct mlx4_en_dev *mdev =
  44                container_of(tc, struct mlx4_en_dev, cycles);
  45        struct mlx4_dev *dev = mdev->dev;
  46
  47        return mlx4_read_clock(dev) & tc->mask;
  48}
  49
  50u64 mlx4_en_get_cqe_ts(struct mlx4_cqe *cqe)
  51{
  52        u64 hi, lo;
  53        struct mlx4_ts_cqe *ts_cqe = (struct mlx4_ts_cqe *)cqe;
  54
  55        lo = (u64)be16_to_cpu(ts_cqe->timestamp_lo);
  56        hi = ((u64)be32_to_cpu(ts_cqe->timestamp_hi) + !lo) << 16;
  57
  58        return hi | lo;
  59}
  60
  61void mlx4_en_fill_hwtstamps(struct mlx4_en_dev *mdev,
  62                            struct skb_shared_hwtstamps *hwts,
  63                            u64 timestamp)
  64{
  65        unsigned long flags;
  66        u64 nsec;
  67
  68        read_lock_irqsave(&mdev->clock_lock, flags);
  69        nsec = timecounter_cyc2time(&mdev->clock, timestamp);
  70        read_unlock_irqrestore(&mdev->clock_lock, flags);
  71
  72        memset(hwts, 0, sizeof(struct skb_shared_hwtstamps));
  73        hwts->hwtstamp = ns_to_ktime(nsec);
  74}
  75
  76/**
  77 * mlx4_en_remove_timestamp - disable PTP device
  78 * @mdev: board private structure
  79 *
  80 * Stop the PTP support.
  81 **/
  82void mlx4_en_remove_timestamp(struct mlx4_en_dev *mdev)
  83{
  84        if (mdev->ptp_clock) {
  85                ptp_clock_unregister(mdev->ptp_clock);
  86                mdev->ptp_clock = NULL;
  87                mlx4_info(mdev, "removed PHC\n");
  88        }
  89}
  90
  91void mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev)
  92{
  93        bool timeout = time_is_before_jiffies(mdev->last_overflow_check +
  94                                              mdev->overflow_period);
  95        unsigned long flags;
  96
  97        if (timeout) {
  98                write_lock_irqsave(&mdev->clock_lock, flags);
  99                timecounter_read(&mdev->clock);
 100                write_unlock_irqrestore(&mdev->clock_lock, flags);
 101                mdev->last_overflow_check = jiffies;
 102        }
 103}
 104
 105/**
 106 * mlx4_en_phc_adjfreq - adjust the frequency of the hardware clock
 107 * @ptp: ptp clock structure
 108 * @delta: Desired frequency change in parts per billion
 109 *
 110 * Adjust the frequency of the PHC cycle counter by the indicated delta from
 111 * the base frequency.
 112 **/
 113static int mlx4_en_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta)
 114{
 115        u64 adj;
 116        u32 diff, mult;
 117        int neg_adj = 0;
 118        unsigned long flags;
 119        struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
 120                                                ptp_clock_info);
 121
 122        if (delta < 0) {
 123                neg_adj = 1;
 124                delta = -delta;
 125        }
 126        mult = mdev->nominal_c_mult;
 127        adj = mult;
 128        adj *= delta;
 129        diff = div_u64(adj, 1000000000ULL);
 130
 131        write_lock_irqsave(&mdev->clock_lock, flags);
 132        timecounter_read(&mdev->clock);
 133        mdev->cycles.mult = neg_adj ? mult - diff : mult + diff;
 134        write_unlock_irqrestore(&mdev->clock_lock, flags);
 135
 136        return 0;
 137}
 138
 139/**
 140 * mlx4_en_phc_adjtime - Shift the time of the hardware clock
 141 * @ptp: ptp clock structure
 142 * @delta: Desired change in nanoseconds
 143 *
 144 * Adjust the timer by resetting the timecounter structure.
 145 **/
 146static int mlx4_en_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
 147{
 148        struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
 149                                                ptp_clock_info);
 150        unsigned long flags;
 151
 152        write_lock_irqsave(&mdev->clock_lock, flags);
 153        timecounter_adjtime(&mdev->clock, delta);
 154        write_unlock_irqrestore(&mdev->clock_lock, flags);
 155
 156        return 0;
 157}
 158
 159/**
 160 * mlx4_en_phc_gettime - Reads the current time from the hardware clock
 161 * @ptp: ptp clock structure
 162 * @ts: timespec structure to hold the current time value
 163 *
 164 * Read the timecounter and return the correct value in ns after converting
 165 * it into a struct timespec.
 166 **/
 167static int mlx4_en_phc_gettime(struct ptp_clock_info *ptp,
 168                               struct timespec64 *ts)
 169{
 170        struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
 171                                                ptp_clock_info);
 172        unsigned long flags;
 173        u64 ns;
 174
 175        write_lock_irqsave(&mdev->clock_lock, flags);
 176        ns = timecounter_read(&mdev->clock);
 177        write_unlock_irqrestore(&mdev->clock_lock, flags);
 178
 179        *ts = ns_to_timespec64(ns);
 180
 181        return 0;
 182}
 183
 184/**
 185 * mlx4_en_phc_settime - Set the current time on the hardware clock
 186 * @ptp: ptp clock structure
 187 * @ts: timespec containing the new time for the cycle counter
 188 *
 189 * Reset the timecounter to use a new base value instead of the kernel
 190 * wall timer value.
 191 **/
 192static int mlx4_en_phc_settime(struct ptp_clock_info *ptp,
 193                               const struct timespec64 *ts)
 194{
 195        struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
 196                                                ptp_clock_info);
 197        u64 ns = timespec64_to_ns(ts);
 198        unsigned long flags;
 199
 200        /* reset the timecounter */
 201        write_lock_irqsave(&mdev->clock_lock, flags);
 202        timecounter_init(&mdev->clock, &mdev->cycles, ns);
 203        write_unlock_irqrestore(&mdev->clock_lock, flags);
 204
 205        return 0;
 206}
 207
 208/**
 209 * mlx4_en_phc_enable - enable or disable an ancillary feature
 210 * @ptp: ptp clock structure
 211 * @request: Desired resource to enable or disable
 212 * @on: Caller passes one to enable or zero to disable
 213 *
 214 * Enable (or disable) ancillary features of the PHC subsystem.
 215 * Currently, no ancillary features are supported.
 216 **/
 217static int mlx4_en_phc_enable(struct ptp_clock_info __always_unused *ptp,
 218                              struct ptp_clock_request __always_unused *request,
 219                              int __always_unused on)
 220{
 221        return -EOPNOTSUPP;
 222}
 223
 224static const struct ptp_clock_info mlx4_en_ptp_clock_info = {
 225        .owner          = THIS_MODULE,
 226        .max_adj        = 100000000,
 227        .n_alarm        = 0,
 228        .n_ext_ts       = 0,
 229        .n_per_out      = 0,
 230        .n_pins         = 0,
 231        .pps            = 0,
 232        .adjfreq        = mlx4_en_phc_adjfreq,
 233        .adjtime        = mlx4_en_phc_adjtime,
 234        .gettime64      = mlx4_en_phc_gettime,
 235        .settime64      = mlx4_en_phc_settime,
 236        .enable         = mlx4_en_phc_enable,
 237};
 238
 239void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev)
 240{
 241        struct mlx4_dev *dev = mdev->dev;
 242        unsigned long flags;
 243        u64 ns, zero = 0;
 244
 245        rwlock_init(&mdev->clock_lock);
 246
 247        memset(&mdev->cycles, 0, sizeof(mdev->cycles));
 248        mdev->cycles.read = mlx4_en_read_clock;
 249        mdev->cycles.mask = CLOCKSOURCE_MASK(48);
 250        /* Using shift to make calculation more accurate. Since current HW
 251         * clock frequency is 427 MHz, and cycles are given using a 48 bits
 252         * register, the biggest shift when calculating using u64, is 14
 253         * (max_cycles * multiplier < 2^64)
 254         */
 255        mdev->cycles.shift = 14;
 256        mdev->cycles.mult =
 257                clocksource_khz2mult(1000 * dev->caps.hca_core_clock, mdev->cycles.shift);
 258        mdev->nominal_c_mult = mdev->cycles.mult;
 259
 260        write_lock_irqsave(&mdev->clock_lock, flags);
 261        timecounter_init(&mdev->clock, &mdev->cycles,
 262                         ktime_to_ns(ktime_get_real()));
 263        write_unlock_irqrestore(&mdev->clock_lock, flags);
 264
 265        /* Calculate period in seconds to call the overflow watchdog - to make
 266         * sure counter is checked at least once every wrap around.
 267         */
 268        ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask, zero, &zero);
 269        do_div(ns, NSEC_PER_SEC / 2 / HZ);
 270        mdev->overflow_period = ns;
 271
 272        /* Configure the PHC */
 273        mdev->ptp_clock_info = mlx4_en_ptp_clock_info;
 274        snprintf(mdev->ptp_clock_info.name, 16, "mlx4 ptp");
 275
 276        mdev->ptp_clock = ptp_clock_register(&mdev->ptp_clock_info,
 277                                             &mdev->pdev->dev);
 278        if (IS_ERR(mdev->ptp_clock)) {
 279                mdev->ptp_clock = NULL;
 280                mlx4_err(mdev, "ptp_clock_register failed\n");
 281        } else {
 282                mlx4_info(mdev, "registered PHC clock\n");
 283        }
 284
 285}
 286