1ec693d47SAmir Vadai /*
2ec693d47SAmir Vadai * Copyright (c) 2012 Mellanox Technologies. All rights reserved.
3ec693d47SAmir Vadai *
4ec693d47SAmir Vadai * This software is available to you under a choice of one of two
5ec693d47SAmir Vadai * licenses. You may choose to be licensed under the terms of the GNU
6ec693d47SAmir Vadai * General Public License (GPL) Version 2, available from the file
7ec693d47SAmir Vadai * COPYING in the main directory of this source tree, or the
8ec693d47SAmir Vadai * OpenIB.org BSD license below:
9ec693d47SAmir Vadai *
10ec693d47SAmir Vadai * Redistribution and use in source and binary forms, with or
11ec693d47SAmir Vadai * without modification, are permitted provided that the following
12ec693d47SAmir Vadai * conditions are met:
13ec693d47SAmir Vadai *
14ec693d47SAmir Vadai * - Redistributions of source code must retain the above
15ec693d47SAmir Vadai * copyright notice, this list of conditions and the following
16ec693d47SAmir Vadai * disclaimer.
17ec693d47SAmir Vadai *
18ec693d47SAmir Vadai * - Redistributions in binary form must reproduce the above
19ec693d47SAmir Vadai * copyright notice, this list of conditions and the following
20ec693d47SAmir Vadai * disclaimer in the documentation and/or other materials
21ec693d47SAmir Vadai * provided with the distribution.
22ec693d47SAmir Vadai *
23ec693d47SAmir Vadai * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24ec693d47SAmir Vadai * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25ec693d47SAmir Vadai * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26ec693d47SAmir Vadai * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27ec693d47SAmir Vadai * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28ec693d47SAmir Vadai * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29ec693d47SAmir Vadai * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30ec693d47SAmir Vadai * SOFTWARE.
31ec693d47SAmir Vadai *
32ec693d47SAmir Vadai */
33ec693d47SAmir Vadai
34ec693d47SAmir Vadai #include <linux/mlx4/device.h>
35d9f39373SRichard Cochran #include <linux/clocksource.h>
36ec693d47SAmir Vadai
37ec693d47SAmir Vadai #include "mlx4_en.h"
38ec693d47SAmir Vadai
39ec693d47SAmir Vadai /* mlx4_en_read_clock - read raw cycle counter (to be used by time counter)
40ec693d47SAmir Vadai */
mlx4_en_read_clock(const struct cyclecounter * tc)41a5a1d1c2SThomas Gleixner static u64 mlx4_en_read_clock(const struct cyclecounter *tc)
42ec693d47SAmir Vadai {
43ec693d47SAmir Vadai struct mlx4_en_dev *mdev =
44ec693d47SAmir Vadai container_of(tc, struct mlx4_en_dev, cycles);
45ec693d47SAmir Vadai struct mlx4_dev *dev = mdev->dev;
46ec693d47SAmir Vadai
47ec693d47SAmir Vadai return mlx4_read_clock(dev) & tc->mask;
48ec693d47SAmir Vadai }
49ec693d47SAmir Vadai
mlx4_en_get_cqe_ts(struct mlx4_cqe * cqe)50ec693d47SAmir Vadai u64 mlx4_en_get_cqe_ts(struct mlx4_cqe *cqe)
51ec693d47SAmir Vadai {
52ec693d47SAmir Vadai u64 hi, lo;
53ec693d47SAmir Vadai struct mlx4_ts_cqe *ts_cqe = (struct mlx4_ts_cqe *)cqe;
54ec693d47SAmir Vadai
55ec693d47SAmir Vadai lo = (u64)be16_to_cpu(ts_cqe->timestamp_lo);
56ec693d47SAmir Vadai hi = ((u64)be32_to_cpu(ts_cqe->timestamp_hi) + !lo) << 16;
57ec693d47SAmir Vadai
58ec693d47SAmir Vadai return hi | lo;
59ec693d47SAmir Vadai }
60ec693d47SAmir Vadai
mlx4_en_get_hwtstamp(struct mlx4_en_dev * mdev,u64 timestamp)61*ab46182dSStanislav Fomichev u64 mlx4_en_get_hwtstamp(struct mlx4_en_dev *mdev, u64 timestamp)
62ec693d47SAmir Vadai {
6399f5711eSEric Dumazet unsigned int seq;
64ec693d47SAmir Vadai u64 nsec;
65ec693d47SAmir Vadai
6699f5711eSEric Dumazet do {
6799f5711eSEric Dumazet seq = read_seqbegin(&mdev->clock_lock);
68ec693d47SAmir Vadai nsec = timecounter_cyc2time(&mdev->clock, timestamp);
6999f5711eSEric Dumazet } while (read_seqretry(&mdev->clock_lock, seq));
70ec693d47SAmir Vadai
71*ab46182dSStanislav Fomichev return ns_to_ktime(nsec);
72*ab46182dSStanislav Fomichev }
73*ab46182dSStanislav Fomichev
mlx4_en_fill_hwtstamps(struct mlx4_en_dev * mdev,struct skb_shared_hwtstamps * hwts,u64 timestamp)74*ab46182dSStanislav Fomichev void mlx4_en_fill_hwtstamps(struct mlx4_en_dev *mdev,
75*ab46182dSStanislav Fomichev struct skb_shared_hwtstamps *hwts,
76*ab46182dSStanislav Fomichev u64 timestamp)
77*ab46182dSStanislav Fomichev {
78ec693d47SAmir Vadai memset(hwts, 0, sizeof(struct skb_shared_hwtstamps));
79*ab46182dSStanislav Fomichev hwts->hwtstamp = mlx4_en_get_hwtstamp(mdev, timestamp);
80ec693d47SAmir Vadai }
81ec693d47SAmir Vadai
82ad7d4eaeSShawn Bohrer /**
83ad7d4eaeSShawn Bohrer * mlx4_en_remove_timestamp - disable PTP device
84ad7d4eaeSShawn Bohrer * @mdev: board private structure
85ad7d4eaeSShawn Bohrer *
86ad7d4eaeSShawn Bohrer * Stop the PTP support.
87ad7d4eaeSShawn Bohrer **/
mlx4_en_remove_timestamp(struct mlx4_en_dev * mdev)88ad7d4eaeSShawn Bohrer void mlx4_en_remove_timestamp(struct mlx4_en_dev *mdev)
89ad7d4eaeSShawn Bohrer {
90ad7d4eaeSShawn Bohrer if (mdev->ptp_clock) {
91ad7d4eaeSShawn Bohrer ptp_clock_unregister(mdev->ptp_clock);
92ad7d4eaeSShawn Bohrer mdev->ptp_clock = NULL;
93ad7d4eaeSShawn Bohrer mlx4_info(mdev, "removed PHC\n");
94ad7d4eaeSShawn Bohrer }
95ad7d4eaeSShawn Bohrer }
96ad7d4eaeSShawn Bohrer
9747d3a075SEric Dumazet #define MLX4_EN_WRAP_AROUND_SEC 10UL
9847d3a075SEric Dumazet /* By scheduling the overflow check every 5 seconds, we have a reasonably
9947d3a075SEric Dumazet * good chance we wont miss a wrap around.
10047d3a075SEric Dumazet * TOTO: Use a timer instead of a work queue to increase the guarantee.
10147d3a075SEric Dumazet */
10247d3a075SEric Dumazet #define MLX4_EN_OVERFLOW_PERIOD (MLX4_EN_WRAP_AROUND_SEC * HZ / 2)
10347d3a075SEric Dumazet
mlx4_en_ptp_overflow_check(struct mlx4_en_dev * mdev)104ad7d4eaeSShawn Bohrer void mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev)
105ad7d4eaeSShawn Bohrer {
106ad7d4eaeSShawn Bohrer bool timeout = time_is_before_jiffies(mdev->last_overflow_check +
10747d3a075SEric Dumazet MLX4_EN_OVERFLOW_PERIOD);
108ad7d4eaeSShawn Bohrer unsigned long flags;
109ad7d4eaeSShawn Bohrer
110ad7d4eaeSShawn Bohrer if (timeout) {
11199f5711eSEric Dumazet write_seqlock_irqsave(&mdev->clock_lock, flags);
112ad7d4eaeSShawn Bohrer timecounter_read(&mdev->clock);
11399f5711eSEric Dumazet write_sequnlock_irqrestore(&mdev->clock_lock, flags);
114ad7d4eaeSShawn Bohrer mdev->last_overflow_check = jiffies;
115ad7d4eaeSShawn Bohrer }
116ad7d4eaeSShawn Bohrer }
117ad7d4eaeSShawn Bohrer
118ad7d4eaeSShawn Bohrer /**
1196ed79596SJacob Keller * mlx4_en_phc_adjfine - adjust the frequency of the hardware clock
120ad7d4eaeSShawn Bohrer * @ptp: ptp clock structure
1216ed79596SJacob Keller * @scaled_ppm: Desired frequency change in scaled parts per million
122ad7d4eaeSShawn Bohrer *
1236ed79596SJacob Keller * Adjust the frequency of the PHC cycle counter by the indicated scaled_ppm
1246ed79596SJacob Keller * from the base frequency.
1256ed79596SJacob Keller *
1266ed79596SJacob Keller * Scaled parts per million is ppm with a 16-bit binary fractional field.
127ad7d4eaeSShawn Bohrer **/
mlx4_en_phc_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)1286ed79596SJacob Keller static int mlx4_en_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
129ad7d4eaeSShawn Bohrer {
1306ed79596SJacob Keller u32 mult;
131ad7d4eaeSShawn Bohrer unsigned long flags;
132ad7d4eaeSShawn Bohrer struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
133ad7d4eaeSShawn Bohrer ptp_clock_info);
134ad7d4eaeSShawn Bohrer
1356ed79596SJacob Keller mult = (u32)adjust_by_scaled_ppm(mdev->nominal_c_mult, scaled_ppm);
136ad7d4eaeSShawn Bohrer
13799f5711eSEric Dumazet write_seqlock_irqsave(&mdev->clock_lock, flags);
138ad7d4eaeSShawn Bohrer timecounter_read(&mdev->clock);
1396ed79596SJacob Keller mdev->cycles.mult = mult;
14099f5711eSEric Dumazet write_sequnlock_irqrestore(&mdev->clock_lock, flags);
141ad7d4eaeSShawn Bohrer
142ad7d4eaeSShawn Bohrer return 0;
143ad7d4eaeSShawn Bohrer }
144ad7d4eaeSShawn Bohrer
145ad7d4eaeSShawn Bohrer /**
146ad7d4eaeSShawn Bohrer * mlx4_en_phc_adjtime - Shift the time of the hardware clock
147ad7d4eaeSShawn Bohrer * @ptp: ptp clock structure
148ad7d4eaeSShawn Bohrer * @delta: Desired change in nanoseconds
149ad7d4eaeSShawn Bohrer *
150ad7d4eaeSShawn Bohrer * Adjust the timer by resetting the timecounter structure.
151ad7d4eaeSShawn Bohrer **/
mlx4_en_phc_adjtime(struct ptp_clock_info * ptp,s64 delta)152ad7d4eaeSShawn Bohrer static int mlx4_en_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
153ad7d4eaeSShawn Bohrer {
154ad7d4eaeSShawn Bohrer struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
155ad7d4eaeSShawn Bohrer ptp_clock_info);
156ad7d4eaeSShawn Bohrer unsigned long flags;
157ad7d4eaeSShawn Bohrer
15899f5711eSEric Dumazet write_seqlock_irqsave(&mdev->clock_lock, flags);
159ce51ff09SRichard Cochran timecounter_adjtime(&mdev->clock, delta);
16099f5711eSEric Dumazet write_sequnlock_irqrestore(&mdev->clock_lock, flags);
161ad7d4eaeSShawn Bohrer
162ad7d4eaeSShawn Bohrer return 0;
163ad7d4eaeSShawn Bohrer }
164ad7d4eaeSShawn Bohrer
165ad7d4eaeSShawn Bohrer /**
166ad7d4eaeSShawn Bohrer * mlx4_en_phc_gettime - Reads the current time from the hardware clock
167ad7d4eaeSShawn Bohrer * @ptp: ptp clock structure
168ad7d4eaeSShawn Bohrer * @ts: timespec structure to hold the current time value
169ad7d4eaeSShawn Bohrer *
170ad7d4eaeSShawn Bohrer * Read the timecounter and return the correct value in ns after converting
171ad7d4eaeSShawn Bohrer * it into a struct timespec.
172ad7d4eaeSShawn Bohrer **/
mlx4_en_phc_gettime(struct ptp_clock_info * ptp,struct timespec64 * ts)173e394b805SRichard Cochran static int mlx4_en_phc_gettime(struct ptp_clock_info *ptp,
174e394b805SRichard Cochran struct timespec64 *ts)
175ad7d4eaeSShawn Bohrer {
176ad7d4eaeSShawn Bohrer struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
177ad7d4eaeSShawn Bohrer ptp_clock_info);
178ad7d4eaeSShawn Bohrer unsigned long flags;
179ad7d4eaeSShawn Bohrer u64 ns;
180ad7d4eaeSShawn Bohrer
18199f5711eSEric Dumazet write_seqlock_irqsave(&mdev->clock_lock, flags);
182ad7d4eaeSShawn Bohrer ns = timecounter_read(&mdev->clock);
18399f5711eSEric Dumazet write_sequnlock_irqrestore(&mdev->clock_lock, flags);
184ad7d4eaeSShawn Bohrer
185f75419c8SRichard Cochran *ts = ns_to_timespec64(ns);
186ad7d4eaeSShawn Bohrer
187ad7d4eaeSShawn Bohrer return 0;
188ad7d4eaeSShawn Bohrer }
189ad7d4eaeSShawn Bohrer
190ad7d4eaeSShawn Bohrer /**
191ad7d4eaeSShawn Bohrer * mlx4_en_phc_settime - Set the current time on the hardware clock
192ad7d4eaeSShawn Bohrer * @ptp: ptp clock structure
193ad7d4eaeSShawn Bohrer * @ts: timespec containing the new time for the cycle counter
194ad7d4eaeSShawn Bohrer *
195ad7d4eaeSShawn Bohrer * Reset the timecounter to use a new base value instead of the kernel
196ad7d4eaeSShawn Bohrer * wall timer value.
197ad7d4eaeSShawn Bohrer **/
mlx4_en_phc_settime(struct ptp_clock_info * ptp,const struct timespec64 * ts)198ad7d4eaeSShawn Bohrer static int mlx4_en_phc_settime(struct ptp_clock_info *ptp,
199e394b805SRichard Cochran const struct timespec64 *ts)
200ad7d4eaeSShawn Bohrer {
201ad7d4eaeSShawn Bohrer struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev,
202ad7d4eaeSShawn Bohrer ptp_clock_info);
203e394b805SRichard Cochran u64 ns = timespec64_to_ns(ts);
204ad7d4eaeSShawn Bohrer unsigned long flags;
205ad7d4eaeSShawn Bohrer
206ad7d4eaeSShawn Bohrer /* reset the timecounter */
20799f5711eSEric Dumazet write_seqlock_irqsave(&mdev->clock_lock, flags);
208ad7d4eaeSShawn Bohrer timecounter_init(&mdev->clock, &mdev->cycles, ns);
20999f5711eSEric Dumazet write_sequnlock_irqrestore(&mdev->clock_lock, flags);
210ad7d4eaeSShawn Bohrer
211ad7d4eaeSShawn Bohrer return 0;
212ad7d4eaeSShawn Bohrer }
213ad7d4eaeSShawn Bohrer
214ad7d4eaeSShawn Bohrer /**
215ad7d4eaeSShawn Bohrer * mlx4_en_phc_enable - enable or disable an ancillary feature
216ad7d4eaeSShawn Bohrer * @ptp: ptp clock structure
217ad7d4eaeSShawn Bohrer * @request: Desired resource to enable or disable
218ad7d4eaeSShawn Bohrer * @on: Caller passes one to enable or zero to disable
219ad7d4eaeSShawn Bohrer *
220ad7d4eaeSShawn Bohrer * Enable (or disable) ancillary features of the PHC subsystem.
221ad7d4eaeSShawn Bohrer * Currently, no ancillary features are supported.
222ad7d4eaeSShawn Bohrer **/
mlx4_en_phc_enable(struct ptp_clock_info __always_unused * ptp,struct ptp_clock_request __always_unused * request,int __always_unused on)223ad7d4eaeSShawn Bohrer static int mlx4_en_phc_enable(struct ptp_clock_info __always_unused *ptp,
224ad7d4eaeSShawn Bohrer struct ptp_clock_request __always_unused *request,
225ad7d4eaeSShawn Bohrer int __always_unused on)
226ad7d4eaeSShawn Bohrer {
227ad7d4eaeSShawn Bohrer return -EOPNOTSUPP;
228ad7d4eaeSShawn Bohrer }
229ad7d4eaeSShawn Bohrer
230ad7d4eaeSShawn Bohrer static const struct ptp_clock_info mlx4_en_ptp_clock_info = {
231ad7d4eaeSShawn Bohrer .owner = THIS_MODULE,
232ad7d4eaeSShawn Bohrer .max_adj = 100000000,
233ad7d4eaeSShawn Bohrer .n_alarm = 0,
234ad7d4eaeSShawn Bohrer .n_ext_ts = 0,
235ad7d4eaeSShawn Bohrer .n_per_out = 0,
2364986b4f0SRichard Cochran .n_pins = 0,
237ad7d4eaeSShawn Bohrer .pps = 0,
2386ed79596SJacob Keller .adjfine = mlx4_en_phc_adjfine,
239ad7d4eaeSShawn Bohrer .adjtime = mlx4_en_phc_adjtime,
240e394b805SRichard Cochran .gettime64 = mlx4_en_phc_gettime,
241e394b805SRichard Cochran .settime64 = mlx4_en_phc_settime,
242ad7d4eaeSShawn Bohrer .enable = mlx4_en_phc_enable,
243ad7d4eaeSShawn Bohrer };
244ad7d4eaeSShawn Bohrer
24531c128b6SEugenia Emantayev
24631c128b6SEugenia Emantayev /* This function calculates the max shift that enables the user range
24731c128b6SEugenia Emantayev * of MLX4_EN_WRAP_AROUND_SEC values in the cycles register.
24831c128b6SEugenia Emantayev */
freq_to_shift(u16 freq)24931c128b6SEugenia Emantayev static u32 freq_to_shift(u16 freq)
25031c128b6SEugenia Emantayev {
25131c128b6SEugenia Emantayev u32 freq_khz = freq * 1000;
25231c128b6SEugenia Emantayev u64 max_val_cycles = freq_khz * 1000 * MLX4_EN_WRAP_AROUND_SEC;
25361b6034cSSlava Shwartsman u64 max_val_cycles_rounded = 1ULL << fls64(max_val_cycles - 1);
25431c128b6SEugenia Emantayev /* calculate max possible multiplier in order to fit in 64bit */
25561b6034cSSlava Shwartsman u64 max_mul = div64_u64(ULLONG_MAX, max_val_cycles_rounded);
25631c128b6SEugenia Emantayev
25731c128b6SEugenia Emantayev /* This comes from the reverse of clocksource_khz2mult */
25831c128b6SEugenia Emantayev return ilog2(div_u64(max_mul * freq_khz, 1000000));
25931c128b6SEugenia Emantayev }
26031c128b6SEugenia Emantayev
mlx4_en_init_timestamp(struct mlx4_en_dev * mdev)261ec693d47SAmir Vadai void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev)
262ec693d47SAmir Vadai {
263ec693d47SAmir Vadai struct mlx4_dev *dev = mdev->dev;
264ad7d4eaeSShawn Bohrer unsigned long flags;
265ec693d47SAmir Vadai
26690683061SEugenia Emantayev /* mlx4_en_init_timestamp is called for each netdev.
26790683061SEugenia Emantayev * mdev->ptp_clock is common for all ports, skip initialization if
26890683061SEugenia Emantayev * was done for other port.
26990683061SEugenia Emantayev */
27090683061SEugenia Emantayev if (mdev->ptp_clock)
27190683061SEugenia Emantayev return;
27290683061SEugenia Emantayev
27399f5711eSEric Dumazet seqlock_init(&mdev->clock_lock);
274ad7d4eaeSShawn Bohrer
275ec693d47SAmir Vadai memset(&mdev->cycles, 0, sizeof(mdev->cycles));
276ec693d47SAmir Vadai mdev->cycles.read = mlx4_en_read_clock;
277ec693d47SAmir Vadai mdev->cycles.mask = CLOCKSOURCE_MASK(48);
27831c128b6SEugenia Emantayev mdev->cycles.shift = freq_to_shift(dev->caps.hca_core_clock);
279ec693d47SAmir Vadai mdev->cycles.mult =
280ec693d47SAmir Vadai clocksource_khz2mult(1000 * dev->caps.hca_core_clock, mdev->cycles.shift);
281ad7d4eaeSShawn Bohrer mdev->nominal_c_mult = mdev->cycles.mult;
282ec693d47SAmir Vadai
28399f5711eSEric Dumazet write_seqlock_irqsave(&mdev->clock_lock, flags);
284ec693d47SAmir Vadai timecounter_init(&mdev->clock, &mdev->cycles,
285ec693d47SAmir Vadai ktime_to_ns(ktime_get_real()));
28699f5711eSEric Dumazet write_sequnlock_irqrestore(&mdev->clock_lock, flags);
287b6c39bfcSAmir Vadai
288ad7d4eaeSShawn Bohrer /* Configure the PHC */
289ad7d4eaeSShawn Bohrer mdev->ptp_clock_info = mlx4_en_ptp_clock_info;
290ad7d4eaeSShawn Bohrer snprintf(mdev->ptp_clock_info.name, 16, "mlx4 ptp");
291ad7d4eaeSShawn Bohrer
292ad7d4eaeSShawn Bohrer mdev->ptp_clock = ptp_clock_register(&mdev->ptp_clock_info,
293ad7d4eaeSShawn Bohrer &mdev->pdev->dev);
294ad7d4eaeSShawn Bohrer if (IS_ERR(mdev->ptp_clock)) {
295ad7d4eaeSShawn Bohrer mdev->ptp_clock = NULL;
296ad7d4eaeSShawn Bohrer mlx4_err(mdev, "ptp_clock_register failed\n");
297efee95f4SNicolas Pitre } else if (mdev->ptp_clock) {
298ad7d4eaeSShawn Bohrer mlx4_info(mdev, "registered PHC clock\n");
299b6c39bfcSAmir Vadai }
300b6c39bfcSAmir Vadai
301ec693d47SAmir Vadai }
302