@@ -202,6 +202,8 @@ void ksz_port_txtstamp(struct dsa_switch *ds, int port,
switch (ptp_msg_type) {
case PTP_MSGTYPE_PDELAY_REQ:
+ fallthrough;
+ case PTP_MSGTYPE_PDELAY_RESP:
break;
default:
@@ -214,6 +216,8 @@ void ksz_port_txtstamp(struct dsa_switch *ds, int port,
/* caching the value to be used in later */
KSZ_SKB_CB(skb)->clone = clone;
+ KSZ_SKB_CB(clone)->ptp_type = type;
+ KSZ_SKB_CB(clone)->ptp_msg_type = ptp_msg_type;
}
static int _ksz_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts)
@@ -37,6 +37,8 @@ struct ksz_tagger_data {
struct ksz_skb_cb {
struct sk_buff *clone;
+ unsigned int ptp_type;
+ u8 ptp_msg_type;
};
#define KSZ_SKB_CB(skb) \
@@ -236,14 +236,54 @@ static void ksz_rcv_timestamp(struct sk_buff *skb, u8 *tag,
*/
static void ksz_xmit_timestamp(struct dsa_port *dp, struct sk_buff *skb)
{
+ struct sk_buff *clone = KSZ_SKB_CB(skb)->clone;
struct ksz_tagger_private *priv;
+ struct ptp_header *ptp_hdr;
+ unsigned int ptp_type;
+ u32 tstamp_raw = 0;
+ u8 ptp_msg_type;
+ s64 correction;
priv = ksz_tagger_private(dp->ds);
if (!test_bit(KSZ_HWTS_EN, &priv->state))
return;
- put_unaligned_be32(0, skb_put(skb, KSZ_PTP_TAG_LEN));
+ if (!clone)
+ goto output_tag;
+
+ ptp_type = KSZ_SKB_CB(clone)->ptp_type;
+ if (ptp_type == PTP_CLASS_NONE)
+ goto output_tag;
+
+ ptp_hdr = ptp_parse_header(skb, ptp_type);
+ if (!ptp_hdr)
+ goto output_tag;
+
+ ptp_msg_type = KSZ_SKB_CB(clone)->ptp_msg_type;
+ if (ptp_msg_type != PTP_MSGTYPE_PDELAY_RESP)
+ goto output_tag;
+
+ correction = (s64)get_unaligned_be64(&ptp_hdr->correction);
+
+ if (correction < 0) {
+ struct timespec64 ts;
+
+ ts = ns_to_timespec64(-correction >> 16);
+ tstamp_raw = ((ts.tv_sec & 3) << 30) | ts.tv_nsec;
+
+ /* Set correction field to 0 and update UDP checksum. */
+ ptp_header_update_correction(skb, ptp_type, ptp_hdr, 0);
+ }
+
+ /* For PDelay_Resp messages, the clone is not required in
+ * skb_complete_tx_timestamp() and should be freed here.
+ */
+ kfree_skb(clone);
+ KSZ_SKB_CB(skb)->clone = NULL;
+
+output_tag:
+ put_unaligned_be32(tstamp_raw, skb_put(skb, KSZ_PTP_TAG_LEN));
}
/* Defer transmit if waiting for egress time stamp is required. */