1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Marvell RVU Ethernet driver |
3 | * |
4 | * Copyright (C) 2020 Marvell. |
5 | * |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | |
10 | #include "otx2_common.h" |
11 | #include "otx2_ptp.h" |
12 | |
13 | static bool is_tstmp_atomic_update_supported(struct otx2_ptp *ptp) |
14 | { |
15 | struct ptp_get_cap_rsp *rsp; |
16 | struct msg_req *req; |
17 | int err; |
18 | |
19 | if (!ptp->nic) |
20 | return false; |
21 | |
22 | mutex_lock(&ptp->nic->mbox.lock); |
23 | req = otx2_mbox_alloc_msg_ptp_get_cap(mbox: &ptp->nic->mbox); |
24 | if (!req) { |
25 | mutex_unlock(lock: &ptp->nic->mbox.lock); |
26 | return false; |
27 | } |
28 | |
29 | err = otx2_sync_mbox_msg(mbox: &ptp->nic->mbox); |
30 | if (err) { |
31 | mutex_unlock(lock: &ptp->nic->mbox.lock); |
32 | return false; |
33 | } |
34 | rsp = (struct ptp_get_cap_rsp *)otx2_mbox_get_rsp(mbox: &ptp->nic->mbox.mbox, devid: 0, |
35 | msg: &req->hdr); |
36 | mutex_unlock(lock: &ptp->nic->mbox.lock); |
37 | |
38 | if (IS_ERR(ptr: rsp)) |
39 | return false; |
40 | |
41 | if (rsp->cap & PTP_CAP_HW_ATOMIC_UPDATE) |
42 | return true; |
43 | |
44 | return false; |
45 | } |
46 | |
47 | static int otx2_ptp_hw_adjtime(struct ptp_clock_info *ptp_info, s64 delta) |
48 | { |
49 | struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, |
50 | ptp_info); |
51 | struct otx2_nic *pfvf = ptp->nic; |
52 | struct ptp_req *req; |
53 | int rc; |
54 | |
55 | if (!ptp->nic) |
56 | return -ENODEV; |
57 | |
58 | mutex_lock(&pfvf->mbox.lock); |
59 | req = otx2_mbox_alloc_msg_ptp_op(mbox: &ptp->nic->mbox); |
60 | if (!req) { |
61 | mutex_unlock(lock: &pfvf->mbox.lock); |
62 | return -ENOMEM; |
63 | } |
64 | req->op = PTP_OP_ADJTIME; |
65 | req->delta = delta; |
66 | rc = otx2_sync_mbox_msg(mbox: &ptp->nic->mbox); |
67 | mutex_unlock(lock: &pfvf->mbox.lock); |
68 | |
69 | return rc; |
70 | } |
71 | |
72 | static u64 otx2_ptp_get_clock(struct otx2_ptp *ptp) |
73 | { |
74 | struct ptp_req *req; |
75 | struct ptp_rsp *rsp; |
76 | int err; |
77 | |
78 | if (!ptp->nic) |
79 | return 0; |
80 | |
81 | req = otx2_mbox_alloc_msg_ptp_op(mbox: &ptp->nic->mbox); |
82 | if (!req) |
83 | return 0; |
84 | |
85 | req->op = PTP_OP_GET_CLOCK; |
86 | |
87 | err = otx2_sync_mbox_msg(mbox: &ptp->nic->mbox); |
88 | if (err) |
89 | return 0; |
90 | |
91 | rsp = (struct ptp_rsp *)otx2_mbox_get_rsp(mbox: &ptp->nic->mbox.mbox, devid: 0, |
92 | msg: &req->hdr); |
93 | if (IS_ERR(ptr: rsp)) |
94 | return 0; |
95 | |
96 | return rsp->clk; |
97 | } |
98 | |
99 | static int otx2_ptp_hw_gettime(struct ptp_clock_info *ptp_info, |
100 | struct timespec64 *ts) |
101 | { |
102 | struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, |
103 | ptp_info); |
104 | u64 tstamp; |
105 | |
106 | tstamp = otx2_ptp_get_clock(ptp); |
107 | |
108 | *ts = ns_to_timespec64(nsec: tstamp); |
109 | return 0; |
110 | } |
111 | |
112 | static int otx2_ptp_hw_settime(struct ptp_clock_info *ptp_info, |
113 | const struct timespec64 *ts) |
114 | { |
115 | struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, |
116 | ptp_info); |
117 | struct otx2_nic *pfvf = ptp->nic; |
118 | struct ptp_req *req; |
119 | u64 nsec; |
120 | int rc; |
121 | |
122 | if (!ptp->nic) |
123 | return -ENODEV; |
124 | |
125 | nsec = timespec64_to_ns(ts); |
126 | |
127 | mutex_lock(&pfvf->mbox.lock); |
128 | req = otx2_mbox_alloc_msg_ptp_op(mbox: &ptp->nic->mbox); |
129 | if (!req) { |
130 | mutex_unlock(lock: &pfvf->mbox.lock); |
131 | return -ENOMEM; |
132 | } |
133 | |
134 | req->op = PTP_OP_SET_CLOCK; |
135 | req->clk = nsec; |
136 | rc = otx2_sync_mbox_msg(mbox: &ptp->nic->mbox); |
137 | mutex_unlock(lock: &pfvf->mbox.lock); |
138 | |
139 | return rc; |
140 | } |
141 | |
142 | static int otx2_ptp_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm) |
143 | { |
144 | struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, |
145 | ptp_info); |
146 | struct ptp_req *req; |
147 | |
148 | if (!ptp->nic) |
149 | return -ENODEV; |
150 | |
151 | req = otx2_mbox_alloc_msg_ptp_op(mbox: &ptp->nic->mbox); |
152 | if (!req) |
153 | return -ENOMEM; |
154 | |
155 | req->op = PTP_OP_ADJFINE; |
156 | req->scaled_ppm = scaled_ppm; |
157 | |
158 | return otx2_sync_mbox_msg(mbox: &ptp->nic->mbox); |
159 | } |
160 | |
161 | static int ptp_set_thresh(struct otx2_ptp *ptp, u64 thresh) |
162 | { |
163 | struct ptp_req *req; |
164 | |
165 | if (!ptp->nic) |
166 | return -ENODEV; |
167 | |
168 | req = otx2_mbox_alloc_msg_ptp_op(mbox: &ptp->nic->mbox); |
169 | if (!req) |
170 | return -ENOMEM; |
171 | |
172 | req->op = PTP_OP_SET_THRESH; |
173 | req->thresh = thresh; |
174 | |
175 | return otx2_sync_mbox_msg(mbox: &ptp->nic->mbox); |
176 | } |
177 | |
178 | static int ptp_pps_on(struct otx2_ptp *ptp, int on, u64 period) |
179 | { |
180 | struct ptp_req *req; |
181 | |
182 | if (!ptp->nic) |
183 | return -ENODEV; |
184 | |
185 | req = otx2_mbox_alloc_msg_ptp_op(mbox: &ptp->nic->mbox); |
186 | if (!req) |
187 | return -ENOMEM; |
188 | |
189 | req->op = PTP_OP_PPS_ON; |
190 | req->pps_on = on; |
191 | req->period = period; |
192 | |
193 | return otx2_sync_mbox_msg(mbox: &ptp->nic->mbox); |
194 | } |
195 | |
196 | static u64 ptp_cc_read(const struct cyclecounter *cc) |
197 | { |
198 | struct otx2_ptp *ptp = container_of(cc, struct otx2_ptp, cycle_counter); |
199 | |
200 | return otx2_ptp_get_clock(ptp); |
201 | } |
202 | |
203 | static u64 ptp_tstmp_read(struct otx2_ptp *ptp) |
204 | { |
205 | struct ptp_req *req; |
206 | struct ptp_rsp *rsp; |
207 | int err; |
208 | |
209 | if (!ptp->nic) |
210 | return 0; |
211 | |
212 | req = otx2_mbox_alloc_msg_ptp_op(mbox: &ptp->nic->mbox); |
213 | if (!req) |
214 | return 0; |
215 | |
216 | req->op = PTP_OP_GET_TSTMP; |
217 | |
218 | err = otx2_sync_mbox_msg(mbox: &ptp->nic->mbox); |
219 | if (err) |
220 | return 0; |
221 | |
222 | rsp = (struct ptp_rsp *)otx2_mbox_get_rsp(mbox: &ptp->nic->mbox.mbox, devid: 0, |
223 | msg: &req->hdr); |
224 | if (IS_ERR(ptr: rsp)) |
225 | return 0; |
226 | |
227 | return rsp->clk; |
228 | } |
229 | |
230 | static int otx2_ptp_tc_adjtime(struct ptp_clock_info *ptp_info, s64 delta) |
231 | { |
232 | struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, |
233 | ptp_info); |
234 | struct otx2_nic *pfvf = ptp->nic; |
235 | |
236 | mutex_lock(&pfvf->mbox.lock); |
237 | timecounter_adjtime(tc: &ptp->time_counter, delta); |
238 | mutex_unlock(lock: &pfvf->mbox.lock); |
239 | |
240 | return 0; |
241 | } |
242 | |
243 | static int otx2_ptp_tc_gettime(struct ptp_clock_info *ptp_info, |
244 | struct timespec64 *ts) |
245 | { |
246 | struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, |
247 | ptp_info); |
248 | u64 tstamp; |
249 | |
250 | mutex_lock(&ptp->nic->mbox.lock); |
251 | tstamp = timecounter_read(tc: &ptp->time_counter); |
252 | mutex_unlock(lock: &ptp->nic->mbox.lock); |
253 | *ts = ns_to_timespec64(nsec: tstamp); |
254 | |
255 | return 0; |
256 | } |
257 | |
258 | static int otx2_ptp_tc_settime(struct ptp_clock_info *ptp_info, |
259 | const struct timespec64 *ts) |
260 | { |
261 | struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, |
262 | ptp_info); |
263 | u64 nsec; |
264 | |
265 | nsec = timespec64_to_ns(ts); |
266 | |
267 | mutex_lock(&ptp->nic->mbox.lock); |
268 | timecounter_init(tc: &ptp->time_counter, cc: &ptp->cycle_counter, start_tstamp: nsec); |
269 | mutex_unlock(lock: &ptp->nic->mbox.lock); |
270 | |
271 | return 0; |
272 | } |
273 | |
274 | static int otx2_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin, |
275 | enum ptp_pin_function func, unsigned int chan) |
276 | { |
277 | switch (func) { |
278 | case PTP_PF_NONE: |
279 | case PTP_PF_EXTTS: |
280 | case PTP_PF_PEROUT: |
281 | break; |
282 | case PTP_PF_PHYSYNC: |
283 | return -1; |
284 | } |
285 | return 0; |
286 | } |
287 | |
288 | static u64 otx2_ptp_hw_tstamp2time(const struct timecounter *time_counter, u64 tstamp) |
289 | { |
290 | /* On HW which supports atomic updates, timecounter is not initialized */ |
291 | return tstamp; |
292 | } |
293 | |
294 | static void otx2_ptp_extts_check(struct work_struct *work) |
295 | { |
296 | struct otx2_ptp *ptp = container_of(work, struct otx2_ptp, |
297 | extts_work.work); |
298 | struct ptp_clock_event event; |
299 | u64 tstmp, new_thresh; |
300 | |
301 | mutex_lock(&ptp->nic->mbox.lock); |
302 | tstmp = ptp_tstmp_read(ptp); |
303 | mutex_unlock(lock: &ptp->nic->mbox.lock); |
304 | |
305 | if (tstmp != ptp->last_extts) { |
306 | event.type = PTP_CLOCK_EXTTS; |
307 | event.index = 0; |
308 | event.timestamp = ptp->ptp_tstamp2nsec(&ptp->time_counter, tstmp); |
309 | ptp_clock_event(ptp: ptp->ptp_clock, event: &event); |
310 | new_thresh = tstmp % 500000000; |
311 | if (ptp->thresh != new_thresh) { |
312 | mutex_lock(&ptp->nic->mbox.lock); |
313 | ptp_set_thresh(ptp, thresh: new_thresh); |
314 | mutex_unlock(lock: &ptp->nic->mbox.lock); |
315 | ptp->thresh = new_thresh; |
316 | } |
317 | ptp->last_extts = tstmp; |
318 | } |
319 | schedule_delayed_work(dwork: &ptp->extts_work, delay: msecs_to_jiffies(m: 200)); |
320 | } |
321 | |
322 | static void otx2_sync_tstamp(struct work_struct *work) |
323 | { |
324 | struct otx2_ptp *ptp = container_of(work, struct otx2_ptp, |
325 | synctstamp_work.work); |
326 | struct otx2_nic *pfvf = ptp->nic; |
327 | u64 tstamp; |
328 | |
329 | mutex_lock(&pfvf->mbox.lock); |
330 | tstamp = otx2_ptp_get_clock(ptp); |
331 | mutex_unlock(lock: &pfvf->mbox.lock); |
332 | |
333 | ptp->tstamp = ptp->ptp_tstamp2nsec(&ptp->time_counter, tstamp); |
334 | ptp->base_ns = tstamp % NSEC_PER_SEC; |
335 | |
336 | schedule_delayed_work(dwork: &ptp->synctstamp_work, delay: msecs_to_jiffies(m: 250)); |
337 | } |
338 | |
339 | static int otx2_ptp_enable(struct ptp_clock_info *ptp_info, |
340 | struct ptp_clock_request *rq, int on) |
341 | { |
342 | struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, |
343 | ptp_info); |
344 | u64 period = 0; |
345 | int pin; |
346 | |
347 | if (!ptp->nic) |
348 | return -ENODEV; |
349 | |
350 | switch (rq->type) { |
351 | case PTP_CLK_REQ_EXTTS: |
352 | pin = ptp_find_pin(ptp: ptp->ptp_clock, func: PTP_PF_EXTTS, |
353 | chan: rq->extts.index); |
354 | if (pin < 0) |
355 | return -EBUSY; |
356 | if (on) |
357 | schedule_delayed_work(dwork: &ptp->extts_work, delay: msecs_to_jiffies(m: 200)); |
358 | else |
359 | cancel_delayed_work_sync(dwork: &ptp->extts_work); |
360 | |
361 | return 0; |
362 | case PTP_CLK_REQ_PEROUT: |
363 | if (rq->perout.flags) |
364 | return -EOPNOTSUPP; |
365 | |
366 | if (rq->perout.index >= ptp_info->n_pins) |
367 | return -EINVAL; |
368 | if (on) { |
369 | period = rq->perout.period.sec * NSEC_PER_SEC + |
370 | rq->perout.period.nsec; |
371 | ptp_pps_on(ptp, on, period); |
372 | } else { |
373 | ptp_pps_on(ptp, on, period); |
374 | } |
375 | return 0; |
376 | default: |
377 | break; |
378 | } |
379 | return -EOPNOTSUPP; |
380 | } |
381 | |
382 | int otx2_ptp_init(struct otx2_nic *pfvf) |
383 | { |
384 | struct otx2_ptp *ptp_ptr; |
385 | struct cyclecounter *cc; |
386 | struct ptp_req *req; |
387 | int err; |
388 | |
389 | if (is_otx2_lbkvf(pdev: pfvf->pdev)) { |
390 | pfvf->ptp = NULL; |
391 | return 0; |
392 | } |
393 | |
394 | mutex_lock(&pfvf->mbox.lock); |
395 | /* check if PTP block is available */ |
396 | req = otx2_mbox_alloc_msg_ptp_op(mbox: &pfvf->mbox); |
397 | if (!req) { |
398 | mutex_unlock(lock: &pfvf->mbox.lock); |
399 | return -ENOMEM; |
400 | } |
401 | |
402 | req->op = PTP_OP_GET_CLOCK; |
403 | |
404 | err = otx2_sync_mbox_msg(mbox: &pfvf->mbox); |
405 | if (err) { |
406 | mutex_unlock(lock: &pfvf->mbox.lock); |
407 | return err; |
408 | } |
409 | mutex_unlock(lock: &pfvf->mbox.lock); |
410 | |
411 | ptp_ptr = kzalloc(size: sizeof(*ptp_ptr), GFP_KERNEL); |
412 | if (!ptp_ptr) { |
413 | err = -ENOMEM; |
414 | goto error; |
415 | } |
416 | |
417 | ptp_ptr->nic = pfvf; |
418 | |
419 | snprintf(buf: ptp_ptr->extts_config.name, size: sizeof(ptp_ptr->extts_config.name), fmt: "TSTAMP" ); |
420 | ptp_ptr->extts_config.index = 0; |
421 | ptp_ptr->extts_config.func = PTP_PF_NONE; |
422 | |
423 | ptp_ptr->ptp_info = (struct ptp_clock_info) { |
424 | .owner = THIS_MODULE, |
425 | .name = "OcteonTX2 PTP" , |
426 | .max_adj = 1000000000ull, |
427 | .n_ext_ts = 1, |
428 | .n_per_out = 1, |
429 | .n_pins = 1, |
430 | .pps = 0, |
431 | .pin_config = &ptp_ptr->extts_config, |
432 | .adjfine = otx2_ptp_adjfine, |
433 | .enable = otx2_ptp_enable, |
434 | .verify = otx2_ptp_verify_pin, |
435 | }; |
436 | |
437 | /* Check whether hardware supports atomic updates to timestamp */ |
438 | if (is_tstmp_atomic_update_supported(ptp: ptp_ptr)) { |
439 | ptp_ptr->ptp_info.adjtime = otx2_ptp_hw_adjtime; |
440 | ptp_ptr->ptp_info.gettime64 = otx2_ptp_hw_gettime; |
441 | ptp_ptr->ptp_info.settime64 = otx2_ptp_hw_settime; |
442 | |
443 | ptp_ptr->ptp_tstamp2nsec = otx2_ptp_hw_tstamp2time; |
444 | } else { |
445 | ptp_ptr->ptp_info.adjtime = otx2_ptp_tc_adjtime; |
446 | ptp_ptr->ptp_info.gettime64 = otx2_ptp_tc_gettime; |
447 | ptp_ptr->ptp_info.settime64 = otx2_ptp_tc_settime; |
448 | |
449 | cc = &ptp_ptr->cycle_counter; |
450 | cc->read = ptp_cc_read; |
451 | cc->mask = CYCLECOUNTER_MASK(64); |
452 | cc->mult = 1; |
453 | cc->shift = 0; |
454 | ptp_ptr->ptp_tstamp2nsec = timecounter_cyc2time; |
455 | |
456 | timecounter_init(tc: &ptp_ptr->time_counter, cc: &ptp_ptr->cycle_counter, |
457 | start_tstamp: ktime_to_ns(kt: ktime_get_real())); |
458 | } |
459 | |
460 | INIT_DELAYED_WORK(&ptp_ptr->extts_work, otx2_ptp_extts_check); |
461 | |
462 | ptp_ptr->ptp_clock = ptp_clock_register(info: &ptp_ptr->ptp_info, parent: pfvf->dev); |
463 | if (IS_ERR_OR_NULL(ptr: ptp_ptr->ptp_clock)) { |
464 | err = ptp_ptr->ptp_clock ? |
465 | PTR_ERR(ptr: ptp_ptr->ptp_clock) : -ENODEV; |
466 | kfree(objp: ptp_ptr); |
467 | goto error; |
468 | } |
469 | |
470 | if (is_dev_otx2(pdev: pfvf->pdev)) { |
471 | ptp_ptr->convert_rx_ptp_tstmp = &otx2_ptp_convert_rx_timestamp; |
472 | ptp_ptr->convert_tx_ptp_tstmp = &otx2_ptp_convert_tx_timestamp; |
473 | } else { |
474 | ptp_ptr->convert_rx_ptp_tstmp = &cn10k_ptp_convert_timestamp; |
475 | ptp_ptr->convert_tx_ptp_tstmp = &cn10k_ptp_convert_timestamp; |
476 | } |
477 | |
478 | INIT_DELAYED_WORK(&ptp_ptr->synctstamp_work, otx2_sync_tstamp); |
479 | |
480 | pfvf->ptp = ptp_ptr; |
481 | |
482 | error: |
483 | return err; |
484 | } |
485 | EXPORT_SYMBOL_GPL(otx2_ptp_init); |
486 | |
487 | void otx2_ptp_destroy(struct otx2_nic *pfvf) |
488 | { |
489 | struct otx2_ptp *ptp = pfvf->ptp; |
490 | |
491 | if (!ptp) |
492 | return; |
493 | |
494 | cancel_delayed_work(dwork: &pfvf->ptp->synctstamp_work); |
495 | |
496 | ptp_clock_unregister(ptp: ptp->ptp_clock); |
497 | kfree(objp: ptp); |
498 | pfvf->ptp = NULL; |
499 | } |
500 | EXPORT_SYMBOL_GPL(otx2_ptp_destroy); |
501 | |
502 | int otx2_ptp_clock_index(struct otx2_nic *pfvf) |
503 | { |
504 | if (!pfvf->ptp) |
505 | return -ENODEV; |
506 | |
507 | return ptp_clock_index(ptp: pfvf->ptp->ptp_clock); |
508 | } |
509 | EXPORT_SYMBOL_GPL(otx2_ptp_clock_index); |
510 | |
511 | int otx2_ptp_tstamp2time(struct otx2_nic *pfvf, u64 tstamp, u64 *tsns) |
512 | { |
513 | if (!pfvf->ptp) |
514 | return -ENODEV; |
515 | |
516 | *tsns = pfvf->ptp->ptp_tstamp2nsec(&pfvf->ptp->time_counter, tstamp); |
517 | |
518 | return 0; |
519 | } |
520 | EXPORT_SYMBOL_GPL(otx2_ptp_tstamp2time); |
521 | |
522 | MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>" ); |
523 | MODULE_DESCRIPTION("Marvell RVU NIC PTP Driver" ); |
524 | MODULE_LICENSE("GPL v2" ); |
525 | |