1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
4 * Hirschmann Hellcreek TSN switch.
6 * Copyright (C) 2019,2020 Hochschule Offenburg
7 * Copyright (C) 2019,2020 Linutronix GmbH
8 * Authors: Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>
9 * Kurt Kanzenbach <kurt@linutronix.de>
12 #include <linux/ptp_classify.h>
14 #include "hellcreek.h"
15 #include "hellcreek_hwtstamp.h"
16 #include "hellcreek_ptp.h"
18 int hellcreek_get_ts_info(struct dsa_switch
*ds
, int port
,
19 struct kernel_ethtool_ts_info
*info
)
21 struct hellcreek
*hellcreek
= ds
->priv
;
23 info
->phc_index
= hellcreek
->ptp_clock
?
24 ptp_clock_index(hellcreek
->ptp_clock
) : -1;
25 info
->so_timestamping
= SOF_TIMESTAMPING_TX_HARDWARE
|
26 SOF_TIMESTAMPING_RX_HARDWARE
|
27 SOF_TIMESTAMPING_RAW_HARDWARE
;
29 /* enabled tx timestamping */
30 info
->tx_types
= BIT(HWTSTAMP_TX_ON
);
32 /* L2 & L4 PTPv2 event rx messages are timestamped */
33 info
->rx_filters
= BIT(HWTSTAMP_FILTER_PTP_V2_EVENT
);
38 /* Enabling/disabling TX and RX HW timestamping for different PTP messages is
39 * not available in the switch. Thus, this function only serves as a check if
40 * the user requested what is actually available or not
42 static int hellcreek_set_hwtstamp_config(struct hellcreek
*hellcreek
, int port
,
43 struct hwtstamp_config
*config
)
45 struct hellcreek_port_hwtstamp
*ps
=
46 &hellcreek
->ports
[port
].port_hwtstamp
;
47 bool tx_tstamp_enable
= false;
48 bool rx_tstamp_enable
= false;
50 /* Interaction with the timestamp hardware is prevented here. It is
51 * enabled when this config function ends successfully
53 clear_bit_unlock(HELLCREEK_HWTSTAMP_ENABLED
, &ps
->state
);
55 switch (config
->tx_type
) {
57 tx_tstamp_enable
= true;
60 /* TX HW timestamping can't be disabled on the switch */
62 config
->tx_type
= HWTSTAMP_TX_ON
;
69 switch (config
->rx_filter
) {
70 /* RX HW timestamping can't be disabled on the switch */
71 case HWTSTAMP_FILTER_NONE
:
72 config
->rx_filter
= HWTSTAMP_FILTER_PTP_V2_EVENT
;
75 case HWTSTAMP_FILTER_PTP_V2_L4_EVENT
:
76 case HWTSTAMP_FILTER_PTP_V2_L4_SYNC
:
77 case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ
:
78 case HWTSTAMP_FILTER_PTP_V2_L2_EVENT
:
79 case HWTSTAMP_FILTER_PTP_V2_L2_SYNC
:
80 case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ
:
81 case HWTSTAMP_FILTER_PTP_V2_EVENT
:
82 case HWTSTAMP_FILTER_PTP_V2_SYNC
:
83 case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ
:
84 config
->rx_filter
= HWTSTAMP_FILTER_PTP_V2_EVENT
;
85 rx_tstamp_enable
= true;
88 /* RX HW timestamping can't be enabled for all messages on the switch */
89 case HWTSTAMP_FILTER_ALL
:
90 config
->rx_filter
= HWTSTAMP_FILTER_PTP_V2_EVENT
;
97 if (!tx_tstamp_enable
)
100 if (!rx_tstamp_enable
)
103 /* If this point is reached, then the requested hwtstamp config is
104 * compatible with the hwtstamp offered by the switch. Therefore,
105 * enable the interaction with the HW timestamping
107 set_bit(HELLCREEK_HWTSTAMP_ENABLED
, &ps
->state
);
112 int hellcreek_port_hwtstamp_set(struct dsa_switch
*ds
, int port
,
115 struct hellcreek
*hellcreek
= ds
->priv
;
116 struct hellcreek_port_hwtstamp
*ps
;
117 struct hwtstamp_config config
;
120 ps
= &hellcreek
->ports
[port
].port_hwtstamp
;
122 if (copy_from_user(&config
, ifr
->ifr_data
, sizeof(config
)))
125 err
= hellcreek_set_hwtstamp_config(hellcreek
, port
, &config
);
129 /* Save the chosen configuration to be returned later */
130 memcpy(&ps
->tstamp_config
, &config
, sizeof(config
));
132 return copy_to_user(ifr
->ifr_data
, &config
, sizeof(config
)) ?
136 int hellcreek_port_hwtstamp_get(struct dsa_switch
*ds
, int port
,
139 struct hellcreek
*hellcreek
= ds
->priv
;
140 struct hellcreek_port_hwtstamp
*ps
;
141 struct hwtstamp_config
*config
;
143 ps
= &hellcreek
->ports
[port
].port_hwtstamp
;
144 config
= &ps
->tstamp_config
;
146 return copy_to_user(ifr
->ifr_data
, config
, sizeof(*config
)) ?
150 /* Returns a pointer to the PTP header if the caller should time stamp, or NULL
151 * if the caller should not.
153 static struct ptp_header
*hellcreek_should_tstamp(struct hellcreek
*hellcreek
,
154 int port
, struct sk_buff
*skb
,
157 struct hellcreek_port_hwtstamp
*ps
=
158 &hellcreek
->ports
[port
].port_hwtstamp
;
159 struct ptp_header
*hdr
;
161 hdr
= ptp_parse_header(skb
, type
);
165 if (!test_bit(HELLCREEK_HWTSTAMP_ENABLED
, &ps
->state
))
171 static u64
hellcreek_get_reserved_field(const struct ptp_header
*hdr
)
173 return be32_to_cpu(hdr
->reserved2
);
176 static void hellcreek_clear_reserved_field(struct ptp_header
*hdr
)
181 static int hellcreek_ptp_hwtstamp_available(struct hellcreek
*hellcreek
,
186 status
= hellcreek_ptp_read(hellcreek
, ts_reg
);
188 if (status
& PR_TS_STATUS_TS_LOST
)
189 dev_err(hellcreek
->dev
,
190 "Tx time stamp lost! This should never happen!\n");
192 /* If hwtstamp is not available, this means the previous hwtstamp was
193 * successfully read, and the one we need is not yet available
195 return (status
& PR_TS_STATUS_TS_AVAIL
) ? 1 : 0;
198 /* Get nanoseconds timestamp from timestamping unit */
199 static u64
hellcreek_ptp_hwtstamp_read(struct hellcreek
*hellcreek
,
204 nsh
= hellcreek_ptp_read(hellcreek
, ts_reg
);
205 nsh
= hellcreek_ptp_read(hellcreek
, ts_reg
);
206 nsh
= hellcreek_ptp_read(hellcreek
, ts_reg
);
207 nsh
= hellcreek_ptp_read(hellcreek
, ts_reg
);
208 nsl
= hellcreek_ptp_read(hellcreek
, ts_reg
);
210 return (u64
)nsl
| ((u64
)nsh
<< 16);
213 static int hellcreek_txtstamp_work(struct hellcreek
*hellcreek
,
214 struct hellcreek_port_hwtstamp
*ps
, int port
)
216 struct skb_shared_hwtstamps shhwtstamps
;
217 unsigned int status_reg
, data_reg
;
218 struct sk_buff
*tmp_skb
;
227 status_reg
= PR_TS_TX_P1_STATUS_C
;
228 data_reg
= PR_TS_TX_P1_DATA_C
;
231 status_reg
= PR_TS_TX_P2_STATUS_C
;
232 data_reg
= PR_TS_TX_P2_DATA_C
;
235 dev_err(hellcreek
->dev
, "Wrong port for timestamping!\n");
239 ts_status
= hellcreek_ptp_hwtstamp_available(hellcreek
, status_reg
);
241 /* Not available yet? */
242 if (ts_status
== 0) {
243 /* Check whether the operation of reading the tx timestamp has
244 * exceeded its allowed period
246 if (time_is_before_jiffies(ps
->tx_tstamp_start
+
247 TX_TSTAMP_TIMEOUT
)) {
248 dev_err(hellcreek
->dev
,
249 "Timeout while waiting for Tx timestamp!\n");
250 goto free_and_clear_skb
;
253 /* The timestamp should be available quickly, while getting it
254 * in high priority. Restart the work
259 mutex_lock(&hellcreek
->ptp_lock
);
260 ns
= hellcreek_ptp_hwtstamp_read(hellcreek
, data_reg
);
261 ns
+= hellcreek_ptp_gettime_seconds(hellcreek
, ns
);
262 mutex_unlock(&hellcreek
->ptp_lock
);
264 /* Now we have the timestamp in nanoseconds, store it in the correct
265 * structure in order to send it to the user
267 memset(&shhwtstamps
, 0, sizeof(shhwtstamps
));
268 shhwtstamps
.hwtstamp
= ns_to_ktime(ns
);
270 tmp_skb
= ps
->tx_skb
;
273 /* skb_complete_tx_timestamp() frees up the client to make another
274 * timestampable transmit. We have to be ready for it by clearing the
275 * ps->tx_skb "flag" beforehand
277 clear_bit_unlock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS
, &ps
->state
);
279 /* Deliver a clone of the original outgoing tx_skb with tx hwtstamp */
280 skb_complete_tx_timestamp(tmp_skb
, &shhwtstamps
);
285 dev_kfree_skb_any(ps
->tx_skb
);
287 clear_bit_unlock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS
, &ps
->state
);
292 static void hellcreek_get_rxts(struct hellcreek
*hellcreek
,
293 struct hellcreek_port_hwtstamp
*ps
,
294 struct sk_buff
*skb
, struct sk_buff_head
*rxq
,
297 struct skb_shared_hwtstamps
*shwt
;
298 struct sk_buff_head received
;
301 /* Construct Rx timestamps for all received PTP packets. */
302 __skb_queue_head_init(&received
);
303 spin_lock_irqsave(&rxq
->lock
, flags
);
304 skb_queue_splice_tail_init(rxq
, &received
);
305 spin_unlock_irqrestore(&rxq
->lock
, flags
);
307 for (; skb
; skb
= __skb_dequeue(&received
)) {
308 struct ptp_header
*hdr
;
312 /* Get nanoseconds from ptp packet */
313 type
= SKB_PTP_TYPE(skb
);
314 hdr
= ptp_parse_header(skb
, type
);
315 ns
= hellcreek_get_reserved_field(hdr
);
316 hellcreek_clear_reserved_field(hdr
);
318 /* Add seconds part */
319 mutex_lock(&hellcreek
->ptp_lock
);
320 ns
+= hellcreek_ptp_gettime_seconds(hellcreek
, ns
);
321 mutex_unlock(&hellcreek
->ptp_lock
);
323 /* Save time stamp */
324 shwt
= skb_hwtstamps(skb
);
325 memset(shwt
, 0, sizeof(*shwt
));
326 shwt
->hwtstamp
= ns_to_ktime(ns
);
331 static void hellcreek_rxtstamp_work(struct hellcreek
*hellcreek
,
332 struct hellcreek_port_hwtstamp
*ps
,
337 skb
= skb_dequeue(&ps
->rx_queue
);
339 hellcreek_get_rxts(hellcreek
, ps
, skb
, &ps
->rx_queue
, port
);
342 long hellcreek_hwtstamp_work(struct ptp_clock_info
*ptp
)
344 struct hellcreek
*hellcreek
= ptp_to_hellcreek(ptp
);
345 struct dsa_switch
*ds
= hellcreek
->ds
;
348 for (i
= 0; i
< ds
->num_ports
; i
++) {
349 struct hellcreek_port_hwtstamp
*ps
;
351 if (!dsa_is_user_port(ds
, i
))
354 ps
= &hellcreek
->ports
[i
].port_hwtstamp
;
356 if (test_bit(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS
, &ps
->state
))
357 restart
|= hellcreek_txtstamp_work(hellcreek
, ps
, i
);
359 hellcreek_rxtstamp_work(hellcreek
, ps
, i
);
362 return restart
? 1 : -1;
365 void hellcreek_port_txtstamp(struct dsa_switch
*ds
, int port
,
368 struct hellcreek
*hellcreek
= ds
->priv
;
369 struct hellcreek_port_hwtstamp
*ps
;
370 struct ptp_header
*hdr
;
371 struct sk_buff
*clone
;
374 ps
= &hellcreek
->ports
[port
].port_hwtstamp
;
376 type
= ptp_classify_raw(skb
);
377 if (type
== PTP_CLASS_NONE
)
380 /* Make sure the message is a PTP message that needs to be timestamped
381 * and the interaction with the HW timestamping is enabled. If not, stop
384 hdr
= hellcreek_should_tstamp(hellcreek
, port
, skb
, type
);
388 clone
= skb_clone_sk(skb
);
392 if (test_and_set_bit_lock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS
,
400 /* store the number of ticks occurred since system start-up till this
403 ps
->tx_tstamp_start
= jiffies
;
405 ptp_schedule_worker(hellcreek
->ptp_clock
, 0);
408 bool hellcreek_port_rxtstamp(struct dsa_switch
*ds
, int port
,
409 struct sk_buff
*skb
, unsigned int type
)
411 struct hellcreek
*hellcreek
= ds
->priv
;
412 struct hellcreek_port_hwtstamp
*ps
;
413 struct ptp_header
*hdr
;
415 ps
= &hellcreek
->ports
[port
].port_hwtstamp
;
417 /* This check only fails if the user did not initialize hardware
418 * timestamping beforehand.
420 if (ps
->tstamp_config
.rx_filter
!= HWTSTAMP_FILTER_PTP_V2_EVENT
)
423 /* Make sure the message is a PTP message that needs to be timestamped
424 * and the interaction with the HW timestamping is enabled. If not, stop
427 hdr
= hellcreek_should_tstamp(hellcreek
, port
, skb
, type
);
431 SKB_PTP_TYPE(skb
) = type
;
433 skb_queue_tail(&ps
->rx_queue
, skb
);
435 ptp_schedule_worker(hellcreek
->ptp_clock
, 0);
440 static void hellcreek_hwtstamp_port_setup(struct hellcreek
*hellcreek
, int port
)
442 struct hellcreek_port_hwtstamp
*ps
=
443 &hellcreek
->ports
[port
].port_hwtstamp
;
445 skb_queue_head_init(&ps
->rx_queue
);
448 int hellcreek_hwtstamp_setup(struct hellcreek
*hellcreek
)
450 struct dsa_switch
*ds
= hellcreek
->ds
;
453 /* Initialize timestamping ports. */
454 for (i
= 0; i
< ds
->num_ports
; ++i
) {
455 if (!dsa_is_user_port(ds
, i
))
458 hellcreek_hwtstamp_port_setup(hellcreek
, i
);
461 /* Select the synchronized clock as the source timekeeper for the
462 * timestamps and enable inline timestamping.
464 hellcreek_ptp_write(hellcreek
, PR_SETTINGS_C_TS_SRC_TK_MASK
|
465 PR_SETTINGS_C_RES3TS
,
471 void hellcreek_hwtstamp_free(struct hellcreek
*hellcreek
)