]>
Commit | Line | Data |
---|---|---|
ef9814de EBE |
1 | /* |
2 | * Copyright (c) 2015, 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 | #include <linux/clocksource.h> | |
24d33d2c FD |
34 | #include <linux/highmem.h> |
35 | #include <rdma/mlx5-abi.h> | |
ef9814de | 36 | #include "en.h" |
9afe9a53 | 37 | #include "clock.h" |
ef9814de EBE |
38 | |
39 | enum { | |
7c39afb3 | 40 | MLX5_CYCLES_SHIFT = 23 |
ef9814de EBE |
41 | }; |
42 | ||
ee7f1220 | 43 | enum { |
7c39afb3 FD |
44 | MLX5_PIN_MODE_IN = 0x0, |
45 | MLX5_PIN_MODE_OUT = 0x1, | |
ee7f1220 EE |
46 | }; |
47 | ||
48 | enum { | |
7c39afb3 FD |
49 | MLX5_OUT_PATTERN_PULSE = 0x0, |
50 | MLX5_OUT_PATTERN_PERIODIC = 0x1, | |
ee7f1220 EE |
51 | }; |
52 | ||
53 | enum { | |
7c39afb3 FD |
54 | MLX5_EVENT_MODE_DISABLE = 0x0, |
55 | MLX5_EVENT_MODE_REPETETIVE = 0x1, | |
56 | MLX5_EVENT_MODE_ONCE_TILL_ARM = 0x2, | |
ee7f1220 EE |
57 | }; |
58 | ||
fa367688 | 59 | enum { |
7c39afb3 FD |
60 | MLX5_MTPPS_FS_ENABLE = BIT(0x0), |
61 | MLX5_MTPPS_FS_PATTERN = BIT(0x2), | |
62 | MLX5_MTPPS_FS_PIN_MODE = BIT(0x3), | |
63 | MLX5_MTPPS_FS_TIME_STAMP = BIT(0x4), | |
64 | MLX5_MTPPS_FS_OUT_PULSE_DURATION = BIT(0x5), | |
65 | MLX5_MTPPS_FS_ENH_OUT_PER_ADJ = BIT(0x7), | |
fa367688 EE |
66 | }; |
67 | ||
7c39afb3 | 68 | static u64 read_internal_timer(const struct cyclecounter *cc) |
ef9814de | 69 | { |
7c39afb3 FD |
70 | struct mlx5_clock *clock = container_of(cc, struct mlx5_clock, cycles); |
71 | struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev, | |
72 | clock); | |
ef9814de | 73 | |
7c39afb3 | 74 | return mlx5_read_internal_timer(mdev) & cc->mask; |
ef9814de EBE |
75 | } |
76 | ||
24d33d2c FD |
77 | static void mlx5_update_clock_info_page(struct mlx5_core_dev *mdev) |
78 | { | |
79 | struct mlx5_ib_clock_info *clock_info = mdev->clock_info; | |
80 | struct mlx5_clock *clock = &mdev->clock; | |
81 | u32 sign; | |
82 | ||
83 | if (!clock_info) | |
84 | return; | |
85 | ||
86 | sign = smp_load_acquire(&clock_info->sign); | |
87 | smp_store_mb(clock_info->sign, | |
88 | sign | MLX5_IB_CLOCK_INFO_KERNEL_UPDATING); | |
89 | ||
90 | clock_info->cycles = clock->tc.cycle_last; | |
91 | clock_info->mult = clock->cycles.mult; | |
92 | clock_info->nsec = clock->tc.nsec; | |
93 | clock_info->frac = clock->tc.frac; | |
94 | ||
95 | smp_store_release(&clock_info->sign, | |
96 | sign + MLX5_IB_CLOCK_INFO_KERNEL_UPDATING * 2); | |
97 | } | |
98 | ||
7c39afb3 | 99 | static void mlx5_pps_out(struct work_struct *work) |
4272f9b8 | 100 | { |
7c39afb3 FD |
101 | struct mlx5_pps *pps_info = container_of(work, struct mlx5_pps, |
102 | out_work); | |
103 | struct mlx5_clock *clock = container_of(pps_info, struct mlx5_clock, | |
104 | pps_info); | |
105 | struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev, | |
106 | clock); | |
4272f9b8 EE |
107 | u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; |
108 | unsigned long flags; | |
109 | int i; | |
110 | ||
7c39afb3 | 111 | for (i = 0; i < clock->ptp_info.n_pins; i++) { |
4272f9b8 EE |
112 | u64 tstart; |
113 | ||
7c39afb3 FD |
114 | write_lock_irqsave(&clock->lock, flags); |
115 | tstart = clock->pps_info.start[i]; | |
116 | clock->pps_info.start[i] = 0; | |
117 | write_unlock_irqrestore(&clock->lock, flags); | |
4272f9b8 EE |
118 | if (!tstart) |
119 | continue; | |
120 | ||
121 | MLX5_SET(mtpps_reg, in, pin, i); | |
122 | MLX5_SET64(mtpps_reg, in, time_stamp, tstart); | |
7c39afb3 FD |
123 | MLX5_SET(mtpps_reg, in, field_select, MLX5_MTPPS_FS_TIME_STAMP); |
124 | mlx5_set_mtpps(mdev, in, sizeof(in)); | |
4272f9b8 EE |
125 | } |
126 | } | |
127 | ||
7c39afb3 | 128 | static void mlx5_timestamp_overflow(struct work_struct *work) |
ef9814de EBE |
129 | { |
130 | struct delayed_work *dwork = to_delayed_work(work); | |
7c39afb3 FD |
131 | struct mlx5_clock *clock = container_of(dwork, struct mlx5_clock, |
132 | overflow_work); | |
0ad9b204 | 133 | unsigned long flags; |
ef9814de | 134 | |
7c39afb3 FD |
135 | write_lock_irqsave(&clock->lock, flags); |
136 | timecounter_read(&clock->tc); | |
24d33d2c | 137 | mlx5_update_clock_info_page(clock->mdev); |
7c39afb3 FD |
138 | write_unlock_irqrestore(&clock->lock, flags); |
139 | schedule_delayed_work(&clock->overflow_work, clock->overflow_period); | |
ef9814de EBE |
140 | } |
141 | ||
7c39afb3 FD |
142 | static int mlx5_ptp_settime(struct ptp_clock_info *ptp, |
143 | const struct timespec64 *ts) | |
3d8c38af | 144 | { |
7c39afb3 FD |
145 | struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, |
146 | ptp_info); | |
3d8c38af | 147 | u64 ns = timespec64_to_ns(ts); |
0ad9b204 | 148 | unsigned long flags; |
3d8c38af | 149 | |
7c39afb3 FD |
150 | write_lock_irqsave(&clock->lock, flags); |
151 | timecounter_init(&clock->tc, &clock->cycles, ns); | |
24d33d2c | 152 | mlx5_update_clock_info_page(clock->mdev); |
7c39afb3 | 153 | write_unlock_irqrestore(&clock->lock, flags); |
3d8c38af EBE |
154 | |
155 | return 0; | |
156 | } | |
157 | ||
7c39afb3 | 158 | static int mlx5_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) |
3d8c38af | 159 | { |
7c39afb3 FD |
160 | struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, |
161 | ptp_info); | |
3d8c38af | 162 | u64 ns; |
0ad9b204 | 163 | unsigned long flags; |
3d8c38af | 164 | |
7c39afb3 FD |
165 | write_lock_irqsave(&clock->lock, flags); |
166 | ns = timecounter_read(&clock->tc); | |
167 | write_unlock_irqrestore(&clock->lock, flags); | |
3d8c38af EBE |
168 | |
169 | *ts = ns_to_timespec64(ns); | |
170 | ||
171 | return 0; | |
172 | } | |
173 | ||
7c39afb3 | 174 | static int mlx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) |
3d8c38af | 175 | { |
7c39afb3 FD |
176 | struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, |
177 | ptp_info); | |
0ad9b204 | 178 | unsigned long flags; |
3d8c38af | 179 | |
7c39afb3 FD |
180 | write_lock_irqsave(&clock->lock, flags); |
181 | timecounter_adjtime(&clock->tc, delta); | |
24d33d2c | 182 | mlx5_update_clock_info_page(clock->mdev); |
7c39afb3 | 183 | write_unlock_irqrestore(&clock->lock, flags); |
3d8c38af EBE |
184 | |
185 | return 0; | |
186 | } | |
187 | ||
7c39afb3 | 188 | static int mlx5_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) |
3d8c38af EBE |
189 | { |
190 | u64 adj; | |
191 | u32 diff; | |
0ad9b204 | 192 | unsigned long flags; |
3d8c38af | 193 | int neg_adj = 0; |
7c39afb3 FD |
194 | struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, |
195 | ptp_info); | |
3d8c38af EBE |
196 | |
197 | if (delta < 0) { | |
198 | neg_adj = 1; | |
199 | delta = -delta; | |
200 | } | |
201 | ||
7c39afb3 | 202 | adj = clock->nominal_c_mult; |
3d8c38af EBE |
203 | adj *= delta; |
204 | diff = div_u64(adj, 1000000000ULL); | |
205 | ||
7c39afb3 FD |
206 | write_lock_irqsave(&clock->lock, flags); |
207 | timecounter_read(&clock->tc); | |
208 | clock->cycles.mult = neg_adj ? clock->nominal_c_mult - diff : | |
209 | clock->nominal_c_mult + diff; | |
24d33d2c | 210 | mlx5_update_clock_info_page(clock->mdev); |
7c39afb3 | 211 | write_unlock_irqrestore(&clock->lock, flags); |
3d8c38af EBE |
212 | |
213 | return 0; | |
214 | } | |
215 | ||
7c39afb3 FD |
216 | static int mlx5_extts_configure(struct ptp_clock_info *ptp, |
217 | struct ptp_clock_request *rq, | |
218 | int on) | |
ee7f1220 | 219 | { |
7c39afb3 FD |
220 | struct mlx5_clock *clock = |
221 | container_of(ptp, struct mlx5_clock, ptp_info); | |
222 | struct mlx5_core_dev *mdev = | |
223 | container_of(clock, struct mlx5_core_dev, clock); | |
ee7f1220 | 224 | u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; |
49c5031c EE |
225 | u32 field_select = 0; |
226 | u8 pin_mode = 0; | |
ee7f1220 EE |
227 | u8 pattern = 0; |
228 | int pin = -1; | |
229 | int err = 0; | |
230 | ||
7c39afb3 | 231 | if (!MLX5_PPS_CAP(mdev)) |
ee7f1220 EE |
232 | return -EOPNOTSUPP; |
233 | ||
7c39afb3 | 234 | if (rq->extts.index >= clock->ptp_info.n_pins) |
ee7f1220 EE |
235 | return -EINVAL; |
236 | ||
237 | if (on) { | |
7c39afb3 | 238 | pin = ptp_find_pin(clock->ptp, PTP_PF_EXTTS, rq->extts.index); |
ee7f1220 EE |
239 | if (pin < 0) |
240 | return -EBUSY; | |
7c39afb3 | 241 | pin_mode = MLX5_PIN_MODE_IN; |
49c5031c | 242 | pattern = !!(rq->extts.flags & PTP_FALLING_EDGE); |
7c39afb3 FD |
243 | field_select = MLX5_MTPPS_FS_PIN_MODE | |
244 | MLX5_MTPPS_FS_PATTERN | | |
245 | MLX5_MTPPS_FS_ENABLE; | |
49c5031c EE |
246 | } else { |
247 | pin = rq->extts.index; | |
7c39afb3 | 248 | field_select = MLX5_MTPPS_FS_ENABLE; |
ee7f1220 EE |
249 | } |
250 | ||
ee7f1220 | 251 | MLX5_SET(mtpps_reg, in, pin, pin); |
49c5031c | 252 | MLX5_SET(mtpps_reg, in, pin_mode, pin_mode); |
ee7f1220 EE |
253 | MLX5_SET(mtpps_reg, in, pattern, pattern); |
254 | MLX5_SET(mtpps_reg, in, enable, on); | |
49c5031c | 255 | MLX5_SET(mtpps_reg, in, field_select, field_select); |
ee7f1220 | 256 | |
7c39afb3 | 257 | err = mlx5_set_mtpps(mdev, in, sizeof(in)); |
ee7f1220 EE |
258 | if (err) |
259 | return err; | |
260 | ||
7c39afb3 FD |
261 | return mlx5_set_mtppse(mdev, pin, 0, |
262 | MLX5_EVENT_MODE_REPETETIVE & on); | |
ee7f1220 EE |
263 | } |
264 | ||
7c39afb3 FD |
265 | static int mlx5_perout_configure(struct ptp_clock_info *ptp, |
266 | struct ptp_clock_request *rq, | |
267 | int on) | |
ee7f1220 | 268 | { |
7c39afb3 FD |
269 | struct mlx5_clock *clock = |
270 | container_of(ptp, struct mlx5_clock, ptp_info); | |
271 | struct mlx5_core_dev *mdev = | |
272 | container_of(clock, struct mlx5_core_dev, clock); | |
ee7f1220 | 273 | u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; |
4272f9b8 | 274 | u64 nsec_now, nsec_delta, time_stamp = 0; |
ee7f1220 EE |
275 | u64 cycles_now, cycles_delta; |
276 | struct timespec64 ts; | |
277 | unsigned long flags; | |
49c5031c EE |
278 | u32 field_select = 0; |
279 | u8 pin_mode = 0; | |
280 | u8 pattern = 0; | |
ee7f1220 | 281 | int pin = -1; |
4272f9b8 | 282 | int err = 0; |
ee7f1220 EE |
283 | s64 ns; |
284 | ||
7c39afb3 | 285 | if (!MLX5_PPS_CAP(mdev)) |
ee7f1220 EE |
286 | return -EOPNOTSUPP; |
287 | ||
7c39afb3 | 288 | if (rq->perout.index >= clock->ptp_info.n_pins) |
ee7f1220 EE |
289 | return -EINVAL; |
290 | ||
291 | if (on) { | |
7c39afb3 | 292 | pin = ptp_find_pin(clock->ptp, PTP_PF_PEROUT, |
ee7f1220 EE |
293 | rq->perout.index); |
294 | if (pin < 0) | |
295 | return -EBUSY; | |
ee7f1220 | 296 | |
7c39afb3 FD |
297 | pin_mode = MLX5_PIN_MODE_OUT; |
298 | pattern = MLX5_OUT_PATTERN_PERIODIC; | |
49c5031c EE |
299 | ts.tv_sec = rq->perout.period.sec; |
300 | ts.tv_nsec = rq->perout.period.nsec; | |
301 | ns = timespec64_to_ns(&ts); | |
302 | ||
ee7f1220 EE |
303 | if ((ns >> 1) != 500000000LL) |
304 | return -EINVAL; | |
49c5031c EE |
305 | |
306 | ts.tv_sec = rq->perout.start.sec; | |
307 | ts.tv_nsec = rq->perout.start.nsec; | |
308 | ns = timespec64_to_ns(&ts); | |
7c39afb3 FD |
309 | cycles_now = mlx5_read_internal_timer(mdev); |
310 | write_lock_irqsave(&clock->lock, flags); | |
311 | nsec_now = timecounter_cyc2time(&clock->tc, cycles_now); | |
49c5031c | 312 | nsec_delta = ns - nsec_now; |
7c39afb3 FD |
313 | cycles_delta = div64_u64(nsec_delta << clock->cycles.shift, |
314 | clock->cycles.mult); | |
315 | write_unlock_irqrestore(&clock->lock, flags); | |
49c5031c | 316 | time_stamp = cycles_now + cycles_delta; |
7c39afb3 FD |
317 | field_select = MLX5_MTPPS_FS_PIN_MODE | |
318 | MLX5_MTPPS_FS_PATTERN | | |
319 | MLX5_MTPPS_FS_ENABLE | | |
320 | MLX5_MTPPS_FS_TIME_STAMP; | |
49c5031c EE |
321 | } else { |
322 | pin = rq->perout.index; | |
7c39afb3 | 323 | field_select = MLX5_MTPPS_FS_ENABLE; |
49c5031c EE |
324 | } |
325 | ||
ee7f1220 | 326 | MLX5_SET(mtpps_reg, in, pin, pin); |
49c5031c EE |
327 | MLX5_SET(mtpps_reg, in, pin_mode, pin_mode); |
328 | MLX5_SET(mtpps_reg, in, pattern, pattern); | |
ee7f1220 EE |
329 | MLX5_SET(mtpps_reg, in, enable, on); |
330 | MLX5_SET64(mtpps_reg, in, time_stamp, time_stamp); | |
49c5031c EE |
331 | MLX5_SET(mtpps_reg, in, field_select, field_select); |
332 | ||
7c39afb3 | 333 | err = mlx5_set_mtpps(mdev, in, sizeof(in)); |
4272f9b8 EE |
334 | if (err) |
335 | return err; | |
336 | ||
7c39afb3 FD |
337 | return mlx5_set_mtppse(mdev, pin, 0, |
338 | MLX5_EVENT_MODE_REPETETIVE & on); | |
ee7f1220 EE |
339 | } |
340 | ||
7c39afb3 FD |
341 | static int mlx5_pps_configure(struct ptp_clock_info *ptp, |
342 | struct ptp_clock_request *rq, | |
343 | int on) | |
cf503308 | 344 | { |
7c39afb3 FD |
345 | struct mlx5_clock *clock = |
346 | container_of(ptp, struct mlx5_clock, ptp_info); | |
cf503308 | 347 | |
7c39afb3 | 348 | clock->pps_info.enabled = !!on; |
cf503308 EE |
349 | return 0; |
350 | } | |
351 | ||
7c39afb3 FD |
352 | static int mlx5_ptp_enable(struct ptp_clock_info *ptp, |
353 | struct ptp_clock_request *rq, | |
354 | int on) | |
ee7f1220 EE |
355 | { |
356 | switch (rq->type) { | |
357 | case PTP_CLK_REQ_EXTTS: | |
7c39afb3 | 358 | return mlx5_extts_configure(ptp, rq, on); |
ee7f1220 | 359 | case PTP_CLK_REQ_PEROUT: |
7c39afb3 | 360 | return mlx5_perout_configure(ptp, rq, on); |
cf503308 | 361 | case PTP_CLK_REQ_PPS: |
7c39afb3 | 362 | return mlx5_pps_configure(ptp, rq, on); |
ee7f1220 EE |
363 | default: |
364 | return -EOPNOTSUPP; | |
365 | } | |
366 | return 0; | |
367 | } | |
368 | ||
7c39afb3 FD |
369 | static int mlx5_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, |
370 | enum ptp_pin_function func, unsigned int chan) | |
ee7f1220 EE |
371 | { |
372 | return (func == PTP_PF_PHYSYNC) ? -EOPNOTSUPP : 0; | |
373 | } | |
374 | ||
7c39afb3 | 375 | static const struct ptp_clock_info mlx5_ptp_clock_info = { |
3d8c38af | 376 | .owner = THIS_MODULE, |
7c39afb3 | 377 | .name = "mlx5_p2p", |
3d8c38af EBE |
378 | .max_adj = 100000000, |
379 | .n_alarm = 0, | |
380 | .n_ext_ts = 0, | |
381 | .n_per_out = 0, | |
382 | .n_pins = 0, | |
383 | .pps = 0, | |
7c39afb3 FD |
384 | .adjfreq = mlx5_ptp_adjfreq, |
385 | .adjtime = mlx5_ptp_adjtime, | |
386 | .gettime64 = mlx5_ptp_gettime, | |
387 | .settime64 = mlx5_ptp_settime, | |
3d8c38af | 388 | .enable = NULL, |
ee7f1220 | 389 | .verify = NULL, |
3d8c38af EBE |
390 | }; |
391 | ||
7c39afb3 | 392 | static int mlx5_init_pin_config(struct mlx5_clock *clock) |
ee7f1220 EE |
393 | { |
394 | int i; | |
395 | ||
7c39afb3 | 396 | clock->ptp_info.pin_config = |
6396bb22 KC |
397 | kcalloc(clock->ptp_info.n_pins, |
398 | sizeof(*clock->ptp_info.pin_config), | |
399 | GFP_KERNEL); | |
7c39afb3 | 400 | if (!clock->ptp_info.pin_config) |
ee7f1220 | 401 | return -ENOMEM; |
7c39afb3 FD |
402 | clock->ptp_info.enable = mlx5_ptp_enable; |
403 | clock->ptp_info.verify = mlx5_ptp_verify; | |
404 | clock->ptp_info.pps = 1; | |
ee7f1220 | 405 | |
7c39afb3 FD |
406 | for (i = 0; i < clock->ptp_info.n_pins; i++) { |
407 | snprintf(clock->ptp_info.pin_config[i].name, | |
408 | sizeof(clock->ptp_info.pin_config[i].name), | |
ee7f1220 | 409 | "mlx5_pps%d", i); |
7c39afb3 FD |
410 | clock->ptp_info.pin_config[i].index = i; |
411 | clock->ptp_info.pin_config[i].func = PTP_PF_NONE; | |
412 | clock->ptp_info.pin_config[i].chan = i; | |
ee7f1220 EE |
413 | } |
414 | ||
415 | return 0; | |
416 | } | |
417 | ||
7c39afb3 | 418 | static void mlx5_get_pps_caps(struct mlx5_core_dev *mdev) |
ee7f1220 | 419 | { |
7c39afb3 | 420 | struct mlx5_clock *clock = &mdev->clock; |
ee7f1220 EE |
421 | u32 out[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; |
422 | ||
7c39afb3 FD |
423 | mlx5_query_mtpps(mdev, out, sizeof(out)); |
424 | ||
425 | clock->ptp_info.n_pins = MLX5_GET(mtpps_reg, out, | |
426 | cap_number_of_pps_pins); | |
427 | clock->ptp_info.n_ext_ts = MLX5_GET(mtpps_reg, out, | |
428 | cap_max_num_of_pps_in_pins); | |
429 | clock->ptp_info.n_per_out = MLX5_GET(mtpps_reg, out, | |
430 | cap_max_num_of_pps_out_pins); | |
431 | ||
432 | clock->pps_info.pin_caps[0] = MLX5_GET(mtpps_reg, out, cap_pin_0_mode); | |
433 | clock->pps_info.pin_caps[1] = MLX5_GET(mtpps_reg, out, cap_pin_1_mode); | |
434 | clock->pps_info.pin_caps[2] = MLX5_GET(mtpps_reg, out, cap_pin_2_mode); | |
435 | clock->pps_info.pin_caps[3] = MLX5_GET(mtpps_reg, out, cap_pin_3_mode); | |
436 | clock->pps_info.pin_caps[4] = MLX5_GET(mtpps_reg, out, cap_pin_4_mode); | |
437 | clock->pps_info.pin_caps[5] = MLX5_GET(mtpps_reg, out, cap_pin_5_mode); | |
438 | clock->pps_info.pin_caps[6] = MLX5_GET(mtpps_reg, out, cap_pin_6_mode); | |
439 | clock->pps_info.pin_caps[7] = MLX5_GET(mtpps_reg, out, cap_pin_7_mode); | |
ee7f1220 EE |
440 | } |
441 | ||
7c39afb3 FD |
442 | void mlx5_pps_event(struct mlx5_core_dev *mdev, |
443 | struct mlx5_eqe *eqe) | |
ee7f1220 | 444 | { |
7c39afb3 FD |
445 | struct mlx5_clock *clock = &mdev->clock; |
446 | struct ptp_clock_event ptp_event; | |
4272f9b8 EE |
447 | struct timespec64 ts; |
448 | u64 nsec_now, nsec_delta; | |
449 | u64 cycles_now, cycles_delta; | |
7c39afb3 | 450 | int pin = eqe->data.pps.pin; |
4272f9b8 EE |
451 | s64 ns; |
452 | unsigned long flags; | |
ee7f1220 | 453 | |
7c39afb3 | 454 | switch (clock->ptp_info.pin_config[pin].func) { |
4272f9b8 | 455 | case PTP_PF_EXTTS: |
afc98a0b FD |
456 | ptp_event.index = pin; |
457 | ptp_event.timestamp = timecounter_cyc2time(&clock->tc, | |
458 | be64_to_cpu(eqe->data.pps.time_stamp)); | |
7c39afb3 FD |
459 | if (clock->pps_info.enabled) { |
460 | ptp_event.type = PTP_CLOCK_PPSUSR; | |
afc98a0b FD |
461 | ptp_event.pps_times.ts_real = |
462 | ns_to_timespec64(ptp_event.timestamp); | |
cf503308 | 463 | } else { |
7c39afb3 | 464 | ptp_event.type = PTP_CLOCK_EXTTS; |
cf503308 | 465 | } |
7c39afb3 | 466 | ptp_clock_event(clock->ptp, &ptp_event); |
4272f9b8 EE |
467 | break; |
468 | case PTP_PF_PEROUT: | |
7c39afb3 FD |
469 | mlx5_ptp_gettime(&clock->ptp_info, &ts); |
470 | cycles_now = mlx5_read_internal_timer(mdev); | |
4272f9b8 EE |
471 | ts.tv_sec += 1; |
472 | ts.tv_nsec = 0; | |
473 | ns = timespec64_to_ns(&ts); | |
7c39afb3 FD |
474 | write_lock_irqsave(&clock->lock, flags); |
475 | nsec_now = timecounter_cyc2time(&clock->tc, cycles_now); | |
4272f9b8 | 476 | nsec_delta = ns - nsec_now; |
7c39afb3 FD |
477 | cycles_delta = div64_u64(nsec_delta << clock->cycles.shift, |
478 | clock->cycles.mult); | |
479 | clock->pps_info.start[pin] = cycles_now + cycles_delta; | |
480 | schedule_work(&clock->pps_info.out_work); | |
481 | write_unlock_irqrestore(&clock->lock, flags); | |
4272f9b8 EE |
482 | break; |
483 | default: | |
7c39afb3 | 484 | mlx5_core_err(mdev, " Unhandled event\n"); |
4272f9b8 | 485 | } |
ee7f1220 EE |
486 | } |
487 | ||
7c39afb3 | 488 | void mlx5_init_clock(struct mlx5_core_dev *mdev) |
ef9814de | 489 | { |
7c39afb3 | 490 | struct mlx5_clock *clock = &mdev->clock; |
ef9814de EBE |
491 | u64 ns; |
492 | u64 frac = 0; | |
493 | u32 dev_freq; | |
494 | ||
7c39afb3 | 495 | dev_freq = MLX5_CAP_GEN(mdev, device_frequency_khz); |
ef9814de | 496 | if (!dev_freq) { |
7c39afb3 | 497 | mlx5_core_warn(mdev, "invalid device_frequency_khz, aborting HW clock init\n"); |
ef9814de EBE |
498 | return; |
499 | } | |
7c39afb3 FD |
500 | rwlock_init(&clock->lock); |
501 | clock->cycles.read = read_internal_timer; | |
502 | clock->cycles.shift = MLX5_CYCLES_SHIFT; | |
503 | clock->cycles.mult = clocksource_khz2mult(dev_freq, | |
504 | clock->cycles.shift); | |
505 | clock->nominal_c_mult = clock->cycles.mult; | |
506 | clock->cycles.mask = CLOCKSOURCE_MASK(41); | |
24d33d2c | 507 | clock->mdev = mdev; |
7c39afb3 FD |
508 | |
509 | timecounter_init(&clock->tc, &clock->cycles, | |
ef9814de EBE |
510 | ktime_to_ns(ktime_get_real())); |
511 | ||
512 | /* Calculate period in seconds to call the overflow watchdog - to make | |
513 | * sure counter is checked at least once every wrap around. | |
514 | */ | |
7c39afb3 | 515 | ns = cyclecounter_cyc2ns(&clock->cycles, clock->cycles.mask, |
ef9814de EBE |
516 | frac, &frac); |
517 | do_div(ns, NSEC_PER_SEC / 2 / HZ); | |
7c39afb3 | 518 | clock->overflow_period = ns; |
ef9814de | 519 | |
24d33d2c FD |
520 | mdev->clock_info_page = alloc_page(GFP_KERNEL); |
521 | if (mdev->clock_info_page) { | |
522 | mdev->clock_info = kmap(mdev->clock_info_page); | |
523 | if (!mdev->clock_info) { | |
524 | __free_page(mdev->clock_info_page); | |
525 | mlx5_core_warn(mdev, "failed to map clock page\n"); | |
526 | } else { | |
527 | mdev->clock_info->sign = 0; | |
528 | mdev->clock_info->nsec = clock->tc.nsec; | |
529 | mdev->clock_info->cycles = clock->tc.cycle_last; | |
530 | mdev->clock_info->mask = clock->cycles.mask; | |
531 | mdev->clock_info->mult = clock->nominal_c_mult; | |
532 | mdev->clock_info->shift = clock->cycles.shift; | |
533 | mdev->clock_info->frac = clock->tc.frac; | |
534 | mdev->clock_info->overflow_period = | |
535 | clock->overflow_period; | |
536 | } | |
537 | } | |
538 | ||
7c39afb3 FD |
539 | INIT_WORK(&clock->pps_info.out_work, mlx5_pps_out); |
540 | INIT_DELAYED_WORK(&clock->overflow_work, mlx5_timestamp_overflow); | |
541 | if (clock->overflow_period) | |
542 | schedule_delayed_work(&clock->overflow_work, 0); | |
ef9814de | 543 | else |
7c39afb3 | 544 | mlx5_core_warn(mdev, "invalid overflow period, overflow_work is not scheduled\n"); |
3d8c38af EBE |
545 | |
546 | /* Configure the PHC */ | |
7c39afb3 | 547 | clock->ptp_info = mlx5_ptp_clock_info; |
3d8c38af | 548 | |
ee7f1220 | 549 | /* Initialize 1PPS data structures */ |
7c39afb3 FD |
550 | if (MLX5_PPS_CAP(mdev)) |
551 | mlx5_get_pps_caps(mdev); | |
552 | if (clock->ptp_info.n_pins) | |
553 | mlx5_init_pin_config(clock); | |
554 | ||
555 | clock->ptp = ptp_clock_register(&clock->ptp_info, | |
556 | &mdev->pdev->dev); | |
557 | if (IS_ERR(clock->ptp)) { | |
558 | mlx5_core_warn(mdev, "ptp_clock_register failed %ld\n", | |
559 | PTR_ERR(clock->ptp)); | |
560 | clock->ptp = NULL; | |
3d8c38af | 561 | } |
ef9814de EBE |
562 | } |
563 | ||
7c39afb3 | 564 | void mlx5_cleanup_clock(struct mlx5_core_dev *mdev) |
ef9814de | 565 | { |
7c39afb3 | 566 | struct mlx5_clock *clock = &mdev->clock; |
ef9814de | 567 | |
7c39afb3 | 568 | if (!MLX5_CAP_GEN(mdev, device_frequency_khz)) |
ef9814de EBE |
569 | return; |
570 | ||
7c39afb3 FD |
571 | if (clock->ptp) { |
572 | ptp_clock_unregister(clock->ptp); | |
573 | clock->ptp = NULL; | |
3d8c38af EBE |
574 | } |
575 | ||
7c39afb3 FD |
576 | cancel_work_sync(&clock->pps_info.out_work); |
577 | cancel_delayed_work_sync(&clock->overflow_work); | |
24d33d2c FD |
578 | |
579 | if (mdev->clock_info) { | |
580 | kunmap(mdev->clock_info_page); | |
581 | __free_page(mdev->clock_info_page); | |
582 | mdev->clock_info = NULL; | |
583 | } | |
584 | ||
7c39afb3 | 585 | kfree(clock->ptp_info.pin_config); |
ef9814de | 586 | } |