1 | /* Broadcom NetXtreme-C/E network driver. |
2 | * |
3 | * Copyright (c) 2021 Broadcom Inc. |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation. |
8 | */ |
9 | #include <linux/kernel.h> |
10 | #include <linux/errno.h> |
11 | #include <linux/pci.h> |
12 | #include <linux/netdevice.h> |
13 | #include <linux/etherdevice.h> |
14 | #include <linux/net_tstamp.h> |
15 | #include <linux/timekeeping.h> |
16 | #include <linux/ptp_classify.h> |
17 | #include <linux/clocksource.h> |
18 | #include "bnxt_hsi.h" |
19 | #include "bnxt.h" |
20 | #include "bnxt_hwrm.h" |
21 | #include "bnxt_ptp.h" |
22 | |
23 | static int bnxt_ptp_cfg_settime(struct bnxt *bp, u64 time) |
24 | { |
25 | struct hwrm_func_ptp_cfg_input *req; |
26 | int rc; |
27 | |
28 | rc = hwrm_req_init(bp, req, HWRM_FUNC_PTP_CFG); |
29 | if (rc) |
30 | return rc; |
31 | |
32 | req->enables = cpu_to_le16(FUNC_PTP_CFG_REQ_ENABLES_PTP_SET_TIME); |
33 | req->ptp_set_time = cpu_to_le64(time); |
34 | return hwrm_req_send(bp, req); |
35 | } |
36 | |
37 | int bnxt_ptp_parse(struct sk_buff *skb, u16 *seq_id, u16 *hdr_off) |
38 | { |
39 | unsigned int ptp_class; |
40 | struct ptp_header *hdr; |
41 | |
42 | ptp_class = ptp_classify_raw(skb); |
43 | |
44 | switch (ptp_class & PTP_CLASS_VMASK) { |
45 | case PTP_CLASS_V1: |
46 | case PTP_CLASS_V2: |
47 | hdr = ptp_parse_header(skb, type: ptp_class); |
48 | if (!hdr) |
49 | return -EINVAL; |
50 | |
51 | *hdr_off = (u8 *)hdr - skb->data; |
52 | *seq_id = ntohs(hdr->sequence_id); |
53 | return 0; |
54 | default: |
55 | return -ERANGE; |
56 | } |
57 | } |
58 | |
59 | static int bnxt_ptp_settime(struct ptp_clock_info *ptp_info, |
60 | const struct timespec64 *ts) |
61 | { |
62 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, |
63 | ptp_info); |
64 | u64 ns = timespec64_to_ns(ts); |
65 | |
66 | if (BNXT_PTP_USE_RTC(ptp->bp)) |
67 | return bnxt_ptp_cfg_settime(bp: ptp->bp, time: ns); |
68 | |
69 | spin_lock_bh(lock: &ptp->ptp_lock); |
70 | timecounter_init(tc: &ptp->tc, cc: &ptp->cc, start_tstamp: ns); |
71 | spin_unlock_bh(lock: &ptp->ptp_lock); |
72 | return 0; |
73 | } |
74 | |
75 | /* Caller holds ptp_lock */ |
76 | static int bnxt_refclk_read(struct bnxt *bp, struct ptp_system_timestamp *sts, |
77 | u64 *ns) |
78 | { |
79 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
80 | u32 high_before, high_now, low; |
81 | |
82 | if (test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) |
83 | return -EIO; |
84 | |
85 | high_before = readl(addr: bp->bar0 + ptp->refclk_mapped_regs[1]); |
86 | ptp_read_system_prets(sts); |
87 | low = readl(addr: bp->bar0 + ptp->refclk_mapped_regs[0]); |
88 | ptp_read_system_postts(sts); |
89 | high_now = readl(addr: bp->bar0 + ptp->refclk_mapped_regs[1]); |
90 | if (high_now != high_before) { |
91 | ptp_read_system_prets(sts); |
92 | low = readl(addr: bp->bar0 + ptp->refclk_mapped_regs[0]); |
93 | ptp_read_system_postts(sts); |
94 | } |
95 | *ns = ((u64)high_now << 32) | low; |
96 | |
97 | return 0; |
98 | } |
99 | |
100 | static void bnxt_ptp_get_current_time(struct bnxt *bp) |
101 | { |
102 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
103 | |
104 | if (!ptp) |
105 | return; |
106 | spin_lock_bh(lock: &ptp->ptp_lock); |
107 | WRITE_ONCE(ptp->old_time, ptp->current_time); |
108 | bnxt_refclk_read(bp, NULL, ns: &ptp->current_time); |
109 | spin_unlock_bh(lock: &ptp->ptp_lock); |
110 | } |
111 | |
112 | static int bnxt_hwrm_port_ts_query(struct bnxt *bp, u32 flags, u64 *ts) |
113 | { |
114 | struct hwrm_port_ts_query_output *resp; |
115 | struct hwrm_port_ts_query_input *req; |
116 | int rc; |
117 | |
118 | rc = hwrm_req_init(bp, req, HWRM_PORT_TS_QUERY); |
119 | if (rc) |
120 | return rc; |
121 | |
122 | req->flags = cpu_to_le32(flags); |
123 | if ((flags & PORT_TS_QUERY_REQ_FLAGS_PATH) == |
124 | PORT_TS_QUERY_REQ_FLAGS_PATH_TX) { |
125 | req->enables = cpu_to_le16(BNXT_PTP_QTS_TX_ENABLES); |
126 | req->ptp_seq_id = cpu_to_le32(bp->ptp_cfg->tx_seqid); |
127 | req->ptp_hdr_offset = cpu_to_le16(bp->ptp_cfg->tx_hdr_off); |
128 | req->ts_req_timeout = cpu_to_le16(BNXT_PTP_QTS_TIMEOUT); |
129 | } |
130 | resp = hwrm_req_hold(bp, req); |
131 | |
132 | rc = hwrm_req_send_silent(bp, req); |
133 | if (!rc) |
134 | *ts = le64_to_cpu(resp->ptp_msg_ts); |
135 | hwrm_req_drop(bp, req); |
136 | return rc; |
137 | } |
138 | |
139 | static int bnxt_ptp_gettimex(struct ptp_clock_info *ptp_info, |
140 | struct timespec64 *ts, |
141 | struct ptp_system_timestamp *sts) |
142 | { |
143 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, |
144 | ptp_info); |
145 | u64 ns, cycles; |
146 | int rc; |
147 | |
148 | spin_lock_bh(lock: &ptp->ptp_lock); |
149 | rc = bnxt_refclk_read(bp: ptp->bp, sts, ns: &cycles); |
150 | if (rc) { |
151 | spin_unlock_bh(lock: &ptp->ptp_lock); |
152 | return rc; |
153 | } |
154 | ns = timecounter_cyc2time(tc: &ptp->tc, cycle_tstamp: cycles); |
155 | spin_unlock_bh(lock: &ptp->ptp_lock); |
156 | *ts = ns_to_timespec64(nsec: ns); |
157 | |
158 | return 0; |
159 | } |
160 | |
161 | /* Caller holds ptp_lock */ |
162 | void bnxt_ptp_update_current_time(struct bnxt *bp) |
163 | { |
164 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
165 | |
166 | bnxt_refclk_read(bp: ptp->bp, NULL, ns: &ptp->current_time); |
167 | WRITE_ONCE(ptp->old_time, ptp->current_time); |
168 | } |
169 | |
170 | static int bnxt_ptp_adjphc(struct bnxt_ptp_cfg *ptp, s64 delta) |
171 | { |
172 | struct hwrm_port_mac_cfg_input *req; |
173 | int rc; |
174 | |
175 | rc = hwrm_req_init(ptp->bp, req, HWRM_PORT_MAC_CFG); |
176 | if (rc) |
177 | return rc; |
178 | |
179 | req->enables = cpu_to_le32(PORT_MAC_CFG_REQ_ENABLES_PTP_ADJ_PHASE); |
180 | req->ptp_adj_phase = cpu_to_le64(delta); |
181 | |
182 | rc = hwrm_req_send(bp: ptp->bp, req); |
183 | if (rc) { |
184 | netdev_err(dev: ptp->bp->dev, format: "ptp adjphc failed. rc = %x\n" , rc); |
185 | } else { |
186 | spin_lock_bh(lock: &ptp->ptp_lock); |
187 | bnxt_ptp_update_current_time(bp: ptp->bp); |
188 | spin_unlock_bh(lock: &ptp->ptp_lock); |
189 | } |
190 | |
191 | return rc; |
192 | } |
193 | |
194 | static int bnxt_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta) |
195 | { |
196 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, |
197 | ptp_info); |
198 | |
199 | if (BNXT_PTP_USE_RTC(ptp->bp)) |
200 | return bnxt_ptp_adjphc(ptp, delta); |
201 | |
202 | spin_lock_bh(lock: &ptp->ptp_lock); |
203 | timecounter_adjtime(tc: &ptp->tc, delta); |
204 | spin_unlock_bh(lock: &ptp->ptp_lock); |
205 | return 0; |
206 | } |
207 | |
208 | static int bnxt_ptp_adjfine_rtc(struct bnxt *bp, long scaled_ppm) |
209 | { |
210 | s32 ppb = scaled_ppm_to_ppb(ppm: scaled_ppm); |
211 | struct hwrm_port_mac_cfg_input *req; |
212 | int rc; |
213 | |
214 | rc = hwrm_req_init(bp, req, HWRM_PORT_MAC_CFG); |
215 | if (rc) |
216 | return rc; |
217 | |
218 | req->ptp_freq_adj_ppb = cpu_to_le32(ppb); |
219 | req->enables = cpu_to_le32(PORT_MAC_CFG_REQ_ENABLES_PTP_FREQ_ADJ_PPB); |
220 | rc = hwrm_req_send(bp, req); |
221 | if (rc) |
222 | netdev_err(dev: bp->dev, |
223 | format: "ptp adjfine failed. rc = %d\n" , rc); |
224 | return rc; |
225 | } |
226 | |
227 | static int bnxt_ptp_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm) |
228 | { |
229 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, |
230 | ptp_info); |
231 | struct bnxt *bp = ptp->bp; |
232 | |
233 | if (!BNXT_MH(bp)) |
234 | return bnxt_ptp_adjfine_rtc(bp, scaled_ppm); |
235 | |
236 | spin_lock_bh(lock: &ptp->ptp_lock); |
237 | timecounter_read(tc: &ptp->tc); |
238 | ptp->cc.mult = adjust_by_scaled_ppm(base: ptp->cmult, scaled_ppm); |
239 | spin_unlock_bh(lock: &ptp->ptp_lock); |
240 | return 0; |
241 | } |
242 | |
243 | void bnxt_ptp_pps_event(struct bnxt *bp, u32 data1, u32 data2) |
244 | { |
245 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
246 | struct ptp_clock_event event; |
247 | u64 ns, pps_ts; |
248 | |
249 | pps_ts = EVENT_PPS_TS(data2, data1); |
250 | spin_lock_bh(lock: &ptp->ptp_lock); |
251 | ns = timecounter_cyc2time(tc: &ptp->tc, cycle_tstamp: pps_ts); |
252 | spin_unlock_bh(lock: &ptp->ptp_lock); |
253 | |
254 | switch (EVENT_DATA2_PPS_EVENT_TYPE(data2)) { |
255 | case ASYNC_EVENT_CMPL_PPS_TIMESTAMP_EVENT_DATA2_EVENT_TYPE_INTERNAL: |
256 | event.pps_times.ts_real = ns_to_timespec64(nsec: ns); |
257 | event.type = PTP_CLOCK_PPSUSR; |
258 | event.index = EVENT_DATA2_PPS_PIN_NUM(data2); |
259 | break; |
260 | case ASYNC_EVENT_CMPL_PPS_TIMESTAMP_EVENT_DATA2_EVENT_TYPE_EXTERNAL: |
261 | event.timestamp = ns; |
262 | event.type = PTP_CLOCK_EXTTS; |
263 | event.index = EVENT_DATA2_PPS_PIN_NUM(data2); |
264 | break; |
265 | } |
266 | |
267 | ptp_clock_event(ptp: bp->ptp_cfg->ptp_clock, event: &event); |
268 | } |
269 | |
270 | static int bnxt_ptp_cfg_pin(struct bnxt *bp, u8 pin, u8 usage) |
271 | { |
272 | struct hwrm_func_ptp_pin_cfg_input *req; |
273 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
274 | u8 state = usage != BNXT_PPS_PIN_NONE; |
275 | u8 *pin_state, *pin_usg; |
276 | u32 enables; |
277 | int rc; |
278 | |
279 | if (!TSIO_PIN_VALID(pin)) { |
280 | netdev_err(dev: ptp->bp->dev, format: "1PPS: Invalid pin. Check pin-function configuration\n" ); |
281 | return -EOPNOTSUPP; |
282 | } |
283 | |
284 | rc = hwrm_req_init(ptp->bp, req, HWRM_FUNC_PTP_PIN_CFG); |
285 | if (rc) |
286 | return rc; |
287 | |
288 | enables = (FUNC_PTP_PIN_CFG_REQ_ENABLES_PIN0_STATE | |
289 | FUNC_PTP_PIN_CFG_REQ_ENABLES_PIN0_USAGE) << (pin * 2); |
290 | req->enables = cpu_to_le32(enables); |
291 | |
292 | pin_state = &req->pin0_state; |
293 | pin_usg = &req->pin0_usage; |
294 | |
295 | *(pin_state + (pin * 2)) = state; |
296 | *(pin_usg + (pin * 2)) = usage; |
297 | |
298 | rc = hwrm_req_send(bp: ptp->bp, req); |
299 | if (rc) |
300 | return rc; |
301 | |
302 | ptp->pps_info.pins[pin].usage = usage; |
303 | ptp->pps_info.pins[pin].state = state; |
304 | |
305 | return 0; |
306 | } |
307 | |
308 | static int bnxt_ptp_cfg_event(struct bnxt *bp, u8 event) |
309 | { |
310 | struct hwrm_func_ptp_cfg_input *req; |
311 | int rc; |
312 | |
313 | rc = hwrm_req_init(bp, req, HWRM_FUNC_PTP_CFG); |
314 | if (rc) |
315 | return rc; |
316 | |
317 | req->enables = cpu_to_le16(FUNC_PTP_CFG_REQ_ENABLES_PTP_PPS_EVENT); |
318 | req->ptp_pps_event = event; |
319 | return hwrm_req_send(bp, req); |
320 | } |
321 | |
322 | int bnxt_ptp_cfg_tstamp_filters(struct bnxt *bp) |
323 | { |
324 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
325 | struct hwrm_port_mac_cfg_input *req; |
326 | int rc; |
327 | |
328 | if (!ptp || !ptp->tstamp_filters) |
329 | return -EIO; |
330 | |
331 | rc = hwrm_req_init(bp, req, HWRM_PORT_MAC_CFG); |
332 | if (rc) |
333 | goto out; |
334 | |
335 | if (!(bp->fw_cap & BNXT_FW_CAP_RX_ALL_PKT_TS) && (ptp->tstamp_filters & |
336 | (PORT_MAC_CFG_REQ_FLAGS_ALL_RX_TS_CAPTURE_ENABLE | |
337 | PORT_MAC_CFG_REQ_FLAGS_ALL_RX_TS_CAPTURE_DISABLE))) { |
338 | ptp->tstamp_filters &= ~(PORT_MAC_CFG_REQ_FLAGS_ALL_RX_TS_CAPTURE_ENABLE | |
339 | PORT_MAC_CFG_REQ_FLAGS_ALL_RX_TS_CAPTURE_DISABLE); |
340 | netdev_warn(dev: bp->dev, format: "Unsupported FW for all RX pkts timestamp filter\n" ); |
341 | } |
342 | |
343 | req->flags = cpu_to_le32(ptp->tstamp_filters); |
344 | req->enables = cpu_to_le32(PORT_MAC_CFG_REQ_ENABLES_RX_TS_CAPTURE_PTP_MSG_TYPE); |
345 | req->rx_ts_capture_ptp_msg_type = cpu_to_le16(ptp->rxctl); |
346 | |
347 | rc = hwrm_req_send(bp, req); |
348 | if (!rc) { |
349 | bp->ptp_all_rx_tstamp = !!(ptp->tstamp_filters & |
350 | PORT_MAC_CFG_REQ_FLAGS_ALL_RX_TS_CAPTURE_ENABLE); |
351 | return 0; |
352 | } |
353 | ptp->tstamp_filters = 0; |
354 | out: |
355 | bp->ptp_all_rx_tstamp = 0; |
356 | netdev_warn(dev: bp->dev, format: "Failed to configure HW packet timestamp filters\n" ); |
357 | return rc; |
358 | } |
359 | |
360 | void bnxt_ptp_reapply_pps(struct bnxt *bp) |
361 | { |
362 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
363 | struct bnxt_pps *pps; |
364 | u32 pin = 0; |
365 | int rc; |
366 | |
367 | if (!ptp || !(bp->fw_cap & BNXT_FW_CAP_PTP_PPS) || |
368 | !(ptp->ptp_info.pin_config)) |
369 | return; |
370 | pps = &ptp->pps_info; |
371 | for (pin = 0; pin < BNXT_MAX_TSIO_PINS; pin++) { |
372 | if (pps->pins[pin].state) { |
373 | rc = bnxt_ptp_cfg_pin(bp, pin, usage: pps->pins[pin].usage); |
374 | if (!rc && pps->pins[pin].event) |
375 | rc = bnxt_ptp_cfg_event(bp, |
376 | event: pps->pins[pin].event); |
377 | if (rc) |
378 | netdev_err(dev: bp->dev, format: "1PPS: Failed to configure pin%d\n" , |
379 | pin); |
380 | } |
381 | } |
382 | } |
383 | |
384 | static int bnxt_get_target_cycles(struct bnxt_ptp_cfg *ptp, u64 target_ns, |
385 | u64 *cycles_delta) |
386 | { |
387 | u64 cycles_now; |
388 | u64 nsec_now, nsec_delta; |
389 | int rc; |
390 | |
391 | spin_lock_bh(lock: &ptp->ptp_lock); |
392 | rc = bnxt_refclk_read(bp: ptp->bp, NULL, ns: &cycles_now); |
393 | if (rc) { |
394 | spin_unlock_bh(lock: &ptp->ptp_lock); |
395 | return rc; |
396 | } |
397 | nsec_now = timecounter_cyc2time(tc: &ptp->tc, cycle_tstamp: cycles_now); |
398 | spin_unlock_bh(lock: &ptp->ptp_lock); |
399 | |
400 | nsec_delta = target_ns - nsec_now; |
401 | *cycles_delta = div64_u64(dividend: nsec_delta << ptp->cc.shift, divisor: ptp->cc.mult); |
402 | return 0; |
403 | } |
404 | |
405 | static int bnxt_ptp_perout_cfg(struct bnxt_ptp_cfg *ptp, |
406 | struct ptp_clock_request *rq) |
407 | { |
408 | struct hwrm_func_ptp_cfg_input *req; |
409 | struct bnxt *bp = ptp->bp; |
410 | struct timespec64 ts; |
411 | u64 target_ns, delta; |
412 | u16 enables; |
413 | int rc; |
414 | |
415 | ts.tv_sec = rq->perout.start.sec; |
416 | ts.tv_nsec = rq->perout.start.nsec; |
417 | target_ns = timespec64_to_ns(ts: &ts); |
418 | |
419 | rc = bnxt_get_target_cycles(ptp, target_ns, cycles_delta: &delta); |
420 | if (rc) |
421 | return rc; |
422 | |
423 | rc = hwrm_req_init(bp, req, HWRM_FUNC_PTP_CFG); |
424 | if (rc) |
425 | return rc; |
426 | |
427 | enables = FUNC_PTP_CFG_REQ_ENABLES_PTP_FREQ_ADJ_EXT_PERIOD | |
428 | FUNC_PTP_CFG_REQ_ENABLES_PTP_FREQ_ADJ_EXT_UP | |
429 | FUNC_PTP_CFG_REQ_ENABLES_PTP_FREQ_ADJ_EXT_PHASE; |
430 | req->enables = cpu_to_le16(enables); |
431 | req->ptp_pps_event = 0; |
432 | req->ptp_freq_adj_dll_source = 0; |
433 | req->ptp_freq_adj_dll_phase = 0; |
434 | req->ptp_freq_adj_ext_period = cpu_to_le32(NSEC_PER_SEC); |
435 | req->ptp_freq_adj_ext_up = 0; |
436 | req->ptp_freq_adj_ext_phase_lower = cpu_to_le32(delta); |
437 | |
438 | return hwrm_req_send(bp, req); |
439 | } |
440 | |
441 | static int bnxt_ptp_enable(struct ptp_clock_info *ptp_info, |
442 | struct ptp_clock_request *rq, int on) |
443 | { |
444 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, |
445 | ptp_info); |
446 | struct bnxt *bp = ptp->bp; |
447 | int pin_id; |
448 | int rc; |
449 | |
450 | switch (rq->type) { |
451 | case PTP_CLK_REQ_EXTTS: |
452 | /* Configure an External PPS IN */ |
453 | pin_id = ptp_find_pin(ptp: ptp->ptp_clock, func: PTP_PF_EXTTS, |
454 | chan: rq->extts.index); |
455 | if (!TSIO_PIN_VALID(pin_id)) |
456 | return -EOPNOTSUPP; |
457 | if (!on) |
458 | break; |
459 | rc = bnxt_ptp_cfg_pin(bp, pin: pin_id, BNXT_PPS_PIN_PPS_IN); |
460 | if (rc) |
461 | return rc; |
462 | rc = bnxt_ptp_cfg_event(bp, BNXT_PPS_EVENT_EXTERNAL); |
463 | if (!rc) |
464 | ptp->pps_info.pins[pin_id].event = BNXT_PPS_EVENT_EXTERNAL; |
465 | return rc; |
466 | case PTP_CLK_REQ_PEROUT: |
467 | /* Configure a Periodic PPS OUT */ |
468 | pin_id = ptp_find_pin(ptp: ptp->ptp_clock, func: PTP_PF_PEROUT, |
469 | chan: rq->perout.index); |
470 | if (!TSIO_PIN_VALID(pin_id)) |
471 | return -EOPNOTSUPP; |
472 | if (!on) |
473 | break; |
474 | |
475 | rc = bnxt_ptp_cfg_pin(bp, pin: pin_id, BNXT_PPS_PIN_PPS_OUT); |
476 | if (!rc) |
477 | rc = bnxt_ptp_perout_cfg(ptp, rq); |
478 | |
479 | return rc; |
480 | case PTP_CLK_REQ_PPS: |
481 | /* Configure PHC PPS IN */ |
482 | rc = bnxt_ptp_cfg_pin(bp, pin: 0, BNXT_PPS_PIN_PPS_IN); |
483 | if (rc) |
484 | return rc; |
485 | rc = bnxt_ptp_cfg_event(bp, BNXT_PPS_EVENT_INTERNAL); |
486 | if (!rc) |
487 | ptp->pps_info.pins[0].event = BNXT_PPS_EVENT_INTERNAL; |
488 | return rc; |
489 | default: |
490 | netdev_err(dev: ptp->bp->dev, format: "Unrecognized PIN function\n" ); |
491 | return -EOPNOTSUPP; |
492 | } |
493 | |
494 | return bnxt_ptp_cfg_pin(bp, pin: pin_id, BNXT_PPS_PIN_NONE); |
495 | } |
496 | |
497 | static int bnxt_hwrm_ptp_cfg(struct bnxt *bp) |
498 | { |
499 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
500 | u32 flags = 0; |
501 | |
502 | switch (ptp->rx_filter) { |
503 | case HWTSTAMP_FILTER_ALL: |
504 | flags = PORT_MAC_CFG_REQ_FLAGS_ALL_RX_TS_CAPTURE_ENABLE; |
505 | break; |
506 | case HWTSTAMP_FILTER_NONE: |
507 | flags = PORT_MAC_CFG_REQ_FLAGS_PTP_RX_TS_CAPTURE_DISABLE; |
508 | if (bp->fw_cap & BNXT_FW_CAP_RX_ALL_PKT_TS) |
509 | flags |= PORT_MAC_CFG_REQ_FLAGS_ALL_RX_TS_CAPTURE_DISABLE; |
510 | break; |
511 | case HWTSTAMP_FILTER_PTP_V2_EVENT: |
512 | case HWTSTAMP_FILTER_PTP_V2_SYNC: |
513 | case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: |
514 | flags = PORT_MAC_CFG_REQ_FLAGS_PTP_RX_TS_CAPTURE_ENABLE; |
515 | break; |
516 | } |
517 | |
518 | if (ptp->tx_tstamp_en) |
519 | flags |= PORT_MAC_CFG_REQ_FLAGS_PTP_TX_TS_CAPTURE_ENABLE; |
520 | else |
521 | flags |= PORT_MAC_CFG_REQ_FLAGS_PTP_TX_TS_CAPTURE_DISABLE; |
522 | |
523 | ptp->tstamp_filters = flags; |
524 | |
525 | return bnxt_ptp_cfg_tstamp_filters(bp); |
526 | } |
527 | |
528 | int bnxt_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) |
529 | { |
530 | struct bnxt *bp = netdev_priv(dev); |
531 | struct hwtstamp_config stmpconf; |
532 | struct bnxt_ptp_cfg *ptp; |
533 | u16 old_rxctl; |
534 | int old_rx_filter, rc; |
535 | u8 old_tx_tstamp_en; |
536 | |
537 | ptp = bp->ptp_cfg; |
538 | if (!ptp) |
539 | return -EOPNOTSUPP; |
540 | |
541 | if (copy_from_user(to: &stmpconf, from: ifr->ifr_data, n: sizeof(stmpconf))) |
542 | return -EFAULT; |
543 | |
544 | if (stmpconf.tx_type != HWTSTAMP_TX_ON && |
545 | stmpconf.tx_type != HWTSTAMP_TX_OFF) |
546 | return -ERANGE; |
547 | |
548 | old_rx_filter = ptp->rx_filter; |
549 | old_rxctl = ptp->rxctl; |
550 | old_tx_tstamp_en = ptp->tx_tstamp_en; |
551 | switch (stmpconf.rx_filter) { |
552 | case HWTSTAMP_FILTER_NONE: |
553 | ptp->rxctl = 0; |
554 | ptp->rx_filter = HWTSTAMP_FILTER_NONE; |
555 | break; |
556 | case HWTSTAMP_FILTER_ALL: |
557 | if (bp->fw_cap & BNXT_FW_CAP_RX_ALL_PKT_TS) { |
558 | ptp->rx_filter = HWTSTAMP_FILTER_ALL; |
559 | break; |
560 | } |
561 | return -EOPNOTSUPP; |
562 | case HWTSTAMP_FILTER_PTP_V2_EVENT: |
563 | case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: |
564 | case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: |
565 | ptp->rxctl = BNXT_PTP_MSG_EVENTS; |
566 | ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; |
567 | break; |
568 | case HWTSTAMP_FILTER_PTP_V2_SYNC: |
569 | case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: |
570 | case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: |
571 | ptp->rxctl = BNXT_PTP_MSG_SYNC; |
572 | ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC; |
573 | break; |
574 | case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: |
575 | case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: |
576 | case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: |
577 | ptp->rxctl = BNXT_PTP_MSG_DELAY_REQ; |
578 | ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ; |
579 | break; |
580 | default: |
581 | return -ERANGE; |
582 | } |
583 | |
584 | if (stmpconf.tx_type == HWTSTAMP_TX_ON) |
585 | ptp->tx_tstamp_en = 1; |
586 | else |
587 | ptp->tx_tstamp_en = 0; |
588 | |
589 | rc = bnxt_hwrm_ptp_cfg(bp); |
590 | if (rc) |
591 | goto ts_set_err; |
592 | |
593 | stmpconf.rx_filter = ptp->rx_filter; |
594 | return copy_to_user(to: ifr->ifr_data, from: &stmpconf, n: sizeof(stmpconf)) ? |
595 | -EFAULT : 0; |
596 | |
597 | ts_set_err: |
598 | ptp->rx_filter = old_rx_filter; |
599 | ptp->rxctl = old_rxctl; |
600 | ptp->tx_tstamp_en = old_tx_tstamp_en; |
601 | return rc; |
602 | } |
603 | |
604 | int bnxt_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) |
605 | { |
606 | struct bnxt *bp = netdev_priv(dev); |
607 | struct hwtstamp_config stmpconf; |
608 | struct bnxt_ptp_cfg *ptp; |
609 | |
610 | ptp = bp->ptp_cfg; |
611 | if (!ptp) |
612 | return -EOPNOTSUPP; |
613 | |
614 | stmpconf.flags = 0; |
615 | stmpconf.tx_type = ptp->tx_tstamp_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; |
616 | |
617 | stmpconf.rx_filter = ptp->rx_filter; |
618 | return copy_to_user(to: ifr->ifr_data, from: &stmpconf, n: sizeof(stmpconf)) ? |
619 | -EFAULT : 0; |
620 | } |
621 | |
622 | static int bnxt_map_regs(struct bnxt *bp, u32 *reg_arr, int count, int reg_win) |
623 | { |
624 | u32 reg_base = *reg_arr & BNXT_GRC_BASE_MASK; |
625 | u32 win_off; |
626 | int i; |
627 | |
628 | for (i = 0; i < count; i++) { |
629 | if ((reg_arr[i] & BNXT_GRC_BASE_MASK) != reg_base) |
630 | return -ERANGE; |
631 | } |
632 | win_off = BNXT_GRCPF_REG_WINDOW_BASE_OUT + (reg_win - 1) * 4; |
633 | writel(val: reg_base, addr: bp->bar0 + win_off); |
634 | return 0; |
635 | } |
636 | |
637 | static int bnxt_map_ptp_regs(struct bnxt *bp) |
638 | { |
639 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
640 | u32 *reg_arr; |
641 | int rc, i; |
642 | |
643 | reg_arr = ptp->refclk_regs; |
644 | if (BNXT_CHIP_P5(bp)) { |
645 | rc = bnxt_map_regs(bp, reg_arr, count: 2, BNXT_PTP_GRC_WIN); |
646 | if (rc) |
647 | return rc; |
648 | for (i = 0; i < 2; i++) |
649 | ptp->refclk_mapped_regs[i] = BNXT_PTP_GRC_WIN_BASE + |
650 | (ptp->refclk_regs[i] & BNXT_GRC_OFFSET_MASK); |
651 | return 0; |
652 | } |
653 | return -ENODEV; |
654 | } |
655 | |
656 | static void bnxt_unmap_ptp_regs(struct bnxt *bp) |
657 | { |
658 | writel(val: 0, addr: bp->bar0 + BNXT_GRCPF_REG_WINDOW_BASE_OUT + |
659 | (BNXT_PTP_GRC_WIN - 1) * 4); |
660 | } |
661 | |
662 | static u64 bnxt_cc_read(const struct cyclecounter *cc) |
663 | { |
664 | struct bnxt_ptp_cfg *ptp = container_of(cc, struct bnxt_ptp_cfg, cc); |
665 | u64 ns = 0; |
666 | |
667 | bnxt_refclk_read(bp: ptp->bp, NULL, ns: &ns); |
668 | return ns; |
669 | } |
670 | |
671 | static void bnxt_stamp_tx_skb(struct bnxt *bp, struct sk_buff *skb) |
672 | { |
673 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
674 | struct skb_shared_hwtstamps timestamp; |
675 | u64 ts = 0, ns = 0; |
676 | int rc; |
677 | |
678 | rc = bnxt_hwrm_port_ts_query(bp, PORT_TS_QUERY_REQ_FLAGS_PATH_TX, ts: &ts); |
679 | if (!rc) { |
680 | memset(×tamp, 0, sizeof(timestamp)); |
681 | spin_lock_bh(lock: &ptp->ptp_lock); |
682 | ns = timecounter_cyc2time(tc: &ptp->tc, cycle_tstamp: ts); |
683 | spin_unlock_bh(lock: &ptp->ptp_lock); |
684 | timestamp.hwtstamp = ns_to_ktime(ns); |
685 | skb_tstamp_tx(orig_skb: ptp->tx_skb, hwtstamps: ×tamp); |
686 | } else { |
687 | netdev_warn_once(bp->dev, |
688 | "TS query for TX timer failed rc = %x\n" , rc); |
689 | } |
690 | |
691 | dev_kfree_skb_any(skb: ptp->tx_skb); |
692 | ptp->tx_skb = NULL; |
693 | atomic_inc(v: &ptp->tx_avail); |
694 | } |
695 | |
696 | static long bnxt_ptp_ts_aux_work(struct ptp_clock_info *ptp_info) |
697 | { |
698 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, |
699 | ptp_info); |
700 | unsigned long now = jiffies; |
701 | struct bnxt *bp = ptp->bp; |
702 | |
703 | if (ptp->tx_skb) |
704 | bnxt_stamp_tx_skb(bp, skb: ptp->tx_skb); |
705 | |
706 | if (!time_after_eq(now, ptp->next_period)) |
707 | return ptp->next_period - now; |
708 | |
709 | bnxt_ptp_get_current_time(bp); |
710 | ptp->next_period = now + HZ; |
711 | if (time_after_eq(now, ptp->next_overflow_check)) { |
712 | spin_lock_bh(lock: &ptp->ptp_lock); |
713 | timecounter_read(tc: &ptp->tc); |
714 | spin_unlock_bh(lock: &ptp->ptp_lock); |
715 | ptp->next_overflow_check = now + BNXT_PHC_OVERFLOW_PERIOD; |
716 | } |
717 | return HZ; |
718 | } |
719 | |
720 | int bnxt_get_tx_ts_p5(struct bnxt *bp, struct sk_buff *skb) |
721 | { |
722 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
723 | |
724 | if (ptp->tx_skb) { |
725 | netdev_err(dev: bp->dev, format: "deferring skb:one SKB is still outstanding\n" ); |
726 | return -EBUSY; |
727 | } |
728 | ptp->tx_skb = skb; |
729 | ptp_schedule_worker(ptp: ptp->ptp_clock, delay: 0); |
730 | return 0; |
731 | } |
732 | |
733 | int bnxt_get_rx_ts_p5(struct bnxt *bp, u64 *ts, u32 pkt_ts) |
734 | { |
735 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
736 | u64 time; |
737 | |
738 | if (!ptp) |
739 | return -ENODEV; |
740 | |
741 | BNXT_READ_TIME64(ptp, time, ptp->old_time); |
742 | *ts = (time & BNXT_HI_TIMER_MASK) | pkt_ts; |
743 | if (pkt_ts < (time & BNXT_LO_TIMER_MASK)) |
744 | *ts += BNXT_LO_TIMER_MASK + 1; |
745 | |
746 | return 0; |
747 | } |
748 | |
749 | static const struct ptp_clock_info bnxt_ptp_caps = { |
750 | .owner = THIS_MODULE, |
751 | .name = "bnxt clock" , |
752 | .max_adj = BNXT_MAX_PHC_DRIFT, |
753 | .n_alarm = 0, |
754 | .n_ext_ts = 0, |
755 | .n_per_out = 0, |
756 | .n_pins = 0, |
757 | .pps = 0, |
758 | .adjfine = bnxt_ptp_adjfine, |
759 | .adjtime = bnxt_ptp_adjtime, |
760 | .do_aux_work = bnxt_ptp_ts_aux_work, |
761 | .gettimex64 = bnxt_ptp_gettimex, |
762 | .settime64 = bnxt_ptp_settime, |
763 | .enable = bnxt_ptp_enable, |
764 | }; |
765 | |
766 | static int bnxt_ptp_verify(struct ptp_clock_info *ptp_info, unsigned int pin, |
767 | enum ptp_pin_function func, unsigned int chan) |
768 | { |
769 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, |
770 | ptp_info); |
771 | /* Allow only PPS pin function configuration */ |
772 | if (ptp->pps_info.pins[pin].usage <= BNXT_PPS_PIN_PPS_OUT && |
773 | func != PTP_PF_PHYSYNC) |
774 | return 0; |
775 | else |
776 | return -EOPNOTSUPP; |
777 | } |
778 | |
779 | static int bnxt_ptp_pps_init(struct bnxt *bp) |
780 | { |
781 | struct hwrm_func_ptp_pin_qcfg_output *resp; |
782 | struct hwrm_func_ptp_pin_qcfg_input *req; |
783 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
784 | struct ptp_clock_info *ptp_info; |
785 | struct bnxt_pps *pps_info; |
786 | u8 *pin_usg; |
787 | u32 i, rc; |
788 | |
789 | /* Query current/default PIN CFG */ |
790 | rc = hwrm_req_init(bp, req, HWRM_FUNC_PTP_PIN_QCFG); |
791 | if (rc) |
792 | return rc; |
793 | |
794 | resp = hwrm_req_hold(bp, req); |
795 | rc = hwrm_req_send(bp, req); |
796 | if (rc || !resp->num_pins) { |
797 | hwrm_req_drop(bp, req); |
798 | return -EOPNOTSUPP; |
799 | } |
800 | |
801 | ptp_info = &ptp->ptp_info; |
802 | pps_info = &ptp->pps_info; |
803 | pps_info->num_pins = resp->num_pins; |
804 | ptp_info->n_pins = pps_info->num_pins; |
805 | ptp_info->pin_config = kcalloc(n: ptp_info->n_pins, |
806 | size: sizeof(*ptp_info->pin_config), |
807 | GFP_KERNEL); |
808 | if (!ptp_info->pin_config) { |
809 | hwrm_req_drop(bp, req); |
810 | return -ENOMEM; |
811 | } |
812 | |
813 | /* Report the TSIO capability to kernel */ |
814 | pin_usg = &resp->pin0_usage; |
815 | for (i = 0; i < pps_info->num_pins; i++, pin_usg++) { |
816 | snprintf(buf: ptp_info->pin_config[i].name, |
817 | size: sizeof(ptp_info->pin_config[i].name), fmt: "bnxt_pps%d" , i); |
818 | ptp_info->pin_config[i].index = i; |
819 | ptp_info->pin_config[i].chan = i; |
820 | if (*pin_usg == BNXT_PPS_PIN_PPS_IN) |
821 | ptp_info->pin_config[i].func = PTP_PF_EXTTS; |
822 | else if (*pin_usg == BNXT_PPS_PIN_PPS_OUT) |
823 | ptp_info->pin_config[i].func = PTP_PF_PEROUT; |
824 | else |
825 | ptp_info->pin_config[i].func = PTP_PF_NONE; |
826 | |
827 | pps_info->pins[i].usage = *pin_usg; |
828 | } |
829 | hwrm_req_drop(bp, req); |
830 | |
831 | /* Only 1 each of ext_ts and per_out pins is available in HW */ |
832 | ptp_info->n_ext_ts = 1; |
833 | ptp_info->n_per_out = 1; |
834 | ptp_info->pps = 1; |
835 | ptp_info->verify = bnxt_ptp_verify; |
836 | |
837 | return 0; |
838 | } |
839 | |
840 | static bool bnxt_pps_config_ok(struct bnxt *bp) |
841 | { |
842 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
843 | |
844 | return !(bp->fw_cap & BNXT_FW_CAP_PTP_PPS) == !ptp->ptp_info.pin_config; |
845 | } |
846 | |
847 | static void bnxt_ptp_timecounter_init(struct bnxt *bp, bool init_tc) |
848 | { |
849 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
850 | |
851 | if (!ptp->ptp_clock) { |
852 | memset(&ptp->cc, 0, sizeof(ptp->cc)); |
853 | ptp->cc.read = bnxt_cc_read; |
854 | ptp->cc.mask = CYCLECOUNTER_MASK(48); |
855 | if (BNXT_MH(bp)) { |
856 | /* Use timecounter based non-real time mode */ |
857 | ptp->cc.shift = BNXT_CYCLES_SHIFT; |
858 | ptp->cc.mult = clocksource_khz2mult(BNXT_DEVCLK_FREQ, shift_constant: ptp->cc.shift); |
859 | ptp->cmult = ptp->cc.mult; |
860 | } else { |
861 | ptp->cc.shift = 0; |
862 | ptp->cc.mult = 1; |
863 | } |
864 | ptp->next_overflow_check = jiffies + BNXT_PHC_OVERFLOW_PERIOD; |
865 | } |
866 | if (init_tc) |
867 | timecounter_init(tc: &ptp->tc, cc: &ptp->cc, start_tstamp: ktime_to_ns(kt: ktime_get_real())); |
868 | } |
869 | |
870 | /* Caller holds ptp_lock */ |
871 | void bnxt_ptp_rtc_timecounter_init(struct bnxt_ptp_cfg *ptp, u64 ns) |
872 | { |
873 | timecounter_init(tc: &ptp->tc, cc: &ptp->cc, start_tstamp: ns); |
874 | /* For RTC, cycle_last must be in sync with the timecounter value. */ |
875 | ptp->tc.cycle_last = ns & ptp->cc.mask; |
876 | } |
877 | |
878 | int bnxt_ptp_init_rtc(struct bnxt *bp, bool phc_cfg) |
879 | { |
880 | struct timespec64 tsp; |
881 | u64 ns; |
882 | int rc; |
883 | |
884 | if (!bp->ptp_cfg || !BNXT_PTP_USE_RTC(bp)) |
885 | return -ENODEV; |
886 | |
887 | if (!phc_cfg) { |
888 | ktime_get_real_ts64(tv: &tsp); |
889 | ns = timespec64_to_ns(ts: &tsp); |
890 | rc = bnxt_ptp_cfg_settime(bp, time: ns); |
891 | if (rc) |
892 | return rc; |
893 | } else { |
894 | rc = bnxt_hwrm_port_ts_query(bp, PORT_TS_QUERY_REQ_FLAGS_CURRENT_TIME, ts: &ns); |
895 | if (rc) |
896 | return rc; |
897 | } |
898 | spin_lock_bh(lock: &bp->ptp_cfg->ptp_lock); |
899 | bnxt_ptp_rtc_timecounter_init(ptp: bp->ptp_cfg, ns); |
900 | spin_unlock_bh(lock: &bp->ptp_cfg->ptp_lock); |
901 | |
902 | return 0; |
903 | } |
904 | |
905 | static void bnxt_ptp_free(struct bnxt *bp) |
906 | { |
907 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
908 | |
909 | if (ptp->ptp_clock) { |
910 | ptp_clock_unregister(ptp: ptp->ptp_clock); |
911 | ptp->ptp_clock = NULL; |
912 | kfree(objp: ptp->ptp_info.pin_config); |
913 | ptp->ptp_info.pin_config = NULL; |
914 | } |
915 | } |
916 | |
917 | int bnxt_ptp_init(struct bnxt *bp, bool phc_cfg) |
918 | { |
919 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
920 | int rc; |
921 | |
922 | if (!ptp) |
923 | return 0; |
924 | |
925 | rc = bnxt_map_ptp_regs(bp); |
926 | if (rc) |
927 | return rc; |
928 | |
929 | if (ptp->ptp_clock && bnxt_pps_config_ok(bp)) |
930 | return 0; |
931 | |
932 | bnxt_ptp_free(bp); |
933 | |
934 | atomic_set(v: &ptp->tx_avail, BNXT_MAX_TX_TS); |
935 | spin_lock_init(&ptp->ptp_lock); |
936 | |
937 | if (BNXT_PTP_USE_RTC(bp)) { |
938 | bnxt_ptp_timecounter_init(bp, init_tc: false); |
939 | rc = bnxt_ptp_init_rtc(bp, phc_cfg); |
940 | if (rc) |
941 | goto out; |
942 | } else { |
943 | bnxt_ptp_timecounter_init(bp, init_tc: true); |
944 | bnxt_ptp_adjfine_rtc(bp, scaled_ppm: 0); |
945 | } |
946 | bnxt_hwrm_func_drv_rgtr(bp, NULL, bmap_size: 0, async_only: true); |
947 | |
948 | ptp->ptp_info = bnxt_ptp_caps; |
949 | if ((bp->fw_cap & BNXT_FW_CAP_PTP_PPS)) { |
950 | if (bnxt_ptp_pps_init(bp)) |
951 | netdev_err(dev: bp->dev, format: "1pps not initialized, continuing without 1pps support\n" ); |
952 | } |
953 | ptp->ptp_clock = ptp_clock_register(info: &ptp->ptp_info, parent: &bp->pdev->dev); |
954 | if (IS_ERR(ptr: ptp->ptp_clock)) { |
955 | int err = PTR_ERR(ptr: ptp->ptp_clock); |
956 | |
957 | ptp->ptp_clock = NULL; |
958 | rc = err; |
959 | goto out; |
960 | } |
961 | if (BNXT_CHIP_P5(bp)) { |
962 | spin_lock_bh(lock: &ptp->ptp_lock); |
963 | bnxt_refclk_read(bp, NULL, ns: &ptp->current_time); |
964 | WRITE_ONCE(ptp->old_time, ptp->current_time); |
965 | spin_unlock_bh(lock: &ptp->ptp_lock); |
966 | ptp_schedule_worker(ptp: ptp->ptp_clock, delay: 0); |
967 | } |
968 | return 0; |
969 | |
970 | out: |
971 | bnxt_ptp_free(bp); |
972 | bnxt_unmap_ptp_regs(bp); |
973 | return rc; |
974 | } |
975 | |
976 | void bnxt_ptp_clear(struct bnxt *bp) |
977 | { |
978 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; |
979 | |
980 | if (!ptp) |
981 | return; |
982 | |
983 | if (ptp->ptp_clock) |
984 | ptp_clock_unregister(ptp: ptp->ptp_clock); |
985 | |
986 | ptp->ptp_clock = NULL; |
987 | kfree(objp: ptp->ptp_info.pin_config); |
988 | ptp->ptp_info.pin_config = NULL; |
989 | |
990 | if (ptp->tx_skb) { |
991 | dev_kfree_skb_any(skb: ptp->tx_skb); |
992 | ptp->tx_skb = NULL; |
993 | } |
994 | bnxt_unmap_ptp_regs(bp); |
995 | } |
996 | |