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