1 | /* |
2 | * cxgb4_ptp.c:Chelsio PTP support for T5/T6 |
3 | * |
4 | * Copyright (c) 2003-2017 Chelsio Communications, Inc. All rights reserved. |
5 | * |
6 | * This software is available to you under a choice of one of two |
7 | * licenses. You may choose to be licensed under the terms of the GNU |
8 | * General Public License (GPL) Version 2, available from the file |
9 | * COPYING in the main directory of this source tree, or the |
10 | * OpenIB.org BSD license below: |
11 | * |
12 | * Redistribution and use in source and binary forms, with or |
13 | * without modification, are permitted provided that the following |
14 | * conditions are met: |
15 | * |
16 | * - Redistributions of source code must retain the above |
17 | * copyright notice, this list of conditions and the following |
18 | * disclaimer. |
19 | * |
20 | * - Redistributions in binary form must reproduce the above |
21 | * copyright notice, this list of conditions and the following |
22 | * disclaimer in the documentation and/or other materials |
23 | * provided with the distribution. |
24 | * |
25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
26 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
27 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
28 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
29 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
30 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
31 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
32 | * SOFTWARE. |
33 | * |
34 | * Written by: Atul Gupta (atul.gupta@chelsio.com) |
35 | */ |
36 | |
37 | #include <linux/module.h> |
38 | #include <linux/net_tstamp.h> |
39 | #include <linux/skbuff.h> |
40 | #include <linux/netdevice.h> |
41 | #include <linux/pps_kernel.h> |
42 | #include <linux/ptp_clock_kernel.h> |
43 | #include <linux/ptp_classify.h> |
44 | #include <linux/udp.h> |
45 | |
46 | #include "cxgb4.h" |
47 | #include "t4_hw.h" |
48 | #include "t4_regs.h" |
49 | #include "t4_msg.h" |
50 | #include "t4fw_api.h" |
51 | #include "cxgb4_ptp.h" |
52 | |
53 | /** |
54 | * cxgb4_ptp_is_ptp_tx - determine whether TX packet is PTP or not |
55 | * @skb: skb of outgoing ptp request |
56 | * |
57 | */ |
58 | bool cxgb4_ptp_is_ptp_tx(struct sk_buff *skb) |
59 | { |
60 | struct udphdr *uh; |
61 | |
62 | uh = udp_hdr(skb); |
63 | return skb->len >= PTP_MIN_LENGTH && |
64 | skb->len <= PTP_IN_TRANSMIT_PACKET_MAXNUM && |
65 | likely(skb->protocol == htons(ETH_P_IP)) && |
66 | ip_hdr(skb)->protocol == IPPROTO_UDP && |
67 | uh->dest == htons(PTP_EVENT_PORT); |
68 | } |
69 | |
70 | bool is_ptp_enabled(struct sk_buff *skb, struct net_device *dev) |
71 | { |
72 | struct port_info *pi; |
73 | |
74 | pi = netdev_priv(dev); |
75 | return (pi->ptp_enable && cxgb4_xmit_with_hwtstamp(skb) && |
76 | cxgb4_ptp_is_ptp_tx(skb)); |
77 | } |
78 | |
79 | /** |
80 | * cxgb4_ptp_is_ptp_rx - determine whether RX packet is PTP or not |
81 | * @skb: skb of incoming ptp request |
82 | * |
83 | */ |
84 | bool cxgb4_ptp_is_ptp_rx(struct sk_buff *skb) |
85 | { |
86 | struct udphdr *uh = (struct udphdr *)(skb->data + ETH_HLEN + |
87 | IPV4_HLEN(skb->data)); |
88 | |
89 | return uh->dest == htons(PTP_EVENT_PORT) && |
90 | uh->source == htons(PTP_EVENT_PORT); |
91 | } |
92 | |
93 | /** |
94 | * cxgb4_ptp_read_hwstamp - read timestamp for TX event PTP message |
95 | * @adapter: board private structure |
96 | * @pi: port private structure |
97 | * |
98 | */ |
99 | void cxgb4_ptp_read_hwstamp(struct adapter *adapter, struct port_info *pi) |
100 | { |
101 | struct skb_shared_hwtstamps *skb_ts = NULL; |
102 | u64 tx_ts; |
103 | |
104 | skb_ts = skb_hwtstamps(skb: adapter->ptp_tx_skb); |
105 | |
106 | tx_ts = t4_read_reg(adap: adapter, |
107 | T5_PORT_REG(pi->port_id, MAC_PORT_TX_TS_VAL_LO)); |
108 | |
109 | tx_ts |= (u64)t4_read_reg(adap: adapter, |
110 | T5_PORT_REG(pi->port_id, |
111 | MAC_PORT_TX_TS_VAL_HI)) << 32; |
112 | skb_ts->hwtstamp = ns_to_ktime(ns: tx_ts); |
113 | skb_tstamp_tx(orig_skb: adapter->ptp_tx_skb, hwtstamps: skb_ts); |
114 | dev_kfree_skb_any(skb: adapter->ptp_tx_skb); |
115 | spin_lock(lock: &adapter->ptp_lock); |
116 | adapter->ptp_tx_skb = NULL; |
117 | spin_unlock(lock: &adapter->ptp_lock); |
118 | } |
119 | |
120 | /** |
121 | * cxgb4_ptprx_timestamping - Enable Timestamp for RX PTP event message |
122 | * @pi: port private structure |
123 | * @port: pot number |
124 | * @mode: RX mode |
125 | * |
126 | */ |
127 | int cxgb4_ptprx_timestamping(struct port_info *pi, u8 port, u16 mode) |
128 | { |
129 | struct adapter *adapter = pi->adapter; |
130 | struct fw_ptp_cmd c; |
131 | int err; |
132 | |
133 | memset(&c, 0, sizeof(c)); |
134 | c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | |
135 | FW_CMD_REQUEST_F | |
136 | FW_CMD_WRITE_F | |
137 | FW_PTP_CMD_PORTID_V(port)); |
138 | c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); |
139 | c.u.init.sc = FW_PTP_SC_RXTIME_STAMP; |
140 | c.u.init.mode = cpu_to_be16(mode); |
141 | |
142 | err = t4_wr_mbox(adap: adapter, mbox: adapter->mbox, cmd: &c, size: sizeof(c), NULL); |
143 | if (err < 0) |
144 | dev_err(adapter->pdev_dev, |
145 | "PTP: %s error %d\n" , __func__, -err); |
146 | return err; |
147 | } |
148 | |
149 | int cxgb4_ptp_txtype(struct adapter *adapter, u8 port) |
150 | { |
151 | struct fw_ptp_cmd c; |
152 | int err; |
153 | |
154 | memset(&c, 0, sizeof(c)); |
155 | c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | |
156 | FW_CMD_REQUEST_F | |
157 | FW_CMD_WRITE_F | |
158 | FW_PTP_CMD_PORTID_V(port)); |
159 | c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); |
160 | c.u.init.sc = FW_PTP_SC_TX_TYPE; |
161 | c.u.init.mode = cpu_to_be16(PTP_TS_NONE); |
162 | |
163 | err = t4_wr_mbox(adap: adapter, mbox: adapter->mbox, cmd: &c, size: sizeof(c), NULL); |
164 | if (err < 0) |
165 | dev_err(adapter->pdev_dev, |
166 | "PTP: %s error %d\n" , __func__, -err); |
167 | |
168 | return err; |
169 | } |
170 | |
171 | int cxgb4_ptp_redirect_rx_packet(struct adapter *adapter, struct port_info *pi) |
172 | { |
173 | struct sge *s = &adapter->sge; |
174 | struct sge_eth_rxq *receive_q = &s->ethrxq[pi->first_qset]; |
175 | struct fw_ptp_cmd c; |
176 | int err; |
177 | |
178 | memset(&c, 0, sizeof(c)); |
179 | c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | |
180 | FW_CMD_REQUEST_F | |
181 | FW_CMD_WRITE_F | |
182 | FW_PTP_CMD_PORTID_V(pi->port_id)); |
183 | |
184 | c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); |
185 | c.u.init.sc = FW_PTP_SC_RDRX_TYPE; |
186 | c.u.init.txchan = pi->tx_chan; |
187 | c.u.init.absid = cpu_to_be16(receive_q->rspq.abs_id); |
188 | |
189 | err = t4_wr_mbox(adap: adapter, mbox: adapter->mbox, cmd: &c, size: sizeof(c), NULL); |
190 | if (err < 0) |
191 | dev_err(adapter->pdev_dev, |
192 | "PTP: %s error %d\n" , __func__, -err); |
193 | return err; |
194 | } |
195 | |
196 | /** |
197 | * cxgb4_ptp_adjfine - Adjust frequency of PHC cycle counter |
198 | * @ptp: ptp clock structure |
199 | * @scaled_ppm: Desired frequency in scaled parts per billion |
200 | * |
201 | * Adjust the frequency of the PHC cycle counter by the indicated amount from |
202 | * the base frequency. |
203 | * |
204 | * Scaled parts per million is ppm with a 16-bit binary fractional field. |
205 | */ |
206 | static int cxgb4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) |
207 | { |
208 | struct adapter *adapter = (struct adapter *)container_of(ptp, |
209 | struct adapter, ptp_clock_info); |
210 | s32 ppb = scaled_ppm_to_ppb(ppm: scaled_ppm); |
211 | struct fw_ptp_cmd c; |
212 | int err; |
213 | |
214 | memset(&c, 0, sizeof(c)); |
215 | c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | |
216 | FW_CMD_REQUEST_F | |
217 | FW_CMD_WRITE_F | |
218 | FW_PTP_CMD_PORTID_V(0)); |
219 | c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); |
220 | c.u.ts.sc = FW_PTP_SC_ADJ_FREQ; |
221 | c.u.ts.sign = (ppb < 0) ? 1 : 0; |
222 | if (ppb < 0) |
223 | ppb = -ppb; |
224 | c.u.ts.ppb = cpu_to_be32(ppb); |
225 | |
226 | err = t4_wr_mbox(adap: adapter, mbox: adapter->mbox, cmd: &c, size: sizeof(c), NULL); |
227 | if (err < 0) |
228 | dev_err(adapter->pdev_dev, |
229 | "PTP: %s error %d\n" , __func__, -err); |
230 | |
231 | return err; |
232 | } |
233 | |
234 | /** |
235 | * cxgb4_ptp_fineadjtime - Shift the time of the hardware clock |
236 | * @adapter: board private structure |
237 | * @delta: Desired change in nanoseconds |
238 | * |
239 | * Adjust the timer by resetting the timecounter structure. |
240 | */ |
241 | static int cxgb4_ptp_fineadjtime(struct adapter *adapter, s64 delta) |
242 | { |
243 | struct fw_ptp_cmd c; |
244 | int err; |
245 | |
246 | memset(&c, 0, sizeof(c)); |
247 | c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | |
248 | FW_CMD_REQUEST_F | |
249 | FW_CMD_WRITE_F | |
250 | FW_PTP_CMD_PORTID_V(0)); |
251 | c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); |
252 | c.u.ts.sc = FW_PTP_SC_ADJ_FTIME; |
253 | c.u.ts.sign = (delta < 0) ? 1 : 0; |
254 | if (delta < 0) |
255 | delta = -delta; |
256 | c.u.ts.tm = cpu_to_be64(delta); |
257 | |
258 | err = t4_wr_mbox(adap: adapter, mbox: adapter->mbox, cmd: &c, size: sizeof(c), NULL); |
259 | if (err < 0) |
260 | dev_err(adapter->pdev_dev, |
261 | "PTP: %s error %d\n" , __func__, -err); |
262 | return err; |
263 | } |
264 | |
265 | /** |
266 | * cxgb4_ptp_adjtime - Shift the time of the hardware clock |
267 | * @ptp: ptp clock structure |
268 | * @delta: Desired change in nanoseconds |
269 | * |
270 | * Adjust the timer by resetting the timecounter structure. |
271 | */ |
272 | static int cxgb4_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) |
273 | { |
274 | struct adapter *adapter = |
275 | (struct adapter *)container_of(ptp, struct adapter, |
276 | ptp_clock_info); |
277 | struct fw_ptp_cmd c; |
278 | s64 sign = 1; |
279 | int err; |
280 | |
281 | if (delta < 0) |
282 | sign = -1; |
283 | |
284 | if (delta * sign > PTP_CLOCK_MAX_ADJTIME) { |
285 | memset(&c, 0, sizeof(c)); |
286 | c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | |
287 | FW_CMD_REQUEST_F | |
288 | FW_CMD_WRITE_F | |
289 | FW_PTP_CMD_PORTID_V(0)); |
290 | c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); |
291 | c.u.ts.sc = FW_PTP_SC_ADJ_TIME; |
292 | c.u.ts.sign = (delta < 0) ? 1 : 0; |
293 | if (delta < 0) |
294 | delta = -delta; |
295 | c.u.ts.tm = cpu_to_be64(delta); |
296 | |
297 | err = t4_wr_mbox(adap: adapter, mbox: adapter->mbox, cmd: &c, size: sizeof(c), NULL); |
298 | if (err < 0) |
299 | dev_err(adapter->pdev_dev, |
300 | "PTP: %s error %d\n" , __func__, -err); |
301 | } else { |
302 | err = cxgb4_ptp_fineadjtime(adapter, delta); |
303 | } |
304 | |
305 | return err; |
306 | } |
307 | |
308 | /** |
309 | * cxgb4_ptp_gettime - Reads the current time from the hardware clock |
310 | * @ptp: ptp clock structure |
311 | * @ts: timespec structure to hold the current time value |
312 | * |
313 | * Read the timecounter and return the correct value in ns after converting |
314 | * it into a struct timespec. |
315 | */ |
316 | static int cxgb4_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) |
317 | { |
318 | struct adapter *adapter = container_of(ptp, struct adapter, |
319 | ptp_clock_info); |
320 | u64 ns; |
321 | |
322 | ns = t4_read_reg(adap: adapter, T5_PORT_REG(0, MAC_PORT_PTP_SUM_LO_A)); |
323 | ns |= (u64)t4_read_reg(adap: adapter, |
324 | T5_PORT_REG(0, MAC_PORT_PTP_SUM_HI_A)) << 32; |
325 | |
326 | /* convert to timespec*/ |
327 | *ts = ns_to_timespec64(nsec: ns); |
328 | return 0; |
329 | } |
330 | |
331 | /** |
332 | * cxgb4_ptp_settime - Set the current time on the hardware clock |
333 | * @ptp: ptp clock structure |
334 | * @ts: timespec containing the new time for the cycle counter |
335 | * |
336 | * Reset value to new base value instead of the kernel |
337 | * wall timer value. |
338 | */ |
339 | static int cxgb4_ptp_settime(struct ptp_clock_info *ptp, |
340 | const struct timespec64 *ts) |
341 | { |
342 | struct adapter *adapter = (struct adapter *)container_of(ptp, |
343 | struct adapter, ptp_clock_info); |
344 | struct fw_ptp_cmd c; |
345 | u64 ns; |
346 | int err; |
347 | |
348 | memset(&c, 0, sizeof(c)); |
349 | c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | |
350 | FW_CMD_REQUEST_F | |
351 | FW_CMD_WRITE_F | |
352 | FW_PTP_CMD_PORTID_V(0)); |
353 | c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); |
354 | c.u.ts.sc = FW_PTP_SC_SET_TIME; |
355 | |
356 | ns = timespec64_to_ns(ts); |
357 | c.u.ts.tm = cpu_to_be64(ns); |
358 | |
359 | err = t4_wr_mbox(adap: adapter, mbox: adapter->mbox, cmd: &c, size: sizeof(c), NULL); |
360 | if (err < 0) |
361 | dev_err(adapter->pdev_dev, |
362 | "PTP: %s error %d\n" , __func__, -err); |
363 | |
364 | return err; |
365 | } |
366 | |
367 | static void cxgb4_init_ptp_timer(struct adapter *adapter) |
368 | { |
369 | struct fw_ptp_cmd c; |
370 | int err; |
371 | |
372 | memset(&c, 0, sizeof(c)); |
373 | c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | |
374 | FW_CMD_REQUEST_F | |
375 | FW_CMD_WRITE_F | |
376 | FW_PTP_CMD_PORTID_V(0)); |
377 | c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); |
378 | c.u.scmd.sc = FW_PTP_SC_INIT_TIMER; |
379 | |
380 | err = t4_wr_mbox(adap: adapter, mbox: adapter->mbox, cmd: &c, size: sizeof(c), NULL); |
381 | if (err < 0) |
382 | dev_err(adapter->pdev_dev, |
383 | "PTP: %s error %d\n" , __func__, -err); |
384 | } |
385 | |
386 | /** |
387 | * cxgb4_ptp_enable - enable or disable an ancillary feature |
388 | * @ptp: ptp clock structure |
389 | * @request: Desired resource to enable or disable |
390 | * @on: Caller passes one to enable or zero to disable |
391 | * |
392 | * Enable (or disable) ancillary features of the PHC subsystem. |
393 | * Currently, no ancillary features are supported. |
394 | */ |
395 | static int cxgb4_ptp_enable(struct ptp_clock_info __always_unused *ptp, |
396 | struct ptp_clock_request __always_unused *request, |
397 | int __always_unused on) |
398 | { |
399 | return -ENOTSUPP; |
400 | } |
401 | |
402 | static const struct ptp_clock_info cxgb4_ptp_clock_info = { |
403 | .owner = THIS_MODULE, |
404 | .name = "cxgb4_clock" , |
405 | .max_adj = MAX_PTP_FREQ_ADJ, |
406 | .n_alarm = 0, |
407 | .n_ext_ts = 0, |
408 | .n_per_out = 0, |
409 | .pps = 0, |
410 | .adjfine = cxgb4_ptp_adjfine, |
411 | .adjtime = cxgb4_ptp_adjtime, |
412 | .gettime64 = cxgb4_ptp_gettime, |
413 | .settime64 = cxgb4_ptp_settime, |
414 | .enable = cxgb4_ptp_enable, |
415 | }; |
416 | |
417 | /** |
418 | * cxgb4_ptp_init - initialize PTP for devices which support it |
419 | * @adapter: board private structure |
420 | * |
421 | * This function performs the required steps for enabling PTP support. |
422 | */ |
423 | void cxgb4_ptp_init(struct adapter *adapter) |
424 | { |
425 | struct timespec64 now; |
426 | /* no need to create a clock device if we already have one */ |
427 | if (!IS_ERR_OR_NULL(ptr: adapter->ptp_clock)) |
428 | return; |
429 | |
430 | adapter->ptp_tx_skb = NULL; |
431 | adapter->ptp_clock_info = cxgb4_ptp_clock_info; |
432 | spin_lock_init(&adapter->ptp_lock); |
433 | |
434 | adapter->ptp_clock = ptp_clock_register(info: &adapter->ptp_clock_info, |
435 | parent: &adapter->pdev->dev); |
436 | if (IS_ERR_OR_NULL(ptr: adapter->ptp_clock)) { |
437 | adapter->ptp_clock = NULL; |
438 | dev_err(adapter->pdev_dev, |
439 | "PTP %s Clock registration has failed\n" , __func__); |
440 | return; |
441 | } |
442 | |
443 | now = ktime_to_timespec64(ktime_get_real()); |
444 | cxgb4_init_ptp_timer(adapter); |
445 | if (cxgb4_ptp_settime(ptp: &adapter->ptp_clock_info, ts: &now) < 0) { |
446 | ptp_clock_unregister(ptp: adapter->ptp_clock); |
447 | adapter->ptp_clock = NULL; |
448 | } |
449 | } |
450 | |
451 | /** |
452 | * cxgb4_ptp_stop - disable PTP device and stop the overflow check |
453 | * @adapter: board private structure |
454 | * |
455 | * Stop the PTP support. |
456 | */ |
457 | void cxgb4_ptp_stop(struct adapter *adapter) |
458 | { |
459 | if (adapter->ptp_tx_skb) { |
460 | dev_kfree_skb_any(skb: adapter->ptp_tx_skb); |
461 | adapter->ptp_tx_skb = NULL; |
462 | } |
463 | |
464 | if (adapter->ptp_clock) { |
465 | ptp_clock_unregister(ptp: adapter->ptp_clock); |
466 | adapter->ptp_clock = NULL; |
467 | } |
468 | } |
469 | |