From nobody Sat Jul 25 03:48:00 2026 Received: from us.padl.com (us.padl.com [216.154.215.154]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C7E992DECBA; Sun, 19 Jul 2026 05:31:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=216.154.215.154 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784439074; cv=none; b=MhEvb5SzhYO9MYFAY1tAB09SL6H7jJhIv4jrBjZ20T5GUtH/3Qg02ybwXgUOvWaojZRRbS12ic+yPS267vegmf4NS0IzrxOnAtSSSxBBfop1AtUsToOXoXNwGs7YXaj3JMotjZ7qasN6yhGy3YFNXbmG1jqovyyhg5KZ1WFNpSI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784439074; c=relaxed/simple; bh=S/m7wEGVPPuy/T0jA6cuudg3kG0H7utcE7QLoebxOpk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=uuCY2K1bjgOV9z7G8o6Xg+svTA41Ab9aZ6aSzWfcFhifPRk9o56N+zW/ly7JFl3P/cFBZixYKvTTgLf8yfz50kX8dgoikyHbQWpVt+5GpLZTzUREMdY6XzjtP2pnoXBKApRqsBGpUz4cFt/1C+Yks4Sd3OchRjYe6l9B5+uI8Ro= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=padl.com; spf=pass smtp.mailfrom=padl.com; dkim=pass (2048-bit key) header.d=padl.com header.i=@padl.com header.b=H5bHsnJ9; arc=none smtp.client-ip=216.154.215.154 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=padl.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=padl.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=padl.com header.i=@padl.com header.b="H5bHsnJ9" Received: from [127.0.1.1] (border.lukktone.com [139.130.100.52]) by us.padl.com (Postfix) with ESMTPSA id AB21C60D3C; Sun, 19 Jul 2026 01:31:01 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=padl.com; s=default; t=1784439065; bh=FapvGTAxBY2u9Vb3QN5cttB3cakXLqKSc2AEx2t5wDw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=H5bHsnJ9bTTSQ6d5IVK09qjimDr6ftve984VWLpNqFrIgtQp5e1qgkzB4IucyMgft ro4HgVKF/ExU3QP4oMW5y84VXJLQy/Bddl7Kd6WwPVvT6OAUveasEuXWqeViiOU2BO Qoxdhnu2ZllUSbR3br7UEr4E3Xt0YJn4+2Hh4tYuvQrg8399j67V+UhmzXq+sneOvF RlAa0X3gy/GBAdmX7omAV9ZxyT1ekZG8JX0Nig6Q4l1+8D6/Cyhv56elO4UNLl3w1N pJlwNopYgYero71QSVaSGnBiiH9d86wHRkLx1bcOVPa4UFxcGlFTc3iCuvg58KoohN 52feMWBryrqgw== From: Luke Howard Date: Sun, 19 Jul 2026 15:30:53 +1000 Subject: [PATCH net-next v3 1/3] net: dsa: mv88e6xxx: use ARRIVAL1 counter for all peer delay messages Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260719-mv88e6xxx-ptp-fixes-v3-1-161386ac8a62@padl.com> References: <20260719-mv88e6xxx-ptp-fixes-v3-0-161386ac8a62@padl.com> In-Reply-To: <20260719-mv88e6xxx-ptp-fixes-v3-0-161386ac8a62@padl.com> To: Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Vivien Didelot , Gregory CLEMENT , Andrew Lunn , Richard Cochran Cc: Cedric Jehasse , Kieran Tyrrell , Max Holtmann , Max Hunter , Christoph Mellauner , Simon Gapp , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luke Howard X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784439056; l=2031; i=lukeh@padl.com; s=20260701; h=from:subject:message-id; bh=S/m7wEGVPPuy/T0jA6cuudg3kG0H7utcE7QLoebxOpk=; b=IclY4je3GtNAPlrKV7v1rjJlGcRH+l+nwFhxu7i1zI9goggmyCfAuK2dGBoWU0Fq/IIVld4ok rAMEIrk0TGEBMHd5jFgWjIvcm4D8eAFsNxxY7ofanVeBdiCClakxuvl X-Developer-Key: i=lukeh@padl.com; a=ed25519; pk=D6YgUnH9/OzSZsduYm9XDbvXn1IHDwKwI1hLTsKNRLw= mv88e6xxx switches have two arrival timestamp counters for timestamping PTP event messages. This permits more than one arriving event message's timestamp to be captured. This is useful for the case where event messages from a grandmaster arrive at the same time as PDelayReq/PDelayResp messages from a peer. Previously only PDelayResp messages were assigned to the second arrival counter; this patch does so for PDelayReq messages as well. Signed-off-by: Luke Howard --- drivers/net/dsa/mv88e6xxx/hwtstamp.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6x= xx/hwtstamp.c index 6e6472a3b75ad..57ff77496864f 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c @@ -319,9 +319,16 @@ static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_c= hip *chip, &ps->rx_queue2); } =20 -static int is_pdelay_resp(const struct ptp_header *hdr) +static bool is_pdelay_msg(const struct ptp_header *hdr) { - return (hdr->tsmt & 0xf) =3D=3D 3; + switch (ptp_get_msgtype(hdr, PTP_CLASS_V2)) { + case PTP_MSGTYPE_PDELAY_REQ: + fallthrough; + case PTP_MSGTYPE_PDELAY_RESP: + return true; + default: + return false; + } } =20 bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port, @@ -343,7 +350,7 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int= port, =20 SKB_PTP_TYPE(skb) =3D type; =20 - if (is_pdelay_resp(hdr)) + if (is_pdelay_msg(hdr)) skb_queue_tail(&ps->rx_queue2, skb); else skb_queue_tail(&ps->rx_queue, skb); @@ -584,8 +591,9 @@ int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chi= p) if (err) return err; =20 - /* Use ARRIVAL1 for peer delay response messages. */ + /* Use ARRIVAL1 for peer delay messages. */ err =3D mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_TS_ARRIVAL_PTR, + MV88E6XXX_PTP_MSGTYPE_PDLAY_REQ | MV88E6XXX_PTP_MSGTYPE_PDLAY_RES); if (err) return err; --=20 2.43.0 From nobody Sat Jul 25 03:48:00 2026 Received: from us.padl.com (us.padl.com [216.154.215.154]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F1F134D8CE; Sun, 19 Jul 2026 05:31:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=216.154.215.154 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784439073; cv=none; b=Ic18cQzUUNbo2pe1r4UQHAPkWUFowWeeln97epaHJe2Xb9EI+9MlbsUwKMb3J3RB3B0dr7M3otCVUGuzni3ISUXPASNwSLHMhukGl5TKZvuUjmAca9OzhPjMAyrTZwi2EjaRXgI4uCyOl/fZD10miotVrjRTLn8yGmU2cWtm6C8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784439073; c=relaxed/simple; bh=VQJDRxAA6T5afKbHAOHhm/scPrBhzoEHJ1AKnjYlzc4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Lt1HFoq8EXnVyruuoyyYCaUc2K/PfqZhRV2OrIB+yTH2FeuLhRh9oLBAhjbwsRmBmgBBy5OPpa8PM1A1G0wE4zD9hsPIcgvmY+O2lTvJ89GWcQyxpqYqNyzBVFSymWo9TrayjOZaQWCskDnN0F7nkUlAHRfwOudLgxY4LQHViEU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=padl.com; spf=pass smtp.mailfrom=padl.com; dkim=pass (2048-bit key) header.d=padl.com header.i=@padl.com header.b=saxBk3RK; arc=none smtp.client-ip=216.154.215.154 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=padl.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=padl.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=padl.com header.i=@padl.com header.b="saxBk3RK" Received: from [127.0.1.1] (border.lukktone.com [139.130.100.52]) by us.padl.com (Postfix) with ESMTPSA id 0F7DE61932; Sun, 19 Jul 2026 01:31:05 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=padl.com; s=default; t=1784439070; bh=iDVz1EaZ6nKjivhC8mxmJVkOeFg8forT8bnVhF6H8LA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=saxBk3RKlV5JS6ku/LYdXlElmNfo13OD8g3jTBfCrfZEapi79q7GtiSmJ6ZEPcVl6 b+8vDyAab7XaM8SbUzIz2xQpzPNk3IvNjO4o0Y8Iy9+1UYzmMiscA6q2znAY3M0ebQ soo08R4SJgrH/KoyxpUZhWu/Z0R4wg3tqynuv+XQ7tTZlLAf+hh5nF8x8PmNJrlVZl QZOMtwPywmGs3W9sVcIw+ZViGv4GoLZowQeNvmEJmTzfzOvPE/ZbFNbPlxtFq3LveI VNchz2luhzuMmywFeKij6dfaC8LCJ1+XCfQ3rldBG4iLN6zjqjKcZX7/OOEO7T9geI lNcnFA2XfT/uw== From: Luke Howard Date: Sun, 19 Jul 2026 15:30:54 +1000 Subject: [PATCH net-next v3 2/3] net: dsa: mv88e6xxx: embedded PTP timestamp support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260719-mv88e6xxx-ptp-fixes-v3-2-161386ac8a62@padl.com> References: <20260719-mv88e6xxx-ptp-fixes-v3-0-161386ac8a62@padl.com> In-Reply-To: <20260719-mv88e6xxx-ptp-fixes-v3-0-161386ac8a62@padl.com> To: Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Vivien Didelot , Gregory CLEMENT , Andrew Lunn , Richard Cochran Cc: Cedric Jehasse , Kieran Tyrrell , Max Holtmann , Max Hunter , Christoph Mellauner , Simon Gapp , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luke Howard X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784439056; l=9946; i=lukeh@padl.com; s=20260701; h=from:subject:message-id; bh=VQJDRxAA6T5afKbHAOHhm/scPrBhzoEHJ1AKnjYlzc4=; b=Y1o3aPqcH8lcJp56V11p7coaL8P+/BBy88k2HbriLbhU1RqCxrsQ3Ls37WHx8j6M/6WwWLn1m Go6ASDrG6MSArmIFAlVys2lrQrFwp/j3BlXNE5ErFXrWwq8LqzXV5ao X-Developer-Key: i=lukeh@padl.com; a=ed25519; pk=D6YgUnH9/OzSZsduYm9XDbvXn1IHDwKwI1hLTsKNRLw= mv88e6xxx switches can support embedding PTP timestamps directly in the frame, either as a trailer or at a configurable offset (typically the reserved bytes in the PTP header). Add support for this on the 88E6341 and 88E6352 switches, being those on which I was able to verify this. Other switch chips may also work. The arrival timestamp offsets are relative to the PTP common header and will work for both L2 and L3 PTP packets; the respective headers are skipped by the number of bytes set in the ETJump and IPJump registers, which are initialized to sensible defaults and are VLAN tag-aware. (Note: the 6352 datasheet incorrectly states that ETJump and IPJump are initialized to zero. They are initialized to 12 and 2 respectively; this is corrected in the 6341 data sheet.) Signed-off-by: Luke Howard --- drivers/net/dsa/mv88e6xxx/chip.c | 3 + drivers/net/dsa/mv88e6xxx/chip.h | 8 +++ drivers/net/dsa/mv88e6xxx/hwtstamp.c | 131 +++++++++++++++++++++++++++++++= ---- drivers/net/dsa/mv88e6xxx/hwtstamp.h | 14 ++++ 4 files changed, 144 insertions(+), 12 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/c= hip.c index 80b877c74513d..c3277c1f3d785 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -6370,6 +6371,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = =3D { .edsa_support =3D MV88E6XXX_EDSA_SUPPORTED, .ptp_support =3D true, .ops =3D &mv88e6341_ops, + .arr_ts_mode =3D offsetof(struct ptp_header, reserved2), }, =20 [MV88E6350] =3D { @@ -6447,6 +6449,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = =3D { .edsa_support =3D MV88E6XXX_EDSA_SUPPORTED, .ptp_support =3D true, .ops =3D &mv88e6352_ops, + .arr_ts_mode =3D offsetof(struct ptp_header, reserved2), }, [MV88E6361] =3D { .prod_num =3D MV88E6XXX_PORT_SWITCH_ID_PROD_6361, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/c= hip.h index e966e7c4cc5de..b6a90eba81c43 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -180,6 +180,14 @@ struct mv88e6xxx_info { * port 0, 1 means internal PHYs range starts at port 1, etc */ unsigned int internal_phys_offset; + + /* Arrival Time Stamp Mode (ArrTSMode); see the ArrTSMode encoding in + * hwtstamp.h. Zero (the default) leaves arrival time stamps in the + * switch registers; non-zero embeds them in the frame, either appended + * as a trailer or overwritten at that byte offset past the start of the + * PTP common header. + */ + unsigned int arr_ts_mode; }; =20 struct mv88e6xxx_atu_entry { diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6x= xx/hwtstamp.c index 57ff77496864f..fa5e897182ca2 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c @@ -15,6 +15,7 @@ #include "hwtstamp.h" #include "ptp.h" #include +#include =20 #define SKB_PTP_TYPE(__skb) (*(unsigned int *)((__skb)->cb)) =20 @@ -245,6 +246,74 @@ static int seq_match(struct sk_buff *skb, u16 ts_seqid) return ts_seqid =3D=3D ntohs(hdr->sequence_id); } =20 +static bool parse_embedded_ts(unsigned int arr_ts_mode, + struct sk_buff *skb, u64 *ns) +{ + struct ptp_header *hdr; + + *ns =3D 0; + + /* APPEND means the switch appended the time stamp as a 4-byte trailer + * (not all switches support this). Any other non-zero value is the byte + * offset past the start of the PTP common header at which the switch + * overwrote the time stamp in place (e.g. the reserved header bytes). + */ + if (arr_ts_mode =3D=3D MV88E6XXX_PTP_ARR_TS_MODE_APPEND && skb->len >=3D = 4) { + if (skb_linearize(skb)) + return false; + + *ns =3D (u64)get_unaligned_be32(skb_tail_pointer(skb) - 4); + if (pskb_trim_rcsum(skb, skb->len - 4)) + return false; + } else if (arr_ts_mode + 4 <=3D sizeof(*hdr)) { + if (skb_linearize(skb)) + return false; + + hdr =3D ptp_parse_header(skb, SKB_PTP_TYPE(skb)); + if (!hdr) + return false; + + *ns =3D (u64)get_unaligned_be32((u8 *)hdr + arr_ts_mode); + memset((u8 *)hdr + arr_ts_mode, 0, 4); + skb_checksum_complete_unset(skb); + } else { + return false; + } + + return true; +} + +static void mv88e6xxx_get_rxts_embedded(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_port_hwtstamp *ps, + struct sk_buff *skb) +{ + struct sk_buff_head *rxq =3D &ps->rx_queue; + struct skb_shared_hwtstamps *shwt; + struct sk_buff_head received; + unsigned long flags; + u64 ns; + + __skb_queue_head_init(&received); + __skb_queue_head(&received, skb); + spin_lock_irqsave(&rxq->lock, flags); + skb_queue_splice_tail_init(rxq, &received); + spin_unlock_irqrestore(&rxq->lock, flags); + + mv88e6xxx_reg_lock(chip); + skb_queue_walk(&received, skb) { + if (!parse_embedded_ts(chip->info->arr_ts_mode, skb, &ns)) + continue; + ns =3D timecounter_cyc2time(&chip->tstamp_tc, ns); + shwt =3D skb_hwtstamps(skb); + memset(shwt, 0, sizeof(*shwt)); + shwt->hwtstamp =3D ns_to_ktime(ns); + } + mv88e6xxx_reg_unlock(chip); + + while ((skb =3D __skb_dequeue(&received))) + netif_rx(skb); +} + static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip, struct mv88e6xxx_port_hwtstamp *ps, struct sk_buff *skb, u16 reg, @@ -307,8 +376,21 @@ static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_c= hip *chip, const struct mv88e6xxx_ptp_ops *ptp_ops =3D chip->info->ops->ptp_ops; struct sk_buff *skb; =20 - skb =3D skb_dequeue(&ps->rx_queue); + if (chip->info->arr_ts_mode) { + /* If arr_ts_mode is set, the timestamps are embedded in the + * frames so a register read is not required. We still need a + * work queue rather than processing inline because + * timecounter_cyc2time() takes the global mutex and this + * cannot be called from mv88e6xxx_port_rxtstamp(). + */ + skb =3D skb_dequeue(&ps->rx_queue); + if (skb) + mv88e6xxx_get_rxts_embedded(chip, ps, skb); =20 + return; + } + + skb =3D skb_dequeue(&ps->rx_queue); if (skb) mv88e6xxx_get_rxts(chip, ps, skb, ptp_ops->arr0_sts_reg, &ps->rx_queue); @@ -350,7 +432,7 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int= port, =20 SKB_PTP_TYPE(skb) =3D type; =20 - if (is_pdelay_msg(hdr)) + if (!chip->info->arr_ts_mode && is_pdelay_msg(hdr)) skb_queue_tail(&ps->rx_queue2, skb); else skb_queue_tail(&ps->rx_queue, skb); @@ -530,14 +612,37 @@ int mv88e6165_global_enable(struct mv88e6xxx_chip *ch= ip) =20 int mv88e6352_hwtstamp_port_disable(struct mv88e6xxx_chip *chip, int port) { - return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0, - MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP); + int err; + + err =3D mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0, + MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP); + if (err) + return err; + + err =3D mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG2, 0); + if (err) + return err; + + return 0; } =20 int mv88e6352_hwtstamp_port_enable(struct mv88e6xxx_chip *chip, int port) { - return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0, - MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH); + int err; + + if (chip->info->arr_ts_mode) { + err =3D mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG2, + chip->info->arr_ts_mode << 8); + if (err) + return err; + } + + err =3D mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0, + MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH); + if (err) + return err; + + return 0; } =20 static int mv88e6xxx_hwtstamp_port_setup(struct mv88e6xxx_chip *chip, int = port) @@ -591,12 +696,14 @@ int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *c= hip) if (err) return err; =20 - /* Use ARRIVAL1 for peer delay messages. */ - err =3D mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_TS_ARRIVAL_PTR, - MV88E6XXX_PTP_MSGTYPE_PDLAY_REQ | - MV88E6XXX_PTP_MSGTYPE_PDLAY_RES); - if (err) - return err; + if (!chip->info->arr_ts_mode) { + /* Use ARRIVAL1 for peer delay messages. */ + err =3D mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_TS_ARRIVAL_PTR, + MV88E6XXX_PTP_MSGTYPE_PDLAY_REQ | + MV88E6XXX_PTP_MSGTYPE_PDLAY_RES); + if (err) + return err; + } =20 /* 88E6341 devices default to timestamping at the PHY, but this has * a hardware issue that results in unreliable timestamps. Force diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.h b/drivers/net/dsa/mv88e6x= xx/hwtstamp.h index c359821d5a6ea..c25f53923e768 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.h +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.h @@ -68,6 +68,20 @@ #define MV88E6XXX_PORT_PTP_CFG2_DEP_IRQ_EN 0x0002 #define MV88E6XXX_PORT_PTP_CFG2_ARR_IRQ_EN 0x0001 =20 +/* Arrival Time Stamp Mode (ArrTSMode), CFG2 bits [15:8]: configures how t= he + * switch embeds the arrival time stamp (PTPArr0Time) into enabled PTP eve= nt + * frames. + * 0x00 frame modification disabled (time stamp read from registe= rs) + * 0x01 append the 4-byte time stamp at the end of the frame, + * growing the frame by four bytes + * 0x04..0xEF overwrite the 4-byte time stamp in place, that many bytes= past + * the start of the PTP common header, without growing the f= rame + * (offsetof(struct ptp_header, reserved2) targets the reser= ved + * bytes of the header) + * others reserved + */ +#define MV88E6XXX_PTP_ARR_TS_MODE_APPEND 0x01 + /* Offset 0x03: PTP LED Configuration */ #define MV88E6XXX_PORT_PTP_LED_CFG 0x03 =20 --=20 2.43.0 From nobody Sat Jul 25 03:48:00 2026 Received: from us.padl.com (us.padl.com [216.154.215.154]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6D3A52E1C7C; Sun, 19 Jul 2026 05:31:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=216.154.215.154 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784439083; cv=none; b=EhoQ6/RvDRxB/t3qaBjzqDygosaLRNj7IThqRkElTvvkGXi1n6/0F0xuCcxgx/MpK0HeObmRLqg9kxUpnzzEVEnmUQ+itGH42X8B0XOUtI9rewS6EFVG8y36d1aEvemAwgey+7OFlie5WsJ/Hz5DGLMmN5fW5j2Ky7aMfpbvd+s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784439083; c=relaxed/simple; bh=p/J5zBvlooAb3AW4HoGNAlUH8lpu+4TEI5vxm/YIb6U=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rbvhNM7uTZY6ywBsyQgfM8EHpyOAOOoaRKewvMt6zyhAvSH+wrRlqAOehCq0IaGJ8o2ADtL6NTVwVl/pP8I34/FiVrbHfKISNMBjJP9FI+fiBpCZ92T0aJBjsH4qCeWq9yn+ufBOqWmppZbs3dBm6VT1UCdqe1RvTaqE5/87tU4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=padl.com; spf=pass smtp.mailfrom=padl.com; dkim=pass (2048-bit key) header.d=padl.com header.i=@padl.com header.b=a5kd8Sco; arc=none smtp.client-ip=216.154.215.154 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=padl.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=padl.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=padl.com header.i=@padl.com header.b="a5kd8Sco" Received: from [127.0.1.1] (border.lukktone.com [139.130.100.52]) by us.padl.com (Postfix) with ESMTPSA id 6A6B9621DD; Sun, 19 Jul 2026 01:31:10 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=padl.com; s=default; t=1784439074; bh=taKk5GGGOvyjf/PNarDXZt/ilsxoc27/UgcXXpAe8YY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=a5kd8ScoTOrarvTIrYsr43j9mDGytKVS/LOu2PEDsumSVNLj2m5gVHnIiCbaxzedW rErkhCAYLNtqM8tyzEuioOldvCAaVFjwZ3FlGCzNRKJ0CFEHnRz6bZpe6vS68C7pji S6+y8ujQtpDcLLTdhEBSVVdnPz/5Jyn9ZdIyXqSVRSOZqa0JFMyTpN1Ufdup0mmfmQ /Qz3YMHzJJnslg/W8/dMB8qfDIoN2QSmkq0tQnrRIsCO9Eo5EQyh/aoJmLT8KK9Do/ ksJZ2DU+7Mi5gkepailgFbeWvrdmz8NZSr+JcyD9Q38NOUE9ub5zVCkCedHr6vOVjh sLI5hnqRogQbQ== From: Luke Howard Date: Sun, 19 Jul 2026 15:30:55 +1000 Subject: [PATCH net-next v3 3/3] net: dsa: mv88e6xxx: apply embedded PTP arrival times inline Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260719-mv88e6xxx-ptp-fixes-v3-3-161386ac8a62@padl.com> References: <20260719-mv88e6xxx-ptp-fixes-v3-0-161386ac8a62@padl.com> In-Reply-To: <20260719-mv88e6xxx-ptp-fixes-v3-0-161386ac8a62@padl.com> To: Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Vivien Didelot , Gregory CLEMENT , Andrew Lunn , Richard Cochran Cc: Cedric Jehasse , Kieran Tyrrell , Max Holtmann , Max Hunter , Christoph Mellauner , Simon Gapp , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luke Howard X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784439056; l=11404; i=lukeh@padl.com; s=20260701; h=from:subject:message-id; bh=p/J5zBvlooAb3AW4HoGNAlUH8lpu+4TEI5vxm/YIb6U=; b=VOt5mDrbHTvllJljve1MzyqnccwMWffsfSTrT/+kyBpUyIaV9m9yt4cHTF7+nQYtJf1Nq5LCT 90Eb196Ud+cCSpNNzl38q7Eh8AEaT/VT64/7XfxWHZKD1xyUT0W8P8S X-Developer-Key: i=lukeh@padl.com; a=ed25519; pk=D6YgUnH9/OzSZsduYm9XDbvXn1IHDwKwI1hLTsKNRLw= Embedded PTP arrival times can be extracted without using the PTP worker, avoiding the issue where PTP general messages could arrive before the timestamped event messages. DSA can deliver the frame normally, similar to ocelot_ptp_rx_timestamp(). The register-based arrival path is unchanged. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Luke Howard --- drivers/net/dsa/mv88e6xxx/chip.c | 1 + drivers/net/dsa/mv88e6xxx/chip.h | 7 ++-- drivers/net/dsa/mv88e6xxx/hwtstamp.c | 69 +++++++++++++-------------------= ---- drivers/net/dsa/mv88e6xxx/ptp.c | 65 ++++++++++++++++++++++++++++----- drivers/net/dsa/mv88e6xxx/ptp.h | 1 + 5 files changed, 87 insertions(+), 56 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/c= hip.c index c3277c1f3d785..624cdf6c80a67 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -6638,6 +6638,7 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(st= ruct device *dev) chip->dev =3D dev; =20 mutex_init(&chip->reg_lock); + spin_lock_init(&chip->ptp_clock_lock); INIT_LIST_HEAD(&chip->mdios); idr_init(&chip->policies); INIT_LIST_HEAD(&chip->msts); diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/c= hip.h index b6a90eba81c43..dd52de71360b9 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -427,9 +427,12 @@ struct mv88e6xxx_chip { /* GPIO resources */ u8 gpio_data[2]; =20 - /* This cyclecounter abstracts the switch PTP time. - * reg_lock must be held for any operation that read()s. + /* This cyclecounter abstracts the switch PTP time. ptp_clock_lock + * protects tstamp_cc and tstamp_tc. tstamp_cycles caches the + * result most recently returned by mv88e6xxx_ptp_read_cycles(). */ + spinlock_t ptp_clock_lock; + u64 tstamp_cycles; struct cyclecounter tstamp_cc; struct timecounter tstamp_tc; struct delayed_work overflow_work; diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6x= xx/hwtstamp.c index fa5e897182ca2..716cbc2a86922 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c @@ -283,35 +283,24 @@ static bool parse_embedded_ts(unsigned int arr_ts_mod= e, return true; } =20 -static void mv88e6xxx_get_rxts_embedded(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_port_hwtstamp *ps, - struct sk_buff *skb) +/* Apply the arrival time the switch embedded in the frame. No register ac= cess + * is needed, so this runs inline on the receive path rather than being ha= nded + * to the PTP worker. + */ +static void mv88e6xxx_ptp_rx_timestamp(struct mv88e6xxx_chip *chip, + struct sk_buff *skb) { - struct sk_buff_head *rxq =3D &ps->rx_queue; struct skb_shared_hwtstamps *shwt; - struct sk_buff_head received; - unsigned long flags; u64 ns; =20 - __skb_queue_head_init(&received); - __skb_queue_head(&received, skb); - spin_lock_irqsave(&rxq->lock, flags); - skb_queue_splice_tail_init(rxq, &received); - spin_unlock_irqrestore(&rxq->lock, flags); + if (!parse_embedded_ts(chip->info->arr_ts_mode, skb, &ns)) + return; =20 - mv88e6xxx_reg_lock(chip); - skb_queue_walk(&received, skb) { - if (!parse_embedded_ts(chip->info->arr_ts_mode, skb, &ns)) - continue; - ns =3D timecounter_cyc2time(&chip->tstamp_tc, ns); - shwt =3D skb_hwtstamps(skb); - memset(shwt, 0, sizeof(*shwt)); - shwt->hwtstamp =3D ns_to_ktime(ns); - } - mv88e6xxx_reg_unlock(chip); + ns =3D mv88e6xxx_timecounter_cyc2time(chip, ns); =20 - while ((skb =3D __skb_dequeue(&received))) - netif_rx(skb); + shwt =3D skb_hwtstamps(skb); + memset(shwt, 0, sizeof(*shwt)); + shwt->hwtstamp =3D ns_to_ktime(ns); } =20 static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip, @@ -358,9 +347,7 @@ static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *c= hip, if (mv88e6xxx_ts_valid(status) && seq_match(skb, seq_id)) { ns =3D timehi << 16 | timelo; =20 - mv88e6xxx_reg_lock(chip); - ns =3D timecounter_cyc2time(&chip->tstamp_tc, ns); - mv88e6xxx_reg_unlock(chip); + ns =3D mv88e6xxx_timecounter_cyc2time(chip, ns); shwt =3D skb_hwtstamps(skb); memset(shwt, 0, sizeof(*shwt)); shwt->hwtstamp =3D ns_to_ktime(ns); @@ -376,20 +363,6 @@ static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_c= hip *chip, const struct mv88e6xxx_ptp_ops *ptp_ops =3D chip->info->ops->ptp_ops; struct sk_buff *skb; =20 - if (chip->info->arr_ts_mode) { - /* If arr_ts_mode is set, the timestamps are embedded in the - * frames so a register read is not required. We still need a - * work queue rather than processing inline because - * timecounter_cyc2time() takes the global mutex and this - * cannot be called from mv88e6xxx_port_rxtstamp(). - */ - skb =3D skb_dequeue(&ps->rx_queue); - if (skb) - mv88e6xxx_get_rxts_embedded(chip, ps, skb); - - return; - } - skb =3D skb_dequeue(&ps->rx_queue); if (skb) mv88e6xxx_get_rxts(chip, ps, skb, ptp_ops->arr0_sts_reg, @@ -432,7 +405,13 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, in= t port, =20 SKB_PTP_TYPE(skb) =3D type; =20 - if (!chip->info->arr_ts_mode && is_pdelay_msg(hdr)) + /* Embedded arrival times can be returned inline. */ + if (chip->info->arr_ts_mode) { + mv88e6xxx_ptp_rx_timestamp(chip, skb); + return false; + } + + if (is_pdelay_msg(hdr)) skb_queue_tail(&ps->rx_queue2, skb); else skb_queue_tail(&ps->rx_queue, skb); @@ -498,9 +477,7 @@ static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chi= p *chip, =20 memset(&shhwtstamps, 0, sizeof(shhwtstamps)); time_raw =3D ((u32)departure_block[2] << 16) | departure_block[1]; - mv88e6xxx_reg_lock(chip); - ns =3D timecounter_cyc2time(&chip->tstamp_tc, time_raw); - mv88e6xxx_reg_unlock(chip); + ns =3D mv88e6xxx_timecounter_cyc2time(chip, time_raw); shhwtstamps.hwtstamp =3D ns_to_ktime(ns); =20 dev_dbg(chip->dev, @@ -543,7 +520,9 @@ long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp) if (test_bit(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, &ps->state)) restart |=3D mv88e6xxx_txtstamp_work(chip, ps); =20 - mv88e6xxx_rxtstamp_work(chip, ps); + /* Embedded arrival times are applied on the receive path. */ + if (!chip->info->arr_ts_mode) + mv88e6xxx_rxtstamp_work(chip, ps); } =20 return restart ? 1 : -1; diff --git a/drivers/net/dsa/mv88e6xxx/ptp.c b/drivers/net/dsa/mv88e6xxx/pt= p.c index f7603573d3a98..2a7f97625e375 100644 --- a/drivers/net/dsa/mv88e6xxx/ptp.c +++ b/drivers/net/dsa/mv88e6xxx/ptp.c @@ -231,15 +231,37 @@ static void mv88e6352_tai_event_work(struct work_stru= ct *ugly) =20 /* We only have one timestamping channel. */ ev.index =3D 0; - mv88e6xxx_reg_lock(chip); - ev.timestamp =3D timecounter_cyc2time(&chip->tstamp_tc, raw_ts); - mv88e6xxx_reg_unlock(chip); + ev.timestamp =3D mv88e6xxx_timecounter_cyc2time(chip, raw_ts); =20 ptp_clock_event(chip->ptp_clock, &ev); out: schedule_delayed_work(&chip->tai_event_work, TAI_EVENT_WORK_INTERVAL); } =20 +/* Refresh the cached counter value that the read() callback returns. The + * read cannot acquire the register lock whilst holding ptp_clock_lock + * because MDIO reads can sleep. The caller must hold reg_lock, which + * serializes against the other timecounter writers. + */ +static void mv88e6xxx_ptp_read_cycles(struct mv88e6xxx_chip *chip) +{ + const struct mv88e6xxx_ptp_ops *ptp_ops =3D chip->info->ops->ptp_ops; + + if (ptp_ops->clock_read) + chip->tstamp_cycles =3D ptp_ops->clock_read(&chip->tstamp_cc); +} + +u64 mv88e6xxx_timecounter_cyc2time(struct mv88e6xxx_chip *chip, u64 cycles) +{ + u64 ns; + + spin_lock_bh(&chip->ptp_clock_lock); + ns =3D timecounter_cyc2time(&chip->tstamp_tc, cycles); + spin_unlock_bh(&chip->ptp_clock_lock); + + return ns; +} + static int mv88e6xxx_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_p= pm) { struct mv88e6xxx_chip *chip =3D ptp_to_chip(ptp); @@ -258,9 +280,12 @@ static int mv88e6xxx_ptp_adjfine(struct ptp_clock_info= *ptp, long scaled_ppm) diff =3D div_u64(adj, chip->cc_coeffs->cc_mult_dem); =20 mv88e6xxx_reg_lock(chip); + mv88e6xxx_ptp_read_cycles(chip); =20 + spin_lock_bh(&chip->ptp_clock_lock); timecounter_read(&chip->tstamp_tc); chip->tstamp_cc.mult =3D neg_adj ? mult - diff : mult + diff; + spin_unlock_bh(&chip->ptp_clock_lock); =20 mv88e6xxx_reg_unlock(chip); =20 @@ -271,8 +296,16 @@ static int mv88e6xxx_ptp_adjtime(struct ptp_clock_info= *ptp, s64 delta) { struct mv88e6xxx_chip *chip =3D ptp_to_chip(ptp); =20 + /* No register access is needed here, but reg_lock still serialises + * this against the other timecounter writers, which drop it only + * after their hardware read has completed. + */ mv88e6xxx_reg_lock(chip); + + spin_lock_bh(&chip->ptp_clock_lock); timecounter_adjtime(&chip->tstamp_tc, delta); + spin_unlock_bh(&chip->ptp_clock_lock); + mv88e6xxx_reg_unlock(chip); =20 return 0; @@ -285,7 +318,12 @@ static int mv88e6xxx_ptp_gettime(struct ptp_clock_info= *ptp, u64 ns; =20 mv88e6xxx_reg_lock(chip); + mv88e6xxx_ptp_read_cycles(chip); + + spin_lock_bh(&chip->ptp_clock_lock); ns =3D timecounter_read(&chip->tstamp_tc); + spin_unlock_bh(&chip->ptp_clock_lock); + mv88e6xxx_reg_unlock(chip); =20 *ts =3D ns_to_timespec64(ns); @@ -302,7 +340,12 @@ static int mv88e6xxx_ptp_settime(struct ptp_clock_info= *ptp, ns =3D timespec64_to_ns(ts); =20 mv88e6xxx_reg_lock(chip); + mv88e6xxx_ptp_read_cycles(chip); + + spin_lock_bh(&chip->ptp_clock_lock); timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc, ns); + spin_unlock_bh(&chip->ptp_clock_lock); + mv88e6xxx_reg_unlock(chip); =20 return 0; @@ -444,14 +487,12 @@ const struct mv88e6xxx_ptp_ops mv88e6390_ptp_ops =3D { (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ), }; =20 +/* Return the value most recently fetched by mv88e6xxx_ptp_read_cycles() + * rather than reading the hardware over MDIO. + */ static u64 mv88e6xxx_ptp_clock_read(struct cyclecounter *cc) { - struct mv88e6xxx_chip *chip =3D cc_to_chip(cc); - - if (chip->info->ops->ptp_ops->clock_read) - return chip->info->ops->ptp_ops->clock_read(cc); - - return 0; + return cc_to_chip(cc)->tstamp_cycles; } =20 /* With a 250MHz input clock, the 32-bit timestamp counter overflows in ~1= 7.2 @@ -486,6 +527,12 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip) chip->tstamp_cc.mult =3D chip->cc_coeffs->cc_mult; chip->tstamp_cc.shift =3D chip->cc_coeffs->cc_shift; =20 + /* Prime the cycle counter cache for the timecounter_init() below. + * The caller holds reg_lock, and nothing can reach the PTP clock + * until ptp_clock_register() below, so no locking is needed here. + */ + mv88e6xxx_ptp_read_cycles(chip); + timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc, ktime_to_ns(ktime_get_real())); =20 diff --git a/drivers/net/dsa/mv88e6xxx/ptp.h b/drivers/net/dsa/mv88e6xxx/pt= p.h index 95bdddb0bf39f..44718e120de20 100644 --- a/drivers/net/dsa/mv88e6xxx/ptp.h +++ b/drivers/net/dsa/mv88e6xxx/ptp.h @@ -68,6 +68,7 @@ =20 int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip); void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip); +u64 mv88e6xxx_timecounter_cyc2time(struct mv88e6xxx_chip *chip, u64 cycles= ); =20 #define ptp_to_chip(ptp) container_of(ptp, struct mv88e6xxx_chip, \ ptp_clock_info) --=20 2.43.0