linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95
@ 2025-07-16  7:30 Wei Fang
  2025-07-16  7:30 ` [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
                   ` (13 more replies)
  0 siblings, 14 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:30 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

This series adds NETC Timer PTP clock driver, which supports precise
periodic pulse, time capture on external pulse and PTP synchronization.
It also adds PTP support to the enetc v4 driver for i.MX95 and optimizes
the PTP-related code in the enetc driver.

---
v1 link: https://lore.kernel.org/imx/20250711065748.250159-1-wei.fang@nxp.com/
---

F.S. Peng (1):
  ptp: netc: add external trigger stamp support

Wei Fang (13):
  dt-bindings: ptp: add NETC Timer PTP clock
  dt-bindings: net: add nxp,netc-timer property
  ptp: netc: add NETC Timer PTP driver support
  ptp: netc: add PTP_CLK_REQ_PPS support
  ptp: netc: add periodic pulse output support
  ptp: netc: add debugfs support to loop back pulse signal
  MAINTAINERS: add NETC Timer PTP clock driver section
  net: enetc: save the parsed information of PTP packet to skb->cb
  net: enetc: Add enetc_update_ptp_sync_msg() to process PTP sync packet
  net: enetc: remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check
  net: enetc: add PTP synchronization support for ENETC v4
  net: enetc: don't update sync packet checksum if checksum offload is
    used
  arm64: dts: imx95: Add NETC Timer support

 .../devicetree/bindings/net/fsl,enetc.yaml    |   23 +
 .../devicetree/bindings/ptp/nxp,ptp-netc.yaml |   67 +
 MAINTAINERS                                   |    9 +
 .../boot/dts/freescale/imx95-19x19-evk.dts    |    4 +
 arch/arm64/boot/dts/freescale/imx95.dtsi      |    1 +
 drivers/net/ethernet/freescale/enetc/enetc.c  |  209 +--
 drivers/net/ethernet/freescale/enetc/enetc.h  |   21 +-
 .../net/ethernet/freescale/enetc/enetc4_hw.h  |    6 +
 .../net/ethernet/freescale/enetc/enetc4_pf.c  |    3 +
 .../ethernet/freescale/enetc/enetc_ethtool.c  |   92 +-
 .../net/ethernet/freescale/enetc/enetc_hw.h   |    1 +
 drivers/ptp/Kconfig                           |   11 +
 drivers/ptp/Makefile                          |    1 +
 drivers/ptp/ptp_netc.c                        | 1193 +++++++++++++++++
 include/linux/fsl/netc_global.h               |   12 +-
 15 files changed, 1551 insertions(+), 102 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
 create mode 100644 drivers/ptp/ptp_netc.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
@ 2025-07-16  7:30 ` Wei Fang
  2025-07-16 19:19   ` Frank Li
  2025-07-17  7:40   ` Krzysztof Kozlowski
  2025-07-16  7:30 ` [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property Wei Fang
                   ` (12 subsequent siblings)
  13 siblings, 2 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:30 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

NXP NETC (Ethernet Controller) is a multi-function PCIe Root Complex
Integrated Endpoint (RCiEP), the Timer is one of its functions which
provides current time with nanosecond resolution, precise periodic
pulse, pulse on timeout (alarm), and time capture on external pulse
support. And also supports time synchronization as required for IEEE
1588 and IEEE 802.1AS-2020. So add device tree binding doc for the
PTP clock based on NETC Timer.

Signed-off-by: Wei Fang <wei.fang@nxp.com>

---
v2 changes:
1. Refine the subject and the commit message
2. Remove "nxp,pps-channel"
3. Add description to "clocks" and "clock-names"
---
 .../devicetree/bindings/ptp/nxp,ptp-netc.yaml | 67 +++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml

diff --git a/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml b/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
new file mode 100644
index 000000000000..6af1899d904f
--- /dev/null
+++ b/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/ptp/nxp,ptp-netc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP NETC Timer PTP clock
+
+description:
+  NETC Timer provides current time with nanosecond resolution, precise
+  periodic pulse, pulse on timeout (alarm), and time capture on external
+  pulse support. And it supports time synchronization as required for
+  IEEE 1588 and IEEE 802.1AS-2020.
+
+maintainers:
+  - Wei Fang <wei.fang@nxp.com>
+  - Clark Wang <xiaoning.wang@nxp.com>
+
+properties:
+  compatible:
+    enum:
+      - pci1131,ee02
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+    description:
+      The reference clock of NETC Timer, if not present, indicates that
+      the system clock of NETC IP is selected as the reference clock.
+
+  clock-names:
+    description:
+      NETC Timer has three reference clock sources, set TMR_CTRL[CK_SEL]
+      by parsing clock name to select one of them as the reference clock.
+      The "system" means that the system clock of NETC IP is used as the
+      reference clock.
+      The "ccm_timer" means another clock from CCM as the reference clock.
+      The "ext_1588" means the reference clock comes from external IO pins.
+    enum:
+      - system
+      - ccm_timer
+      - ext_1588
+
+required:
+  - compatible
+  - reg
+
+allOf:
+  - $ref: /schemas/pci/pci-device.yaml
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    pcie {
+        #address-cells = <3>;
+        #size-cells = <2>;
+
+        ethernet@18,0 {
+            compatible = "pci1131,ee02";
+            reg = <0x00c000 0 0 0 0>;
+            clocks = <&scmi_clk 18>;
+            clock-names = "ccm_timer";
+        };
+    };
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
  2025-07-16  7:30 ` [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
@ 2025-07-16  7:30 ` Wei Fang
  2025-07-16 19:28   ` Frank Li
  2025-07-17  7:42   ` Krzysztof Kozlowski
  2025-07-16  7:31 ` [PATCH v2 net-next 03/14] ptp: netc: add NETC Timer PTP driver support Wei Fang
                   ` (11 subsequent siblings)
  13 siblings, 2 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:30 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

NETC is a multi-function PCIe Root Complex Integrated Endpoint (RCiEP)
that contains multiple PCIe functions, such as ENETC and Timer. Timer
provides PTP time synchronization functionality and ENETC provides the
NIC functionality.

For some platforms, such as i.MX95, it has only one timer instance, so
the binding relationship between Timer and ENETC is fixed. But for some
platforms, such as i.MX943, it has 3 Timer instances, by setting the
EaTBCR registers of the IERB module, we can specify any Timer instance
to be bound to the ENETC instance.

Therefore, add "nxp,netc-timer" property to bind ENETC instance to a
specified Timer instance so that ENETC can support PTP synchronization
through Timer.

Signed-off-by: Wei Fang <wei.fang@nxp.com>

---
v2 changes:
new patch
---
 .../devicetree/bindings/net/fsl,enetc.yaml    | 23 +++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
index ca70f0050171..ae05f2982653 100644
--- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
+++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
@@ -44,6 +44,13 @@ properties:
     unevaluatedProperties: false
     description: Optional child node for ENETC instance, otherwise use NETC EMDIO.
 
+  nxp,netc-timer:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Specifies a reference to a node representing a NETC Timer device,
+      which provides time synchronization as required for IEEE 1588 and
+      IEEE 802.1AS-2020.
+
 required:
   - compatible
   - reg
@@ -62,6 +69,7 @@ allOf:
       properties:
         clocks: false
         clock-names: false
+        nxp,netc-timer: false
 
 unevaluatedProperties: false
 
@@ -86,3 +94,18 @@ examples:
             };
         };
     };
+  - |
+    pcie {
+      #address-cells = <3>;
+      #size-cells = <2>;
+
+      ethernet@0,0 {
+          compatible = "pci1131,e101";
+          reg = <0x000000 0 0 0 0>;
+          clocks = <&scmi_clk 102>;
+          clock-names = "ref";
+          nxp,netc-timer = <&netc_timer>;
+          phy-handle = <&ethphy0>;
+          phy-mode = "rgmii-id";
+      };
+    };
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 03/14] ptp: netc: add NETC Timer PTP driver support
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
  2025-07-16  7:30 ` [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
  2025-07-16  7:30 ` [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 19:58   ` Frank Li
  2025-07-23 16:09   ` Vladimir Oltean
  2025-07-16  7:31 ` [PATCH v2 net-next 04/14] ptp: netc: add PTP_CLK_REQ_PPS support Wei Fang
                   ` (10 subsequent siblings)
  13 siblings, 2 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

NETC Timer provides current time with nanosecond resolution, precise
periodic pulse, pulse on timeout (alarm), and time capture on external
pulse support. And it supports time synchronization as required for
IEEE 1588 and IEEE 802.1AS-2020. The enetc v4 driver can implement PTP
synchronization through the relevant interfaces provided by the driver.
Note that the current driver does not support PEROUT, PPS and EXTTS yet,
and support will be added one by one in subsequent patches.

Signed-off-by: Wei Fang <wei.fang@nxp.com>

---
v2 changes:
1. Rename netc_timer_get_source_clk() to
   netc_timer_get_reference_clk_source() and refactor it
2. Remove the scaled_ppm check in netc_timer_adjfine()
3. Add a comment in netc_timer_cur_time_read()
4. Add linux/bitfield.h to fix the build errors
---
 drivers/ptp/Kconfig             |  11 +
 drivers/ptp/Makefile            |   1 +
 drivers/ptp/ptp_netc.c          | 568 ++++++++++++++++++++++++++++++++
 include/linux/fsl/netc_global.h |  12 +-
 4 files changed, 591 insertions(+), 1 deletion(-)
 create mode 100644 drivers/ptp/ptp_netc.c

diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 204278eb215e..3e005b992aef 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -252,4 +252,15 @@ config PTP_S390
 	  driver provides the raw clock value without the delta to
 	  userspace. That way userspace programs like chrony could steer
 	  the kernel clock.
+
+config PTP_1588_CLOCK_NETC
+	bool "NXP NETC Timer PTP Driver"
+	depends on PTP_1588_CLOCK=y
+	depends on PCI_MSI
+	help
+	  This driver adds support for using the NXP NETC Timer as a PTP
+	  clock. This clock is used by ENETC MAC or NETC Switch for PTP
+	  synchronization. It also supports periodic output signal (e.g.
+	  PPS) and external trigger timestamping.
+
 endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index 25f846fe48c9..d48fe4009fa4 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_PTP_1588_CLOCK_VMW)	+= ptp_vmw.o
 obj-$(CONFIG_PTP_1588_CLOCK_OCP)	+= ptp_ocp.o
 obj-$(CONFIG_PTP_DFL_TOD)		+= ptp_dfl_tod.o
 obj-$(CONFIG_PTP_S390)			+= ptp_s390.o
+obj-$(CONFIG_PTP_1588_CLOCK_NETC)	+= ptp_netc.o
diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
new file mode 100644
index 000000000000..82cb1e6a0fe9
--- /dev/null
+++ b/drivers/ptp/ptp_netc.c
@@ -0,0 +1,568 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * NXP NETC Timer driver
+ * Copyright 2025 NXP
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/fsl/netc_global.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/ptp_clock_kernel.h>
+
+#define NETC_TMR_PCI_VENDOR		0x1131
+#define NETC_TMR_PCI_DEVID		0xee02
+
+#define NETC_TMR_CTRL			0x0080
+#define  TMR_CTRL_CK_SEL		GENMASK(1, 0)
+#define  TMR_CTRL_TE			BIT(2)
+#define  TMR_COMP_MODE			BIT(15)
+#define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
+#define  TMR_CTRL_FS			BIT(28)
+#define  TMR_ALARM1P			BIT(31)
+
+#define NETC_TMR_TEVENT			0x0084
+#define  TMR_TEVENT_ALM1EN		BIT(16)
+#define  TMR_TEVENT_ALM2EN		BIT(17)
+
+#define NETC_TMR_TEMASK			0x0088
+#define NETC_TMR_CNT_L			0x0098
+#define NETC_TMR_CNT_H			0x009c
+#define NETC_TMR_ADD			0x00a0
+#define NETC_TMR_PRSC			0x00a8
+#define NETC_TMR_OFF_L			0x00b0
+#define NETC_TMR_OFF_H			0x00b4
+
+/* i = 0, 1, i indicates the index of TMR_ALARM */
+#define NETC_TMR_ALARM_L(i)		(0x00b8 + (i) * 8)
+#define NETC_TMR_ALARM_H(i)		(0x00bc + (i) * 8)
+
+#define NETC_TMR_FIPER_CTRL		0x00dc
+#define  FIPER_CTRL_DIS(i)		(BIT(7) << (i) * 8)
+#define  FIPER_CTRL_PG(i)		(BIT(6) << (i) * 8)
+
+#define NETC_TMR_CUR_TIME_L		0x00f0
+#define NETC_TMR_CUR_TIME_H		0x00f4
+
+#define NETC_TMR_REGS_BAR		0
+
+#define NETC_TMR_FIPER_NUM		3
+#define NETC_TMR_DEFAULT_PRSC		2
+#define NETC_TMR_DEFAULT_ALARM		GENMASK_ULL(63, 0)
+
+/* 1588 timer reference clock source select */
+#define NETC_TMR_CCM_TIMER1		0 /* enet_timer1_clk_root, from CCM */
+#define NETC_TMR_SYSTEM_CLK		1 /* enet_clk_root/2, from CCM */
+#define NETC_TMR_EXT_OSC		2 /* tmr_1588_clk, from IO pins */
+
+#define NETC_TMR_SYSCLK_333M		333333333U
+
+struct netc_timer {
+	void __iomem *base;
+	struct pci_dev *pdev;
+	spinlock_t lock; /* Prevent concurrent access to registers */
+
+	struct clk *src_clk;
+	struct ptp_clock *clock;
+	struct ptp_clock_info caps;
+	int phc_index;
+	u32 clk_select;
+	u32 clk_freq;
+	u32 oclk_prsc;
+	/* High 32-bit is integer part, low 32-bit is fractional part */
+	u64 period;
+
+	int irq;
+};
+
+#define netc_timer_rd(p, o)		netc_read((p)->base + (o))
+#define netc_timer_wr(p, o, v)		netc_write((p)->base + (o), v)
+#define ptp_to_netc_timer(ptp)		container_of((ptp), struct netc_timer, caps)
+
+static u64 netc_timer_cnt_read(struct netc_timer *priv)
+{
+	u32 tmr_cnt_l, tmr_cnt_h;
+	u64 ns;
+
+	/* The user must read the TMR_CNC_L register first to get
+	 * correct 64-bit TMR_CNT_H/L counter values.
+	 */
+	tmr_cnt_l = netc_timer_rd(priv, NETC_TMR_CNT_L);
+	tmr_cnt_h = netc_timer_rd(priv, NETC_TMR_CNT_H);
+	ns = (((u64)tmr_cnt_h) << 32) | tmr_cnt_l;
+
+	return ns;
+}
+
+static void netc_timer_cnt_write(struct netc_timer *priv, u64 ns)
+{
+	u32 tmr_cnt_h = upper_32_bits(ns);
+	u32 tmr_cnt_l = lower_32_bits(ns);
+
+	/* The user must write to TMR_CNT_L register first. */
+	netc_timer_wr(priv, NETC_TMR_CNT_L, tmr_cnt_l);
+	netc_timer_wr(priv, NETC_TMR_CNT_H, tmr_cnt_h);
+}
+
+static u64 netc_timer_offset_read(struct netc_timer *priv)
+{
+	u32 tmr_off_l, tmr_off_h;
+	u64 offset;
+
+	tmr_off_l = netc_timer_rd(priv, NETC_TMR_OFF_L);
+	tmr_off_h = netc_timer_rd(priv, NETC_TMR_OFF_H);
+	offset = (((u64)tmr_off_h) << 32) | tmr_off_l;
+
+	return offset;
+}
+
+static void netc_timer_offset_write(struct netc_timer *priv, u64 offset)
+{
+	u32 tmr_off_h = upper_32_bits(offset);
+	u32 tmr_off_l = lower_32_bits(offset);
+
+	netc_timer_wr(priv, NETC_TMR_OFF_L, tmr_off_l);
+	netc_timer_wr(priv, NETC_TMR_OFF_H, tmr_off_h);
+}
+
+static u64 netc_timer_cur_time_read(struct netc_timer *priv)
+{
+	u32 time_h, time_l;
+	u64 ns;
+
+	/* The user should read NETC_TMR_CUR_TIME_L first to
+	 * get correct current time.
+	 */
+	time_l = netc_timer_rd(priv, NETC_TMR_CUR_TIME_L);
+	time_h = netc_timer_rd(priv, NETC_TMR_CUR_TIME_H);
+	ns = (u64)time_h << 32 | time_l;
+
+	return ns;
+}
+
+static void netc_timer_alarm_write(struct netc_timer *priv,
+				   u64 alarm, int index)
+{
+	u32 alarm_h = upper_32_bits(alarm);
+	u32 alarm_l = lower_32_bits(alarm);
+
+	netc_timer_wr(priv, NETC_TMR_ALARM_L(index), alarm_l);
+	netc_timer_wr(priv, NETC_TMR_ALARM_H(index), alarm_h);
+}
+
+static void netc_timer_adjust_period(struct netc_timer *priv, u64 period)
+{
+	u32 fractional_period = lower_32_bits(period);
+	u32 integral_period = upper_32_bits(period);
+	u32 tmr_ctrl, old_tmr_ctrl;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	old_tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
+	tmr_ctrl = u32_replace_bits(old_tmr_ctrl, integral_period,
+				    TMR_CTRL_TCLK_PERIOD);
+	if (tmr_ctrl != old_tmr_ctrl)
+		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
+
+	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int netc_timer_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+	struct netc_timer *priv = ptp_to_netc_timer(ptp);
+	u64 new_period;
+
+	new_period = adjust_by_scaled_ppm(priv->period, scaled_ppm);
+	netc_timer_adjust_period(priv, new_period);
+
+	return 0;
+}
+
+static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+	struct netc_timer *priv = ptp_to_netc_timer(ptp);
+	u64 tmr_cnt, tmr_off;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	tmr_off = netc_timer_offset_read(priv);
+	if (delta < 0 && tmr_off < abs(delta)) {
+		delta += tmr_off;
+		if (!tmr_off)
+			netc_timer_offset_write(priv, 0);
+
+		tmr_cnt = netc_timer_cnt_read(priv);
+		tmr_cnt += delta;
+		netc_timer_cnt_write(priv, tmr_cnt);
+	} else {
+		tmr_off += delta;
+		netc_timer_offset_write(priv, tmr_off);
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static int netc_timer_gettimex64(struct ptp_clock_info *ptp,
+				 struct timespec64 *ts,
+				 struct ptp_system_timestamp *sts)
+{
+	struct netc_timer *priv = ptp_to_netc_timer(ptp);
+	unsigned long flags;
+	u64 ns;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	ptp_read_system_prets(sts);
+	ns = netc_timer_cur_time_read(priv);
+	ptp_read_system_postts(sts);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	*ts = ns_to_timespec64(ns);
+
+	return 0;
+}
+
+static int netc_timer_settime64(struct ptp_clock_info *ptp,
+				const struct timespec64 *ts)
+{
+	struct netc_timer *priv = ptp_to_netc_timer(ptp);
+	u64 ns = timespec64_to_ns(ts);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	netc_timer_offset_write(priv, 0);
+	netc_timer_cnt_write(priv, ns);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+int netc_timer_get_phc_index(struct pci_dev *timer_pdev)
+{
+	struct netc_timer *priv;
+
+	if (!timer_pdev)
+		return -ENODEV;
+
+	priv = pci_get_drvdata(timer_pdev);
+	if (!priv)
+		return -EINVAL;
+
+	return priv->phc_index;
+}
+EXPORT_SYMBOL_GPL(netc_timer_get_phc_index);
+
+static const struct ptp_clock_info netc_timer_ptp_caps = {
+	.owner		= THIS_MODULE,
+	.name		= "NETC Timer PTP clock",
+	.max_adj	= 500000000,
+	.n_alarm	= 2,
+	.n_pins		= 0,
+	.adjfine	= netc_timer_adjfine,
+	.adjtime	= netc_timer_adjtime,
+	.gettimex64	= netc_timer_gettimex64,
+	.settime64	= netc_timer_settime64,
+};
+
+static void netc_timer_init(struct netc_timer *priv)
+{
+	u32 tmr_emask = TMR_TEVENT_ALM1EN | TMR_TEVENT_ALM2EN;
+	u32 fractional_period = lower_32_bits(priv->period);
+	u32 integral_period = upper_32_bits(priv->period);
+	u32 tmr_ctrl, fiper_ctrl;
+	struct timespec64 now;
+	u64 ns;
+	int i;
+
+	/* Software must enable timer first and the clock selected must be
+	 * active, otherwise, the registers which are in the timer clock
+	 * domain are not accessible.
+	 */
+	tmr_ctrl = (priv->clk_select & TMR_CTRL_CK_SEL) | TMR_CTRL_TE;
+	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
+	netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc);
+
+	/* Disable FIPER by default */
+	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
+	for (i = 0; i < NETC_TMR_FIPER_NUM; i++) {
+		fiper_ctrl |= FIPER_CTRL_DIS(i);
+		fiper_ctrl &= ~FIPER_CTRL_PG(i);
+	}
+	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
+
+	ktime_get_real_ts64(&now);
+	ns = timespec64_to_ns(&now);
+	netc_timer_cnt_write(priv, ns);
+
+	/* Allow atomic writes to TCLK_PERIOD and TMR_ADD, An update to
+	 * TCLK_PERIOD does not take effect until TMR_ADD is written.
+	 */
+	tmr_ctrl |= ((integral_period << 16) & TMR_CTRL_TCLK_PERIOD) |
+		     TMR_COMP_MODE | TMR_CTRL_FS;
+	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
+	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
+	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
+}
+
+static int netc_timer_pci_probe(struct pci_dev *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct netc_timer *priv;
+	int err, len;
+
+	pcie_flr(pdev);
+	err = pci_enable_device_mem(pdev);
+	if (err)
+		return dev_err_probe(dev, err, "Failed to enable device\n");
+
+	err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+	if (err) {
+		dev_err(dev, "dma_set_mask_and_coherent() failed, err:%pe\n",
+			ERR_PTR(err));
+		goto disable_dev;
+	}
+
+	err = pci_request_mem_regions(pdev, KBUILD_MODNAME);
+	if (err) {
+		dev_err(dev, "pci_request_regions() failed, err:%pe\n",
+			ERR_PTR(err));
+		goto disable_dev;
+	}
+
+	pci_set_master(pdev);
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		err = -ENOMEM;
+		goto release_mem_regions;
+	}
+
+	priv->pdev = pdev;
+	len = pci_resource_len(pdev, NETC_TMR_REGS_BAR);
+	priv->base = ioremap(pci_resource_start(pdev, NETC_TMR_REGS_BAR), len);
+	if (!priv->base) {
+		err = -ENXIO;
+		dev_err(dev, "ioremap() failed\n");
+		goto free_priv;
+	}
+
+	pci_set_drvdata(pdev, priv);
+
+	return 0;
+
+free_priv:
+	kfree(priv);
+release_mem_regions:
+	pci_release_mem_regions(pdev);
+disable_dev:
+	pci_disable_device(pdev);
+
+	return err;
+}
+
+static void netc_timer_pci_remove(struct pci_dev *pdev)
+{
+	struct netc_timer *priv = pci_get_drvdata(pdev);
+
+	iounmap(priv->base);
+	kfree(priv);
+	pci_release_mem_regions(pdev);
+	pci_disable_device(pdev);
+}
+
+static int netc_timer_get_reference_clk_source(struct netc_timer *priv)
+{
+	struct device *dev = &priv->pdev->dev;
+	struct device_node *np = dev->of_node;
+	const char *clk_name = NULL;
+	u64 ns = NSEC_PER_SEC;
+
+	/* Select NETC system clock as the reference clock by default */
+	priv->clk_select = NETC_TMR_SYSTEM_CLK;
+	priv->clk_freq = NETC_TMR_SYSCLK_333M;
+	priv->period = div_u64(ns << 32, priv->clk_freq);
+
+	if (!np)
+		return 0;
+
+	of_property_read_string(np, "clock-names", &clk_name);
+	if (!clk_name)
+		return 0;
+
+	/* Update the clock source of the reference clock if the clock
+	 * name is specified in DTS node.
+	 */
+	if (!strcmp(clk_name, "system"))
+		priv->clk_select = NETC_TMR_SYSTEM_CLK;
+	else if (!strcmp(clk_name, "ccm_timer"))
+		priv->clk_select = NETC_TMR_CCM_TIMER1;
+	else if (!strcmp(clk_name, "ext_1588"))
+		priv->clk_select = NETC_TMR_EXT_OSC;
+	else
+		return -EINVAL;
+
+	priv->src_clk = devm_clk_get(dev, clk_name);
+	if (IS_ERR(priv->src_clk)) {
+		dev_err(dev, "Failed to get reference clock source\n");
+		return PTR_ERR(priv->src_clk);
+	}
+
+	priv->clk_freq = clk_get_rate(priv->src_clk);
+	priv->period = div_u64(ns << 32, priv->clk_freq);
+
+	return 0;
+}
+
+static int netc_timer_parse_dt(struct netc_timer *priv)
+{
+	return netc_timer_get_reference_clk_source(priv);
+}
+
+static irqreturn_t netc_timer_isr(int irq, void *data)
+{
+	struct netc_timer *priv = data;
+	u32 tmr_event, tmr_emask;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	tmr_event = netc_timer_rd(priv, NETC_TMR_TEVENT);
+	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
+
+	tmr_event &= tmr_emask;
+	if (tmr_event & TMR_TEVENT_ALM1EN)
+		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
+
+	if (tmr_event & TMR_TEVENT_ALM2EN)
+		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 1);
+
+	/* Clear interrupts status */
+	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static int netc_timer_init_msix_irq(struct netc_timer *priv)
+{
+	struct pci_dev *pdev = priv->pdev;
+	char irq_name[64];
+	int err, n;
+
+	n = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
+	if (n != 1) {
+		err = (n < 0) ? n : -EPERM;
+		dev_err(&pdev->dev, "pci_alloc_irq_vectors() failed\n");
+		return err;
+	}
+
+	priv->irq = pci_irq_vector(pdev, 0);
+	snprintf(irq_name, sizeof(irq_name), "ptp-netc %s", pci_name(pdev));
+	err = request_irq(priv->irq, netc_timer_isr, 0, irq_name, priv);
+	if (err) {
+		dev_err(&pdev->dev, "request_irq() failed\n");
+		pci_free_irq_vectors(pdev);
+		return err;
+	}
+
+	return 0;
+}
+
+static void netc_timer_free_msix_irq(struct netc_timer *priv)
+{
+	struct pci_dev *pdev = priv->pdev;
+
+	disable_irq(priv->irq);
+	free_irq(priv->irq, priv);
+	pci_free_irq_vectors(pdev);
+}
+
+static int netc_timer_probe(struct pci_dev *pdev,
+			    const struct pci_device_id *id)
+{
+	struct device *dev = &pdev->dev;
+	struct netc_timer *priv;
+	int err;
+
+	err = netc_timer_pci_probe(pdev);
+	if (err)
+		return err;
+
+	priv = pci_get_drvdata(pdev);
+	err = netc_timer_parse_dt(priv);
+	if (err) {
+		dev_err(dev, "Failed to parse DT node\n");
+		goto timer_pci_remove;
+	}
+
+	priv->caps = netc_timer_ptp_caps;
+	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
+	priv->phc_index = -1; /* initialize it as an invalid index */
+	spin_lock_init(&priv->lock);
+
+	err = clk_prepare_enable(priv->src_clk);
+	if (err) {
+		dev_err(dev, "Failed to enable timer source clock\n");
+		goto timer_pci_remove;
+	}
+
+	err = netc_timer_init_msix_irq(priv);
+	if (err)
+		goto disable_clk;
+
+	netc_timer_init(priv);
+	priv->clock = ptp_clock_register(&priv->caps, dev);
+	if (IS_ERR(priv->clock)) {
+		err = PTR_ERR(priv->clock);
+		goto free_msix_irq;
+	}
+
+	priv->phc_index = ptp_clock_index(priv->clock);
+
+	return 0;
+
+free_msix_irq:
+	netc_timer_free_msix_irq(priv);
+disable_clk:
+	clk_disable_unprepare(priv->src_clk);
+timer_pci_remove:
+	netc_timer_pci_remove(pdev);
+
+	return err;
+}
+
+static void netc_timer_remove(struct pci_dev *pdev)
+{
+	struct netc_timer *priv = pci_get_drvdata(pdev);
+
+	ptp_clock_unregister(priv->clock);
+	netc_timer_free_msix_irq(priv);
+	clk_disable_unprepare(priv->src_clk);
+	netc_timer_pci_remove(pdev);
+}
+
+static const struct pci_device_id netc_timer_id_table[] = {
+	{ PCI_DEVICE(NETC_TMR_PCI_VENDOR, NETC_TMR_PCI_DEVID) },
+	{ 0, } /* End of table. */
+};
+MODULE_DEVICE_TABLE(pci, netc_timer_id_table);
+
+static struct pci_driver netc_timer_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = netc_timer_id_table,
+	.probe = netc_timer_probe,
+	.remove = netc_timer_remove,
+};
+module_pci_driver(netc_timer_driver);
+
+MODULE_DESCRIPTION("NXP NETC Timer PTP Driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/include/linux/fsl/netc_global.h b/include/linux/fsl/netc_global.h
index fdecca8c90f0..59c835e67ada 100644
--- a/include/linux/fsl/netc_global.h
+++ b/include/linux/fsl/netc_global.h
@@ -1,10 +1,11 @@
 /* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
-/* Copyright 2024 NXP
+/* Copyright 2024-2025 NXP
  */
 #ifndef __NETC_GLOBAL_H
 #define __NETC_GLOBAL_H
 
 #include <linux/io.h>
+#include <linux/pci.h>
 
 static inline u32 netc_read(void __iomem *reg)
 {
@@ -16,4 +17,13 @@ static inline void netc_write(void __iomem *reg, u32 val)
 	iowrite32(val, reg);
 }
 
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK_NETC)
+int netc_timer_get_phc_index(struct pci_dev *timer_pdev);
+#else
+static inline int netc_timer_get_phc_index(struct pci_dev *timer_pdev)
+{
+	return -ENODEV;
+}
+#endif
+
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 04/14] ptp: netc: add PTP_CLK_REQ_PPS support
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (2 preceding siblings ...)
  2025-07-16  7:31 ` [PATCH v2 net-next 03/14] ptp: netc: add NETC Timer PTP driver support Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 20:05   ` Frank Li
  2025-07-16  7:31 ` [PATCH v2 net-next 05/14] ptp: netc: add periodic pulse output support Wei Fang
                   ` (9 subsequent siblings)
  13 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

The NETC Times is able to generate the PPS event, so add PTP_CLK_REQ_PPS
support. In addition, if there is a time drift when PPS is enabled, the
PPS event will not be generated at an integral second of PHC. Based on
the suggestion from IP team, FIPER should be disabled before adjusting
the hardware time and then rearm ALARM after the time adjustment to make
the next PPS event be generated at an integral second of PHC. Finally,
re-enable FIPER.

Signed-off-by: Wei Fang <wei.fang@nxp.com>

---
v2 changes:
1. Refine the subject and the commit message
2. Add a comment to netc_timer_enable_pps()
3. Remove the "nxp,pps-channel" logic from the driver
---
 drivers/ptp/ptp_netc.c | 176 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 175 insertions(+), 1 deletion(-)

diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
index 82cb1e6a0fe9..e39605c5b73b 100644
--- a/drivers/ptp/ptp_netc.c
+++ b/drivers/ptp/ptp_netc.c
@@ -24,6 +24,8 @@
 #define  TMR_ALARM1P			BIT(31)
 
 #define NETC_TMR_TEVENT			0x0084
+#define  TMR_TEVNET_PPEN(i)		BIT(7 - (i))
+#define  TMR_TEVENT_PPEN_ALL		GENMASK(7, 5)
 #define  TMR_TEVENT_ALM1EN		BIT(16)
 #define  TMR_TEVENT_ALM2EN		BIT(17)
 
@@ -39,9 +41,15 @@
 #define NETC_TMR_ALARM_L(i)		(0x00b8 + (i) * 8)
 #define NETC_TMR_ALARM_H(i)		(0x00bc + (i) * 8)
 
+/* i = 0, 1, 2. i indicates the index of TMR_FIPER. */
+#define NETC_TMR_FIPER(i)		(0x00d0 + (i) * 4)
+
 #define NETC_TMR_FIPER_CTRL		0x00dc
 #define  FIPER_CTRL_DIS(i)		(BIT(7) << (i) * 8)
 #define  FIPER_CTRL_PG(i)		(BIT(6) << (i) * 8)
+#define  FIPER_CTRL_FS_ALARM(i)		(BIT(5) << (i) * 8)
+#define  FIPER_CTRL_PW(i)		(GENMASK(4, 0) << (i) * 8)
+#define  FIPER_CTRL_SET_PW(i, v)	(((v) & GENMASK(4, 0)) << 8 * (i))
 
 #define NETC_TMR_CUR_TIME_L		0x00f0
 #define NETC_TMR_CUR_TIME_H		0x00f4
@@ -51,6 +59,9 @@
 #define NETC_TMR_FIPER_NUM		3
 #define NETC_TMR_DEFAULT_PRSC		2
 #define NETC_TMR_DEFAULT_ALARM		GENMASK_ULL(63, 0)
+#define NETC_TMR_DEFAULT_PPS_CHANNEL	0
+#define NETC_TMR_DEFAULT_FIPER		GENMASK(31, 0)
+#define NETC_TMR_FIPER_MAX_PW		GENMASK(4, 0)
 
 /* 1588 timer reference clock source select */
 #define NETC_TMR_CCM_TIMER1		0 /* enet_timer1_clk_root, from CCM */
@@ -75,6 +86,8 @@ struct netc_timer {
 	u64 period;
 
 	int irq;
+	u8 pps_channel;
+	bool pps_enabled;
 };
 
 #define netc_timer_rd(p, o)		netc_read((p)->base + (o))
@@ -152,6 +165,147 @@ static void netc_timer_alarm_write(struct netc_timer *priv,
 	netc_timer_wr(priv, NETC_TMR_ALARM_H(index), alarm_h);
 }
 
+static u32 netc_timer_get_integral_period(struct netc_timer *priv)
+{
+	u32 tmr_ctrl, integral_period;
+
+	tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
+	integral_period = FIELD_GET(TMR_CTRL_TCLK_PERIOD, tmr_ctrl);
+
+	return integral_period;
+}
+
+static u32 netc_timer_calculate_fiper_pw(struct netc_timer *priv,
+					 u32 fiper)
+{
+	u64 divisor, pulse_width;
+
+	/* Set the FIPER pulse width to half FIPER interval by default.
+	 * pulse_width = (fiper / 2) / TMR_GCLK_period,
+	 * TMR_GCLK_period = NSEC_PER_SEC / TMR_GCLK_freq,
+	 * TMR_GCLK_freq = (clk_freq / oclk_prsc) Hz,
+	 * so pulse_width = fiper * clk_freq / (2 * NSEC_PER_SEC * oclk_prsc).
+	 */
+	divisor = mul_u32_u32(2000000000U, priv->oclk_prsc);
+	pulse_width = div64_u64(mul_u32_u32(fiper, priv->clk_freq), divisor);
+
+	/* The FIPER_PW field only has 5 bits, need to update oclk_prsc */
+	if (pulse_width > NETC_TMR_FIPER_MAX_PW)
+		pulse_width = NETC_TMR_FIPER_MAX_PW;
+
+	return pulse_width;
+}
+
+static void netc_timer_set_pps_alarm(struct netc_timer *priv, int channel,
+				     u32 integral_period)
+{
+	u64 alarm;
+
+	/* Get the alarm value */
+	alarm = netc_timer_cur_time_read(priv) +  NSEC_PER_MSEC;
+	alarm = roundup_u64(alarm, NSEC_PER_SEC);
+	alarm = roundup_u64(alarm, integral_period);
+
+	netc_timer_alarm_write(priv, alarm, 0);
+}
+
+/* Note that users should not use this API to output PPS signal on
+ * external pins, because PTP_CLK_REQ_PPS trigger internal PPS event
+ * for input into kernel PPS subsystem. See:
+ * https://lore.kernel.org/r/20201117213826.18235-1-a.fatoum@pengutronix.de
+ */
+static int netc_timer_enable_pps(struct netc_timer *priv,
+				 struct ptp_clock_request *rq, int on)
+{
+	u32 tmr_emask, fiper, fiper_ctrl;
+	u8 channel = priv->pps_channel;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
+	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
+
+	if (on) {
+		u32 integral_period, fiper_pw;
+
+		if (priv->pps_enabled)
+			goto unlock_spinlock;
+
+		integral_period = netc_timer_get_integral_period(priv);
+		fiper = NSEC_PER_SEC - integral_period;
+		fiper_pw = netc_timer_calculate_fiper_pw(priv, fiper);
+		fiper_ctrl &= ~(FIPER_CTRL_DIS(channel) | FIPER_CTRL_PW(channel) |
+				FIPER_CTRL_FS_ALARM(channel));
+		fiper_ctrl |= FIPER_CTRL_SET_PW(channel, fiper_pw);
+		tmr_emask |= TMR_TEVNET_PPEN(channel);
+		priv->pps_enabled = true;
+		netc_timer_set_pps_alarm(priv, channel, integral_period);
+	} else {
+		if (!priv->pps_enabled)
+			goto unlock_spinlock;
+
+		fiper = NETC_TMR_DEFAULT_FIPER;
+		tmr_emask &= ~TMR_TEVNET_PPEN(channel);
+		fiper_ctrl |= FIPER_CTRL_DIS(channel);
+		priv->pps_enabled = false;
+	}
+
+	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
+	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
+	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
+
+unlock_spinlock:
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static void netc_timer_disable_pps_fiper(struct netc_timer *priv)
+{
+	u32 fiper = NETC_TMR_DEFAULT_FIPER;
+	u8 channel = priv->pps_channel;
+	u32 fiper_ctrl;
+
+	if (!priv->pps_enabled)
+		return;
+
+	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
+	fiper_ctrl |= FIPER_CTRL_DIS(channel);
+	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
+	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
+}
+
+static void netc_timer_enable_pps_fiper(struct netc_timer *priv)
+{
+	u32 fiper_ctrl, integral_period, fiper;
+	u8 channel = priv->pps_channel;
+
+	if (!priv->pps_enabled)
+		return;
+
+	integral_period = netc_timer_get_integral_period(priv);
+	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
+	fiper_ctrl &= ~FIPER_CTRL_DIS(channel);
+	fiper = NSEC_PER_SEC - integral_period;
+	netc_timer_set_pps_alarm(priv, channel, integral_period);
+	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
+	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
+}
+
+static int netc_timer_enable(struct ptp_clock_info *ptp,
+			     struct ptp_clock_request *rq, int on)
+{
+	struct netc_timer *priv = ptp_to_netc_timer(ptp);
+
+	switch (rq->type) {
+	case PTP_CLK_REQ_PPS:
+		return netc_timer_enable_pps(priv, rq, on);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
 static void netc_timer_adjust_period(struct netc_timer *priv, u64 period)
 {
 	u32 fractional_period = lower_32_bits(period);
@@ -164,8 +318,11 @@ static void netc_timer_adjust_period(struct netc_timer *priv, u64 period)
 	old_tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
 	tmr_ctrl = u32_replace_bits(old_tmr_ctrl, integral_period,
 				    TMR_CTRL_TCLK_PERIOD);
-	if (tmr_ctrl != old_tmr_ctrl)
+	if (tmr_ctrl != old_tmr_ctrl) {
+		netc_timer_disable_pps_fiper(priv);
 		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
+		netc_timer_enable_pps_fiper(priv);
+	}
 
 	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
 
@@ -191,6 +348,8 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
 
 	spin_lock_irqsave(&priv->lock, flags);
 
+	netc_timer_disable_pps_fiper(priv);
+
 	tmr_off = netc_timer_offset_read(priv);
 	if (delta < 0 && tmr_off < abs(delta)) {
 		delta += tmr_off;
@@ -205,6 +364,8 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
 		netc_timer_offset_write(priv, tmr_off);
 	}
 
+	netc_timer_enable_pps_fiper(priv);
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
@@ -239,8 +400,12 @@ static int netc_timer_settime64(struct ptp_clock_info *ptp,
 	unsigned long flags;
 
 	spin_lock_irqsave(&priv->lock, flags);
+
+	netc_timer_disable_pps_fiper(priv);
 	netc_timer_offset_write(priv, 0);
 	netc_timer_cnt_write(priv, ns);
+	netc_timer_enable_pps_fiper(priv);
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
@@ -267,10 +432,12 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
 	.max_adj	= 500000000,
 	.n_alarm	= 2,
 	.n_pins		= 0,
+	.pps		= 1,
 	.adjfine	= netc_timer_adjfine,
 	.adjtime	= netc_timer_adjtime,
 	.gettimex64	= netc_timer_gettimex64,
 	.settime64	= netc_timer_settime64,
+	.enable		= netc_timer_enable,
 };
 
 static void netc_timer_init(struct netc_timer *priv)
@@ -429,6 +596,7 @@ static int netc_timer_parse_dt(struct netc_timer *priv)
 static irqreturn_t netc_timer_isr(int irq, void *data)
 {
 	struct netc_timer *priv = data;
+	struct ptp_clock_event event;
 	u32 tmr_event, tmr_emask;
 	unsigned long flags;
 
@@ -444,6 +612,11 @@ static irqreturn_t netc_timer_isr(int irq, void *data)
 	if (tmr_event & TMR_TEVENT_ALM2EN)
 		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 1);
 
+	if (tmr_event & TMR_TEVENT_PPEN_ALL) {
+		event.type = PTP_CLOCK_PPS;
+		ptp_clock_event(priv->clock, &event);
+	}
+
 	/* Clear interrupts status */
 	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
 
@@ -506,6 +679,7 @@ static int netc_timer_probe(struct pci_dev *pdev,
 
 	priv->caps = netc_timer_ptp_caps;
 	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
+	priv->pps_channel = NETC_TMR_DEFAULT_PPS_CHANNEL;
 	priv->phc_index = -1; /* initialize it as an invalid index */
 	spin_lock_init(&priv->lock);
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 05/14] ptp: netc: add periodic pulse output support
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (3 preceding siblings ...)
  2025-07-16  7:31 ` [PATCH v2 net-next 04/14] ptp: netc: add PTP_CLK_REQ_PPS support Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 20:26   ` Frank Li
  2025-07-16  7:31 ` [PATCH v2 net-next 06/14] ptp: netc: add external trigger stamp support Wei Fang
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

NETC Timer has three pulse channels, all of which support periodic pulse
output. Bind the channel to a ALARM register and then sets a future time
into the ALARM register. When the current time is greater than the ALARM
value, the FIPER register will be triggered to count down, and when the
count reaches 0, the pulse will be triggered. The PPS signal is also
implemented in this way. However, for i.MX95, only ALARM1 can be used for
periodic pulse output, and for i.MX943, ALARM1 and ALARM2 can be used for
periodic pulse output, but NETC Timer has three channels, so for i.MX95,
only one channel can work at the same time, and for i.MX943, at most two
channel can work at the same time. Otherwise, if multiple channels share
the same ALARM register, some channel pulses will not meet expectations.
Therefore, the current implementation does not allow multiple channels to
share the same ALARM register at the same time.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 drivers/ptp/ptp_netc.c | 281 ++++++++++++++++++++++++++++++++++++-----
 1 file changed, 250 insertions(+), 31 deletions(-)

diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
index e39605c5b73b..289cdd50ae3d 100644
--- a/drivers/ptp/ptp_netc.c
+++ b/drivers/ptp/ptp_netc.c
@@ -55,6 +55,10 @@
 #define NETC_TMR_CUR_TIME_H		0x00f4
 
 #define NETC_TMR_REGS_BAR		0
+#define NETC_GLOBAL_OFFSET		0x10000
+#define NETC_GLOBAL_IPBRR0		0xbf8
+#define  IPBRR0_IP_REV			GENMASK(15, 0)
+#define NETC_REV_4_1			0x0401
 
 #define NETC_TMR_FIPER_NUM		3
 #define NETC_TMR_DEFAULT_PRSC		2
@@ -62,6 +66,7 @@
 #define NETC_TMR_DEFAULT_PPS_CHANNEL	0
 #define NETC_TMR_DEFAULT_FIPER		GENMASK(31, 0)
 #define NETC_TMR_FIPER_MAX_PW		GENMASK(4, 0)
+#define NETC_TMR_ALARM_NUM		2
 
 /* 1588 timer reference clock source select */
 #define NETC_TMR_CCM_TIMER1		0 /* enet_timer1_clk_root, from CCM */
@@ -70,6 +75,19 @@
 
 #define NETC_TMR_SYSCLK_333M		333333333U
 
+enum netc_pp_type {
+	NETC_PP_PPS = 1,
+	NETC_PP_PEROUT,
+};
+
+struct netc_pp {
+	enum netc_pp_type type;
+	bool enabled;
+	int alarm_id;
+	u32 period; /* pulse period, ns */
+	u64 stime; /* start time, ns */
+};
+
 struct netc_timer {
 	void __iomem *base;
 	struct pci_dev *pdev;
@@ -87,7 +105,9 @@ struct netc_timer {
 
 	int irq;
 	u8 pps_channel;
-	bool pps_enabled;
+	u8 fs_alarm_num;
+	u8 fs_alarm_bitmap;
+	struct netc_pp pp[NETC_TMR_FIPER_NUM]; /* periodic pulse */
 };
 
 #define netc_timer_rd(p, o)		netc_read((p)->base + (o))
@@ -199,6 +219,7 @@ static u32 netc_timer_calculate_fiper_pw(struct netc_timer *priv,
 static void netc_timer_set_pps_alarm(struct netc_timer *priv, int channel,
 				     u32 integral_period)
 {
+	struct netc_pp *pp = &priv->pp[channel];
 	u64 alarm;
 
 	/* Get the alarm value */
@@ -206,7 +227,51 @@ static void netc_timer_set_pps_alarm(struct netc_timer *priv, int channel,
 	alarm = roundup_u64(alarm, NSEC_PER_SEC);
 	alarm = roundup_u64(alarm, integral_period);
 
-	netc_timer_alarm_write(priv, alarm, 0);
+	netc_timer_alarm_write(priv, alarm, pp->alarm_id);
+}
+
+static void netc_timer_set_perout_alarm(struct netc_timer *priv, int channel,
+					u32 integral_period)
+{
+	u64 cur_time = netc_timer_cur_time_read(priv);
+	struct netc_pp *pp = &priv->pp[channel];
+	u64 alarm, delta, min_time;
+	u32 period = pp->period;
+	u64 stime = pp->stime;
+
+	min_time = cur_time + NSEC_PER_MSEC + period;
+	if (stime < min_time) {
+		delta = min_time - stime;
+		stime += roundup_u64(delta, period);
+	}
+
+	alarm = roundup_u64(stime - period, integral_period);
+	netc_timer_alarm_write(priv, alarm, pp->alarm_id);
+}
+
+static int netc_timer_get_alarm_id(struct netc_timer *priv)
+{
+	int i;
+
+	for (i = 0; i < priv->fs_alarm_num; i++) {
+		if (!(priv->fs_alarm_bitmap & BIT(i))) {
+			priv->fs_alarm_bitmap |= BIT(i);
+			break;
+		}
+	}
+
+	return i;
+}
+
+static u64 netc_timer_get_gclk_period(struct netc_timer *priv)
+{
+	/* TMR_GCLK_freq = (clk_freq / oclk_prsc) Hz.
+	 * TMR_GCLK_period = NSEC_PER_SEC / TMR_GCLK_freq.
+	 * TMR_GCLK_period = (NSEC_PER_SEC * oclk_prsc) / clk_freq
+	 */
+
+	return div_u64(mul_u32_u32(NSEC_PER_SEC, priv->oclk_prsc),
+		       priv->clk_freq);
 }
 
 /* Note that users should not use this API to output PPS signal on
@@ -217,20 +282,43 @@ static void netc_timer_set_pps_alarm(struct netc_timer *priv, int channel,
 static int netc_timer_enable_pps(struct netc_timer *priv,
 				 struct ptp_clock_request *rq, int on)
 {
+	struct device *dev = &priv->pdev->dev;
 	u32 tmr_emask, fiper, fiper_ctrl;
 	u8 channel = priv->pps_channel;
 	unsigned long flags;
+	struct netc_pp *pp;
+	int alarm_id;
+	int err = 0;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
+	pp = &priv->pp[channel];
+	if (pp->type == NETC_PP_PEROUT) {
+		dev_err(dev, "FIPER%u is being used for PEROUT\n", channel);
+		err = -EBUSY;
+		goto unlock_spinlock;
+	}
+
 	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
 	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
 
 	if (on) {
 		u32 integral_period, fiper_pw;
 
-		if (priv->pps_enabled)
+		if (pp->enabled)
+			goto unlock_spinlock;
+
+		alarm_id = netc_timer_get_alarm_id(priv);
+		if (alarm_id == priv->fs_alarm_num) {
+			dev_err(dev, "No available ALARMs\n");
+			err = -EBUSY;
 			goto unlock_spinlock;
+		}
+
+		pp->enabled = true;
+		pp->type = NETC_PP_PPS;
+		pp->alarm_id = alarm_id;
+		pp->period = NSEC_PER_SEC;
 
 		integral_period = netc_timer_get_integral_period(priv);
 		fiper = NSEC_PER_SEC - integral_period;
@@ -238,17 +326,19 @@ static int netc_timer_enable_pps(struct netc_timer *priv,
 		fiper_ctrl &= ~(FIPER_CTRL_DIS(channel) | FIPER_CTRL_PW(channel) |
 				FIPER_CTRL_FS_ALARM(channel));
 		fiper_ctrl |= FIPER_CTRL_SET_PW(channel, fiper_pw);
+		fiper_ctrl |= alarm_id ? FIPER_CTRL_FS_ALARM(channel) : 0;
 		tmr_emask |= TMR_TEVNET_PPEN(channel);
-		priv->pps_enabled = true;
 		netc_timer_set_pps_alarm(priv, channel, integral_period);
 	} else {
-		if (!priv->pps_enabled)
+		if (!pp->enabled)
 			goto unlock_spinlock;
 
+		priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id);
+		memset(pp, 0, sizeof(*pp));
+
 		fiper = NETC_TMR_DEFAULT_FIPER;
 		tmr_emask &= ~TMR_TEVNET_PPEN(channel);
 		fiper_ctrl |= FIPER_CTRL_DIS(channel);
-		priv->pps_enabled = false;
 	}
 
 	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
@@ -258,38 +348,150 @@ static int netc_timer_enable_pps(struct netc_timer *priv,
 unlock_spinlock:
 	spin_unlock_irqrestore(&priv->lock, flags);
 
-	return 0;
+	return err;
 }
 
-static void netc_timer_disable_pps_fiper(struct netc_timer *priv)
+static int net_timer_enable_perout(struct netc_timer *priv,
+				   struct ptp_clock_request *rq, int on)
 {
-	u32 fiper = NETC_TMR_DEFAULT_FIPER;
-	u8 channel = priv->pps_channel;
-	u32 fiper_ctrl;
+	struct device *dev = &priv->pdev->dev;
+	u32 tmr_emask, fiper, fiper_ctrl;
+	u32 channel = rq->perout.index;
+	unsigned long flags;
+	struct netc_pp *pp;
+	int alarm_id;
+	int err = 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
 
-	if (!priv->pps_enabled)
-		return;
+	pp = &priv->pp[channel];
+	if (pp->type == NETC_PP_PPS) {
+		dev_err(dev, "FIPER%u is being used for PPS\n", channel);
+		err = -EBUSY;
+		goto unlock_spinlock;
+	}
 
+	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
 	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
-	fiper_ctrl |= FIPER_CTRL_DIS(channel);
+	if (on) {
+		u64 period_ns, gclk_period, max_period, min_period;
+		struct timespec64 period, stime;
+		u32 integral_period, fiper_pw;
+
+		period.tv_sec = rq->perout.period.sec;
+		period.tv_nsec = rq->perout.period.nsec;
+		period_ns = timespec64_to_ns(&period);
+
+		integral_period = netc_timer_get_integral_period(priv);
+		max_period = (u64)NETC_TMR_DEFAULT_FIPER + integral_period;
+		gclk_period = netc_timer_get_gclk_period(priv);
+		min_period = gclk_period * 4 + integral_period;
+		if (period_ns > max_period || period_ns < min_period) {
+			dev_err(dev, "The period range is %llu ~ %llu\n",
+				min_period, max_period);
+			err = -EINVAL;
+			goto unlock_spinlock;
+		}
+
+		stime.tv_sec = rq->perout.start.sec;
+		stime.tv_nsec = rq->perout.start.nsec;
+
+		tmr_emask |= TMR_TEVNET_PPEN(channel);
+
+		/* Set to desired FIPER interval in ns - TCLK_PERIOD */
+		fiper = period_ns - integral_period;
+		fiper_pw = netc_timer_calculate_fiper_pw(priv, fiper);
+
+		if (pp->enabled) {
+			alarm_id = pp->alarm_id;
+		} else {
+			alarm_id = netc_timer_get_alarm_id(priv);
+			if (alarm_id == priv->fs_alarm_num) {
+				dev_err(dev, "No available ALARMs\n");
+				err = -EBUSY;
+				goto unlock_spinlock;
+			}
+
+			pp->type = NETC_PP_PEROUT;
+			pp->enabled = true;
+			pp->alarm_id = alarm_id;
+		}
+
+		pp->stime = timespec64_to_ns(&stime);
+		pp->period = period_ns;
+
+		fiper_ctrl &= ~(FIPER_CTRL_DIS(channel) | FIPER_CTRL_PW(channel) |
+				FIPER_CTRL_FS_ALARM(channel));
+		fiper_ctrl |= FIPER_CTRL_SET_PW(channel, fiper_pw);
+		fiper_ctrl |= alarm_id ? FIPER_CTRL_FS_ALARM(channel) : 0;
+
+		netc_timer_set_perout_alarm(priv, channel, integral_period);
+	} else {
+		if (!pp->enabled)
+			goto unlock_spinlock;
+
+		tmr_emask &= ~TMR_TEVNET_PPEN(channel);
+		fiper = NETC_TMR_DEFAULT_FIPER;
+		fiper_ctrl |= FIPER_CTRL_DIS(channel);
+
+		alarm_id = pp->alarm_id;
+		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, alarm_id);
+		priv->fs_alarm_bitmap &= ~BIT(alarm_id);
+		memset(pp, 0, sizeof(*pp));
+	}
+
+	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
 	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
 	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
+
+unlock_spinlock:
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return err;
 }
 
-static void netc_timer_enable_pps_fiper(struct netc_timer *priv)
+static void netc_timer_disable_fiper(struct netc_timer *priv)
 {
-	u32 fiper_ctrl, integral_period, fiper;
-	u8 channel = priv->pps_channel;
+	u32 fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
+	int i;
 
-	if (!priv->pps_enabled)
-		return;
+	for (i = 0; i < NETC_TMR_FIPER_NUM; i++) {
+		struct netc_pp *pp = &priv->pp[i];
+
+		if (!pp->enabled)
+			continue;
+
+		fiper_ctrl |= FIPER_CTRL_DIS(i);
+		netc_timer_wr(priv, NETC_TMR_FIPER(i), NETC_TMR_DEFAULT_FIPER);
+	}
+
+	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
+}
+
+static void netc_timer_enable_fiper(struct netc_timer *priv)
+{
+	u32 integral_period = netc_timer_get_integral_period(priv);
+	u32 fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
+	int i;
+
+	for (i = 0; i < NETC_TMR_FIPER_NUM; i++) {
+		struct netc_pp *pp = &priv->pp[i];
+		u32 fiper;
+
+		if (!pp->enabled)
+			continue;
+
+		fiper_ctrl &= ~FIPER_CTRL_DIS(i);
+
+		if (pp->type == NETC_PP_PPS)
+			netc_timer_set_pps_alarm(priv, i, integral_period);
+		else if (pp->type == NETC_PP_PEROUT)
+			netc_timer_set_perout_alarm(priv, i, integral_period);
+
+		fiper = pp->period - integral_period;
+		netc_timer_wr(priv, NETC_TMR_FIPER(i), fiper);
+	}
 
-	integral_period = netc_timer_get_integral_period(priv);
-	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
-	fiper_ctrl &= ~FIPER_CTRL_DIS(channel);
-	fiper = NSEC_PER_SEC - integral_period;
-	netc_timer_set_pps_alarm(priv, channel, integral_period);
-	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
 	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
 }
 
@@ -301,6 +503,8 @@ static int netc_timer_enable(struct ptp_clock_info *ptp,
 	switch (rq->type) {
 	case PTP_CLK_REQ_PPS:
 		return netc_timer_enable_pps(priv, rq, on);
+	case PTP_CLK_REQ_PEROUT:
+		return net_timer_enable_perout(priv, rq, on);
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -319,9 +523,9 @@ static void netc_timer_adjust_period(struct netc_timer *priv, u64 period)
 	tmr_ctrl = u32_replace_bits(old_tmr_ctrl, integral_period,
 				    TMR_CTRL_TCLK_PERIOD);
 	if (tmr_ctrl != old_tmr_ctrl) {
-		netc_timer_disable_pps_fiper(priv);
+		netc_timer_disable_fiper(priv);
 		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
-		netc_timer_enable_pps_fiper(priv);
+		netc_timer_enable_fiper(priv);
 	}
 
 	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
@@ -348,7 +552,7 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	netc_timer_disable_pps_fiper(priv);
+	netc_timer_disable_fiper(priv);
 
 	tmr_off = netc_timer_offset_read(priv);
 	if (delta < 0 && tmr_off < abs(delta)) {
@@ -364,7 +568,7 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
 		netc_timer_offset_write(priv, tmr_off);
 	}
 
-	netc_timer_enable_pps_fiper(priv);
+	netc_timer_enable_fiper(priv);
 
 	spin_unlock_irqrestore(&priv->lock, flags);
 
@@ -401,10 +605,10 @@ static int netc_timer_settime64(struct ptp_clock_info *ptp,
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	netc_timer_disable_pps_fiper(priv);
+	netc_timer_disable_fiper(priv);
 	netc_timer_offset_write(priv, 0);
 	netc_timer_cnt_write(priv, ns);
-	netc_timer_enable_pps_fiper(priv);
+	netc_timer_enable_fiper(priv);
 
 	spin_unlock_irqrestore(&priv->lock, flags);
 
@@ -433,6 +637,7 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
 	.n_alarm	= 2,
 	.n_pins		= 0,
 	.pps		= 1,
+	.n_per_out	= 3,
 	.adjfine	= netc_timer_adjfine,
 	.adjtime	= netc_timer_adjtime,
 	.gettimex64	= netc_timer_gettimex64,
@@ -659,6 +864,15 @@ static void netc_timer_free_msix_irq(struct netc_timer *priv)
 	pci_free_irq_vectors(pdev);
 }
 
+static int netc_timer_get_global_ip_rev(struct netc_timer *priv)
+{
+	u32 val;
+
+	val = netc_timer_rd(priv, NETC_GLOBAL_OFFSET + NETC_GLOBAL_IPBRR0);
+
+	return val & IPBRR0_IP_REV;
+}
+
 static int netc_timer_probe(struct pci_dev *pdev,
 			    const struct pci_device_id *id)
 {
@@ -689,6 +903,11 @@ static int netc_timer_probe(struct pci_dev *pdev,
 		goto timer_pci_remove;
 	}
 
+	if (netc_timer_get_global_ip_rev(priv) == NETC_REV_4_1)
+		priv->fs_alarm_num = 1;
+	else
+		priv->fs_alarm_num = NETC_TMR_ALARM_NUM;
+
 	err = netc_timer_init_msix_irq(priv);
 	if (err)
 		goto disable_clk;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 06/14] ptp: netc: add external trigger stamp support
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (4 preceding siblings ...)
  2025-07-16  7:31 ` [PATCH v2 net-next 05/14] ptp: netc: add periodic pulse output support Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 20:30   ` Frank Li
  2025-07-16  7:31 ` [PATCH v2 net-next 07/14] ptp: netc: add debugfs support to loop back pulse signal Wei Fang
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

From: "F.S. Peng" <fushi.peng@nxp.com>

The NETC Timer is capable of recording the timestamp on receipt of an
external pulse on a GPIO pin. It supports two such external triggers.
The recorded value is saved in a 16 entry FIFO accessed by
TMR_ETTSa_H/L. An interrupt can be generated when the trigger occurs,
when the FIFO reaches a threshold, and if the FIFO overflows.

Signed-off-by: F.S. Peng <fushi.peng@nxp.com>
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 drivers/ptp/ptp_netc.c | 118 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)

diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
index 289cdd50ae3d..c2fc6351db5b 100644
--- a/drivers/ptp/ptp_netc.c
+++ b/drivers/ptp/ptp_netc.c
@@ -18,6 +18,8 @@
 #define NETC_TMR_CTRL			0x0080
 #define  TMR_CTRL_CK_SEL		GENMASK(1, 0)
 #define  TMR_CTRL_TE			BIT(2)
+#define  TMR_ETEP1			BIT(8)
+#define  TMR_ETEP2			BIT(9)
 #define  TMR_COMP_MODE			BIT(15)
 #define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
 #define  TMR_CTRL_FS			BIT(28)
@@ -28,12 +30,26 @@
 #define  TMR_TEVENT_PPEN_ALL		GENMASK(7, 5)
 #define  TMR_TEVENT_ALM1EN		BIT(16)
 #define  TMR_TEVENT_ALM2EN		BIT(17)
+#define  TMR_TEVENT_ETS1_THREN		BIT(20)
+#define  TMR_TEVENT_ETS2_THREN		BIT(21)
+#define  TMR_TEVENT_ETS1EN		BIT(24)
+#define  TMR_TEVENT_ETS2EN		BIT(25)
+#define  TMR_TEVENT_ETS1_OVEN		BIT(28)
+#define  TMR_TEVENT_ETS2_OVEN		BIT(29)
+#define  TMR_TEVENT_ETS1		(TMR_TEVENT_ETS1_THREN | \
+					 TMR_TEVENT_ETS1EN | TMR_TEVENT_ETS1_OVEN)
+#define  TMR_TEVENT_ETS2		(TMR_TEVENT_ETS2_THREN | \
+					 TMR_TEVENT_ETS2EN | TMR_TEVENT_ETS2_OVEN)
 
 #define NETC_TMR_TEMASK			0x0088
+#define NETC_TMR_STAT			0x0094
+#define  TMR_STAT_ETS1_VLD		BIT(24)
+#define  TMR_STAT_ETS2_VLD		BIT(25)
 #define NETC_TMR_CNT_L			0x0098
 #define NETC_TMR_CNT_H			0x009c
 #define NETC_TMR_ADD			0x00a0
 #define NETC_TMR_PRSC			0x00a8
+#define NETC_TMR_ECTRL			0x00ac
 #define NETC_TMR_OFF_L			0x00b0
 #define NETC_TMR_OFF_H			0x00b4
 
@@ -51,6 +67,10 @@
 #define  FIPER_CTRL_PW(i)		(GENMASK(4, 0) << (i) * 8)
 #define  FIPER_CTRL_SET_PW(i, v)	(((v) & GENMASK(4, 0)) << 8 * (i))
 
+#define NETC_TMR_ETTS1_L		0x00e0
+#define NETC_TMR_ETTS1_H		0x00e4
+#define NETC_TMR_ETTS2_L		0x00e8
+#define NETC_TMR_ETTS2_H		0x00ec
 #define NETC_TMR_CUR_TIME_L		0x00f0
 #define NETC_TMR_CUR_TIME_H		0x00f4
 
@@ -67,6 +87,7 @@
 #define NETC_TMR_DEFAULT_FIPER		GENMASK(31, 0)
 #define NETC_TMR_FIPER_MAX_PW		GENMASK(4, 0)
 #define NETC_TMR_ALARM_NUM		2
+#define NETC_TMR_DEFAULT_ETTF_THR	7
 
 /* 1588 timer reference clock source select */
 #define NETC_TMR_CCM_TIMER1		0 /* enet_timer1_clk_root, from CCM */
@@ -450,6 +471,91 @@ static int net_timer_enable_perout(struct netc_timer *priv,
 	return err;
 }
 
+static void netc_timer_handle_etts_event(struct netc_timer *priv, int index,
+					 bool update_event)
+{
+	u32 regoff_l, regoff_h, etts_l, etts_h, ets_vld;
+	struct ptp_clock_event event;
+
+	switch (index) {
+	case 0:
+		ets_vld = TMR_STAT_ETS1_VLD;
+		regoff_l = NETC_TMR_ETTS1_L;
+		regoff_h = NETC_TMR_ETTS1_H;
+		break;
+	case 1:
+		ets_vld = TMR_STAT_ETS2_VLD;
+		regoff_l = NETC_TMR_ETTS2_L;
+		regoff_h = NETC_TMR_ETTS2_H;
+		break;
+	default:
+		return;
+	}
+
+	if (!(netc_timer_rd(priv, NETC_TMR_STAT) & ets_vld))
+		return;
+
+	do {
+		etts_l = netc_timer_rd(priv, regoff_l);
+		etts_h = netc_timer_rd(priv, regoff_h);
+	} while (netc_timer_rd(priv, NETC_TMR_STAT) & ets_vld);
+
+	if (update_event) {
+		event.type = PTP_CLOCK_EXTTS;
+		event.index = index;
+		event.timestamp = (u64)etts_h << 32;
+		event.timestamp |= etts_l;
+		ptp_clock_event(priv->clock, &event);
+	}
+}
+
+static int netc_timer_enable_extts(struct netc_timer *priv,
+				   struct ptp_clock_request *rq, int on)
+{
+	u32 ets_emask, tmr_emask, tmr_ctrl, ettp_bit;
+	unsigned long flags;
+
+	/* Reject requests to enable time stamping on both edges */
+	if ((rq->extts.flags & PTP_EXTTS_EDGES) == PTP_EXTTS_EDGES)
+		return -EOPNOTSUPP;
+
+	switch (rq->extts.index) {
+	case 0:
+		ettp_bit = TMR_ETEP1;
+		ets_emask = TMR_TEVENT_ETS1;
+		break;
+	case 1:
+		ettp_bit = TMR_ETEP2;
+		ets_emask = TMR_TEVENT_ETS2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	netc_timer_handle_etts_event(priv, rq->extts.index, false);
+	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
+	if (on) {
+		tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
+		if (rq->extts.flags & PTP_FALLING_EDGE)
+			tmr_ctrl |= ettp_bit;
+		else
+			tmr_ctrl &= ~ettp_bit;
+
+		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
+		tmr_emask |= ets_emask;
+	} else {
+		tmr_emask &= ~ets_emask;
+	}
+
+	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
 static void netc_timer_disable_fiper(struct netc_timer *priv)
 {
 	u32 fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
@@ -505,6 +611,8 @@ static int netc_timer_enable(struct ptp_clock_info *ptp,
 		return netc_timer_enable_pps(priv, rq, on);
 	case PTP_CLK_REQ_PEROUT:
 		return net_timer_enable_perout(priv, rq, on);
+	case PTP_CLK_REQ_EXTTS:
+		return netc_timer_enable_extts(priv, rq, on);
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -638,6 +746,9 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
 	.n_pins		= 0,
 	.pps		= 1,
 	.n_per_out	= 3,
+	.n_ext_ts	= 2,
+	.supported_extts_flags = PTP_RISING_EDGE | PTP_FALLING_EDGE |
+				 PTP_STRICT_FLAGS,
 	.adjfine	= netc_timer_adjfine,
 	.adjtime	= netc_timer_adjtime,
 	.gettimex64	= netc_timer_gettimex64,
@@ -670,6 +781,7 @@ static void netc_timer_init(struct netc_timer *priv)
 		fiper_ctrl &= ~FIPER_CTRL_PG(i);
 	}
 	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
+	netc_timer_wr(priv, NETC_TMR_ECTRL, NETC_TMR_DEFAULT_ETTF_THR);
 
 	ktime_get_real_ts64(&now);
 	ns = timespec64_to_ns(&now);
@@ -822,6 +934,12 @@ static irqreturn_t netc_timer_isr(int irq, void *data)
 		ptp_clock_event(priv->clock, &event);
 	}
 
+	if (tmr_event & TMR_TEVENT_ETS1)
+		netc_timer_handle_etts_event(priv, 0, true);
+
+	if (tmr_event & TMR_TEVENT_ETS2)
+		netc_timer_handle_etts_event(priv, 1, true);
+
 	/* Clear interrupts status */
 	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 07/14] ptp: netc: add debugfs support to loop back pulse signal
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (5 preceding siblings ...)
  2025-07-16  7:31 ` [PATCH v2 net-next 06/14] ptp: netc: add external trigger stamp support Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 20:32   ` Frank Li
  2025-07-16  7:31 ` [PATCH v2 net-next 08/14] MAINTAINERS: add NETC Timer PTP clock driver section Wei Fang
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

The NETC Timer supports to loop back the output pulse signal of Fiper-n
into Trigger-n input, so that we can leverage this feature to validate
some other features without external hardware support. For example, we
can use it to test external trigger stamp (EXTTS). And we can combine
EXTTS with loopback mode to check whether the generation time of PPS is
aligned with an integral second of PHC, or the periodic output signal
(PTP_CLK_REQ_PEROUT) whether is generated at the specified time. So add
the debugfs interfaces to enable the loopback mode of Fiper1 and Fiper2.

An example to test the generation time of PPS event.

$ echo 1 > /sys/kernel/debug/netc_timer0/fiper1-loopback
$ echo 1 > /sys/class/ptp/ptp0/pps_enable
$ testptp -d /dev/ptp0 -e 3
external time stamp request okay
event index 0 at 108.000000018
event index 0 at 109.000000018
event index 0 at 110.000000018

An example to test the generation time of the periodic output signal.

$ echo 1 > /sys/kernel/debug/netc_timer0/fiper1-loopback
$ echo 0 260 0 1 500000000 > /sys/class/ptp/ptp0/period
$ testptp -d /dev/ptp0 -e 3
external time stamp request okay
event index 0 at 260.000000016
event index 0 at 261.500000015
event index 0 at 263.000000016

Signed-off-by: Wei Fang <wei.fang@nxp.com>

---
v2 changes:
1. Remove the check of the return value of debugfs_create_dir()
---
 drivers/ptp/ptp_netc.c | 114 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)

diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
index c2fc6351db5b..2a077eb2f0eb 100644
--- a/drivers/ptp/ptp_netc.c
+++ b/drivers/ptp/ptp_netc.c
@@ -6,6 +6,7 @@
 
 #include <linux/bitfield.h>
 #include <linux/clk.h>
+#include <linux/debugfs.h>
 #include <linux/fsl/netc_global.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -22,6 +23,8 @@
 #define  TMR_ETEP2			BIT(9)
 #define  TMR_COMP_MODE			BIT(15)
 #define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
+#define  TMR_CTRL_PP2L			BIT(26)
+#define  TMR_CTRL_PP1L			BIT(27)
 #define  TMR_CTRL_FS			BIT(28)
 #define  TMR_ALARM1P			BIT(31)
 
@@ -129,6 +132,7 @@ struct netc_timer {
 	u8 fs_alarm_num;
 	u8 fs_alarm_bitmap;
 	struct netc_pp pp[NETC_TMR_FIPER_NUM]; /* periodic pulse */
+	struct dentry *debugfs_root;
 };
 
 #define netc_timer_rd(p, o)		netc_read((p)->base + (o))
@@ -991,6 +995,114 @@ static int netc_timer_get_global_ip_rev(struct netc_timer *priv)
 	return val & IPBRR0_IP_REV;
 }
 
+static int netc_timer_get_fiper_loopback(struct netc_timer *priv,
+					 int fiper, u64 *val)
+{
+	unsigned long flags;
+	u32 tmr_ctrl;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	switch (fiper) {
+	case 0:
+		*val = tmr_ctrl & TMR_CTRL_PP1L ? 1 : 0;
+		break;
+	case 1:
+		*val = tmr_ctrl & TMR_CTRL_PP2L ? 1 : 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int netc_timer_set_fiper_loopback(struct netc_timer *priv,
+					 int fiper, u64 val)
+{
+	unsigned long flags;
+	u32 tmr_ctrl;
+	int err = 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
+	switch (fiper) {
+	case 0:
+		tmr_ctrl = u32_replace_bits(tmr_ctrl, val ? 1 : 0,
+					    TMR_CTRL_PP1L);
+		break;
+	case 1:
+		tmr_ctrl = u32_replace_bits(tmr_ctrl, val ? 1 : 0,
+					    TMR_CTRL_PP2L);
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	if (!err)
+		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return err;
+}
+
+static int netc_timer_get_fiper1_loopback(void *data, u64 *val)
+{
+	struct netc_timer *priv = data;
+
+	return netc_timer_get_fiper_loopback(priv, 0, val);
+}
+
+static int netc_timer_set_fiper1_loopback(void *data, u64 val)
+{
+	struct netc_timer *priv = data;
+
+	return netc_timer_set_fiper_loopback(priv, 0, val);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(netc_timer_fiper1_fops, netc_timer_get_fiper1_loopback,
+			 netc_timer_set_fiper1_loopback, "%llu\n");
+
+static int netc_timer_get_fiper2_loopback(void *data, u64 *val)
+{
+	struct netc_timer *priv = data;
+
+	return netc_timer_get_fiper_loopback(priv, 1, val);
+}
+
+static int netc_timer_set_fiper2_loopback(void *data, u64 val)
+{
+	struct netc_timer *priv = data;
+
+	return netc_timer_set_fiper_loopback(priv, 1, val);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(netc_timer_fiper2_fops, netc_timer_get_fiper2_loopback,
+			 netc_timer_set_fiper2_loopback, "%llu\n");
+
+static void netc_timer_create_debugfs(struct netc_timer *priv)
+{
+	char debugfs_name[24];
+
+	snprintf(debugfs_name, sizeof(debugfs_name), "netc_timer%d",
+		 priv->phc_index);
+	priv->debugfs_root = debugfs_create_dir(debugfs_name, NULL);
+	debugfs_create_file("fiper1-loopback", 0600, priv->debugfs_root,
+			    priv, &netc_timer_fiper1_fops);
+	debugfs_create_file("fiper2-loopback", 0600, priv->debugfs_root,
+			    priv, &netc_timer_fiper2_fops);
+}
+
+static void netc_timer_remove_debugfs(struct netc_timer *priv)
+{
+	debugfs_remove(priv->debugfs_root);
+	priv->debugfs_root = NULL;
+}
+
 static int netc_timer_probe(struct pci_dev *pdev,
 			    const struct pci_device_id *id)
 {
@@ -1038,6 +1150,7 @@ static int netc_timer_probe(struct pci_dev *pdev,
 	}
 
 	priv->phc_index = ptp_clock_index(priv->clock);
+	netc_timer_create_debugfs(priv);
 
 	return 0;
 
@@ -1055,6 +1168,7 @@ static void netc_timer_remove(struct pci_dev *pdev)
 {
 	struct netc_timer *priv = pci_get_drvdata(pdev);
 
+	netc_timer_remove_debugfs(priv);
 	ptp_clock_unregister(priv->clock);
 	netc_timer_free_msix_irq(priv);
 	clk_disable_unprepare(priv->src_clk);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 08/14] MAINTAINERS: add NETC Timer PTP clock driver section
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (6 preceding siblings ...)
  2025-07-16  7:31 ` [PATCH v2 net-next 07/14] ptp: netc: add debugfs support to loop back pulse signal Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 20:33   ` Frank Li
  2025-07-16  7:31 ` [PATCH v2 net-next 09/14] net: enetc: save the parsed information of PTP packet to skb->cb Wei Fang
                   ` (5 subsequent siblings)
  13 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

Add a section entry for NXP NETC Timer PTP clock driver.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 MAINTAINERS | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index d1554f33d0ac..dacc5824dca6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18005,6 +18005,15 @@ F:	Documentation/devicetree/bindings/clock/imx*
 F:	drivers/clk/imx/
 F:	include/dt-bindings/clock/imx*
 
+NXP NETC TIMER PTP CLOCK DRIVER
+M:	Wei Fang <wei.fang@nxp.com>
+M:	Clark Wang <xiaoning.wang@nxp.com>
+L:	imx@lists.linux.dev
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
+F:	drivers/ptp/ptp_netc.c
+
 NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
 M:	Jagan Teki <jagan@amarulasolutions.com>
 S:	Maintained
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 09/14] net: enetc: save the parsed information of PTP packet to skb->cb
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (7 preceding siblings ...)
  2025-07-16  7:31 ` [PATCH v2 net-next 08/14] MAINTAINERS: add NETC Timer PTP clock driver section Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 20:46   ` Frank Li
  2025-07-16  7:31 ` [PATCH v2 net-next 10/14] net: enetc: Add enetc_update_ptp_sync_msg() to process PTP sync packet Wei Fang
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

Currently, the Tx PTP packets are parsed twice in the enetc driver, once
in enetc_xmit() and once in enetc_map_tx_buffs(). The latter is duplicate
and is unnecessary, since the parsed information can be saved to skb->cb
so that enetc_map_tx_buffs() can get the previously parsed data from
skb->cb. Therefore, we add struct enetc_skb_cb as the format of the data
in the skb->cb buffer to save the parsed information of PTP packet.

In addition, the variables offset1 and offset2 in enetc_map_tx_buffs()
are renamed to corr_off and tstamp_off to make them easier to understand.

Signed-off-by: Wei Fang <wei.fang@nxp.com>

---
v2 changes:
1. Add description of offset1 and offset2 being renamed in the commit
message.
---
 drivers/net/ethernet/freescale/enetc/enetc.c | 65 ++++++++++----------
 drivers/net/ethernet/freescale/enetc/enetc.h |  9 +++
 2 files changed, 43 insertions(+), 31 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index e4287725832e..c1373163a096 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -225,13 +225,12 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 {
 	bool do_vlan, do_onestep_tstamp = false, do_twostep_tstamp = false;
 	struct enetc_ndev_priv *priv = netdev_priv(tx_ring->ndev);
+	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
 	struct enetc_hw *hw = &priv->si->hw;
 	struct enetc_tx_swbd *tx_swbd;
 	int len = skb_headlen(skb);
 	union enetc_tx_bd temp_bd;
-	u8 msgtype, twostep, udp;
 	union enetc_tx_bd *txbd;
-	u16 offset1, offset2;
 	int i, count = 0;
 	skb_frag_t *frag;
 	unsigned int f;
@@ -280,16 +279,10 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 	count++;
 
 	do_vlan = skb_vlan_tag_present(skb);
-	if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
-		if (enetc_ptp_parse(skb, &udp, &msgtype, &twostep, &offset1,
-				    &offset2) ||
-		    msgtype != PTP_MSGTYPE_SYNC || twostep)
-			WARN_ONCE(1, "Bad packet for one-step timestamping\n");
-		else
-			do_onestep_tstamp = true;
-	} else if (skb->cb[0] & ENETC_F_TX_TSTAMP) {
+	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)
+		do_onestep_tstamp = true;
+	else if (enetc_cb->flag & ENETC_F_TX_TSTAMP)
 		do_twostep_tstamp = true;
-	}
 
 	tx_swbd->do_twostep_tstamp = do_twostep_tstamp;
 	tx_swbd->qbv_en = !!(priv->active_offloads & ENETC_F_QBV);
@@ -333,6 +326,8 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 		}
 
 		if (do_onestep_tstamp) {
+			u16 tstamp_off = enetc_cb->origin_tstamp_off;
+			u16 corr_off = enetc_cb->correction_off;
 			__be32 new_sec_l, new_nsec;
 			u32 lo, hi, nsec, val;
 			__be16 new_sec_h;
@@ -362,32 +357,32 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 			new_sec_h = htons((sec >> 32) & 0xffff);
 			new_sec_l = htonl(sec & 0xffffffff);
 			new_nsec = htonl(nsec);
-			if (udp) {
+			if (enetc_cb->udp) {
 				struct udphdr *uh = udp_hdr(skb);
 				__be32 old_sec_l, old_nsec;
 				__be16 old_sec_h;
 
-				old_sec_h = *(__be16 *)(data + offset2);
+				old_sec_h = *(__be16 *)(data + tstamp_off);
 				inet_proto_csum_replace2(&uh->check, skb, old_sec_h,
 							 new_sec_h, false);
 
-				old_sec_l = *(__be32 *)(data + offset2 + 2);
+				old_sec_l = *(__be32 *)(data + tstamp_off + 2);
 				inet_proto_csum_replace4(&uh->check, skb, old_sec_l,
 							 new_sec_l, false);
 
-				old_nsec = *(__be32 *)(data + offset2 + 6);
+				old_nsec = *(__be32 *)(data + tstamp_off + 6);
 				inet_proto_csum_replace4(&uh->check, skb, old_nsec,
 							 new_nsec, false);
 			}
 
-			*(__be16 *)(data + offset2) = new_sec_h;
-			*(__be32 *)(data + offset2 + 2) = new_sec_l;
-			*(__be32 *)(data + offset2 + 6) = new_nsec;
+			*(__be16 *)(data + tstamp_off) = new_sec_h;
++			*(__be32 *)(data + tstamp_off + 2) = new_sec_l;
++			*(__be32 *)(data + tstamp_off + 6) = new_nsec;
 
 			/* Configure single-step register */
 			val = ENETC_PM0_SINGLE_STEP_EN;
-			val |= ENETC_SET_SINGLE_STEP_OFFSET(offset1);
-			if (udp)
+			val |= ENETC_SET_SINGLE_STEP_OFFSET(corr_off);
+			if (enetc_cb->udp)
 				val |= ENETC_PM0_SINGLE_STEP_CH;
 
 			enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP,
@@ -938,12 +933,13 @@ static int enetc_map_tx_tso_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb
 static netdev_tx_t enetc_start_xmit(struct sk_buff *skb,
 				    struct net_device *ndev)
 {
+	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 	struct enetc_bdr *tx_ring;
 	int count;
 
 	/* Queue one-step Sync packet if already locked */
-	if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
+	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
 		if (test_and_set_bit_lock(ENETC_TX_ONESTEP_TSTAMP_IN_PROGRESS,
 					  &priv->flags)) {
 			skb_queue_tail(&priv->tx_skbs, skb);
@@ -1005,24 +1001,29 @@ static netdev_tx_t enetc_start_xmit(struct sk_buff *skb,
 
 netdev_tx_t enetc_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
+	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 	u8 udp, msgtype, twostep;
 	u16 offset1, offset2;
 
-	/* Mark tx timestamp type on skb->cb[0] if requires */
+	/* Mark tx timestamp type on enetc_cb->flag if requires */
 	if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
-	    (priv->active_offloads & ENETC_F_TX_TSTAMP_MASK)) {
-		skb->cb[0] = priv->active_offloads & ENETC_F_TX_TSTAMP_MASK;
-	} else {
-		skb->cb[0] = 0;
-	}
+	    (priv->active_offloads & ENETC_F_TX_TSTAMP_MASK))
+		enetc_cb->flag = priv->active_offloads & ENETC_F_TX_TSTAMP_MASK;
+	else
+		enetc_cb->flag = 0;
 
 	/* Fall back to two-step timestamp if not one-step Sync packet */
-	if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
+	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
 		if (enetc_ptp_parse(skb, &udp, &msgtype, &twostep,
 				    &offset1, &offset2) ||
-		    msgtype != PTP_MSGTYPE_SYNC || twostep != 0)
-			skb->cb[0] = ENETC_F_TX_TSTAMP;
+		    msgtype != PTP_MSGTYPE_SYNC || twostep != 0) {
+			enetc_cb->flag = ENETC_F_TX_TSTAMP;
+		} else {
+			enetc_cb->udp = !!udp;
+			enetc_cb->correction_off = offset1;
+			enetc_cb->origin_tstamp_off = offset2;
+		}
 	}
 
 	return enetc_start_xmit(skb, ndev);
@@ -1214,7 +1215,9 @@ static bool enetc_clean_tx_ring(struct enetc_bdr *tx_ring, int napi_budget)
 		if (xdp_frame) {
 			xdp_return_frame(xdp_frame);
 		} else if (skb) {
-			if (unlikely(skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)) {
+			struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
+
+			if (unlikely(enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)) {
 				/* Start work to release lock for next one-step
 				 * timestamping packet. And send one skb in
 				 * tx_skbs queue if has.
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index 62e8ee4d2f04..ce3fed95091b 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -54,6 +54,15 @@ struct enetc_tx_swbd {
 	u8 qbv_en:1;
 };
 
+struct enetc_skb_cb {
+	u8 flag;
+	bool udp;
+	u16 correction_off;
+	u16 origin_tstamp_off;
+};
+
+#define ENETC_SKB_CB(skb) ((struct enetc_skb_cb *)((skb)->cb))
+
 struct enetc_lso_t {
 	bool	ipv6;
 	bool	tcp;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 10/14] net: enetc: Add enetc_update_ptp_sync_msg() to process PTP sync packet
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (8 preceding siblings ...)
  2025-07-16  7:31 ` [PATCH v2 net-next 09/14] net: enetc: save the parsed information of PTP packet to skb->cb Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 20:49   ` Frank Li
  2025-07-16  7:31 ` [PATCH v2 net-next 11/14] net: enetc: remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check Wei Fang
                   ` (3 subsequent siblings)
  13 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

Currently, the PTP Sync packets are processed in enetc_map_tx_buffs(),
which makes the function too long and not concise enough. Secondly,
for the upcoming ENETC v4 one-step support, some appropriate changes
are also needed. Therefore, enetc_update_ptp_sync_msg() is extracted
from enetc_map_tx_buffs() as a helper function to process the PTP Sync
packets.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 drivers/net/ethernet/freescale/enetc/enetc.c  | 129 ++++++++++--------
 .../net/ethernet/freescale/enetc/enetc_hw.h   |   1 +
 2 files changed, 71 insertions(+), 59 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index c1373163a096..ef002ed2fdb9 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -221,12 +221,79 @@ static void enetc_unwind_tx_frame(struct enetc_bdr *tx_ring, int count, int i)
 	}
 }
 
+static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
+				     struct sk_buff *skb)
+{
+	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
+	u16 tstamp_off = enetc_cb->origin_tstamp_off;
+	u16 corr_off = enetc_cb->correction_off;
+	struct enetc_si *si = priv->si;
+	struct enetc_hw *hw = &si->hw;
+	__be32 new_sec_l, new_nsec;
+	__be16 new_sec_h;
+	u32 lo, hi, nsec;
+	u8 *data;
+	u64 sec;
+	u32 val;
+
+	lo = enetc_rd_hot(hw, ENETC_SICTR0);
+	hi = enetc_rd_hot(hw, ENETC_SICTR1);
+	sec = (u64)hi << 32 | lo;
+	nsec = do_div(sec, 1000000000);
+
+	/* Update originTimestamp field of Sync packet
+	 * - 48 bits seconds field
+	 * - 32 bits nanseconds field
+	 *
+	 * In addition, the UDP checksum needs to be updated
+	 * by software after updating originTimestamp field,
+	 * otherwise the hardware will calculate the wrong
+	 * checksum when updating the correction field and
+	 * update it to the packet.
+	 */
+
+	data = skb_mac_header(skb);
+	new_sec_h = htons((sec >> 32) & 0xffff);
+	new_sec_l = htonl(sec & 0xffffffff);
+	new_nsec = htonl(nsec);
+	if (enetc_cb->udp) {
+		struct udphdr *uh = udp_hdr(skb);
+		__be32 old_sec_l, old_nsec;
+		__be16 old_sec_h;
+
+		old_sec_h = *(__be16 *)(data + tstamp_off);
+		inet_proto_csum_replace2(&uh->check, skb, old_sec_h,
+					 new_sec_h, false);
+
+		old_sec_l = *(__be32 *)(data + tstamp_off + 2);
+		inet_proto_csum_replace4(&uh->check, skb, old_sec_l,
+					 new_sec_l, false);
+
+		old_nsec = *(__be32 *)(data + tstamp_off + 6);
+		inet_proto_csum_replace4(&uh->check, skb, old_nsec,
+					 new_nsec, false);
+	}
+
+	*(__be16 *)(data + tstamp_off) = new_sec_h;
+	*(__be32 *)(data + tstamp_off + 2) = new_sec_l;
+	*(__be32 *)(data + tstamp_off + 6) = new_nsec;
+
+	/* Configure single-step register */
+	val = ENETC_PM0_SINGLE_STEP_EN;
+	val |= ENETC_SET_SINGLE_STEP_OFFSET(corr_off);
+	if (enetc_cb->udp)
+		val |= ENETC_PM0_SINGLE_STEP_CH;
+
+	enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP, val);
+
+	return lo & ENETC_TXBD_TSTAMP;
+}
+
 static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 {
 	bool do_vlan, do_onestep_tstamp = false, do_twostep_tstamp = false;
 	struct enetc_ndev_priv *priv = netdev_priv(tx_ring->ndev);
 	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
-	struct enetc_hw *hw = &priv->si->hw;
 	struct enetc_tx_swbd *tx_swbd;
 	int len = skb_headlen(skb);
 	union enetc_tx_bd temp_bd;
@@ -326,67 +393,11 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 		}
 
 		if (do_onestep_tstamp) {
-			u16 tstamp_off = enetc_cb->origin_tstamp_off;
-			u16 corr_off = enetc_cb->correction_off;
-			__be32 new_sec_l, new_nsec;
-			u32 lo, hi, nsec, val;
-			__be16 new_sec_h;
-			u8 *data;
-			u64 sec;
-
-			lo = enetc_rd_hot(hw, ENETC_SICTR0);
-			hi = enetc_rd_hot(hw, ENETC_SICTR1);
-			sec = (u64)hi << 32 | lo;
-			nsec = do_div(sec, 1000000000);
+			u32 tstamp = enetc_update_ptp_sync_msg(priv, skb);
 
 			/* Configure extension BD */
-			temp_bd.ext.tstamp = cpu_to_le32(lo & 0x3fffffff);
+			temp_bd.ext.tstamp = cpu_to_le32(tstamp);
 			e_flags |= ENETC_TXBD_E_FLAGS_ONE_STEP_PTP;
-
-			/* Update originTimestamp field of Sync packet
-			 * - 48 bits seconds field
-			 * - 32 bits nanseconds field
-			 *
-			 * In addition, the UDP checksum needs to be updated
-			 * by software after updating originTimestamp field,
-			 * otherwise the hardware will calculate the wrong
-			 * checksum when updating the correction field and
-			 * update it to the packet.
-			 */
-			data = skb_mac_header(skb);
-			new_sec_h = htons((sec >> 32) & 0xffff);
-			new_sec_l = htonl(sec & 0xffffffff);
-			new_nsec = htonl(nsec);
-			if (enetc_cb->udp) {
-				struct udphdr *uh = udp_hdr(skb);
-				__be32 old_sec_l, old_nsec;
-				__be16 old_sec_h;
-
-				old_sec_h = *(__be16 *)(data + tstamp_off);
-				inet_proto_csum_replace2(&uh->check, skb, old_sec_h,
-							 new_sec_h, false);
-
-				old_sec_l = *(__be32 *)(data + tstamp_off + 2);
-				inet_proto_csum_replace4(&uh->check, skb, old_sec_l,
-							 new_sec_l, false);
-
-				old_nsec = *(__be32 *)(data + tstamp_off + 6);
-				inet_proto_csum_replace4(&uh->check, skb, old_nsec,
-							 new_nsec, false);
-			}
-
-			*(__be16 *)(data + tstamp_off) = new_sec_h;
-+			*(__be32 *)(data + tstamp_off + 2) = new_sec_l;
-+			*(__be32 *)(data + tstamp_off + 6) = new_nsec;
-
-			/* Configure single-step register */
-			val = ENETC_PM0_SINGLE_STEP_EN;
-			val |= ENETC_SET_SINGLE_STEP_OFFSET(corr_off);
-			if (enetc_cb->udp)
-				val |= ENETC_PM0_SINGLE_STEP_CH;
-
-			enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP,
-					  val);
 		} else if (do_twostep_tstamp) {
 			skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
 			e_flags |= ENETC_TXBD_E_FLAGS_TWO_STEP_PTP;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 73763e8f4879..377c96325814 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -614,6 +614,7 @@ enum enetc_txbd_flags {
 #define ENETC_TXBD_STATS_WIN	BIT(7)
 #define ENETC_TXBD_TXSTART_MASK GENMASK(24, 0)
 #define ENETC_TXBD_FLAGS_OFFSET 24
+#define ENETC_TXBD_TSTAMP	GENMASK(29, 0)
 
 static inline __le32 enetc_txbd_set_tx_start(u64 tx_start, u8 flags)
 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 11/14] net: enetc: remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (9 preceding siblings ...)
  2025-07-16  7:31 ` [PATCH v2 net-next 10/14] net: enetc: Add enetc_update_ptp_sync_msg() to process PTP sync packet Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 20:50   ` Frank Li
  2025-07-16  7:31 ` [PATCH v2 net-next 12/14] net: enetc: add PTP synchronization support for ENETC v4 Wei Fang
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

The ENETC_F_RX_TSTAMP flag of priv->active_offloads can only be set when
CONFIG_FSL_ENETC_PTP_CLOCK is enabled. Similarly, rx_ring->ext_en can
only be set when CONFIG_FSL_ENETC_PTP_CLOCK is enabled as well. So it is
safe to remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 drivers/net/ethernet/freescale/enetc/enetc.c | 3 +--
 drivers/net/ethernet/freescale/enetc/enetc.h | 4 ++--
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index ef002ed2fdb9..4325eb3d9481 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -1411,8 +1411,7 @@ static void enetc_get_offloads(struct enetc_bdr *rx_ring,
 		__vlan_hwaccel_put_tag(skb, tpid, le16_to_cpu(rxbd->r.vlan_opt));
 	}
 
-	if (IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK) &&
-	    (priv->active_offloads & ENETC_F_RX_TSTAMP))
+	if (priv->active_offloads & ENETC_F_RX_TSTAMP)
 		enetc_get_rx_tstamp(rx_ring->ndev, rxbd, skb);
 }
 
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index ce3fed95091b..c65aa7b88122 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -226,7 +226,7 @@ static inline union enetc_rx_bd *enetc_rxbd(struct enetc_bdr *rx_ring, int i)
 {
 	int hw_idx = i;
 
-	if (IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK) && rx_ring->ext_en)
+	if (rx_ring->ext_en)
 		hw_idx = 2 * i;
 
 	return &(((union enetc_rx_bd *)rx_ring->bd_base)[hw_idx]);
@@ -240,7 +240,7 @@ static inline void enetc_rxbd_next(struct enetc_bdr *rx_ring,
 
 	new_rxbd++;
 
-	if (IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK) && rx_ring->ext_en)
+	if (rx_ring->ext_en)
 		new_rxbd++;
 
 	if (unlikely(++new_index == rx_ring->bd_count)) {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 12/14] net: enetc: add PTP synchronization support for ENETC v4
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (10 preceding siblings ...)
  2025-07-16  7:31 ` [PATCH v2 net-next 11/14] net: enetc: remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 21:01   ` Frank Li
  2025-07-16  7:31 ` [PATCH v2 net-next 13/14] net: enetc: don't update sync packet checksum if checksum offload is used Wei Fang
  2025-07-16  7:31 ` [PATCH v2 14/14] arm64: dts: imx95: Add NETC Timer support Wei Fang
  13 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

Regarding PTP, ENETC v4 has some changes compared to ENETC v1 (LS1028A),
mainly as follows.

1. ENETC v4 uses a different PTP driver, so the way to get phc_index is
different from LS1028A. Therefore, enetc_get_ts_info() has been modified
appropriately to be compatible with ENETC v1 and v4.

2. The hardware of ENETC v4 does not support "dma-coherent", therefore,
to support PTP one-step, the PTP sync packets must be modified before
calling dma_map_single() to map the DMA cache of the packets. Otherwise,
the modification is invalid, the originTimestamp and correction fields
of the sent packets will still be the values before the modification.

3. The PMa_SINGLE_STEP register has changed in ENETC v4, not only the
register offset, but also some register fields. Therefore, two helper
functions are added, enetc_set_one_step_ts() for ENETC v1 and
enetc4_set_one_step_ts() for ENETC v4.

Signed-off-by: Wei Fang <wei.fang@nxp.com>

---
v2 changes:
1. Move the definition of enetc_ptp_clock_is_enabled() to resolve build
errors.
2. Add parsing of "nxp,netc-timer" property to get PCIe device of NETC
Timer.
---
 drivers/net/ethernet/freescale/enetc/enetc.c  | 55 +++++++----
 drivers/net/ethernet/freescale/enetc/enetc.h  |  8 ++
 .../net/ethernet/freescale/enetc/enetc4_hw.h  |  6 ++
 .../net/ethernet/freescale/enetc/enetc4_pf.c  |  3 +
 .../ethernet/freescale/enetc/enetc_ethtool.c  | 92 ++++++++++++++++---
 5 files changed, 135 insertions(+), 29 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 4325eb3d9481..6e04dd825a95 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -221,6 +221,31 @@ static void enetc_unwind_tx_frame(struct enetc_bdr *tx_ring, int count, int i)
 	}
 }
 
+static void enetc_set_one_step_ts(struct enetc_si *si, bool udp, int offset)
+{
+	u32 val = ENETC_PM0_SINGLE_STEP_EN;
+
+	val |= ENETC_SET_SINGLE_STEP_OFFSET(offset);
+	if (udp)
+		val |= ENETC_PM0_SINGLE_STEP_CH;
+
+	/* the "Correction" field of a packet is updated based on the
+	 * current time and the timestamp provided
+	 */
+	enetc_port_mac_wr(si, ENETC_PM0_SINGLE_STEP, val);
+}
+
+static void enetc4_set_one_step_ts(struct enetc_si *si, bool udp, int offset)
+{
+	u32 val = PM_SINGLE_STEP_EN;
+
+	val |= PM_SINGLE_STEP_OFFSET_SET(offset);
+	if (udp)
+		val |= PM_SINGLE_STEP_CH;
+
+	enetc_port_mac_wr(si, ENETC4_PM_SINGLE_STEP(0), val);
+}
+
 static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
 				     struct sk_buff *skb)
 {
@@ -234,7 +259,6 @@ static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
 	u32 lo, hi, nsec;
 	u8 *data;
 	u64 sec;
-	u32 val;
 
 	lo = enetc_rd_hot(hw, ENETC_SICTR0);
 	hi = enetc_rd_hot(hw, ENETC_SICTR1);
@@ -279,12 +303,10 @@ static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
 	*(__be32 *)(data + tstamp_off + 6) = new_nsec;
 
 	/* Configure single-step register */
-	val = ENETC_PM0_SINGLE_STEP_EN;
-	val |= ENETC_SET_SINGLE_STEP_OFFSET(corr_off);
-	if (enetc_cb->udp)
-		val |= ENETC_PM0_SINGLE_STEP_CH;
-
-	enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP, val);
+	if (is_enetc_rev1(si))
+		enetc_set_one_step_ts(si, enetc_cb->udp, corr_off);
+	else
+		enetc4_set_one_step_ts(si, enetc_cb->udp, corr_off);
 
 	return lo & ENETC_TXBD_TSTAMP;
 }
@@ -303,6 +325,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 	unsigned int f;
 	dma_addr_t dma;
 	u8 flags = 0;
+	u32 tstamp;
 
 	enetc_clear_tx_bd(&temp_bd);
 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
@@ -327,6 +350,13 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 		}
 	}
 
+	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
+		do_onestep_tstamp = true;
+		tstamp = enetc_update_ptp_sync_msg(priv, skb);
+	} else if (enetc_cb->flag & ENETC_F_TX_TSTAMP) {
+		do_twostep_tstamp = true;
+	}
+
 	i = tx_ring->next_to_use;
 	txbd = ENETC_TXBD(*tx_ring, i);
 	prefetchw(txbd);
@@ -346,11 +376,6 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 	count++;
 
 	do_vlan = skb_vlan_tag_present(skb);
-	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)
-		do_onestep_tstamp = true;
-	else if (enetc_cb->flag & ENETC_F_TX_TSTAMP)
-		do_twostep_tstamp = true;
-
 	tx_swbd->do_twostep_tstamp = do_twostep_tstamp;
 	tx_swbd->qbv_en = !!(priv->active_offloads & ENETC_F_QBV);
 	tx_swbd->check_wb = tx_swbd->do_twostep_tstamp || tx_swbd->qbv_en;
@@ -393,8 +418,6 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 		}
 
 		if (do_onestep_tstamp) {
-			u32 tstamp = enetc_update_ptp_sync_msg(priv, skb);
-
 			/* Configure extension BD */
 			temp_bd.ext.tstamp = cpu_to_le32(tstamp);
 			e_flags |= ENETC_TXBD_E_FLAGS_ONE_STEP_PTP;
@@ -3314,7 +3337,7 @@ int enetc_hwtstamp_set(struct net_device *ndev,
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 	int err, new_offloads = priv->active_offloads;
 
-	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
+	if (!enetc_ptp_clock_is_enabled(priv->si))
 		return -EOPNOTSUPP;
 
 	switch (config->tx_type) {
@@ -3364,7 +3387,7 @@ int enetc_hwtstamp_get(struct net_device *ndev,
 {
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 
-	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
+	if (!enetc_ptp_clock_is_enabled(priv->si))
 		return -EOPNOTSUPP;
 
 	if (priv->active_offloads & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index c65aa7b88122..6bacd851358c 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -598,6 +598,14 @@ static inline void enetc_cbd_free_data_mem(struct enetc_si *si, int size,
 void enetc_reset_ptcmsdur(struct enetc_hw *hw);
 void enetc_set_ptcmsdur(struct enetc_hw *hw, u32 *queue_max_sdu);
 
+static inline bool enetc_ptp_clock_is_enabled(struct enetc_si *si)
+{
+	if (is_enetc_rev1(si))
+		return IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK);
+
+	return IS_ENABLED(CONFIG_PTP_1588_CLOCK_NETC);
+}
+
 #ifdef CONFIG_FSL_ENETC_QOS
 int enetc_qos_query_caps(struct net_device *ndev, void *type_data);
 int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
index aa25b445d301..a8113c9057eb 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
@@ -171,6 +171,12 @@
 /* Port MAC 0/1 Pause Quanta Threshold Register */
 #define ENETC4_PM_PAUSE_THRESH(mac)	(0x5064 + (mac) * 0x400)
 
+#define ENETC4_PM_SINGLE_STEP(mac)	(0x50c0 + (mac) * 0x400)
+#define  PM_SINGLE_STEP_CH		BIT(6)
+#define  PM_SINGLE_STEP_OFFSET		GENMASK(15, 7)
+#define   PM_SINGLE_STEP_OFFSET_SET(o)  FIELD_PREP(PM_SINGLE_STEP_OFFSET, o)
+#define  PM_SINGLE_STEP_EN		BIT(31)
+
 /* Port MAC 0 Interface Mode Control Register */
 #define ENETC4_PM_IF_MODE(mac)		(0x5300 + (mac) * 0x400)
 #define  PM_IF_MODE_IFMODE		GENMASK(2, 0)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index b3dc1afeefd1..107f59169e67 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -569,6 +569,9 @@ static const struct net_device_ops enetc4_ndev_ops = {
 	.ndo_set_features	= enetc4_pf_set_features,
 	.ndo_vlan_rx_add_vid	= enetc_vlan_rx_add_vid,
 	.ndo_vlan_rx_kill_vid	= enetc_vlan_rx_del_vid,
+	.ndo_eth_ioctl		= enetc_ioctl,
+	.ndo_hwtstamp_get	= enetc_hwtstamp_get,
+	.ndo_hwtstamp_set	= enetc_hwtstamp_set,
 };
 
 static struct phylink_pcs *
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
index 961e76cd8489..404dcb102b47 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
@@ -2,8 +2,11 @@
 /* Copyright 2017-2019 NXP */
 
 #include <linux/ethtool_netlink.h>
+#include <linux/fsl/netc_global.h>
 #include <linux/net_tstamp.h>
 #include <linux/module.h>
+#include <linux/of.h>
+
 #include "enetc.h"
 
 static const u32 enetc_si_regs[] = {
@@ -877,23 +880,49 @@ static int enetc_set_coalesce(struct net_device *ndev,
 	return 0;
 }
 
-static int enetc_get_ts_info(struct net_device *ndev,
-			     struct kernel_ethtool_ts_info *info)
+static struct pci_dev *enetc4_get_default_timer_pdev(struct enetc_si *si)
 {
-	struct enetc_ndev_priv *priv = netdev_priv(ndev);
-	int *phc_idx;
-
-	phc_idx = symbol_get(enetc_phc_index);
-	if (phc_idx) {
-		info->phc_index = *phc_idx;
-		symbol_put(enetc_phc_index);
+	struct pci_bus *bus = si->pdev->bus;
+	int domain = pci_domain_nr(bus);
+	int bus_num = bus->number;
+	int devfn;
+
+	switch (si->revision) {
+	case ENETC_REV_4_1:
+		devfn = PCI_DEVFN(24, 0);
+		break;
+	default:
+		return NULL;
 	}
 
-	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK)) {
-		info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE;
+	return pci_dev_get(pci_get_domain_bus_and_slot(domain, bus_num, devfn));
+}
 
-		return 0;
-	}
+static struct pci_dev *enetc4_get_timer_pdev(struct enetc_si *si)
+{
+	struct device_node *np = si->pdev->dev.of_node;
+	struct fwnode_handle *timer_fwnode;
+	struct device_node *timer_np;
+
+	if (!np)
+		return enetc4_get_default_timer_pdev(si);
+
+	timer_np = of_parse_phandle(np, "nxp,netc-timer", 0);
+	if (!timer_np)
+		return enetc4_get_default_timer_pdev(si);
+
+	timer_fwnode = of_fwnode_handle(timer_np);
+	of_node_put(timer_np);
+	if (!timer_fwnode)
+		return NULL;
+
+	return pci_dev_get(to_pci_dev(timer_fwnode->dev));
+}
+
+static void enetc_get_ts_generic_info(struct net_device *ndev,
+				      struct kernel_ethtool_ts_info *info)
+{
+	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 
 	info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
 				SOF_TIMESTAMPING_RX_HARDWARE |
@@ -908,6 +937,42 @@ static int enetc_get_ts_info(struct net_device *ndev,
 
 	info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
 			   (1 << HWTSTAMP_FILTER_ALL);
+}
+
+static int enetc_get_ts_info(struct net_device *ndev,
+			     struct kernel_ethtool_ts_info *info)
+{
+	struct enetc_ndev_priv *priv = netdev_priv(ndev);
+	struct enetc_si *si = priv->si;
+	struct pci_dev *timer_pdev;
+	int *phc_idx;
+
+	if (!enetc_ptp_clock_is_enabled(si))
+		goto timestamp_tx_sw;
+
+	if (is_enetc_rev1(si)) {
+		phc_idx = symbol_get(enetc_phc_index);
+		if (phc_idx) {
+			info->phc_index = *phc_idx;
+			symbol_put(enetc_phc_index);
+		}
+	} else {
+		timer_pdev = enetc4_get_timer_pdev(si);
+		if (!timer_pdev)
+			goto timestamp_tx_sw;
+
+		info->phc_index = netc_timer_get_phc_index(timer_pdev);
+		pci_dev_put(timer_pdev);
+		if (info->phc_index < 0)
+			goto timestamp_tx_sw;
+	}
+
+	enetc_get_ts_generic_info(ndev, info);
+
+	return 0;
+
+timestamp_tx_sw:
+	info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE;
 
 	return 0;
 }
@@ -1296,6 +1361,7 @@ const struct ethtool_ops enetc4_pf_ethtool_ops = {
 	.get_rxfh = enetc_get_rxfh,
 	.set_rxfh = enetc_set_rxfh,
 	.get_rxfh_fields = enetc_get_rxfh_fields,
+	.get_ts_info = enetc_get_ts_info,
 };
 
 void enetc_set_ethtool_ops(struct net_device *ndev)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 net-next 13/14] net: enetc: don't update sync packet checksum if checksum offload is used
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (11 preceding siblings ...)
  2025-07-16  7:31 ` [PATCH v2 net-next 12/14] net: enetc: add PTP synchronization support for ENETC v4 Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 21:03   ` Frank Li
  2025-07-16  7:31 ` [PATCH v2 14/14] arm64: dts: imx95: Add NETC Timer support Wei Fang
  13 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

For ENETC v4, the hardware has the capability to support Tx checksum
offload. so the enetc driver does not need to update the UDP checksum
of PTP sync packets if Tx checksum offload is enabled.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 drivers/net/ethernet/freescale/enetc/enetc.c | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 6e04dd825a95..cf72d50246a9 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -247,7 +247,7 @@ static void enetc4_set_one_step_ts(struct enetc_si *si, bool udp, int offset)
 }
 
 static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
-				     struct sk_buff *skb)
+				     struct sk_buff *skb, bool csum_offload)
 {
 	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
 	u16 tstamp_off = enetc_cb->origin_tstamp_off;
@@ -269,18 +269,17 @@ static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
 	 * - 48 bits seconds field
 	 * - 32 bits nanseconds field
 	 *
-	 * In addition, the UDP checksum needs to be updated
-	 * by software after updating originTimestamp field,
-	 * otherwise the hardware will calculate the wrong
-	 * checksum when updating the correction field and
-	 * update it to the packet.
+	 * In addition, if csum_offload is false, the UDP checksum needs
+	 * to be updated by software after updating originTimestamp field,
+	 * otherwise the hardware will calculate the wrong checksum when
+	 * updating the correction field and update it to the packet.
 	 */
 
 	data = skb_mac_header(skb);
 	new_sec_h = htons((sec >> 32) & 0xffff);
 	new_sec_l = htonl(sec & 0xffffffff);
 	new_nsec = htonl(nsec);
-	if (enetc_cb->udp) {
+	if (enetc_cb->udp && !csum_offload) {
 		struct udphdr *uh = udp_hdr(skb);
 		__be32 old_sec_l, old_nsec;
 		__be16 old_sec_h;
@@ -319,6 +318,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 	struct enetc_tx_swbd *tx_swbd;
 	int len = skb_headlen(skb);
 	union enetc_tx_bd temp_bd;
+	bool csum_offload = false;
 	union enetc_tx_bd *txbd;
 	int i, count = 0;
 	skb_frag_t *frag;
@@ -345,6 +345,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 				temp_bd.l4_aux = FIELD_PREP(ENETC_TX_BD_L4T,
 							    ENETC_TXBD_L4T_UDP);
 			flags |= ENETC_TXBD_FLAGS_CSUM_LSO | ENETC_TXBD_FLAGS_L4CS;
+			csum_offload = true;
 		} else if (skb_checksum_help(skb)) {
 			return 0;
 		}
@@ -352,7 +353,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
 
 	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
 		do_onestep_tstamp = true;
-		tstamp = enetc_update_ptp_sync_msg(priv, skb);
+		tstamp = enetc_update_ptp_sync_msg(priv, skb, csum_offload);
 	} else if (enetc_cb->flag & ENETC_F_TX_TSTAMP) {
 		do_twostep_tstamp = true;
 	}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 14/14] arm64: dts: imx95: Add NETC Timer support
  2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (12 preceding siblings ...)
  2025-07-16  7:31 ` [PATCH v2 net-next 13/14] net: enetc: don't update sync packet checksum if checksum offload is used Wei Fang
@ 2025-07-16  7:31 ` Wei Fang
  2025-07-16 21:04   ` Frank Li
  13 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-16  7:31 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam
  Cc: fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

Enable NETC Timer to provide precise periodic pulse, time capture on
external pulse and PTP synchronization support.

Signed-off-by: Wei Fang <wei.fang@nxp.com>

---
v2 changes:
new patch
---
 arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts | 4 ++++
 arch/arm64/boot/dts/freescale/imx95.dtsi          | 1 +
 2 files changed, 5 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
index 6886ea766655..9a119d788c1e 100644
--- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
@@ -418,6 +418,10 @@ ethphy0: ethernet-phy@1 {
 	};
 };
 
+&netc_timer {
+	status = "okay";
+};
+
 &pcie0 {
 	pinctrl-0 = <&pinctrl_pcie0>;
 	pinctrl-names = "default";
diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi
index 632631a29112..04be9fb8cb31 100644
--- a/arch/arm64/boot/dts/freescale/imx95.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx95.dtsi
@@ -1893,6 +1893,7 @@ enetc_port2: ethernet@10,0 {
 				};
 
 				netc_timer: ethernet@18,0 {
+					compatible = "pci1131,ee02";
 					reg = <0x00c000 0 0 0 0>;
 					status = "disabled";
 				};
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-16  7:30 ` [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
@ 2025-07-16 19:19   ` Frank Li
  2025-07-17  7:40   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 64+ messages in thread
From: Frank Li @ 2025-07-16 19:19 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:30:58PM +0800, Wei Fang wrote:
> NXP NETC (Ethernet Controller) is a multi-function PCIe Root Complex
> Integrated Endpoint (RCiEP), the Timer is one of its functions which
> provides current time with nanosecond resolution, precise periodic
> pulse, pulse on timeout (alarm), and time capture on external pulse
> support. And also supports time synchronization as required for IEEE
> 1588 and IEEE 802.1AS-2020. So add device tree binding doc for the
> PTP clock based on NETC Timer.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>

Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
> ---
> v2 changes:
> 1. Refine the subject and the commit message
> 2. Remove "nxp,pps-channel"
> 3. Add description to "clocks" and "clock-names"
> ---
>  .../devicetree/bindings/ptp/nxp,ptp-netc.yaml | 67 +++++++++++++++++++
>  1 file changed, 67 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
>
> diff --git a/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml b/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
> new file mode 100644
> index 000000000000..6af1899d904f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
> @@ -0,0 +1,67 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/ptp/nxp,ptp-netc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: NXP NETC Timer PTP clock
> +
> +description:
> +  NETC Timer provides current time with nanosecond resolution, precise
> +  periodic pulse, pulse on timeout (alarm), and time capture on external
> +  pulse support. And it supports time synchronization as required for
> +  IEEE 1588 and IEEE 802.1AS-2020.
> +
> +maintainers:
> +  - Wei Fang <wei.fang@nxp.com>
> +  - Clark Wang <xiaoning.wang@nxp.com>
> +
> +properties:
> +  compatible:
> +    enum:
> +      - pci1131,ee02
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 1
> +    description:
> +      The reference clock of NETC Timer, if not present, indicates that
> +      the system clock of NETC IP is selected as the reference clock.
> +
> +  clock-names:
> +    description:
> +      NETC Timer has three reference clock sources, set TMR_CTRL[CK_SEL]
> +      by parsing clock name to select one of them as the reference clock.
> +      The "system" means that the system clock of NETC IP is used as the
> +      reference clock.
> +      The "ccm_timer" means another clock from CCM as the reference clock.
> +      The "ext_1588" means the reference clock comes from external IO pins.
> +    enum:
> +      - system
> +      - ccm_timer
> +      - ext_1588
> +
> +required:
> +  - compatible
> +  - reg
> +
> +allOf:
> +  - $ref: /schemas/pci/pci-device.yaml
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    pcie {
> +        #address-cells = <3>;
> +        #size-cells = <2>;
> +
> +        ethernet@18,0 {
> +            compatible = "pci1131,ee02";
> +            reg = <0x00c000 0 0 0 0>;
> +            clocks = <&scmi_clk 18>;
> +            clock-names = "ccm_timer";
> +        };
> +    };
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-16  7:30 ` [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property Wei Fang
@ 2025-07-16 19:28   ` Frank Li
  2025-07-17  3:23     ` Wei Fang
  2025-07-17  7:42   ` Krzysztof Kozlowski
  1 sibling, 1 reply; 64+ messages in thread
From: Frank Li @ 2025-07-16 19:28 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:30:59PM +0800, Wei Fang wrote:
> NETC is a multi-function PCIe Root Complex Integrated Endpoint (RCiEP)
> that contains multiple PCIe functions, such as ENETC and Timer. Timer
> provides PTP time synchronization functionality and ENETC provides the
> NIC functionality.
>
> For some platforms, such as i.MX95, it has only one timer instance, so
> the binding relationship between Timer and ENETC is fixed. But for some
> platforms, such as i.MX943, it has 3 Timer instances, by setting the
> EaTBCR registers of the IERB module, we can specify any Timer instance
> to be bound to the ENETC instance.
>
> Therefore, add "nxp,netc-timer" property to bind ENETC instance to a
> specified Timer instance so that ENETC can support PTP synchronization
> through Timer.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
>
> ---
> v2 changes:
> new patch
> ---
>  .../devicetree/bindings/net/fsl,enetc.yaml    | 23 +++++++++++++++++++
>  1 file changed, 23 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> index ca70f0050171..ae05f2982653 100644
> --- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> +++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> @@ -44,6 +44,13 @@ properties:
>      unevaluatedProperties: false
>      description: Optional child node for ENETC instance, otherwise use NETC EMDIO.
>
> +  nxp,netc-timer:
> +    $ref: /schemas/types.yaml#/definitions/phandle
> +    description:
> +      Specifies a reference to a node representing a NETC Timer device,
> +      which provides time synchronization as required for IEEE 1588 and
> +      IEEE 802.1AS-2020.
> +

I think it is quite common. add ptp-timer ethernet-controller.yaml?

Frank

>  required:
>    - compatible
>    - reg
> @@ -62,6 +69,7 @@ allOf:
>        properties:
>          clocks: false
>          clock-names: false
> +        nxp,netc-timer: false
>
>  unevaluatedProperties: false
>
> @@ -86,3 +94,18 @@ examples:
>              };
>          };
>      };
> +  - |
> +    pcie {
> +      #address-cells = <3>;
> +      #size-cells = <2>;
> +
> +      ethernet@0,0 {
> +          compatible = "pci1131,e101";
> +          reg = <0x000000 0 0 0 0>;
> +          clocks = <&scmi_clk 102>;
> +          clock-names = "ref";
> +          nxp,netc-timer = <&netc_timer>;
> +          phy-handle = <&ethphy0>;
> +          phy-mode = "rgmii-id";
> +      };
> +    };
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 03/14] ptp: netc: add NETC Timer PTP driver support
  2025-07-16  7:31 ` [PATCH v2 net-next 03/14] ptp: netc: add NETC Timer PTP driver support Wei Fang
@ 2025-07-16 19:58   ` Frank Li
  2025-07-17  8:42     ` Wei Fang
  2025-07-23 16:09   ` Vladimir Oltean
  1 sibling, 1 reply; 64+ messages in thread
From: Frank Li @ 2025-07-16 19:58 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:00PM +0800, Wei Fang wrote:
> NETC Timer provides current time with nanosecond resolution, precise
> periodic pulse, pulse on timeout (alarm), and time capture on external
> pulse support. And it supports time synchronization as required for
> IEEE 1588 and IEEE 802.1AS-2020. The enetc v4 driver can implement PTP

New paragraph

Implement PTP ....

Missing feature ... will be added in subsequent patches.

> synchronization through the relevant interfaces provided by the driver.
> Note that the current driver does not support PEROUT, PPS and EXTTS yet,
> and support will be added one by one in subsequent patches.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
>
> ---
> v2 changes:
> 1. Rename netc_timer_get_source_clk() to
>    netc_timer_get_reference_clk_source() and refactor it
> 2. Remove the scaled_ppm check in netc_timer_adjfine()
> 3. Add a comment in netc_timer_cur_time_read()
> 4. Add linux/bitfield.h to fix the build errors
> ---
>  drivers/ptp/Kconfig             |  11 +
>  drivers/ptp/Makefile            |   1 +
>  drivers/ptp/ptp_netc.c          | 568 ++++++++++++++++++++++++++++++++
>  include/linux/fsl/netc_global.h |  12 +-
>  4 files changed, 591 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/ptp/ptp_netc.c
>
> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
> index 204278eb215e..3e005b992aef 100644
> --- a/drivers/ptp/Kconfig
> +++ b/drivers/ptp/Kconfig
> @@ -252,4 +252,15 @@ config PTP_S390
>  	  driver provides the raw clock value without the delta to
>  	  userspace. That way userspace programs like chrony could steer
>  	  the kernel clock.
> +
> +config PTP_1588_CLOCK_NETC
> +	bool "NXP NETC Timer PTP Driver"
> +	depends on PTP_1588_CLOCK=y
> +	depends on PCI_MSI
> +	help
> +	  This driver adds support for using the NXP NETC Timer as a PTP
> +	  clock. This clock is used by ENETC MAC or NETC Switch for PTP
> +	  synchronization. It also supports periodic output signal (e.g.
> +	  PPS) and external trigger timestamping.
> +
>  endmenu
> diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
> index 25f846fe48c9..d48fe4009fa4 100644
> --- a/drivers/ptp/Makefile
> +++ b/drivers/ptp/Makefile
> @@ -23,3 +23,4 @@ obj-$(CONFIG_PTP_1588_CLOCK_VMW)	+= ptp_vmw.o
>  obj-$(CONFIG_PTP_1588_CLOCK_OCP)	+= ptp_ocp.o
>  obj-$(CONFIG_PTP_DFL_TOD)		+= ptp_dfl_tod.o
>  obj-$(CONFIG_PTP_S390)			+= ptp_s390.o
> +obj-$(CONFIG_PTP_1588_CLOCK_NETC)	+= ptp_netc.o
> diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> new file mode 100644
> index 000000000000..82cb1e6a0fe9
> --- /dev/null
> +++ b/drivers/ptp/ptp_netc.c
> @@ -0,0 +1,568 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
> +/*
> + * NXP NETC Timer driver
> + * Copyright 2025 NXP
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/fsl/netc_global.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/ptp_clock_kernel.h>
> +
> +#define NETC_TMR_PCI_VENDOR		0x1131
> +#define NETC_TMR_PCI_DEVID		0xee02
> +
> +#define NETC_TMR_CTRL			0x0080
> +#define  TMR_CTRL_CK_SEL		GENMASK(1, 0)
> +#define  TMR_CTRL_TE			BIT(2)
> +#define  TMR_COMP_MODE			BIT(15)
> +#define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
> +#define  TMR_CTRL_FS			BIT(28)
> +#define  TMR_ALARM1P			BIT(31)
> +
> +#define NETC_TMR_TEVENT			0x0084
> +#define  TMR_TEVENT_ALM1EN		BIT(16)
> +#define  TMR_TEVENT_ALM2EN		BIT(17)
> +
> +#define NETC_TMR_TEMASK			0x0088
> +#define NETC_TMR_CNT_L			0x0098
> +#define NETC_TMR_CNT_H			0x009c
> +#define NETC_TMR_ADD			0x00a0
> +#define NETC_TMR_PRSC			0x00a8
> +#define NETC_TMR_OFF_L			0x00b0
> +#define NETC_TMR_OFF_H			0x00b4
> +
> +/* i = 0, 1, i indicates the index of TMR_ALARM */
> +#define NETC_TMR_ALARM_L(i)		(0x00b8 + (i) * 8)
> +#define NETC_TMR_ALARM_H(i)		(0x00bc + (i) * 8)
> +
> +#define NETC_TMR_FIPER_CTRL		0x00dc
> +#define  FIPER_CTRL_DIS(i)		(BIT(7) << (i) * 8)
> +#define  FIPER_CTRL_PG(i)		(BIT(6) << (i) * 8)
> +
> +#define NETC_TMR_CUR_TIME_L		0x00f0
> +#define NETC_TMR_CUR_TIME_H		0x00f4
> +
> +#define NETC_TMR_REGS_BAR		0
> +
> +#define NETC_TMR_FIPER_NUM		3
> +#define NETC_TMR_DEFAULT_PRSC		2
> +#define NETC_TMR_DEFAULT_ALARM		GENMASK_ULL(63, 0)
> +
> +/* 1588 timer reference clock source select */
> +#define NETC_TMR_CCM_TIMER1		0 /* enet_timer1_clk_root, from CCM */
> +#define NETC_TMR_SYSTEM_CLK		1 /* enet_clk_root/2, from CCM */
> +#define NETC_TMR_EXT_OSC		2 /* tmr_1588_clk, from IO pins */
> +
> +#define NETC_TMR_SYSCLK_333M		333333333U
> +
> +struct netc_timer {
> +	void __iomem *base;
> +	struct pci_dev *pdev;
> +	spinlock_t lock; /* Prevent concurrent access to registers */
> +
> +	struct clk *src_clk;
> +	struct ptp_clock *clock;
> +	struct ptp_clock_info caps;
> +	int phc_index;
> +	u32 clk_select;
> +	u32 clk_freq;
> +	u32 oclk_prsc;
> +	/* High 32-bit is integer part, low 32-bit is fractional part */
> +	u64 period;
> +
> +	int irq;
> +};
> +
> +#define netc_timer_rd(p, o)		netc_read((p)->base + (o))
> +#define netc_timer_wr(p, o, v)		netc_write((p)->base + (o), v)
> +#define ptp_to_netc_timer(ptp)		container_of((ptp), struct netc_timer, caps)
> +
> +static u64 netc_timer_cnt_read(struct netc_timer *priv)
> +{
> +	u32 tmr_cnt_l, tmr_cnt_h;
> +	u64 ns;
> +
> +	/* The user must read the TMR_CNC_L register first to get
> +	 * correct 64-bit TMR_CNT_H/L counter values.
> +	 */

Need comment about there are snapshot in side chip. So it is safe to
read NETC_TMR_CNT_L then read NETC_TMR_CNT_H, otherwise
NETC_TMR_CNT_L may change, during read NETC_TMR_CNT_H.


> +	tmr_cnt_l = netc_timer_rd(priv, NETC_TMR_CNT_L);
> +	tmr_cnt_h = netc_timer_rd(priv, NETC_TMR_CNT_H);
> +	ns = (((u64)tmr_cnt_h) << 32) | tmr_cnt_l;
> +
> +	return ns;
> +}
> +
> +static void netc_timer_cnt_write(struct netc_timer *priv, u64 ns)
> +{
> +	u32 tmr_cnt_h = upper_32_bits(ns);
> +	u32 tmr_cnt_l = lower_32_bits(ns);
> +
> +	/* The user must write to TMR_CNT_L register first. */
> +	netc_timer_wr(priv, NETC_TMR_CNT_L, tmr_cnt_l);
> +	netc_timer_wr(priv, NETC_TMR_CNT_H, tmr_cnt_h);
> +}
> +
> +static u64 netc_timer_offset_read(struct netc_timer *priv)
> +{
> +	u32 tmr_off_l, tmr_off_h;
> +	u64 offset;
> +
> +	tmr_off_l = netc_timer_rd(priv, NETC_TMR_OFF_L);
> +	tmr_off_h = netc_timer_rd(priv, NETC_TMR_OFF_H);
> +	offset = (((u64)tmr_off_h) << 32) | tmr_off_l;
> +
> +	return offset;
> +}
> +
> +static void netc_timer_offset_write(struct netc_timer *priv, u64 offset)
> +{
> +	u32 tmr_off_h = upper_32_bits(offset);
> +	u32 tmr_off_l = lower_32_bits(offset);
> +
> +	netc_timer_wr(priv, NETC_TMR_OFF_L, tmr_off_l);
> +	netc_timer_wr(priv, NETC_TMR_OFF_H, tmr_off_h);
> +}
> +
> +static u64 netc_timer_cur_time_read(struct netc_timer *priv)
> +{
> +	u32 time_h, time_l;
> +	u64 ns;
> +
> +	/* The user should read NETC_TMR_CUR_TIME_L first to
> +	 * get correct current time.
> +	 */
> +	time_l = netc_timer_rd(priv, NETC_TMR_CUR_TIME_L);
> +	time_h = netc_timer_rd(priv, NETC_TMR_CUR_TIME_H);
> +	ns = (u64)time_h << 32 | time_l;
> +
> +	return ns;
> +}
> +
> +static void netc_timer_alarm_write(struct netc_timer *priv,
> +				   u64 alarm, int index)
> +{
> +	u32 alarm_h = upper_32_bits(alarm);
> +	u32 alarm_l = lower_32_bits(alarm);
> +
> +	netc_timer_wr(priv, NETC_TMR_ALARM_L(index), alarm_l);
> +	netc_timer_wr(priv, NETC_TMR_ALARM_H(index), alarm_h);
> +}
> +
> +static void netc_timer_adjust_period(struct netc_timer *priv, u64 period)
> +{
> +	u32 fractional_period = lower_32_bits(period);
> +	u32 integral_period = upper_32_bits(period);
> +	u32 tmr_ctrl, old_tmr_ctrl;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	old_tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
> +	tmr_ctrl = u32_replace_bits(old_tmr_ctrl, integral_period,
> +				    TMR_CTRL_TCLK_PERIOD);
> +	if (tmr_ctrl != old_tmr_ctrl)
> +		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +
> +	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +}
> +
> +static int netc_timer_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
> +{
> +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> +	u64 new_period;
> +
> +	new_period = adjust_by_scaled_ppm(priv->period, scaled_ppm);
> +	netc_timer_adjust_period(priv, new_period);
> +
> +	return 0;
> +}
> +
> +static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
> +{
> +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> +	u64 tmr_cnt, tmr_off;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	tmr_off = netc_timer_offset_read(priv);
> +	if (delta < 0 && tmr_off < abs(delta)) {
> +		delta += tmr_off;
> +		if (!tmr_off)
> +			netc_timer_offset_write(priv, 0);
> +
> +		tmr_cnt = netc_timer_cnt_read(priv);
> +		tmr_cnt += delta;
> +		netc_timer_cnt_write(priv, tmr_cnt);
> +	} else {
> +		tmr_off += delta;
> +		netc_timer_offset_write(priv, tmr_off);
> +	}
> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int netc_timer_gettimex64(struct ptp_clock_info *ptp,
> +				 struct timespec64 *ts,
> +				 struct ptp_system_timestamp *sts)
> +{
> +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> +	unsigned long flags;
> +	u64 ns;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	ptp_read_system_prets(sts);
> +	ns = netc_timer_cur_time_read(priv);
> +	ptp_read_system_postts(sts);
> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	*ts = ns_to_timespec64(ns);
> +
> +	return 0;
> +}
> +
> +static int netc_timer_settime64(struct ptp_clock_info *ptp,
> +				const struct timespec64 *ts)
> +{
> +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> +	u64 ns = timespec64_to_ns(ts);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +	netc_timer_offset_write(priv, 0);
> +	netc_timer_cnt_write(priv, ns);
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +
> +int netc_timer_get_phc_index(struct pci_dev *timer_pdev)
> +{
> +	struct netc_timer *priv;
> +
> +	if (!timer_pdev)
> +		return -ENODEV;
> +
> +	priv = pci_get_drvdata(timer_pdev);
> +	if (!priv)
> +		return -EINVAL;
> +
> +	return priv->phc_index;
> +}
> +EXPORT_SYMBOL_GPL(netc_timer_get_phc_index);
> +
> +static const struct ptp_clock_info netc_timer_ptp_caps = {
> +	.owner		= THIS_MODULE,
> +	.name		= "NETC Timer PTP clock",
> +	.max_adj	= 500000000,
> +	.n_alarm	= 2,
> +	.n_pins		= 0,
> +	.adjfine	= netc_timer_adjfine,
> +	.adjtime	= netc_timer_adjtime,
> +	.gettimex64	= netc_timer_gettimex64,
> +	.settime64	= netc_timer_settime64,
> +};
> +
> +static void netc_timer_init(struct netc_timer *priv)
> +{
> +	u32 tmr_emask = TMR_TEVENT_ALM1EN | TMR_TEVENT_ALM2EN;
> +	u32 fractional_period = lower_32_bits(priv->period);
> +	u32 integral_period = upper_32_bits(priv->period);
> +	u32 tmr_ctrl, fiper_ctrl;
> +	struct timespec64 now;
> +	u64 ns;
> +	int i;
> +
> +	/* Software must enable timer first and the clock selected must be
> +	 * active, otherwise, the registers which are in the timer clock
> +	 * domain are not accessible.
> +	 */
> +	tmr_ctrl = (priv->clk_select & TMR_CTRL_CK_SEL) | TMR_CTRL_TE;
> +	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +	netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc);
> +
> +	/* Disable FIPER by default */
> +	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> +	for (i = 0; i < NETC_TMR_FIPER_NUM; i++) {
> +		fiper_ctrl |= FIPER_CTRL_DIS(i);
> +		fiper_ctrl &= ~FIPER_CTRL_PG(i);
> +	}
> +	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +
> +	ktime_get_real_ts64(&now);
> +	ns = timespec64_to_ns(&now);
> +	netc_timer_cnt_write(priv, ns);
> +
> +	/* Allow atomic writes to TCLK_PERIOD and TMR_ADD, An update to
> +	 * TCLK_PERIOD does not take effect until TMR_ADD is written.
> +	 */
> +	tmr_ctrl |= ((integral_period << 16) & TMR_CTRL_TCLK_PERIOD) |
> +		     TMR_COMP_MODE | TMR_CTRL_FS;
> +	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
> +	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
> +}
> +
> +static int netc_timer_pci_probe(struct pci_dev *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct netc_timer *priv;
> +	int err, len;
> +
> +	pcie_flr(pdev);
> +	err = pci_enable_device_mem(pdev);
> +	if (err)
> +		return dev_err_probe(dev, err, "Failed to enable device\n");
> +
> +	err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> +	if (err) {
> +		dev_err(dev, "dma_set_mask_and_coherent() failed, err:%pe\n",
> +			ERR_PTR(err));
> +		goto disable_dev;
> +	}

Needn't check return value for dma_set_mask_and_coherent() when mask >= 32
It is never return fail.

use devm_add_action_or_reset() to avoid all goto here. then dev_err =>
dev_err_probe().

> +
> +	err = pci_request_mem_regions(pdev, KBUILD_MODNAME);
> +	if (err) {
> +		dev_err(dev, "pci_request_regions() failed, err:%pe\n",
> +			ERR_PTR(err));
> +		goto disable_dev;
> +	}
> +
> +	pci_set_master(pdev);
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);

devm_kzalloc()

> +	if (!priv) {
> +		err = -ENOMEM;
> +		goto release_mem_regions;
> +	}
> +
> +	priv->pdev = pdev;
> +	len = pci_resource_len(pdev, NETC_TMR_REGS_BAR);
> +	priv->base = ioremap(pci_resource_start(pdev, NETC_TMR_REGS_BAR), len);
> +	if (!priv->base) {
> +		err = -ENXIO;
> +		dev_err(dev, "ioremap() failed\n");
> +		goto free_priv;
> +	}

pci_ioremap_bar()?

> +
> +	pci_set_drvdata(pdev, priv);
> +
> +	return 0;
> +
> +free_priv:
> +	kfree(priv);
> +release_mem_regions:
> +	pci_release_mem_regions(pdev);
> +disable_dev:
> +	pci_disable_device(pdev);
> +
> +	return err;
> +}
> +
> +static void netc_timer_pci_remove(struct pci_dev *pdev)
> +{
> +	struct netc_timer *priv = pci_get_drvdata(pdev);
> +
> +	iounmap(priv->base);
> +	kfree(priv);
> +	pci_release_mem_regions(pdev);
> +	pci_disable_device(pdev);
> +}
> +
> +static int netc_timer_get_reference_clk_source(struct netc_timer *priv)
> +{
> +	struct device *dev = &priv->pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	const char *clk_name = NULL;
> +	u64 ns = NSEC_PER_SEC;
> +
> +	/* Select NETC system clock as the reference clock by default */
> +	priv->clk_select = NETC_TMR_SYSTEM_CLK;
> +	priv->clk_freq = NETC_TMR_SYSCLK_333M;
> +	priv->period = div_u64(ns << 32, priv->clk_freq);
> +
> +	if (!np)
> +		return 0;
> +
> +	of_property_read_string(np, "clock-names", &clk_name);
> +	if (!clk_name)
> +		return 0;

Don't perfer parser this property by youself.

you can use devm_clk_bulk_get_all() clk_bulk_data have clock name, you
can use it.

or use loop to try 3 time devm_clk_get(), first one return success is what
you want.

> +
> +	/* Update the clock source of the reference clock if the clock
> +	 * name is specified in DTS node.
> +	 */
> +	if (!strcmp(clk_name, "system"))

strncmp();

"..." maybe longer than len of clc_name.

> +		priv->clk_select = NETC_TMR_SYSTEM_CLK;
> +	else if (!strcmp(clk_name, "ccm_timer"))
> +		priv->clk_select = NETC_TMR_CCM_TIMER1;
> +	else if (!strcmp(clk_name, "ext_1588"))
> +		priv->clk_select = NETC_TMR_EXT_OSC;
> +	else
> +		return -EINVAL;
> +
> +	priv->src_clk = devm_clk_get(dev, clk_name);
> +	if (IS_ERR(priv->src_clk)) {
> +		dev_err(dev, "Failed to get reference clock source\n");
> +		return PTR_ERR(priv->src_clk);
> +	}
> +
> +	priv->clk_freq = clk_get_rate(priv->src_clk);
> +	priv->period = div_u64(ns << 32, priv->clk_freq);
> +
> +	return 0;
> +}
> +
> +static int netc_timer_parse_dt(struct netc_timer *priv)
> +{
> +	return netc_timer_get_reference_clk_source(priv);
> +}
> +
> +static irqreturn_t netc_timer_isr(int irq, void *data)
> +{
> +	struct netc_timer *priv = data;
> +	u32 tmr_event, tmr_emask;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	tmr_event = netc_timer_rd(priv, NETC_TMR_TEVENT);
> +	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
> +
> +	tmr_event &= tmr_emask;
> +	if (tmr_event & TMR_TEVENT_ALM1EN)
> +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> +
> +	if (tmr_event & TMR_TEVENT_ALM2EN)
> +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 1);
> +
> +	/* Clear interrupts status */
> +	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);

clean irq status should be just after read it (before "tmr_event &= tmr_emask;)

otherwise the new irq maybe missed by netc_timer_alarm_write()

> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int netc_timer_init_msix_irq(struct netc_timer *priv)
> +{
> +	struct pci_dev *pdev = priv->pdev;
> +	char irq_name[64];
> +	int err, n;
> +
> +	n = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
> +	if (n != 1) {
> +		err = (n < 0) ? n : -EPERM;
> +		dev_err(&pdev->dev, "pci_alloc_irq_vectors() failed\n");
> +		return err;
> +	}
> +
> +	priv->irq = pci_irq_vector(pdev, 0);
> +	snprintf(irq_name, sizeof(irq_name), "ptp-netc %s", pci_name(pdev));
> +	err = request_irq(priv->irq, netc_timer_isr, 0, irq_name, priv);
> +	if (err) {
> +		dev_err(&pdev->dev, "request_irq() failed\n");
> +		pci_free_irq_vectors(pdev);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static void netc_timer_free_msix_irq(struct netc_timer *priv)
> +{
> +	struct pci_dev *pdev = priv->pdev;
> +
> +	disable_irq(priv->irq);
> +	free_irq(priv->irq, priv);
> +	pci_free_irq_vectors(pdev);
> +}
> +
> +static int netc_timer_probe(struct pci_dev *pdev,
> +			    const struct pci_device_id *id)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct netc_timer *priv;
> +	int err;
> +
> +	err = netc_timer_pci_probe(pdev);
> +	if (err)
> +		return err;
> +
> +	priv = pci_get_drvdata(pdev);
> +	err = netc_timer_parse_dt(priv);
> +	if (err) {
> +		dev_err(dev, "Failed to parse DT node\n");
> +		goto timer_pci_remove;
> +	}
> +
> +	priv->caps = netc_timer_ptp_caps;
> +	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
> +	priv->phc_index = -1; /* initialize it as an invalid index */
> +	spin_lock_init(&priv->lock);
> +
> +	err = clk_prepare_enable(priv->src_clk);

use devm_ function

> +	if (err) {
> +		dev_err(dev, "Failed to enable timer source clock\n");
> +		goto timer_pci_remove;
> +	}
> +
> +	err = netc_timer_init_msix_irq(priv);
> +	if (err)
> +		goto disable_clk;
> +
> +	netc_timer_init(priv);
> +	priv->clock = ptp_clock_register(&priv->caps, dev);
> +	if (IS_ERR(priv->clock)) {
> +		err = PTR_ERR(priv->clock);
> +		goto free_msix_irq;
> +	}
> +
> +	priv->phc_index = ptp_clock_index(priv->clock);
> +
> +	return 0;
> +
> +free_msix_irq:
> +	netc_timer_free_msix_irq(priv);
> +disable_clk:
> +	clk_disable_unprepare(priv->src_clk);
> +timer_pci_remove:
> +	netc_timer_pci_remove(pdev);

devm_add_action_or_reset() to simpify goto and netc_timer_remove().

> +
> +	return err;
> +}
> +
> +static void netc_timer_remove(struct pci_dev *pdev)
> +{
> +	struct netc_timer *priv = pci_get_drvdata(pdev);
> +
> +	ptp_clock_unregister(priv->clock);
> +	netc_timer_free_msix_irq(priv);
> +	clk_disable_unprepare(priv->src_clk);
> +	netc_timer_pci_remove(pdev);
> +}
> +
> +static const struct pci_device_id netc_timer_id_table[] = {
> +	{ PCI_DEVICE(NETC_TMR_PCI_VENDOR, NETC_TMR_PCI_DEVID) },
> +	{ 0, } /* End of table. */

just { }

needn't /* End of table. */

Frank
> +};
> +MODULE_DEVICE_TABLE(pci, netc_timer_id_table);
> +
> +static struct pci_driver netc_timer_driver = {
> +	.name = KBUILD_MODNAME,
> +	.id_table = netc_timer_id_table,
> +	.probe = netc_timer_probe,
> +	.remove = netc_timer_remove,
> +};
> +module_pci_driver(netc_timer_driver);
> +
> +MODULE_DESCRIPTION("NXP NETC Timer PTP Driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/include/linux/fsl/netc_global.h b/include/linux/fsl/netc_global.h
> index fdecca8c90f0..59c835e67ada 100644
> --- a/include/linux/fsl/netc_global.h
> +++ b/include/linux/fsl/netc_global.h
> @@ -1,10 +1,11 @@
>  /* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
> -/* Copyright 2024 NXP
> +/* Copyright 2024-2025 NXP
>   */
>  #ifndef __NETC_GLOBAL_H
>  #define __NETC_GLOBAL_H
>
>  #include <linux/io.h>
> +#include <linux/pci.h>
>
>  static inline u32 netc_read(void __iomem *reg)
>  {
> @@ -16,4 +17,13 @@ static inline void netc_write(void __iomem *reg, u32 val)
>  	iowrite32(val, reg);
>  }
>
> +#if IS_ENABLED(CONFIG_PTP_1588_CLOCK_NETC)
> +int netc_timer_get_phc_index(struct pci_dev *timer_pdev);
> +#else
> +static inline int netc_timer_get_phc_index(struct pci_dev *timer_pdev)
> +{
> +	return -ENODEV;
> +}
> +#endif
> +
>  #endif
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 04/14] ptp: netc: add PTP_CLK_REQ_PPS support
  2025-07-16  7:31 ` [PATCH v2 net-next 04/14] ptp: netc: add PTP_CLK_REQ_PPS support Wei Fang
@ 2025-07-16 20:05   ` Frank Li
  2025-07-17 11:59     ` Wei Fang
  0 siblings, 1 reply; 64+ messages in thread
From: Frank Li @ 2025-07-16 20:05 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:01PM +0800, Wei Fang wrote:
> The NETC Times is able to generate the PPS event, so add PTP_CLK_REQ_PPS
> support. In addition, if there is a time drift when PPS is enabled, the
> PPS event will not be generated at an integral second of PHC. Based on
> the suggestion from IP team, FIPER should be disabled before adjusting
> the hardware time and then rearm ALARM after the time adjustment to make
> the next PPS event be generated at an integral second of PHC. Finally,
> re-enable FIPER.

Add PTP_CLK_REQ_PPS supports.

The suggested steps by IP team if time drift happen:
  1: Disable FIPER before adjusting the hardware time
  2: rearm ALARM after the time adjustment to make ...
  3: re-enable FIPER.

>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
>
> ---
> v2 changes:
> 1. Refine the subject and the commit message
> 2. Add a comment to netc_timer_enable_pps()
> 3. Remove the "nxp,pps-channel" logic from the driver
> ---
>  drivers/ptp/ptp_netc.c | 176 ++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 175 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> index 82cb1e6a0fe9..e39605c5b73b 100644
> --- a/drivers/ptp/ptp_netc.c
> +++ b/drivers/ptp/ptp_netc.c
> @@ -24,6 +24,8 @@
>  #define  TMR_ALARM1P			BIT(31)
>
>  #define NETC_TMR_TEVENT			0x0084
> +#define  TMR_TEVNET_PPEN(i)		BIT(7 - (i))
> +#define  TMR_TEVENT_PPEN_ALL		GENMASK(7, 5)
>  #define  TMR_TEVENT_ALM1EN		BIT(16)
>  #define  TMR_TEVENT_ALM2EN		BIT(17)
>
> @@ -39,9 +41,15 @@
>  #define NETC_TMR_ALARM_L(i)		(0x00b8 + (i) * 8)
>  #define NETC_TMR_ALARM_H(i)		(0x00bc + (i) * 8)
>
> +/* i = 0, 1, 2. i indicates the index of TMR_FIPER. */
> +#define NETC_TMR_FIPER(i)		(0x00d0 + (i) * 4)
> +
>  #define NETC_TMR_FIPER_CTRL		0x00dc
>  #define  FIPER_CTRL_DIS(i)		(BIT(7) << (i) * 8)
>  #define  FIPER_CTRL_PG(i)		(BIT(6) << (i) * 8)
> +#define  FIPER_CTRL_FS_ALARM(i)		(BIT(5) << (i) * 8)
> +#define  FIPER_CTRL_PW(i)		(GENMASK(4, 0) << (i) * 8)
> +#define  FIPER_CTRL_SET_PW(i, v)	(((v) & GENMASK(4, 0)) << 8 * (i))
>
>  #define NETC_TMR_CUR_TIME_L		0x00f0
>  #define NETC_TMR_CUR_TIME_H		0x00f4
> @@ -51,6 +59,9 @@
>  #define NETC_TMR_FIPER_NUM		3
>  #define NETC_TMR_DEFAULT_PRSC		2
>  #define NETC_TMR_DEFAULT_ALARM		GENMASK_ULL(63, 0)
> +#define NETC_TMR_DEFAULT_PPS_CHANNEL	0
> +#define NETC_TMR_DEFAULT_FIPER		GENMASK(31, 0)
> +#define NETC_TMR_FIPER_MAX_PW		GENMASK(4, 0)
>
>  /* 1588 timer reference clock source select */
>  #define NETC_TMR_CCM_TIMER1		0 /* enet_timer1_clk_root, from CCM */
> @@ -75,6 +86,8 @@ struct netc_timer {
>  	u64 period;
>
>  	int irq;
> +	u8 pps_channel;
> +	bool pps_enabled;
>  };
>
>  #define netc_timer_rd(p, o)		netc_read((p)->base + (o))
> @@ -152,6 +165,147 @@ static void netc_timer_alarm_write(struct netc_timer *priv,
>  	netc_timer_wr(priv, NETC_TMR_ALARM_H(index), alarm_h);
>  }
>
> +static u32 netc_timer_get_integral_period(struct netc_timer *priv)
> +{
> +	u32 tmr_ctrl, integral_period;
> +
> +	tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
> +	integral_period = FIELD_GET(TMR_CTRL_TCLK_PERIOD, tmr_ctrl);
> +
> +	return integral_period;
> +}
> +
> +static u32 netc_timer_calculate_fiper_pw(struct netc_timer *priv,
> +					 u32 fiper)
> +{
> +	u64 divisor, pulse_width;
> +
> +	/* Set the FIPER pulse width to half FIPER interval by default.
> +	 * pulse_width = (fiper / 2) / TMR_GCLK_period,
> +	 * TMR_GCLK_period = NSEC_PER_SEC / TMR_GCLK_freq,
> +	 * TMR_GCLK_freq = (clk_freq / oclk_prsc) Hz,
> +	 * so pulse_width = fiper * clk_freq / (2 * NSEC_PER_SEC * oclk_prsc).
> +	 */
> +	divisor = mul_u32_u32(2000000000U, priv->oclk_prsc);

is it 2*PSEC_PER_SEC ?

Frank
> +	pulse_width = div64_u64(mul_u32_u32(fiper, priv->clk_freq), divisor);
> +
> +	/* The FIPER_PW field only has 5 bits, need to update oclk_prsc */
> +	if (pulse_width > NETC_TMR_FIPER_MAX_PW)
> +		pulse_width = NETC_TMR_FIPER_MAX_PW;
> +
> +	return pulse_width;
> +}
> +
> +static void netc_timer_set_pps_alarm(struct netc_timer *priv, int channel,
> +				     u32 integral_period)
> +{
> +	u64 alarm;
> +
> +	/* Get the alarm value */
> +	alarm = netc_timer_cur_time_read(priv) +  NSEC_PER_MSEC;
> +	alarm = roundup_u64(alarm, NSEC_PER_SEC);
> +	alarm = roundup_u64(alarm, integral_period);
> +
> +	netc_timer_alarm_write(priv, alarm, 0);
> +}
> +
> +/* Note that users should not use this API to output PPS signal on
> + * external pins, because PTP_CLK_REQ_PPS trigger internal PPS event
> + * for input into kernel PPS subsystem. See:
> + * https://lore.kernel.org/r/20201117213826.18235-1-a.fatoum@pengutronix.de
> + */
> +static int netc_timer_enable_pps(struct netc_timer *priv,
> +				 struct ptp_clock_request *rq, int on)
> +{
> +	u32 tmr_emask, fiper, fiper_ctrl;
> +	u8 channel = priv->pps_channel;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
> +	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> +
> +	if (on) {
> +		u32 integral_period, fiper_pw;
> +
> +		if (priv->pps_enabled)
> +			goto unlock_spinlock;
> +
> +		integral_period = netc_timer_get_integral_period(priv);
> +		fiper = NSEC_PER_SEC - integral_period;
> +		fiper_pw = netc_timer_calculate_fiper_pw(priv, fiper);
> +		fiper_ctrl &= ~(FIPER_CTRL_DIS(channel) | FIPER_CTRL_PW(channel) |
> +				FIPER_CTRL_FS_ALARM(channel));
> +		fiper_ctrl |= FIPER_CTRL_SET_PW(channel, fiper_pw);
> +		tmr_emask |= TMR_TEVNET_PPEN(channel);
> +		priv->pps_enabled = true;
> +		netc_timer_set_pps_alarm(priv, channel, integral_period);
> +	} else {
> +		if (!priv->pps_enabled)
> +			goto unlock_spinlock;
> +
> +		fiper = NETC_TMR_DEFAULT_FIPER;
> +		tmr_emask &= ~TMR_TEVNET_PPEN(channel);
> +		fiper_ctrl |= FIPER_CTRL_DIS(channel);
> +		priv->pps_enabled = false;
> +	}
> +
> +	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
> +	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
> +	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +
> +unlock_spinlock:
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +
> +static void netc_timer_disable_pps_fiper(struct netc_timer *priv)
> +{
> +	u32 fiper = NETC_TMR_DEFAULT_FIPER;
> +	u8 channel = priv->pps_channel;
> +	u32 fiper_ctrl;
> +
> +	if (!priv->pps_enabled)
> +		return;
> +
> +	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> +	fiper_ctrl |= FIPER_CTRL_DIS(channel);
> +	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
> +	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +}
> +
> +static void netc_timer_enable_pps_fiper(struct netc_timer *priv)
> +{
> +	u32 fiper_ctrl, integral_period, fiper;
> +	u8 channel = priv->pps_channel;
> +
> +	if (!priv->pps_enabled)
> +		return;
> +
> +	integral_period = netc_timer_get_integral_period(priv);
> +	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> +	fiper_ctrl &= ~FIPER_CTRL_DIS(channel);
> +	fiper = NSEC_PER_SEC - integral_period;
> +	netc_timer_set_pps_alarm(priv, channel, integral_period);
> +	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
> +	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +}
> +
> +static int netc_timer_enable(struct ptp_clock_info *ptp,
> +			     struct ptp_clock_request *rq, int on)
> +{
> +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> +
> +	switch (rq->type) {
> +	case PTP_CLK_REQ_PPS:
> +		return netc_timer_enable_pps(priv, rq, on);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
>  static void netc_timer_adjust_period(struct netc_timer *priv, u64 period)
>  {
>  	u32 fractional_period = lower_32_bits(period);
> @@ -164,8 +318,11 @@ static void netc_timer_adjust_period(struct netc_timer *priv, u64 period)
>  	old_tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
>  	tmr_ctrl = u32_replace_bits(old_tmr_ctrl, integral_period,
>  				    TMR_CTRL_TCLK_PERIOD);
> -	if (tmr_ctrl != old_tmr_ctrl)
> +	if (tmr_ctrl != old_tmr_ctrl) {
> +		netc_timer_disable_pps_fiper(priv);
>  		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +		netc_timer_enable_pps_fiper(priv);
> +	}
>
>  	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
>
> @@ -191,6 +348,8 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
>
>  	spin_lock_irqsave(&priv->lock, flags);
>
> +	netc_timer_disable_pps_fiper(priv);
> +
>  	tmr_off = netc_timer_offset_read(priv);
>  	if (delta < 0 && tmr_off < abs(delta)) {
>  		delta += tmr_off;
> @@ -205,6 +364,8 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
>  		netc_timer_offset_write(priv, tmr_off);
>  	}
>
> +	netc_timer_enable_pps_fiper(priv);
> +
>  	spin_unlock_irqrestore(&priv->lock, flags);
>
>  	return 0;
> @@ -239,8 +400,12 @@ static int netc_timer_settime64(struct ptp_clock_info *ptp,
>  	unsigned long flags;
>
>  	spin_lock_irqsave(&priv->lock, flags);
> +
> +	netc_timer_disable_pps_fiper(priv);
>  	netc_timer_offset_write(priv, 0);
>  	netc_timer_cnt_write(priv, ns);
> +	netc_timer_enable_pps_fiper(priv);
> +
>  	spin_unlock_irqrestore(&priv->lock, flags);
>
>  	return 0;
> @@ -267,10 +432,12 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
>  	.max_adj	= 500000000,
>  	.n_alarm	= 2,
>  	.n_pins		= 0,
> +	.pps		= 1,
>  	.adjfine	= netc_timer_adjfine,
>  	.adjtime	= netc_timer_adjtime,
>  	.gettimex64	= netc_timer_gettimex64,
>  	.settime64	= netc_timer_settime64,
> +	.enable		= netc_timer_enable,
>  };
>
>  static void netc_timer_init(struct netc_timer *priv)
> @@ -429,6 +596,7 @@ static int netc_timer_parse_dt(struct netc_timer *priv)
>  static irqreturn_t netc_timer_isr(int irq, void *data)
>  {
>  	struct netc_timer *priv = data;
> +	struct ptp_clock_event event;
>  	u32 tmr_event, tmr_emask;
>  	unsigned long flags;
>
> @@ -444,6 +612,11 @@ static irqreturn_t netc_timer_isr(int irq, void *data)
>  	if (tmr_event & TMR_TEVENT_ALM2EN)
>  		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 1);
>
> +	if (tmr_event & TMR_TEVENT_PPEN_ALL) {
> +		event.type = PTP_CLOCK_PPS;
> +		ptp_clock_event(priv->clock, &event);
> +	}
> +
>  	/* Clear interrupts status */
>  	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
>
> @@ -506,6 +679,7 @@ static int netc_timer_probe(struct pci_dev *pdev,
>
>  	priv->caps = netc_timer_ptp_caps;
>  	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
> +	priv->pps_channel = NETC_TMR_DEFAULT_PPS_CHANNEL;
>  	priv->phc_index = -1; /* initialize it as an invalid index */
>  	spin_lock_init(&priv->lock);
>
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 05/14] ptp: netc: add periodic pulse output support
  2025-07-16  7:31 ` [PATCH v2 net-next 05/14] ptp: netc: add periodic pulse output support Wei Fang
@ 2025-07-16 20:26   ` Frank Li
  2025-07-17 12:11     ` Wei Fang
  0 siblings, 1 reply; 64+ messages in thread
From: Frank Li @ 2025-07-16 20:26 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:02PM +0800, Wei Fang wrote:
> NETC Timer has three pulse channels, all of which support periodic pulse
> output. Bind the channel to a ALARM register and then sets a future time
> into the ALARM register. When the current time is greater than the ALARM
> value, the FIPER register will be triggered to count down, and when the
> count reaches 0, the pulse will be triggered. The PPS signal is also
> implemented in this way. However, for i.MX95, only ALARM1 can be used for
> periodic pulse output, and for i.MX943, ALARM1 and ALARM2 can be used for
> periodic pulse output, but NETC Timer has three channels, so for i.MX95,
> only one channel can work at the same time, and for i.MX943, at most two
> channel can work at the same time. Otherwise, if multiple channels share
> the same ALARM register, some channel pulses will not meet expectations.
> Therefore, the current implementation does not allow multiple channels to
> share the same ALARM register at the same time.

Can you simple said

NETC Timer has three pulse channels. i.MX95 have one ALARM1. i.MX943 have
ALARM1 and ALARM2. So only one channel can work for i.MX95. Two channels
for imx943 at most.

Current (driver or IP) implementation don't allow multiple channels to
share the same alarm register at the same time.

>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
> ---
>  drivers/ptp/ptp_netc.c | 281 ++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 250 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> index e39605c5b73b..289cdd50ae3d 100644
> --- a/drivers/ptp/ptp_netc.c
> +++ b/drivers/ptp/ptp_netc.c
> @@ -55,6 +55,10 @@
>  #define NETC_TMR_CUR_TIME_H		0x00f4
>
>  #define NETC_TMR_REGS_BAR		0
> +#define NETC_GLOBAL_OFFSET		0x10000
> +#define NETC_GLOBAL_IPBRR0		0xbf8
> +#define  IPBRR0_IP_REV			GENMASK(15, 0)
> +#define NETC_REV_4_1			0x0401
>
>  #define NETC_TMR_FIPER_NUM		3
>  #define NETC_TMR_DEFAULT_PRSC		2
> @@ -62,6 +66,7 @@
>  #define NETC_TMR_DEFAULT_PPS_CHANNEL	0
>  #define NETC_TMR_DEFAULT_FIPER		GENMASK(31, 0)
>  #define NETC_TMR_FIPER_MAX_PW		GENMASK(4, 0)
> +#define NETC_TMR_ALARM_NUM		2
>
>  /* 1588 timer reference clock source select */
>  #define NETC_TMR_CCM_TIMER1		0 /* enet_timer1_clk_root, from CCM */
> @@ -70,6 +75,19 @@
>
>  #define NETC_TMR_SYSCLK_333M		333333333U
>
> +enum netc_pp_type {
> +	NETC_PP_PPS = 1,
> +	NETC_PP_PEROUT,
> +};
> +
> +struct netc_pp {
> +	enum netc_pp_type type;
> +	bool enabled;
> +	int alarm_id;
> +	u32 period; /* pulse period, ns */
> +	u64 stime; /* start time, ns */
> +};
> +
>  struct netc_timer {
>  	void __iomem *base;
>  	struct pci_dev *pdev;
> @@ -87,7 +105,9 @@ struct netc_timer {
>
>  	int irq;
>  	u8 pps_channel;
> -	bool pps_enabled;
> +	u8 fs_alarm_num;
> +	u8 fs_alarm_bitmap;
> +	struct netc_pp pp[NETC_TMR_FIPER_NUM]; /* periodic pulse */
>  };
>
>  #define netc_timer_rd(p, o)		netc_read((p)->base + (o))
> @@ -199,6 +219,7 @@ static u32 netc_timer_calculate_fiper_pw(struct netc_timer *priv,
>  static void netc_timer_set_pps_alarm(struct netc_timer *priv, int channel,
>  				     u32 integral_period)
>  {
> +	struct netc_pp *pp = &priv->pp[channel];
>  	u64 alarm;
>
>  	/* Get the alarm value */
> @@ -206,7 +227,51 @@ static void netc_timer_set_pps_alarm(struct netc_timer *priv, int channel,
>  	alarm = roundup_u64(alarm, NSEC_PER_SEC);
>  	alarm = roundup_u64(alarm, integral_period);
>
> -	netc_timer_alarm_write(priv, alarm, 0);
> +	netc_timer_alarm_write(priv, alarm, pp->alarm_id);
> +}
> +
> +static void netc_timer_set_perout_alarm(struct netc_timer *priv, int channel,
> +					u32 integral_period)
> +{
> +	u64 cur_time = netc_timer_cur_time_read(priv);
> +	struct netc_pp *pp = &priv->pp[channel];
> +	u64 alarm, delta, min_time;
> +	u32 period = pp->period;
> +	u64 stime = pp->stime;
> +
> +	min_time = cur_time + NSEC_PER_MSEC + period;
> +	if (stime < min_time) {
> +		delta = min_time - stime;
> +		stime += roundup_u64(delta, period);
> +	}
> +
> +	alarm = roundup_u64(stime - period, integral_period);
> +	netc_timer_alarm_write(priv, alarm, pp->alarm_id);
> +}
> +
> +static int netc_timer_get_alarm_id(struct netc_timer *priv)
> +{
> +	int i;
> +
> +	for (i = 0; i < priv->fs_alarm_num; i++) {
> +		if (!(priv->fs_alarm_bitmap & BIT(i))) {

fnd_next_zero_bit()?

or use ffz();

Frank
> +			priv->fs_alarm_bitmap |= BIT(i);
> +			break;
> +		}
> +	}
> +
> +	return i;
> +}
> +
> +static u64 netc_timer_get_gclk_period(struct netc_timer *priv)
> +{
> +	/* TMR_GCLK_freq = (clk_freq / oclk_prsc) Hz.
> +	 * TMR_GCLK_period = NSEC_PER_SEC / TMR_GCLK_freq.
> +	 * TMR_GCLK_period = (NSEC_PER_SEC * oclk_prsc) / clk_freq
> +	 */
> +
> +	return div_u64(mul_u32_u32(NSEC_PER_SEC, priv->oclk_prsc),
> +		       priv->clk_freq);
>  }
>
>  /* Note that users should not use this API to output PPS signal on
> @@ -217,20 +282,43 @@ static void netc_timer_set_pps_alarm(struct netc_timer *priv, int channel,
>  static int netc_timer_enable_pps(struct netc_timer *priv,
>  				 struct ptp_clock_request *rq, int on)
>  {
> +	struct device *dev = &priv->pdev->dev;
>  	u32 tmr_emask, fiper, fiper_ctrl;
>  	u8 channel = priv->pps_channel;
>  	unsigned long flags;
> +	struct netc_pp *pp;
> +	int alarm_id;
> +	int err = 0;
>
>  	spin_lock_irqsave(&priv->lock, flags);
>
> +	pp = &priv->pp[channel];
> +	if (pp->type == NETC_PP_PEROUT) {
> +		dev_err(dev, "FIPER%u is being used for PEROUT\n", channel);
> +		err = -EBUSY;
> +		goto unlock_spinlock;
> +	}
> +
>  	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
>  	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
>
>  	if (on) {
>  		u32 integral_period, fiper_pw;
>
> -		if (priv->pps_enabled)
> +		if (pp->enabled)
> +			goto unlock_spinlock;
> +
> +		alarm_id = netc_timer_get_alarm_id(priv);
> +		if (alarm_id == priv->fs_alarm_num) {
> +			dev_err(dev, "No available ALARMs\n");
> +			err = -EBUSY;
>  			goto unlock_spinlock;
> +		}
> +
> +		pp->enabled = true;
> +		pp->type = NETC_PP_PPS;
> +		pp->alarm_id = alarm_id;
> +		pp->period = NSEC_PER_SEC;
>
>  		integral_period = netc_timer_get_integral_period(priv);
>  		fiper = NSEC_PER_SEC - integral_period;
> @@ -238,17 +326,19 @@ static int netc_timer_enable_pps(struct netc_timer *priv,
>  		fiper_ctrl &= ~(FIPER_CTRL_DIS(channel) | FIPER_CTRL_PW(channel) |
>  				FIPER_CTRL_FS_ALARM(channel));
>  		fiper_ctrl |= FIPER_CTRL_SET_PW(channel, fiper_pw);
> +		fiper_ctrl |= alarm_id ? FIPER_CTRL_FS_ALARM(channel) : 0;
>  		tmr_emask |= TMR_TEVNET_PPEN(channel);
> -		priv->pps_enabled = true;
>  		netc_timer_set_pps_alarm(priv, channel, integral_period);
>  	} else {
> -		if (!priv->pps_enabled)
> +		if (!pp->enabled)
>  			goto unlock_spinlock;
>
> +		priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id);
> +		memset(pp, 0, sizeof(*pp));
> +
>  		fiper = NETC_TMR_DEFAULT_FIPER;
>  		tmr_emask &= ~TMR_TEVNET_PPEN(channel);
>  		fiper_ctrl |= FIPER_CTRL_DIS(channel);
> -		priv->pps_enabled = false;
>  	}
>
>  	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
> @@ -258,38 +348,150 @@ static int netc_timer_enable_pps(struct netc_timer *priv,
>  unlock_spinlock:
>  	spin_unlock_irqrestore(&priv->lock, flags);
>
> -	return 0;
> +	return err;
>  }
>
> -static void netc_timer_disable_pps_fiper(struct netc_timer *priv)
> +static int net_timer_enable_perout(struct netc_timer *priv,
> +				   struct ptp_clock_request *rq, int on)
>  {
> -	u32 fiper = NETC_TMR_DEFAULT_FIPER;
> -	u8 channel = priv->pps_channel;
> -	u32 fiper_ctrl;
> +	struct device *dev = &priv->pdev->dev;
> +	u32 tmr_emask, fiper, fiper_ctrl;
> +	u32 channel = rq->perout.index;
> +	unsigned long flags;
> +	struct netc_pp *pp;
> +	int alarm_id;
> +	int err = 0;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
>
> -	if (!priv->pps_enabled)
> -		return;
> +	pp = &priv->pp[channel];
> +	if (pp->type == NETC_PP_PPS) {
> +		dev_err(dev, "FIPER%u is being used for PPS\n", channel);
> +		err = -EBUSY;
> +		goto unlock_spinlock;
> +	}
>
> +	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
>  	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> -	fiper_ctrl |= FIPER_CTRL_DIS(channel);
> +	if (on) {
> +		u64 period_ns, gclk_period, max_period, min_period;
> +		struct timespec64 period, stime;
> +		u32 integral_period, fiper_pw;
> +
> +		period.tv_sec = rq->perout.period.sec;
> +		period.tv_nsec = rq->perout.period.nsec;
> +		period_ns = timespec64_to_ns(&period);
> +
> +		integral_period = netc_timer_get_integral_period(priv);
> +		max_period = (u64)NETC_TMR_DEFAULT_FIPER + integral_period;
> +		gclk_period = netc_timer_get_gclk_period(priv);
> +		min_period = gclk_period * 4 + integral_period;
> +		if (period_ns > max_period || period_ns < min_period) {
> +			dev_err(dev, "The period range is %llu ~ %llu\n",
> +				min_period, max_period);
> +			err = -EINVAL;
> +			goto unlock_spinlock;
> +		}
> +
> +		stime.tv_sec = rq->perout.start.sec;
> +		stime.tv_nsec = rq->perout.start.nsec;
> +
> +		tmr_emask |= TMR_TEVNET_PPEN(channel);
> +
> +		/* Set to desired FIPER interval in ns - TCLK_PERIOD */
> +		fiper = period_ns - integral_period;
> +		fiper_pw = netc_timer_calculate_fiper_pw(priv, fiper);
> +
> +		if (pp->enabled) {
> +			alarm_id = pp->alarm_id;
> +		} else {
> +			alarm_id = netc_timer_get_alarm_id(priv);
> +			if (alarm_id == priv->fs_alarm_num) {
> +				dev_err(dev, "No available ALARMs\n");
> +				err = -EBUSY;
> +				goto unlock_spinlock;
> +			}
> +
> +			pp->type = NETC_PP_PEROUT;
> +			pp->enabled = true;
> +			pp->alarm_id = alarm_id;
> +		}
> +
> +		pp->stime = timespec64_to_ns(&stime);
> +		pp->period = period_ns;
> +
> +		fiper_ctrl &= ~(FIPER_CTRL_DIS(channel) | FIPER_CTRL_PW(channel) |
> +				FIPER_CTRL_FS_ALARM(channel));
> +		fiper_ctrl |= FIPER_CTRL_SET_PW(channel, fiper_pw);
> +		fiper_ctrl |= alarm_id ? FIPER_CTRL_FS_ALARM(channel) : 0;
> +
> +		netc_timer_set_perout_alarm(priv, channel, integral_period);
> +	} else {
> +		if (!pp->enabled)
> +			goto unlock_spinlock;
> +
> +		tmr_emask &= ~TMR_TEVNET_PPEN(channel);
> +		fiper = NETC_TMR_DEFAULT_FIPER;
> +		fiper_ctrl |= FIPER_CTRL_DIS(channel);
> +
> +		alarm_id = pp->alarm_id;
> +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, alarm_id);
> +		priv->fs_alarm_bitmap &= ~BIT(alarm_id);
> +		memset(pp, 0, sizeof(*pp));
> +	}
> +
> +	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
>  	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
>  	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +
> +unlock_spinlock:
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return err;
>  }
>
> -static void netc_timer_enable_pps_fiper(struct netc_timer *priv)
> +static void netc_timer_disable_fiper(struct netc_timer *priv)
>  {
> -	u32 fiper_ctrl, integral_period, fiper;
> -	u8 channel = priv->pps_channel;
> +	u32 fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> +	int i;
>
> -	if (!priv->pps_enabled)
> -		return;
> +	for (i = 0; i < NETC_TMR_FIPER_NUM; i++) {
> +		struct netc_pp *pp = &priv->pp[i];
> +
> +		if (!pp->enabled)
> +			continue;
> +
> +		fiper_ctrl |= FIPER_CTRL_DIS(i);
> +		netc_timer_wr(priv, NETC_TMR_FIPER(i), NETC_TMR_DEFAULT_FIPER);
> +	}
> +
> +	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +}
> +
> +static void netc_timer_enable_fiper(struct netc_timer *priv)
> +{
> +	u32 integral_period = netc_timer_get_integral_period(priv);
> +	u32 fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> +	int i;
> +
> +	for (i = 0; i < NETC_TMR_FIPER_NUM; i++) {
> +		struct netc_pp *pp = &priv->pp[i];
> +		u32 fiper;
> +
> +		if (!pp->enabled)
> +			continue;
> +
> +		fiper_ctrl &= ~FIPER_CTRL_DIS(i);
> +
> +		if (pp->type == NETC_PP_PPS)
> +			netc_timer_set_pps_alarm(priv, i, integral_period);
> +		else if (pp->type == NETC_PP_PEROUT)
> +			netc_timer_set_perout_alarm(priv, i, integral_period);
> +
> +		fiper = pp->period - integral_period;
> +		netc_timer_wr(priv, NETC_TMR_FIPER(i), fiper);
> +	}
>
> -	integral_period = netc_timer_get_integral_period(priv);
> -	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> -	fiper_ctrl &= ~FIPER_CTRL_DIS(channel);
> -	fiper = NSEC_PER_SEC - integral_period;
> -	netc_timer_set_pps_alarm(priv, channel, integral_period);
> -	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
>  	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
>  }
>
> @@ -301,6 +503,8 @@ static int netc_timer_enable(struct ptp_clock_info *ptp,
>  	switch (rq->type) {
>  	case PTP_CLK_REQ_PPS:
>  		return netc_timer_enable_pps(priv, rq, on);
> +	case PTP_CLK_REQ_PEROUT:
> +		return net_timer_enable_perout(priv, rq, on);
>  	default:
>  		return -EOPNOTSUPP;
>  	}
> @@ -319,9 +523,9 @@ static void netc_timer_adjust_period(struct netc_timer *priv, u64 period)
>  	tmr_ctrl = u32_replace_bits(old_tmr_ctrl, integral_period,
>  				    TMR_CTRL_TCLK_PERIOD);
>  	if (tmr_ctrl != old_tmr_ctrl) {
> -		netc_timer_disable_pps_fiper(priv);
> +		netc_timer_disable_fiper(priv);
>  		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> -		netc_timer_enable_pps_fiper(priv);
> +		netc_timer_enable_fiper(priv);
>  	}
>
>  	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
> @@ -348,7 +552,7 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
>
>  	spin_lock_irqsave(&priv->lock, flags);
>
> -	netc_timer_disable_pps_fiper(priv);
> +	netc_timer_disable_fiper(priv);
>
>  	tmr_off = netc_timer_offset_read(priv);
>  	if (delta < 0 && tmr_off < abs(delta)) {
> @@ -364,7 +568,7 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
>  		netc_timer_offset_write(priv, tmr_off);
>  	}
>
> -	netc_timer_enable_pps_fiper(priv);
> +	netc_timer_enable_fiper(priv);
>
>  	spin_unlock_irqrestore(&priv->lock, flags);
>
> @@ -401,10 +605,10 @@ static int netc_timer_settime64(struct ptp_clock_info *ptp,
>
>  	spin_lock_irqsave(&priv->lock, flags);
>
> -	netc_timer_disable_pps_fiper(priv);
> +	netc_timer_disable_fiper(priv);
>  	netc_timer_offset_write(priv, 0);
>  	netc_timer_cnt_write(priv, ns);
> -	netc_timer_enable_pps_fiper(priv);
> +	netc_timer_enable_fiper(priv);
>
>  	spin_unlock_irqrestore(&priv->lock, flags);
>
> @@ -433,6 +637,7 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
>  	.n_alarm	= 2,
>  	.n_pins		= 0,
>  	.pps		= 1,
> +	.n_per_out	= 3,
>  	.adjfine	= netc_timer_adjfine,
>  	.adjtime	= netc_timer_adjtime,
>  	.gettimex64	= netc_timer_gettimex64,
> @@ -659,6 +864,15 @@ static void netc_timer_free_msix_irq(struct netc_timer *priv)
>  	pci_free_irq_vectors(pdev);
>  }
>
> +static int netc_timer_get_global_ip_rev(struct netc_timer *priv)
> +{
> +	u32 val;
> +
> +	val = netc_timer_rd(priv, NETC_GLOBAL_OFFSET + NETC_GLOBAL_IPBRR0);
> +
> +	return val & IPBRR0_IP_REV;
> +}
> +
>  static int netc_timer_probe(struct pci_dev *pdev,
>  			    const struct pci_device_id *id)
>  {
> @@ -689,6 +903,11 @@ static int netc_timer_probe(struct pci_dev *pdev,
>  		goto timer_pci_remove;
>  	}
>
> +	if (netc_timer_get_global_ip_rev(priv) == NETC_REV_4_1)
> +		priv->fs_alarm_num = 1;
> +	else
> +		priv->fs_alarm_num = NETC_TMR_ALARM_NUM;
> +
>  	err = netc_timer_init_msix_irq(priv);
>  	if (err)
>  		goto disable_clk;
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 06/14] ptp: netc: add external trigger stamp support
  2025-07-16  7:31 ` [PATCH v2 net-next 06/14] ptp: netc: add external trigger stamp support Wei Fang
@ 2025-07-16 20:30   ` Frank Li
  0 siblings, 0 replies; 64+ messages in thread
From: Frank Li @ 2025-07-16 20:30 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:03PM +0800, Wei Fang wrote:
> From: "F.S. Peng" <fushi.peng@nxp.com>
>
> The NETC Timer is capable of recording the timestamp on receipt of an
> external pulse on a GPIO pin. It supports two such external triggers.
> The recorded value is saved in a 16 entry FIFO accessed by
> TMR_ETTSa_H/L. An interrupt can be generated when the trigger occurs,
> when the FIFO reaches a threshold, and if the FIFO overflows.
>
> Signed-off-by: F.S. Peng <fushi.peng@nxp.com>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>

Reviewed-by: Frank Li <Frank.Li@nxp.com>

> ---
>  drivers/ptp/ptp_netc.c | 118 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 118 insertions(+)
>
> diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> index 289cdd50ae3d..c2fc6351db5b 100644
> --- a/drivers/ptp/ptp_netc.c
> +++ b/drivers/ptp/ptp_netc.c
> @@ -18,6 +18,8 @@
>  #define NETC_TMR_CTRL			0x0080
>  #define  TMR_CTRL_CK_SEL		GENMASK(1, 0)
>  #define  TMR_CTRL_TE			BIT(2)
> +#define  TMR_ETEP1			BIT(8)
> +#define  TMR_ETEP2			BIT(9)
>  #define  TMR_COMP_MODE			BIT(15)
>  #define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
>  #define  TMR_CTRL_FS			BIT(28)
> @@ -28,12 +30,26 @@
>  #define  TMR_TEVENT_PPEN_ALL		GENMASK(7, 5)
>  #define  TMR_TEVENT_ALM1EN		BIT(16)
>  #define  TMR_TEVENT_ALM2EN		BIT(17)
> +#define  TMR_TEVENT_ETS1_THREN		BIT(20)
> +#define  TMR_TEVENT_ETS2_THREN		BIT(21)
> +#define  TMR_TEVENT_ETS1EN		BIT(24)
> +#define  TMR_TEVENT_ETS2EN		BIT(25)
> +#define  TMR_TEVENT_ETS1_OVEN		BIT(28)
> +#define  TMR_TEVENT_ETS2_OVEN		BIT(29)
> +#define  TMR_TEVENT_ETS1		(TMR_TEVENT_ETS1_THREN | \
> +					 TMR_TEVENT_ETS1EN | TMR_TEVENT_ETS1_OVEN)
> +#define  TMR_TEVENT_ETS2		(TMR_TEVENT_ETS2_THREN | \
> +					 TMR_TEVENT_ETS2EN | TMR_TEVENT_ETS2_OVEN)
>
>  #define NETC_TMR_TEMASK			0x0088
> +#define NETC_TMR_STAT			0x0094
> +#define  TMR_STAT_ETS1_VLD		BIT(24)
> +#define  TMR_STAT_ETS2_VLD		BIT(25)
>  #define NETC_TMR_CNT_L			0x0098
>  #define NETC_TMR_CNT_H			0x009c
>  #define NETC_TMR_ADD			0x00a0
>  #define NETC_TMR_PRSC			0x00a8
> +#define NETC_TMR_ECTRL			0x00ac
>  #define NETC_TMR_OFF_L			0x00b0
>  #define NETC_TMR_OFF_H			0x00b4
>
> @@ -51,6 +67,10 @@
>  #define  FIPER_CTRL_PW(i)		(GENMASK(4, 0) << (i) * 8)
>  #define  FIPER_CTRL_SET_PW(i, v)	(((v) & GENMASK(4, 0)) << 8 * (i))
>
> +#define NETC_TMR_ETTS1_L		0x00e0
> +#define NETC_TMR_ETTS1_H		0x00e4
> +#define NETC_TMR_ETTS2_L		0x00e8
> +#define NETC_TMR_ETTS2_H		0x00ec
>  #define NETC_TMR_CUR_TIME_L		0x00f0
>  #define NETC_TMR_CUR_TIME_H		0x00f4
>
> @@ -67,6 +87,7 @@
>  #define NETC_TMR_DEFAULT_FIPER		GENMASK(31, 0)
>  #define NETC_TMR_FIPER_MAX_PW		GENMASK(4, 0)
>  #define NETC_TMR_ALARM_NUM		2
> +#define NETC_TMR_DEFAULT_ETTF_THR	7
>
>  /* 1588 timer reference clock source select */
>  #define NETC_TMR_CCM_TIMER1		0 /* enet_timer1_clk_root, from CCM */
> @@ -450,6 +471,91 @@ static int net_timer_enable_perout(struct netc_timer *priv,
>  	return err;
>  }
>
> +static void netc_timer_handle_etts_event(struct netc_timer *priv, int index,
> +					 bool update_event)
> +{
> +	u32 regoff_l, regoff_h, etts_l, etts_h, ets_vld;
> +	struct ptp_clock_event event;
> +
> +	switch (index) {
> +	case 0:
> +		ets_vld = TMR_STAT_ETS1_VLD;
> +		regoff_l = NETC_TMR_ETTS1_L;
> +		regoff_h = NETC_TMR_ETTS1_H;
> +		break;
> +	case 1:
> +		ets_vld = TMR_STAT_ETS2_VLD;
> +		regoff_l = NETC_TMR_ETTS2_L;
> +		regoff_h = NETC_TMR_ETTS2_H;
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	if (!(netc_timer_rd(priv, NETC_TMR_STAT) & ets_vld))
> +		return;
> +
> +	do {
> +		etts_l = netc_timer_rd(priv, regoff_l);
> +		etts_h = netc_timer_rd(priv, regoff_h);
> +	} while (netc_timer_rd(priv, NETC_TMR_STAT) & ets_vld);
> +
> +	if (update_event) {
> +		event.type = PTP_CLOCK_EXTTS;
> +		event.index = index;
> +		event.timestamp = (u64)etts_h << 32;
> +		event.timestamp |= etts_l;
> +		ptp_clock_event(priv->clock, &event);
> +	}
> +}
> +
> +static int netc_timer_enable_extts(struct netc_timer *priv,
> +				   struct ptp_clock_request *rq, int on)
> +{
> +	u32 ets_emask, tmr_emask, tmr_ctrl, ettp_bit;
> +	unsigned long flags;
> +
> +	/* Reject requests to enable time stamping on both edges */
> +	if ((rq->extts.flags & PTP_EXTTS_EDGES) == PTP_EXTTS_EDGES)
> +		return -EOPNOTSUPP;
> +
> +	switch (rq->extts.index) {
> +	case 0:
> +		ettp_bit = TMR_ETEP1;
> +		ets_emask = TMR_TEVENT_ETS1;
> +		break;
> +	case 1:
> +		ettp_bit = TMR_ETEP2;
> +		ets_emask = TMR_TEVENT_ETS2;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	netc_timer_handle_etts_event(priv, rq->extts.index, false);
> +	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
> +	if (on) {
> +		tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
> +		if (rq->extts.flags & PTP_FALLING_EDGE)
> +			tmr_ctrl |= ettp_bit;
> +		else
> +			tmr_ctrl &= ~ettp_bit;
> +
> +		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +		tmr_emask |= ets_emask;
> +	} else {
> +		tmr_emask &= ~ets_emask;
> +	}
> +
> +	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +
>  static void netc_timer_disable_fiper(struct netc_timer *priv)
>  {
>  	u32 fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> @@ -505,6 +611,8 @@ static int netc_timer_enable(struct ptp_clock_info *ptp,
>  		return netc_timer_enable_pps(priv, rq, on);
>  	case PTP_CLK_REQ_PEROUT:
>  		return net_timer_enable_perout(priv, rq, on);
> +	case PTP_CLK_REQ_EXTTS:
> +		return netc_timer_enable_extts(priv, rq, on);
>  	default:
>  		return -EOPNOTSUPP;
>  	}
> @@ -638,6 +746,9 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
>  	.n_pins		= 0,
>  	.pps		= 1,
>  	.n_per_out	= 3,
> +	.n_ext_ts	= 2,
> +	.supported_extts_flags = PTP_RISING_EDGE | PTP_FALLING_EDGE |
> +				 PTP_STRICT_FLAGS,
>  	.adjfine	= netc_timer_adjfine,
>  	.adjtime	= netc_timer_adjtime,
>  	.gettimex64	= netc_timer_gettimex64,
> @@ -670,6 +781,7 @@ static void netc_timer_init(struct netc_timer *priv)
>  		fiper_ctrl &= ~FIPER_CTRL_PG(i);
>  	}
>  	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +	netc_timer_wr(priv, NETC_TMR_ECTRL, NETC_TMR_DEFAULT_ETTF_THR);
>
>  	ktime_get_real_ts64(&now);
>  	ns = timespec64_to_ns(&now);
> @@ -822,6 +934,12 @@ static irqreturn_t netc_timer_isr(int irq, void *data)
>  		ptp_clock_event(priv->clock, &event);
>  	}
>
> +	if (tmr_event & TMR_TEVENT_ETS1)
> +		netc_timer_handle_etts_event(priv, 0, true);
> +
> +	if (tmr_event & TMR_TEVENT_ETS2)
> +		netc_timer_handle_etts_event(priv, 1, true);
> +
>  	/* Clear interrupts status */
>  	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
>
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 07/14] ptp: netc: add debugfs support to loop back pulse signal
  2025-07-16  7:31 ` [PATCH v2 net-next 07/14] ptp: netc: add debugfs support to loop back pulse signal Wei Fang
@ 2025-07-16 20:32   ` Frank Li
  0 siblings, 0 replies; 64+ messages in thread
From: Frank Li @ 2025-07-16 20:32 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:04PM +0800, Wei Fang wrote:
> The NETC Timer supports to loop back the output pulse signal of Fiper-n
> into Trigger-n input, so that we can leverage this feature to validate
> some other features without external hardware support. For example, we
> can use it to test external trigger stamp (EXTTS). And we can combine
> EXTTS with loopback mode to check whether the generation time of PPS is
> aligned with an integral second of PHC, or the periodic output signal
> (PTP_CLK_REQ_PEROUT) whether is generated at the specified time. So add
> the debugfs interfaces to enable the loopback mode of Fiper1 and Fiper2.
>
> An example to test the generation time of PPS event.
>
> $ echo 1 > /sys/kernel/debug/netc_timer0/fiper1-loopback
> $ echo 1 > /sys/class/ptp/ptp0/pps_enable
> $ testptp -d /dev/ptp0 -e 3
> external time stamp request okay
> event index 0 at 108.000000018
> event index 0 at 109.000000018
> event index 0 at 110.000000018
>
> An example to test the generation time of the periodic output signal.
>
> $ echo 1 > /sys/kernel/debug/netc_timer0/fiper1-loopback
> $ echo 0 260 0 1 500000000 > /sys/class/ptp/ptp0/period
> $ testptp -d /dev/ptp0 -e 3
> external time stamp request okay
> event index 0 at 260.000000016
> event index 0 at 261.500000015
> event index 0 at 263.000000016
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>

Reviewed-by: Frank Li <Frank.Li@nxp.com>

>
> ---
> v2 changes:
> 1. Remove the check of the return value of debugfs_create_dir()
> ---
>  drivers/ptp/ptp_netc.c | 114 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 114 insertions(+)
>
> diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> index c2fc6351db5b..2a077eb2f0eb 100644
> --- a/drivers/ptp/ptp_netc.c
> +++ b/drivers/ptp/ptp_netc.c
> @@ -6,6 +6,7 @@
>
>  #include <linux/bitfield.h>
>  #include <linux/clk.h>
> +#include <linux/debugfs.h>
>  #include <linux/fsl/netc_global.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
> @@ -22,6 +23,8 @@
>  #define  TMR_ETEP2			BIT(9)
>  #define  TMR_COMP_MODE			BIT(15)
>  #define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
> +#define  TMR_CTRL_PP2L			BIT(26)
> +#define  TMR_CTRL_PP1L			BIT(27)
>  #define  TMR_CTRL_FS			BIT(28)
>  #define  TMR_ALARM1P			BIT(31)
>
> @@ -129,6 +132,7 @@ struct netc_timer {
>  	u8 fs_alarm_num;
>  	u8 fs_alarm_bitmap;
>  	struct netc_pp pp[NETC_TMR_FIPER_NUM]; /* periodic pulse */
> +	struct dentry *debugfs_root;
>  };
>
>  #define netc_timer_rd(p, o)		netc_read((p)->base + (o))
> @@ -991,6 +995,114 @@ static int netc_timer_get_global_ip_rev(struct netc_timer *priv)
>  	return val & IPBRR0_IP_REV;
>  }
>
> +static int netc_timer_get_fiper_loopback(struct netc_timer *priv,
> +					 int fiper, u64 *val)
> +{
> +	unsigned long flags;
> +	u32 tmr_ctrl;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +	tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	switch (fiper) {
> +	case 0:
> +		*val = tmr_ctrl & TMR_CTRL_PP1L ? 1 : 0;
> +		break;
> +	case 1:
> +		*val = tmr_ctrl & TMR_CTRL_PP2L ? 1 : 0;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int netc_timer_set_fiper_loopback(struct netc_timer *priv,
> +					 int fiper, u64 val)
> +{
> +	unsigned long flags;
> +	u32 tmr_ctrl;
> +	int err = 0;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
> +	switch (fiper) {
> +	case 0:
> +		tmr_ctrl = u32_replace_bits(tmr_ctrl, val ? 1 : 0,
> +					    TMR_CTRL_PP1L);
> +		break;
> +	case 1:
> +		tmr_ctrl = u32_replace_bits(tmr_ctrl, val ? 1 : 0,
> +					    TMR_CTRL_PP2L);
> +		break;
> +	default:
> +		err = -EINVAL;
> +	}
> +
> +	if (!err)
> +		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return err;
> +}
> +
> +static int netc_timer_get_fiper1_loopback(void *data, u64 *val)
> +{
> +	struct netc_timer *priv = data;
> +
> +	return netc_timer_get_fiper_loopback(priv, 0, val);
> +}
> +
> +static int netc_timer_set_fiper1_loopback(void *data, u64 val)
> +{
> +	struct netc_timer *priv = data;
> +
> +	return netc_timer_set_fiper_loopback(priv, 0, val);
> +}
> +
> +DEFINE_DEBUGFS_ATTRIBUTE(netc_timer_fiper1_fops, netc_timer_get_fiper1_loopback,
> +			 netc_timer_set_fiper1_loopback, "%llu\n");
> +
> +static int netc_timer_get_fiper2_loopback(void *data, u64 *val)
> +{
> +	struct netc_timer *priv = data;
> +
> +	return netc_timer_get_fiper_loopback(priv, 1, val);
> +}
> +
> +static int netc_timer_set_fiper2_loopback(void *data, u64 val)
> +{
> +	struct netc_timer *priv = data;
> +
> +	return netc_timer_set_fiper_loopback(priv, 1, val);
> +}
> +
> +DEFINE_DEBUGFS_ATTRIBUTE(netc_timer_fiper2_fops, netc_timer_get_fiper2_loopback,
> +			 netc_timer_set_fiper2_loopback, "%llu\n");
> +
> +static void netc_timer_create_debugfs(struct netc_timer *priv)
> +{
> +	char debugfs_name[24];
> +
> +	snprintf(debugfs_name, sizeof(debugfs_name), "netc_timer%d",
> +		 priv->phc_index);
> +	priv->debugfs_root = debugfs_create_dir(debugfs_name, NULL);
> +	debugfs_create_file("fiper1-loopback", 0600, priv->debugfs_root,
> +			    priv, &netc_timer_fiper1_fops);
> +	debugfs_create_file("fiper2-loopback", 0600, priv->debugfs_root,
> +			    priv, &netc_timer_fiper2_fops);
> +}
> +
> +static void netc_timer_remove_debugfs(struct netc_timer *priv)
> +{
> +	debugfs_remove(priv->debugfs_root);
> +	priv->debugfs_root = NULL;
> +}
> +
>  static int netc_timer_probe(struct pci_dev *pdev,
>  			    const struct pci_device_id *id)
>  {
> @@ -1038,6 +1150,7 @@ static int netc_timer_probe(struct pci_dev *pdev,
>  	}
>
>  	priv->phc_index = ptp_clock_index(priv->clock);
> +	netc_timer_create_debugfs(priv);
>
>  	return 0;
>
> @@ -1055,6 +1168,7 @@ static void netc_timer_remove(struct pci_dev *pdev)
>  {
>  	struct netc_timer *priv = pci_get_drvdata(pdev);
>
> +	netc_timer_remove_debugfs(priv);
>  	ptp_clock_unregister(priv->clock);
>  	netc_timer_free_msix_irq(priv);
>  	clk_disable_unprepare(priv->src_clk);
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 08/14] MAINTAINERS: add NETC Timer PTP clock driver section
  2025-07-16  7:31 ` [PATCH v2 net-next 08/14] MAINTAINERS: add NETC Timer PTP clock driver section Wei Fang
@ 2025-07-16 20:33   ` Frank Li
  0 siblings, 0 replies; 64+ messages in thread
From: Frank Li @ 2025-07-16 20:33 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:05PM +0800, Wei Fang wrote:
> Add a section entry for NXP NETC Timer PTP clock driver.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>

Reviewed-by: Frank Li <Frank.Li@nxp.com>

> ---
>  MAINTAINERS | 9 +++++++++
>  1 file changed, 9 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d1554f33d0ac..dacc5824dca6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -18005,6 +18005,15 @@ F:	Documentation/devicetree/bindings/clock/imx*
>  F:	drivers/clk/imx/
>  F:	include/dt-bindings/clock/imx*
>
> +NXP NETC TIMER PTP CLOCK DRIVER
> +M:	Wei Fang <wei.fang@nxp.com>
> +M:	Clark Wang <xiaoning.wang@nxp.com>
> +L:	imx@lists.linux.dev
> +L:	netdev@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
> +F:	drivers/ptp/ptp_netc.c
> +
>  NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
>  M:	Jagan Teki <jagan@amarulasolutions.com>
>  S:	Maintained
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 09/14] net: enetc: save the parsed information of PTP packet to skb->cb
  2025-07-16  7:31 ` [PATCH v2 net-next 09/14] net: enetc: save the parsed information of PTP packet to skb->cb Wei Fang
@ 2025-07-16 20:46   ` Frank Li
  2025-07-17 12:20     ` Wei Fang
  0 siblings, 1 reply; 64+ messages in thread
From: Frank Li @ 2025-07-16 20:46 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:06PM +0800, Wei Fang wrote:
> Currently, the Tx PTP packets are parsed twice in the enetc driver, once
> in enetc_xmit() and once in enetc_map_tx_buffs(). The latter is duplicate
> and is unnecessary, since the parsed information can be saved to skb->cb
> so that enetc_map_tx_buffs() can get the previously parsed data from
> skb->cb. Therefore, we add struct enetc_skb_cb as the format of the data
> in the skb->cb buffer to save the parsed information of PTP packet.

Add struct enetc_skb_cb as the format of the data in the skb-cb buffer to
save the parsed information of PTP packet.

Use saved information in enetc_map_tx_buffs() to avoid parse data again.

>
> In addition, the variables offset1 and offset2 in enetc_map_tx_buffs()
> are renamed to corr_off and tstamp_off to make them easier to understand.

Also, rename variables offset1 and offset2 in enetc_map_tx_buffs() to
corr_off and tstamp_off for better readability.

>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
>
> ---
> v2 changes:
> 1. Add description of offset1 and offset2 being renamed in the commit
> message.
> ---
>  drivers/net/ethernet/freescale/enetc/enetc.c | 65 ++++++++++----------
>  drivers/net/ethernet/freescale/enetc/enetc.h |  9 +++
>  2 files changed, 43 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
> index e4287725832e..c1373163a096 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.c
> @@ -225,13 +225,12 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  {
>  	bool do_vlan, do_onestep_tstamp = false, do_twostep_tstamp = false;
>  	struct enetc_ndev_priv *priv = netdev_priv(tx_ring->ndev);
> +	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
>  	struct enetc_hw *hw = &priv->si->hw;
>  	struct enetc_tx_swbd *tx_swbd;
>  	int len = skb_headlen(skb);
>  	union enetc_tx_bd temp_bd;
> -	u8 msgtype, twostep, udp;
>  	union enetc_tx_bd *txbd;
> -	u16 offset1, offset2;
>  	int i, count = 0;
>  	skb_frag_t *frag;
>  	unsigned int f;
> @@ -280,16 +279,10 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  	count++;
>
>  	do_vlan = skb_vlan_tag_present(skb);
> -	if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
> -		if (enetc_ptp_parse(skb, &udp, &msgtype, &twostep, &offset1,
> -				    &offset2) ||
> -		    msgtype != PTP_MSGTYPE_SYNC || twostep)
> -			WARN_ONCE(1, "Bad packet for one-step timestamping\n");
> -		else
> -			do_onestep_tstamp = true;
> -	} else if (skb->cb[0] & ENETC_F_TX_TSTAMP) {
> +	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)
> +		do_onestep_tstamp = true;
> +	else if (enetc_cb->flag & ENETC_F_TX_TSTAMP)
>  		do_twostep_tstamp = true;
> -	}
>
>  	tx_swbd->do_twostep_tstamp = do_twostep_tstamp;
>  	tx_swbd->qbv_en = !!(priv->active_offloads & ENETC_F_QBV);
> @@ -333,6 +326,8 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  		}
>
>  		if (do_onestep_tstamp) {
> +			u16 tstamp_off = enetc_cb->origin_tstamp_off;
> +			u16 corr_off = enetc_cb->correction_off;
>  			__be32 new_sec_l, new_nsec;
>  			u32 lo, hi, nsec, val;
>  			__be16 new_sec_h;
> @@ -362,32 +357,32 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  			new_sec_h = htons((sec >> 32) & 0xffff);
>  			new_sec_l = htonl(sec & 0xffffffff);
>  			new_nsec = htonl(nsec);
> -			if (udp) {
> +			if (enetc_cb->udp) {
>  				struct udphdr *uh = udp_hdr(skb);
>  				__be32 old_sec_l, old_nsec;
>  				__be16 old_sec_h;
>
> -				old_sec_h = *(__be16 *)(data + offset2);
> +				old_sec_h = *(__be16 *)(data + tstamp_off);
>  				inet_proto_csum_replace2(&uh->check, skb, old_sec_h,
>  							 new_sec_h, false);
>
> -				old_sec_l = *(__be32 *)(data + offset2 + 2);
> +				old_sec_l = *(__be32 *)(data + tstamp_off + 2);
>  				inet_proto_csum_replace4(&uh->check, skb, old_sec_l,
>  							 new_sec_l, false);
>
> -				old_nsec = *(__be32 *)(data + offset2 + 6);
> +				old_nsec = *(__be32 *)(data + tstamp_off + 6);
>  				inet_proto_csum_replace4(&uh->check, skb, old_nsec,
>  							 new_nsec, false);
>  			}
>
> -			*(__be16 *)(data + offset2) = new_sec_h;
> -			*(__be32 *)(data + offset2 + 2) = new_sec_l;
> -			*(__be32 *)(data + offset2 + 6) = new_nsec;
> +			*(__be16 *)(data + tstamp_off) = new_sec_h;
> ++			*(__be32 *)(data + tstamp_off + 2) = new_sec_l;
> ++			*(__be32 *)(data + tstamp_off + 6) = new_nsec;

strange why there are two ++ here.

>
>  			/* Configure single-step register */
>  			val = ENETC_PM0_SINGLE_STEP_EN;
> -			val |= ENETC_SET_SINGLE_STEP_OFFSET(offset1);
> -			if (udp)
> +			val |= ENETC_SET_SINGLE_STEP_OFFSET(corr_off);
> +			if (enetc_cb->udp)
>  				val |= ENETC_PM0_SINGLE_STEP_CH;
>
>  			enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP,
> @@ -938,12 +933,13 @@ static int enetc_map_tx_tso_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb
>  static netdev_tx_t enetc_start_xmit(struct sk_buff *skb,
>  				    struct net_device *ndev)
>  {
> +	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
>  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
>  	struct enetc_bdr *tx_ring;
>  	int count;
>
>  	/* Queue one-step Sync packet if already locked */
> -	if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
> +	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
>  		if (test_and_set_bit_lock(ENETC_TX_ONESTEP_TSTAMP_IN_PROGRESS,
>  					  &priv->flags)) {
>  			skb_queue_tail(&priv->tx_skbs, skb);
> @@ -1005,24 +1001,29 @@ static netdev_tx_t enetc_start_xmit(struct sk_buff *skb,
>
>  netdev_tx_t enetc_xmit(struct sk_buff *skb, struct net_device *ndev)
>  {
> +	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
>  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
>  	u8 udp, msgtype, twostep;
>  	u16 offset1, offset2;
>
> -	/* Mark tx timestamp type on skb->cb[0] if requires */
> +	/* Mark tx timestamp type on enetc_cb->flag if requires */
>  	if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
> -	    (priv->active_offloads & ENETC_F_TX_TSTAMP_MASK)) {
> -		skb->cb[0] = priv->active_offloads & ENETC_F_TX_TSTAMP_MASK;
> -	} else {
> -		skb->cb[0] = 0;
> -	}
> +	    (priv->active_offloads & ENETC_F_TX_TSTAMP_MASK))
> +		enetc_cb->flag = priv->active_offloads & ENETC_F_TX_TSTAMP_MASK;
> +	else
> +		enetc_cb->flag = 0;
>
>  	/* Fall back to two-step timestamp if not one-step Sync packet */
> -	if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
> +	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
>  		if (enetc_ptp_parse(skb, &udp, &msgtype, &twostep,
>  				    &offset1, &offset2) ||
> -		    msgtype != PTP_MSGTYPE_SYNC || twostep != 0)
> -			skb->cb[0] = ENETC_F_TX_TSTAMP;
> +		    msgtype != PTP_MSGTYPE_SYNC || twostep != 0) {
> +			enetc_cb->flag = ENETC_F_TX_TSTAMP;
> +		} else {
> +			enetc_cb->udp = !!udp;
> +			enetc_cb->correction_off = offset1;
> +			enetc_cb->origin_tstamp_off = offset2;
> +		}
>  	}
>
>  	return enetc_start_xmit(skb, ndev);
> @@ -1214,7 +1215,9 @@ static bool enetc_clean_tx_ring(struct enetc_bdr *tx_ring, int napi_budget)
>  		if (xdp_frame) {
>  			xdp_return_frame(xdp_frame);
>  		} else if (skb) {
> -			if (unlikely(skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)) {
> +			struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
> +
> +			if (unlikely(enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)) {
>  				/* Start work to release lock for next one-step
>  				 * timestamping packet. And send one skb in
>  				 * tx_skbs queue if has.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
> index 62e8ee4d2f04..ce3fed95091b 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.h
> @@ -54,6 +54,15 @@ struct enetc_tx_swbd {
>  	u8 qbv_en:1;
>  };
>
> +struct enetc_skb_cb {
> +	u8 flag;
> +	bool udp;
> +	u16 correction_off;
> +	u16 origin_tstamp_off;
> +};
> +
> +#define ENETC_SKB_CB(skb) ((struct enetc_skb_cb *)((skb)->cb))
> +
>  struct enetc_lso_t {
>  	bool	ipv6;
>  	bool	tcp;
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 10/14] net: enetc: Add enetc_update_ptp_sync_msg() to process PTP sync packet
  2025-07-16  7:31 ` [PATCH v2 net-next 10/14] net: enetc: Add enetc_update_ptp_sync_msg() to process PTP sync packet Wei Fang
@ 2025-07-16 20:49   ` Frank Li
  0 siblings, 0 replies; 64+ messages in thread
From: Frank Li @ 2025-07-16 20:49 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:07PM +0800, Wei Fang wrote:
> Currently, the PTP Sync packets are processed in enetc_map_tx_buffs(),
> which makes the function too long and not concise enough. Secondly,
> for the upcoming ENETC v4 one-step support, some appropriate changes
> are also needed. Therefore, enetc_update_ptp_sync_msg() is extracted
> from enetc_map_tx_buffs() as a helper function to process the PTP Sync
> packets.


net: enetc: Extract enetc_update_ptp_sync_msg() to handle PTP Sync packets

Move PTP Sync packet processing from enetc_map_tx_buffs() to a new helper
function enetc_update_ptp_sync_msg() to simplify the original function.

Prepare for upcoming ENETC v4 one-step support.

Frank


>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
> ---
>  drivers/net/ethernet/freescale/enetc/enetc.c  | 129 ++++++++++--------
>  .../net/ethernet/freescale/enetc/enetc_hw.h   |   1 +
>  2 files changed, 71 insertions(+), 59 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
> index c1373163a096..ef002ed2fdb9 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.c
> @@ -221,12 +221,79 @@ static void enetc_unwind_tx_frame(struct enetc_bdr *tx_ring, int count, int i)
>  	}
>  }
>
> +static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
> +				     struct sk_buff *skb)
> +{
> +	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
> +	u16 tstamp_off = enetc_cb->origin_tstamp_off;
> +	u16 corr_off = enetc_cb->correction_off;
> +	struct enetc_si *si = priv->si;
> +	struct enetc_hw *hw = &si->hw;
> +	__be32 new_sec_l, new_nsec;
> +	__be16 new_sec_h;
> +	u32 lo, hi, nsec;
> +	u8 *data;
> +	u64 sec;
> +	u32 val;
> +
> +	lo = enetc_rd_hot(hw, ENETC_SICTR0);
> +	hi = enetc_rd_hot(hw, ENETC_SICTR1);
> +	sec = (u64)hi << 32 | lo;
> +	nsec = do_div(sec, 1000000000);
> +
> +	/* Update originTimestamp field of Sync packet
> +	 * - 48 bits seconds field
> +	 * - 32 bits nanseconds field
> +	 *
> +	 * In addition, the UDP checksum needs to be updated
> +	 * by software after updating originTimestamp field,
> +	 * otherwise the hardware will calculate the wrong
> +	 * checksum when updating the correction field and
> +	 * update it to the packet.
> +	 */
> +
> +	data = skb_mac_header(skb);
> +	new_sec_h = htons((sec >> 32) & 0xffff);
> +	new_sec_l = htonl(sec & 0xffffffff);
> +	new_nsec = htonl(nsec);
> +	if (enetc_cb->udp) {
> +		struct udphdr *uh = udp_hdr(skb);
> +		__be32 old_sec_l, old_nsec;
> +		__be16 old_sec_h;
> +
> +		old_sec_h = *(__be16 *)(data + tstamp_off);
> +		inet_proto_csum_replace2(&uh->check, skb, old_sec_h,
> +					 new_sec_h, false);
> +
> +		old_sec_l = *(__be32 *)(data + tstamp_off + 2);
> +		inet_proto_csum_replace4(&uh->check, skb, old_sec_l,
> +					 new_sec_l, false);
> +
> +		old_nsec = *(__be32 *)(data + tstamp_off + 6);
> +		inet_proto_csum_replace4(&uh->check, skb, old_nsec,
> +					 new_nsec, false);
> +	}
> +
> +	*(__be16 *)(data + tstamp_off) = new_sec_h;
> +	*(__be32 *)(data + tstamp_off + 2) = new_sec_l;
> +	*(__be32 *)(data + tstamp_off + 6) = new_nsec;
> +
> +	/* Configure single-step register */
> +	val = ENETC_PM0_SINGLE_STEP_EN;
> +	val |= ENETC_SET_SINGLE_STEP_OFFSET(corr_off);
> +	if (enetc_cb->udp)
> +		val |= ENETC_PM0_SINGLE_STEP_CH;
> +
> +	enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP, val);
> +
> +	return lo & ENETC_TXBD_TSTAMP;
> +}
> +
>  static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  {
>  	bool do_vlan, do_onestep_tstamp = false, do_twostep_tstamp = false;
>  	struct enetc_ndev_priv *priv = netdev_priv(tx_ring->ndev);
>  	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
> -	struct enetc_hw *hw = &priv->si->hw;
>  	struct enetc_tx_swbd *tx_swbd;
>  	int len = skb_headlen(skb);
>  	union enetc_tx_bd temp_bd;
> @@ -326,67 +393,11 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  		}
>
>  		if (do_onestep_tstamp) {
> -			u16 tstamp_off = enetc_cb->origin_tstamp_off;
> -			u16 corr_off = enetc_cb->correction_off;
> -			__be32 new_sec_l, new_nsec;
> -			u32 lo, hi, nsec, val;
> -			__be16 new_sec_h;
> -			u8 *data;
> -			u64 sec;
> -
> -			lo = enetc_rd_hot(hw, ENETC_SICTR0);
> -			hi = enetc_rd_hot(hw, ENETC_SICTR1);
> -			sec = (u64)hi << 32 | lo;
> -			nsec = do_div(sec, 1000000000);
> +			u32 tstamp = enetc_update_ptp_sync_msg(priv, skb);
>
>  			/* Configure extension BD */
> -			temp_bd.ext.tstamp = cpu_to_le32(lo & 0x3fffffff);
> +			temp_bd.ext.tstamp = cpu_to_le32(tstamp);
>  			e_flags |= ENETC_TXBD_E_FLAGS_ONE_STEP_PTP;
> -
> -			/* Update originTimestamp field of Sync packet
> -			 * - 48 bits seconds field
> -			 * - 32 bits nanseconds field
> -			 *
> -			 * In addition, the UDP checksum needs to be updated
> -			 * by software after updating originTimestamp field,
> -			 * otherwise the hardware will calculate the wrong
> -			 * checksum when updating the correction field and
> -			 * update it to the packet.
> -			 */
> -			data = skb_mac_header(skb);
> -			new_sec_h = htons((sec >> 32) & 0xffff);
> -			new_sec_l = htonl(sec & 0xffffffff);
> -			new_nsec = htonl(nsec);
> -			if (enetc_cb->udp) {
> -				struct udphdr *uh = udp_hdr(skb);
> -				__be32 old_sec_l, old_nsec;
> -				__be16 old_sec_h;
> -
> -				old_sec_h = *(__be16 *)(data + tstamp_off);
> -				inet_proto_csum_replace2(&uh->check, skb, old_sec_h,
> -							 new_sec_h, false);
> -
> -				old_sec_l = *(__be32 *)(data + tstamp_off + 2);
> -				inet_proto_csum_replace4(&uh->check, skb, old_sec_l,
> -							 new_sec_l, false);
> -
> -				old_nsec = *(__be32 *)(data + tstamp_off + 6);
> -				inet_proto_csum_replace4(&uh->check, skb, old_nsec,
> -							 new_nsec, false);
> -			}
> -
> -			*(__be16 *)(data + tstamp_off) = new_sec_h;
> -+			*(__be32 *)(data + tstamp_off + 2) = new_sec_l;
> -+			*(__be32 *)(data + tstamp_off + 6) = new_nsec;
> -
> -			/* Configure single-step register */
> -			val = ENETC_PM0_SINGLE_STEP_EN;
> -			val |= ENETC_SET_SINGLE_STEP_OFFSET(corr_off);
> -			if (enetc_cb->udp)
> -				val |= ENETC_PM0_SINGLE_STEP_CH;
> -
> -			enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP,
> -					  val);
>  		} else if (do_twostep_tstamp) {
>  			skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
>  			e_flags |= ENETC_TXBD_E_FLAGS_TWO_STEP_PTP;
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
> index 73763e8f4879..377c96325814 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
> @@ -614,6 +614,7 @@ enum enetc_txbd_flags {
>  #define ENETC_TXBD_STATS_WIN	BIT(7)
>  #define ENETC_TXBD_TXSTART_MASK GENMASK(24, 0)
>  #define ENETC_TXBD_FLAGS_OFFSET 24
> +#define ENETC_TXBD_TSTAMP	GENMASK(29, 0)
>
>  static inline __le32 enetc_txbd_set_tx_start(u64 tx_start, u8 flags)
>  {
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 11/14] net: enetc: remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check
  2025-07-16  7:31 ` [PATCH v2 net-next 11/14] net: enetc: remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check Wei Fang
@ 2025-07-16 20:50   ` Frank Li
  0 siblings, 0 replies; 64+ messages in thread
From: Frank Li @ 2025-07-16 20:50 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:08PM +0800, Wei Fang wrote:
> The ENETC_F_RX_TSTAMP flag of priv->active_offloads can only be set when
> CONFIG_FSL_ENETC_PTP_CLOCK is enabled. Similarly, rx_ring->ext_en can
> only be set when CONFIG_FSL_ENETC_PTP_CLOCK is enabled as well. So it is
> safe to remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
> Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>

Reviewed-by: Frank Li <Frank.Li@nxp.com>

> ---
>  drivers/net/ethernet/freescale/enetc/enetc.c | 3 +--
>  drivers/net/ethernet/freescale/enetc/enetc.h | 4 ++--
>  2 files changed, 3 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
> index ef002ed2fdb9..4325eb3d9481 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.c
> @@ -1411,8 +1411,7 @@ static void enetc_get_offloads(struct enetc_bdr *rx_ring,
>  		__vlan_hwaccel_put_tag(skb, tpid, le16_to_cpu(rxbd->r.vlan_opt));
>  	}
>
> -	if (IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK) &&
> -	    (priv->active_offloads & ENETC_F_RX_TSTAMP))
> +	if (priv->active_offloads & ENETC_F_RX_TSTAMP)
>  		enetc_get_rx_tstamp(rx_ring->ndev, rxbd, skb);
>  }
>
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
> index ce3fed95091b..c65aa7b88122 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.h
> @@ -226,7 +226,7 @@ static inline union enetc_rx_bd *enetc_rxbd(struct enetc_bdr *rx_ring, int i)
>  {
>  	int hw_idx = i;
>
> -	if (IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK) && rx_ring->ext_en)
> +	if (rx_ring->ext_en)
>  		hw_idx = 2 * i;
>
>  	return &(((union enetc_rx_bd *)rx_ring->bd_base)[hw_idx]);
> @@ -240,7 +240,7 @@ static inline void enetc_rxbd_next(struct enetc_bdr *rx_ring,
>
>  	new_rxbd++;
>
> -	if (IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK) && rx_ring->ext_en)
> +	if (rx_ring->ext_en)
>  		new_rxbd++;
>
>  	if (unlikely(++new_index == rx_ring->bd_count)) {
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 12/14] net: enetc: add PTP synchronization support for ENETC v4
  2025-07-16  7:31 ` [PATCH v2 net-next 12/14] net: enetc: add PTP synchronization support for ENETC v4 Wei Fang
@ 2025-07-16 21:01   ` Frank Li
  2025-07-17 12:35     ` Wei Fang
  0 siblings, 1 reply; 64+ messages in thread
From: Frank Li @ 2025-07-16 21:01 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:09PM +0800, Wei Fang wrote:
> Regarding PTP, ENETC v4 has some changes compared to ENETC v1 (LS1028A),
> mainly as follows.
>
> 1. ENETC v4 uses a different PTP driver, so the way to get phc_index is
> different from LS1028A. Therefore, enetc_get_ts_info() has been modified
> appropriately to be compatible with ENETC v1 and v4.
>
> 2. The hardware of ENETC v4 does not support "dma-coherent", therefore,
> to support PTP one-step, the PTP sync packets must be modified before
> calling dma_map_single() to map the DMA cache of the packets. Otherwise,
> the modification is invalid, the originTimestamp and correction fields
> of the sent packets will still be the values before the modification.
>
> 3. The PMa_SINGLE_STEP register has changed in ENETC v4, not only the
> register offset, but also some register fields. Therefore, two helper
> functions are added, enetc_set_one_step_ts() for ENETC v1 and
> enetc4_set_one_step_ts() for ENETC v4.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
>
> ---
> v2 changes:
> 1. Move the definition of enetc_ptp_clock_is_enabled() to resolve build
> errors.
> 2. Add parsing of "nxp,netc-timer" property to get PCIe device of NETC
> Timer.
> ---
>  drivers/net/ethernet/freescale/enetc/enetc.c  | 55 +++++++----
>  drivers/net/ethernet/freescale/enetc/enetc.h  |  8 ++
>  .../net/ethernet/freescale/enetc/enetc4_hw.h  |  6 ++
>  .../net/ethernet/freescale/enetc/enetc4_pf.c  |  3 +
>  .../ethernet/freescale/enetc/enetc_ethtool.c  | 92 ++++++++++++++++---
>  5 files changed, 135 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
> index 4325eb3d9481..6e04dd825a95 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.c
> @@ -221,6 +221,31 @@ static void enetc_unwind_tx_frame(struct enetc_bdr *tx_ring, int count, int i)
>  	}
>  }
>
> +static void enetc_set_one_step_ts(struct enetc_si *si, bool udp, int offset)
> +{
> +	u32 val = ENETC_PM0_SINGLE_STEP_EN;
> +
> +	val |= ENETC_SET_SINGLE_STEP_OFFSET(offset);
> +	if (udp)
> +		val |= ENETC_PM0_SINGLE_STEP_CH;
> +
> +	/* the "Correction" field of a packet is updated based on the
> +	 * current time and the timestamp provided
> +	 */
> +	enetc_port_mac_wr(si, ENETC_PM0_SINGLE_STEP, val);
> +}
> +
> +static void enetc4_set_one_step_ts(struct enetc_si *si, bool udp, int offset)
> +{
> +	u32 val = PM_SINGLE_STEP_EN;
> +
> +	val |= PM_SINGLE_STEP_OFFSET_SET(offset);
> +	if (udp)
> +		val |= PM_SINGLE_STEP_CH;
> +
> +	enetc_port_mac_wr(si, ENETC4_PM_SINGLE_STEP(0), val);
> +}
> +
>  static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
>  				     struct sk_buff *skb)
>  {
> @@ -234,7 +259,6 @@ static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
>  	u32 lo, hi, nsec;
>  	u8 *data;
>  	u64 sec;
> -	u32 val;
>
>  	lo = enetc_rd_hot(hw, ENETC_SICTR0);
>  	hi = enetc_rd_hot(hw, ENETC_SICTR1);
> @@ -279,12 +303,10 @@ static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
>  	*(__be32 *)(data + tstamp_off + 6) = new_nsec;
>
>  	/* Configure single-step register */
> -	val = ENETC_PM0_SINGLE_STEP_EN;
> -	val |= ENETC_SET_SINGLE_STEP_OFFSET(corr_off);
> -	if (enetc_cb->udp)
> -		val |= ENETC_PM0_SINGLE_STEP_CH;
> -
> -	enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP, val);
> +	if (is_enetc_rev1(si))
> +		enetc_set_one_step_ts(si, enetc_cb->udp, corr_off);
> +	else
> +		enetc4_set_one_step_ts(si, enetc_cb->udp, corr_off);

Can you use callback function to avoid change this logic when new version
appear in future?

>
>  	return lo & ENETC_TXBD_TSTAMP;
>  }
> @@ -303,6 +325,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  	unsigned int f;
>  	dma_addr_t dma;
>  	u8 flags = 0;
> +	u32 tstamp;
>
>  	enetc_clear_tx_bd(&temp_bd);
>  	if (skb->ip_summed == CHECKSUM_PARTIAL) {
> @@ -327,6 +350,13 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  		}
>  	}
>
> +	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
> +		do_onestep_tstamp = true;
> +		tstamp = enetc_update_ptp_sync_msg(priv, skb);
> +	} else if (enetc_cb->flag & ENETC_F_TX_TSTAMP) {
> +		do_twostep_tstamp = true;
> +	}
> +
>  	i = tx_ring->next_to_use;
>  	txbd = ENETC_TXBD(*tx_ring, i);
>  	prefetchw(txbd);
> @@ -346,11 +376,6 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  	count++;
>
>  	do_vlan = skb_vlan_tag_present(skb);
> -	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)
> -		do_onestep_tstamp = true;
> -	else if (enetc_cb->flag & ENETC_F_TX_TSTAMP)
> -		do_twostep_tstamp = true;
> -

why need move this block up?

>  	tx_swbd->do_twostep_tstamp = do_twostep_tstamp;
>  	tx_swbd->qbv_en = !!(priv->active_offloads & ENETC_F_QBV);
>  	tx_swbd->check_wb = tx_swbd->do_twostep_tstamp || tx_swbd->qbv_en;
> @@ -393,8 +418,6 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  		}
>
>  		if (do_onestep_tstamp) {
> -			u32 tstamp = enetc_update_ptp_sync_msg(priv, skb);
> -
>  			/* Configure extension BD */
>  			temp_bd.ext.tstamp = cpu_to_le32(tstamp);
>  			e_flags |= ENETC_TXBD_E_FLAGS_ONE_STEP_PTP;
> @@ -3314,7 +3337,7 @@ int enetc_hwtstamp_set(struct net_device *ndev,
>  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
>  	int err, new_offloads = priv->active_offloads;
>
> -	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
> +	if (!enetc_ptp_clock_is_enabled(priv->si))
>  		return -EOPNOTSUPP;
>
>  	switch (config->tx_type) {
> @@ -3364,7 +3387,7 @@ int enetc_hwtstamp_get(struct net_device *ndev,
>  {
>  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
>
> -	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
> +	if (!enetc_ptp_clock_is_enabled(priv->si))
>  		return -EOPNOTSUPP;
>
>  	if (priv->active_offloads & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
> index c65aa7b88122..6bacd851358c 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.h
> @@ -598,6 +598,14 @@ static inline void enetc_cbd_free_data_mem(struct enetc_si *si, int size,
>  void enetc_reset_ptcmsdur(struct enetc_hw *hw);
>  void enetc_set_ptcmsdur(struct enetc_hw *hw, u32 *queue_max_sdu);
>
> +static inline bool enetc_ptp_clock_is_enabled(struct enetc_si *si)
> +{
> +	if (is_enetc_rev1(si))
> +		return IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK);
> +
> +	return IS_ENABLED(CONFIG_PTP_1588_CLOCK_NETC);
> +}
> +

why v1 check CONFIG_FSL_ENETC_PTP_CLOCK and other check
CONFIG_PTP_1588_CLOCK_NETC

Frank
>  #ifdef CONFIG_FSL_ENETC_QOS
>  int enetc_qos_query_caps(struct net_device *ndev, void *type_data);
>  int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
> index aa25b445d301..a8113c9057eb 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
> @@ -171,6 +171,12 @@
>  /* Port MAC 0/1 Pause Quanta Threshold Register */
>  #define ENETC4_PM_PAUSE_THRESH(mac)	(0x5064 + (mac) * 0x400)
>
> +#define ENETC4_PM_SINGLE_STEP(mac)	(0x50c0 + (mac) * 0x400)
> +#define  PM_SINGLE_STEP_CH		BIT(6)
> +#define  PM_SINGLE_STEP_OFFSET		GENMASK(15, 7)
> +#define   PM_SINGLE_STEP_OFFSET_SET(o)  FIELD_PREP(PM_SINGLE_STEP_OFFSET, o)
> +#define  PM_SINGLE_STEP_EN		BIT(31)
> +
>  /* Port MAC 0 Interface Mode Control Register */
>  #define ENETC4_PM_IF_MODE(mac)		(0x5300 + (mac) * 0x400)
>  #define  PM_IF_MODE_IFMODE		GENMASK(2, 0)
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> index b3dc1afeefd1..107f59169e67 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> @@ -569,6 +569,9 @@ static const struct net_device_ops enetc4_ndev_ops = {
>  	.ndo_set_features	= enetc4_pf_set_features,
>  	.ndo_vlan_rx_add_vid	= enetc_vlan_rx_add_vid,
>  	.ndo_vlan_rx_kill_vid	= enetc_vlan_rx_del_vid,
> +	.ndo_eth_ioctl		= enetc_ioctl,
> +	.ndo_hwtstamp_get	= enetc_hwtstamp_get,
> +	.ndo_hwtstamp_set	= enetc_hwtstamp_set,
>  };
>
>  static struct phylink_pcs *
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
> index 961e76cd8489..404dcb102b47 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
> @@ -2,8 +2,11 @@
>  /* Copyright 2017-2019 NXP */
>
>  #include <linux/ethtool_netlink.h>
> +#include <linux/fsl/netc_global.h>
>  #include <linux/net_tstamp.h>
>  #include <linux/module.h>
> +#include <linux/of.h>
> +
>  #include "enetc.h"
>
>  static const u32 enetc_si_regs[] = {
> @@ -877,23 +880,49 @@ static int enetc_set_coalesce(struct net_device *ndev,
>  	return 0;
>  }
>
> -static int enetc_get_ts_info(struct net_device *ndev,
> -			     struct kernel_ethtool_ts_info *info)
> +static struct pci_dev *enetc4_get_default_timer_pdev(struct enetc_si *si)
>  {
> -	struct enetc_ndev_priv *priv = netdev_priv(ndev);
> -	int *phc_idx;
> -
> -	phc_idx = symbol_get(enetc_phc_index);
> -	if (phc_idx) {
> -		info->phc_index = *phc_idx;
> -		symbol_put(enetc_phc_index);
> +	struct pci_bus *bus = si->pdev->bus;
> +	int domain = pci_domain_nr(bus);
> +	int bus_num = bus->number;
> +	int devfn;
> +
> +	switch (si->revision) {
> +	case ENETC_REV_4_1:
> +		devfn = PCI_DEVFN(24, 0);
> +		break;
> +	default:
> +		return NULL;
>  	}
>
> -	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK)) {
> -		info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE;
> +	return pci_dev_get(pci_get_domain_bus_and_slot(domain, bus_num, devfn));
> +}
>
> -		return 0;
> -	}
> +static struct pci_dev *enetc4_get_timer_pdev(struct enetc_si *si)
> +{
> +	struct device_node *np = si->pdev->dev.of_node;
> +	struct fwnode_handle *timer_fwnode;
> +	struct device_node *timer_np;
> +
> +	if (!np)
> +		return enetc4_get_default_timer_pdev(si);
> +
> +	timer_np = of_parse_phandle(np, "nxp,netc-timer", 0);
> +	if (!timer_np)
> +		return enetc4_get_default_timer_pdev(si);
> +
> +	timer_fwnode = of_fwnode_handle(timer_np);
> +	of_node_put(timer_np);
> +	if (!timer_fwnode)
> +		return NULL;
> +
> +	return pci_dev_get(to_pci_dev(timer_fwnode->dev));
> +}
> +
> +static void enetc_get_ts_generic_info(struct net_device *ndev,
> +				      struct kernel_ethtool_ts_info *info)
> +{
> +	struct enetc_ndev_priv *priv = netdev_priv(ndev);
>
>  	info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
>  				SOF_TIMESTAMPING_RX_HARDWARE |
> @@ -908,6 +937,42 @@ static int enetc_get_ts_info(struct net_device *ndev,
>
>  	info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
>  			   (1 << HWTSTAMP_FILTER_ALL);
> +}
> +
> +static int enetc_get_ts_info(struct net_device *ndev,
> +			     struct kernel_ethtool_ts_info *info)
> +{
> +	struct enetc_ndev_priv *priv = netdev_priv(ndev);
> +	struct enetc_si *si = priv->si;
> +	struct pci_dev *timer_pdev;
> +	int *phc_idx;
> +
> +	if (!enetc_ptp_clock_is_enabled(si))
> +		goto timestamp_tx_sw;
> +
> +	if (is_enetc_rev1(si)) {
> +		phc_idx = symbol_get(enetc_phc_index);
> +		if (phc_idx) {
> +			info->phc_index = *phc_idx;
> +			symbol_put(enetc_phc_index);
> +		}
> +	} else {
> +		timer_pdev = enetc4_get_timer_pdev(si);
> +		if (!timer_pdev)
> +			goto timestamp_tx_sw;
> +
> +		info->phc_index = netc_timer_get_phc_index(timer_pdev);
> +		pci_dev_put(timer_pdev);
> +		if (info->phc_index < 0)
> +			goto timestamp_tx_sw;
> +	}
> +
> +	enetc_get_ts_generic_info(ndev, info);
> +
> +	return 0;
> +
> +timestamp_tx_sw:
> +	info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE;
>
>  	return 0;
>  }
> @@ -1296,6 +1361,7 @@ const struct ethtool_ops enetc4_pf_ethtool_ops = {
>  	.get_rxfh = enetc_get_rxfh,
>  	.set_rxfh = enetc_set_rxfh,
>  	.get_rxfh_fields = enetc_get_rxfh_fields,
> +	.get_ts_info = enetc_get_ts_info,
>  };
>
>  void enetc_set_ethtool_ops(struct net_device *ndev)
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 13/14] net: enetc: don't update sync packet checksum if checksum offload is used
  2025-07-16  7:31 ` [PATCH v2 net-next 13/14] net: enetc: don't update sync packet checksum if checksum offload is used Wei Fang
@ 2025-07-16 21:03   ` Frank Li
  0 siblings, 0 replies; 64+ messages in thread
From: Frank Li @ 2025-07-16 21:03 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:10PM +0800, Wei Fang wrote:
> For ENETC v4, the hardware has the capability to support Tx checksum
> offload. so the enetc driver does not need to update the UDP checksum
> of PTP sync packets if Tx checksum offload is enabled.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>

Reviewed-by: Frank Li <Frank.Li@nxp.com>
> ---
>  drivers/net/ethernet/freescale/enetc/enetc.c | 17 +++++++++--------
>  1 file changed, 9 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
> index 6e04dd825a95..cf72d50246a9 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.c
> @@ -247,7 +247,7 @@ static void enetc4_set_one_step_ts(struct enetc_si *si, bool udp, int offset)
>  }
>
>  static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
> -				     struct sk_buff *skb)
> +				     struct sk_buff *skb, bool csum_offload)
>  {
>  	struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
>  	u16 tstamp_off = enetc_cb->origin_tstamp_off;
> @@ -269,18 +269,17 @@ static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
>  	 * - 48 bits seconds field
>  	 * - 32 bits nanseconds field
>  	 *
> -	 * In addition, the UDP checksum needs to be updated
> -	 * by software after updating originTimestamp field,
> -	 * otherwise the hardware will calculate the wrong
> -	 * checksum when updating the correction field and
> -	 * update it to the packet.
> +	 * In addition, if csum_offload is false, the UDP checksum needs
> +	 * to be updated by software after updating originTimestamp field,
> +	 * otherwise the hardware will calculate the wrong checksum when
> +	 * updating the correction field and update it to the packet.
>  	 */
>
>  	data = skb_mac_header(skb);
>  	new_sec_h = htons((sec >> 32) & 0xffff);
>  	new_sec_l = htonl(sec & 0xffffffff);
>  	new_nsec = htonl(nsec);
> -	if (enetc_cb->udp) {
> +	if (enetc_cb->udp && !csum_offload) {
>  		struct udphdr *uh = udp_hdr(skb);
>  		__be32 old_sec_l, old_nsec;
>  		__be16 old_sec_h;
> @@ -319,6 +318,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  	struct enetc_tx_swbd *tx_swbd;
>  	int len = skb_headlen(skb);
>  	union enetc_tx_bd temp_bd;
> +	bool csum_offload = false;
>  	union enetc_tx_bd *txbd;
>  	int i, count = 0;
>  	skb_frag_t *frag;
> @@ -345,6 +345,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>  				temp_bd.l4_aux = FIELD_PREP(ENETC_TX_BD_L4T,
>  							    ENETC_TXBD_L4T_UDP);
>  			flags |= ENETC_TXBD_FLAGS_CSUM_LSO | ENETC_TXBD_FLAGS_L4CS;
> +			csum_offload = true;
>  		} else if (skb_checksum_help(skb)) {
>  			return 0;
>  		}
> @@ -352,7 +353,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
>
>  	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
>  		do_onestep_tstamp = true;
> -		tstamp = enetc_update_ptp_sync_msg(priv, skb);
> +		tstamp = enetc_update_ptp_sync_msg(priv, skb, csum_offload);
>  	} else if (enetc_cb->flag & ENETC_F_TX_TSTAMP) {
>  		do_twostep_tstamp = true;
>  	}
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 14/14] arm64: dts: imx95: Add NETC Timer support
  2025-07-16  7:31 ` [PATCH v2 14/14] arm64: dts: imx95: Add NETC Timer support Wei Fang
@ 2025-07-16 21:04   ` Frank Li
  0 siblings, 0 replies; 64+ messages in thread
From: Frank Li @ 2025-07-16 21:04 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:11PM +0800, Wei Fang wrote:
> Enable NETC Timer to provide precise periodic pulse, time capture on
> external pulse and PTP synchronization support.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>

Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
> ---
> v2 changes:
> new patch
> ---
>  arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts | 4 ++++
>  arch/arm64/boot/dts/freescale/imx95.dtsi          | 1 +
>  2 files changed, 5 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
> index 6886ea766655..9a119d788c1e 100644
> --- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
> +++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
> @@ -418,6 +418,10 @@ ethphy0: ethernet-phy@1 {
>  	};
>  };
>
> +&netc_timer {
> +	status = "okay";
> +};
> +
>  &pcie0 {
>  	pinctrl-0 = <&pinctrl_pcie0>;
>  	pinctrl-names = "default";
> diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi
> index 632631a29112..04be9fb8cb31 100644
> --- a/arch/arm64/boot/dts/freescale/imx95.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx95.dtsi
> @@ -1893,6 +1893,7 @@ enetc_port2: ethernet@10,0 {
>  				};
>
>  				netc_timer: ethernet@18,0 {
> +					compatible = "pci1131,ee02";
>  					reg = <0x00c000 0 0 0 0>;
>  					status = "disabled";
>  				};
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-16 19:28   ` Frank Li
@ 2025-07-17  3:23     ` Wei Fang
  0 siblings, 0 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-17  3:23 UTC (permalink / raw)
  To: Frank Li, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org
  Cc: richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> On Wed, Jul 16, 2025 at 03:30:59PM +0800, Wei Fang wrote:
> > NETC is a multi-function PCIe Root Complex Integrated Endpoint (RCiEP)
> > that contains multiple PCIe functions, such as ENETC and Timer. Timer
> > provides PTP time synchronization functionality and ENETC provides the
> > NIC functionality.
> >
> > For some platforms, such as i.MX95, it has only one timer instance, so
> > the binding relationship between Timer and ENETC is fixed. But for
> > some platforms, such as i.MX943, it has 3 Timer instances, by setting
> > the EaTBCR registers of the IERB module, we can specify any Timer
> > instance to be bound to the ENETC instance.
> >
> > Therefore, add "nxp,netc-timer" property to bind ENETC instance to a
> > specified Timer instance so that ENETC can support PTP synchronization
> > through Timer.
> >
> > Signed-off-by: Wei Fang <wei.fang@nxp.com>
> >
> > ---
> > v2 changes:
> > new patch
> > ---
> >  .../devicetree/bindings/net/fsl,enetc.yaml    | 23 +++++++++++++++++++
> >  1 file changed, 23 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > index ca70f0050171..ae05f2982653 100644
> > --- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > +++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > @@ -44,6 +44,13 @@ properties:
> >      unevaluatedProperties: false
> >      description: Optional child node for ENETC instance, otherwise use
> NETC EMDIO.
> >
> > +  nxp,netc-timer:
> > +    $ref: /schemas/types.yaml#/definitions/phandle
> > +    description:
> > +      Specifies a reference to a node representing a NETC Timer device,
> > +      which provides time synchronization as required for IEEE 1588 and
> > +      IEEE 802.1AS-2020.
> > +
> 
> I think it is quite common. add ptp-timer ethernet-controller.yaml?
> 

Good suggestion, but I'd like to ask for the opinions of others.
Rob, Krzysztof, Conor, what do you think?


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-16  7:30 ` [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
  2025-07-16 19:19   ` Frank Li
@ 2025-07-17  7:40   ` Krzysztof Kozlowski
  2025-07-17  8:30     ` Wei Fang
  1 sibling, 1 reply; 64+ messages in thread
From: Krzysztof Kozlowski @ 2025-07-17  7:40 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam, fushi.peng, devicetree, netdev, linux-kernel, imx,
	kernel

On Wed, Jul 16, 2025 at 03:30:58PM +0800, Wei Fang wrote:
> +properties:
> +  compatible:
> +    enum:
> +      - pci1131,ee02
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 1
> +    description:
> +      The reference clock of NETC Timer, if not present, indicates that
> +      the system clock of NETC IP is selected as the reference clock.

If not present...

> +
> +  clock-names:

... this also is not present...

> +    description:
> +      NETC Timer has three reference clock sources, set TMR_CTRL[CK_SEL]
> +      by parsing clock name to select one of them as the reference clock.
> +      The "system" means that the system clock of NETC IP is used as the
> +      reference clock.
> +      The "ccm_timer" means another clock from CCM as the reference clock.
> +      The "ext_1588" means the reference clock comes from external IO pins.
> +    enum:
> +      - system

So what does system mean?

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-16  7:30 ` [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property Wei Fang
  2025-07-16 19:28   ` Frank Li
@ 2025-07-17  7:42   ` Krzysztof Kozlowski
  2025-07-17  8:32     ` Wei Fang
  1 sibling, 1 reply; 64+ messages in thread
From: Krzysztof Kozlowski @ 2025-07-17  7:42 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	vladimir.oltean, xiaoning.wang, andrew+netdev, davem, edumazet,
	kuba, pabeni, vadim.fedorenko, Frank.Li, shawnguo, s.hauer,
	festevam, fushi.peng, devicetree, netdev, linux-kernel, imx,
	kernel

On Wed, Jul 16, 2025 at 03:30:59PM +0800, Wei Fang wrote:
> NETC is a multi-function PCIe Root Complex Integrated Endpoint (RCiEP)
> that contains multiple PCIe functions, such as ENETC and Timer. Timer
> provides PTP time synchronization functionality and ENETC provides the
> NIC functionality.
> 
> For some platforms, such as i.MX95, it has only one timer instance, so
> the binding relationship between Timer and ENETC is fixed. But for some
> platforms, such as i.MX943, it has 3 Timer instances, by setting the
> EaTBCR registers of the IERB module, we can specify any Timer instance
> to be bound to the ENETC instance.
> 
> Therefore, add "nxp,netc-timer" property to bind ENETC instance to a
> specified Timer instance so that ENETC can support PTP synchronization
> through Timer.
> 
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
> 
> ---
> v2 changes:
> new patch
> ---
>  .../devicetree/bindings/net/fsl,enetc.yaml    | 23 +++++++++++++++++++
>  1 file changed, 23 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> index ca70f0050171..ae05f2982653 100644
> --- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> +++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> @@ -44,6 +44,13 @@ properties:
>      unevaluatedProperties: false
>      description: Optional child node for ENETC instance, otherwise use NETC EMDIO.
>  
> +  nxp,netc-timer:

Heh, you got comments to use existing properties for PTP devices and
consumers. I also said to you to use cell arguments how existing
bindings use it.

You did not respond that you are not going to use existing properties.

So why existing timestamper is not correct? Is this not a timestamper?
If it is, why do we need to repeat the same discussion...

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-17  7:40   ` Krzysztof Kozlowski
@ 2025-07-17  8:30     ` Wei Fang
  2025-07-17  9:05       ` Vladimir Oltean
  2025-07-17 10:04       ` Krzysztof Kozlowski
  0 siblings, 2 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-17  8:30 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> On Wed, Jul 16, 2025 at 03:30:58PM +0800, Wei Fang wrote:
> > +properties:
> > +  compatible:
> > +    enum:
> > +      - pci1131,ee02
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    maxItems: 1
> > +    description:
> > +      The reference clock of NETC Timer, if not present, indicates that
> > +      the system clock of NETC IP is selected as the reference clock.
> 
> If not present...
> 
> > +
> > +  clock-names:
> 
> ... this also is not present...
> 
> > +    description:
> > +      NETC Timer has three reference clock sources, set
> TMR_CTRL[CK_SEL]
> > +      by parsing clock name to select one of them as the reference clock.
> > +      The "system" means that the system clock of NETC IP is used as the
> > +      reference clock.
> > +      The "ccm_timer" means another clock from CCM as the reference
> clock.
> > +      The "ext_1588" means the reference clock comes from external IO
> pins.
> > +    enum:
> > +      - system
> 
> So what does system mean?
> 

"system" is the system clock of the NETC subsystem, we can explicitly specify
this clock as the PTP reference clock of the Timer in the DT node. Or do not
add clock properties to the DT node, it implicitly indicates that the reference
clock of the Timer is the "system" clock.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-17  7:42   ` Krzysztof Kozlowski
@ 2025-07-17  8:32     ` Wei Fang
  2025-07-17  9:12       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-17  8:32 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> On Wed, Jul 16, 2025 at 03:30:59PM +0800, Wei Fang wrote:
> > NETC is a multi-function PCIe Root Complex Integrated Endpoint (RCiEP)
> > that contains multiple PCIe functions, such as ENETC and Timer. Timer
> > provides PTP time synchronization functionality and ENETC provides the
> > NIC functionality.
> >
> > For some platforms, such as i.MX95, it has only one timer instance, so
> > the binding relationship between Timer and ENETC is fixed. But for some
> > platforms, such as i.MX943, it has 3 Timer instances, by setting the
> > EaTBCR registers of the IERB module, we can specify any Timer instance
> > to be bound to the ENETC instance.
> >
> > Therefore, add "nxp,netc-timer" property to bind ENETC instance to a
> > specified Timer instance so that ENETC can support PTP synchronization
> > through Timer.
> >
> > Signed-off-by: Wei Fang <wei.fang@nxp.com>
> >
> > ---
> > v2 changes:
> > new patch
> > ---
> >  .../devicetree/bindings/net/fsl,enetc.yaml    | 23 +++++++++++++++++++
> >  1 file changed, 23 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > index ca70f0050171..ae05f2982653 100644
> > --- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > +++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > @@ -44,6 +44,13 @@ properties:
> >      unevaluatedProperties: false
> >      description: Optional child node for ENETC instance, otherwise use
> NETC EMDIO.
> >
> > +  nxp,netc-timer:
> 
> Heh, you got comments to use existing properties for PTP devices and
> consumers. I also said to you to use cell arguments how existing
> bindings use it.
> 
> You did not respond that you are not going to use existing properties.
> 
> So why existing timestamper is not correct? Is this not a timestamper?
> If it is, why do we need to repeat the same discussion...
> 

I do not think it is timestamper. Each ENETC has the ability to record the
sending/receiving timestamp of the packets on the Tx/Rx BD, but the
timestamp comes from the Timer. For platforms have multiple Timer
instances, the NETC hardware supports binding the specified Timer
instance to the ENETC instance, and this binding is implemented by setting
the registers in the NETC IERB. Therefore, this new property is added to
indicate which Timer instance is bound to the current ENETC instance.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 03/14] ptp: netc: add NETC Timer PTP driver support
  2025-07-16 19:58   ` Frank Li
@ 2025-07-17  8:42     ` Wei Fang
  0 siblings, 0 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-17  8:42 UTC (permalink / raw)
  To: Frank Li
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> > +static u64 netc_timer_cnt_read(struct netc_timer *priv)
> > +{
> > +	u32 tmr_cnt_l, tmr_cnt_h;
> > +	u64 ns;
> > +
> > +	/* The user must read the TMR_CNC_L register first to get
> > +	 * correct 64-bit TMR_CNT_H/L counter values.
> > +	 */
> 
> Need comment about there are snapshot in side chip. So it is safe to
> read NETC_TMR_CNT_L then read NETC_TMR_CNT_H, otherwise
> NETC_TMR_CNT_L may change, during read NETC_TMR_CNT_H.
> 

Okay, I will improve the comment.

> > +static int netc_timer_pci_probe(struct pci_dev *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct netc_timer *priv;
> > +	int err, len;
> > +
> > +	pcie_flr(pdev);
> > +	err = pci_enable_device_mem(pdev);
> > +	if (err)
> > +		return dev_err_probe(dev, err, "Failed to enable device\n");
> > +
> > +	err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> > +	if (err) {
> > +		dev_err(dev, "dma_set_mask_and_coherent() failed, err:%pe\n",
> > +			ERR_PTR(err));
> > +		goto disable_dev;
> > +	}
> 
> Needn't check return value for dma_set_mask_and_coherent() when mask >=
> 32
> It is never return fail.

Okay, I will remove the check.

> 
> use devm_add_action_or_reset() to avoid all goto here. then dev_err =>
> dev_err_probe().
> 

hm..., netdev does not encourage the use of auto-clean APIs such as "devm_".
https://elixir.bootlin.com/linux/v6.16-rc6/source/Documentation/process/maintainer-netdev.rst#L391

I can use helpers like devm_kzalloc() and devm_clk_xx(), but for me, using
devm_add_action_or_reset() does not bring much benefit.
> > +
> > +	err = pci_request_mem_regions(pdev, KBUILD_MODNAME);
> > +	if (err) {
> > +		dev_err(dev, "pci_request_regions() failed, err:%pe\n",
> > +			ERR_PTR(err));
> > +		goto disable_dev;
> > +	}
> > +
> > +	pci_set_master(pdev);
> > +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> 
> devm_kzalloc()
> 
> > +	if (!priv) {
> > +		err = -ENOMEM;
> > +		goto release_mem_regions;
> > +	}
> > +
> > +	priv->pdev = pdev;
> > +	len = pci_resource_len(pdev, NETC_TMR_REGS_BAR);
> > +	priv->base = ioremap(pci_resource_start(pdev, NETC_TMR_REGS_BAR),
> len);
> > +	if (!priv->base) {
> > +		err = -ENXIO;
> > +		dev_err(dev, "ioremap() failed\n");
> > +		goto free_priv;
> > +	}
> 
> pci_ioremap_bar()?
> 

Great, thanks.

> > +static int netc_timer_get_reference_clk_source(struct netc_timer *priv)
> > +{
> > +	struct device *dev = &priv->pdev->dev;
> > +	struct device_node *np = dev->of_node;
> > +	const char *clk_name = NULL;
> > +	u64 ns = NSEC_PER_SEC;
> > +
> > +	/* Select NETC system clock as the reference clock by default */
> > +	priv->clk_select = NETC_TMR_SYSTEM_CLK;
> > +	priv->clk_freq = NETC_TMR_SYSCLK_333M;
> > +	priv->period = div_u64(ns << 32, priv->clk_freq);
> > +
> > +	if (!np)
> > +		return 0;
> > +
> > +	of_property_read_string(np, "clock-names", &clk_name);
> > +	if (!clk_name)
> > +		return 0;
> 
> Don't perfer parser this property by youself.
> 
> you can use devm_clk_bulk_get_all() clk_bulk_data have clock name, you
> can use it.
> 
> or use loop to try 3 time devm_clk_get(), first one return success is what
> you want.
> 
> > +
> > +	/* Update the clock source of the reference clock if the clock
> > +	 * name is specified in DTS node.
> > +	 */
> > +	if (!strcmp(clk_name, "system"))
> 
> strncmp();
> 
> "..." maybe longer than len of clc_name.
> 
> > +		priv->clk_select = NETC_TMR_SYSTEM_CLK;
> > +	else if (!strcmp(clk_name, "ccm_timer"))
> > +		priv->clk_select = NETC_TMR_CCM_TIMER1;
> > +	else if (!strcmp(clk_name, "ext_1588"))
> > +		priv->clk_select = NETC_TMR_EXT_OSC;
> > +	else
> > +		return -EINVAL;
> > +
> > +	priv->src_clk = devm_clk_get(dev, clk_name);
> > +	if (IS_ERR(priv->src_clk)) {
> > +		dev_err(dev, "Failed to get reference clock source\n");
> > +		return PTR_ERR(priv->src_clk);
> > +	}
> > +
> > +	priv->clk_freq = clk_get_rate(priv->src_clk);
> > +	priv->period = div_u64(ns << 32, priv->clk_freq);
> > +
> > +	return 0;
> > +}
> > +
> > +static int netc_timer_parse_dt(struct netc_timer *priv)
> > +{
> > +	return netc_timer_get_reference_clk_source(priv);
> > +}
> > +
> > +static irqreturn_t netc_timer_isr(int irq, void *data)
> > +{
> > +	struct netc_timer *priv = data;
> > +	u32 tmr_event, tmr_emask;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&priv->lock, flags);
> > +
> > +	tmr_event = netc_timer_rd(priv, NETC_TMR_TEVENT);
> > +	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
> > +
> > +	tmr_event &= tmr_emask;
> > +	if (tmr_event & TMR_TEVENT_ALM1EN)
> > +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> > +
> > +	if (tmr_event & TMR_TEVENT_ALM2EN)
> > +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 1);
> > +
> > +	/* Clear interrupts status */
> > +	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
> 
> clean irq status should be just after read it (before "tmr_event &= tmr_emask;)
> 

we only need to clear the status bits of enabled irqs (tmr_event &= tmr_emask),
Interrupts not enabled by tmr_emask will not generate interrupt. I can move
the clear irq status after " tmr_event &= tmr_emask;"

> otherwise the new irq maybe missed by netc_timer_alarm_write()


> 
> > +
> > +	spin_unlock_irqrestore(&priv->lock, flags);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static int netc_timer_init_msix_irq(struct netc_timer *priv)
> > +{
> > +	struct pci_dev *pdev = priv->pdev;
> > +	char irq_name[64];
> > +	int err, n;
> > +
> > +	n = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
> > +	if (n != 1) {
> > +		err = (n < 0) ? n : -EPERM;
> > +		dev_err(&pdev->dev, "pci_alloc_irq_vectors() failed\n");
> > +		return err;
> > +	}
> > +
> > +	priv->irq = pci_irq_vector(pdev, 0);
> > +	snprintf(irq_name, sizeof(irq_name), "ptp-netc %s", pci_name(pdev));
> > +	err = request_irq(priv->irq, netc_timer_isr, 0, irq_name, priv);
> > +	if (err) {
> > +		dev_err(&pdev->dev, "request_irq() failed\n");
> > +		pci_free_irq_vectors(pdev);
> > +		return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void netc_timer_free_msix_irq(struct netc_timer *priv)
> > +{
> > +	struct pci_dev *pdev = priv->pdev;
> > +
> > +	disable_irq(priv->irq);
> > +	free_irq(priv->irq, priv);
> > +	pci_free_irq_vectors(pdev);
> > +}
> > +
> > +static int netc_timer_probe(struct pci_dev *pdev,
> > +			    const struct pci_device_id *id)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct netc_timer *priv;
> > +	int err;
> > +
> > +	err = netc_timer_pci_probe(pdev);
> > +	if (err)
> > +		return err;
> > +
> > +	priv = pci_get_drvdata(pdev);
> > +	err = netc_timer_parse_dt(priv);
> > +	if (err) {
> > +		dev_err(dev, "Failed to parse DT node\n");
> > +		goto timer_pci_remove;
> > +	}
> > +
> > +	priv->caps = netc_timer_ptp_caps;
> > +	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
> > +	priv->phc_index = -1; /* initialize it as an invalid index */
> > +	spin_lock_init(&priv->lock);
> > +
> > +	err = clk_prepare_enable(priv->src_clk);
> 
> use devm_ function
> 
> > +	if (err) {
> > +		dev_err(dev, "Failed to enable timer source clock\n");
> > +		goto timer_pci_remove;
> > +	}
> > +
> > +	err = netc_timer_init_msix_irq(priv);
> > +	if (err)
> > +		goto disable_clk;
> > +
> > +	netc_timer_init(priv);
> > +	priv->clock = ptp_clock_register(&priv->caps, dev);
> > +	if (IS_ERR(priv->clock)) {
> > +		err = PTR_ERR(priv->clock);
> > +		goto free_msix_irq;
> > +	}
> > +
> > +	priv->phc_index = ptp_clock_index(priv->clock);
> > +
> > +	return 0;
> > +
> > +free_msix_irq:
> > +	netc_timer_free_msix_irq(priv);
> > +disable_clk:
> > +	clk_disable_unprepare(priv->src_clk);
> > +timer_pci_remove:
> > +	netc_timer_pci_remove(pdev);
> 
> devm_add_action_or_reset() to simpify goto and netc_timer_remove().
> 
> > +
> > +	return err;
> > +}
> > +
> > +static void netc_timer_remove(struct pci_dev *pdev)
> > +{
> > +	struct netc_timer *priv = pci_get_drvdata(pdev);
> > +
> > +	ptp_clock_unregister(priv->clock);
> > +	netc_timer_free_msix_irq(priv);
> > +	clk_disable_unprepare(priv->src_clk);
> > +	netc_timer_pci_remove(pdev);
> > +}
> > +
> > +static const struct pci_device_id netc_timer_id_table[] = {
> > +	{ PCI_DEVICE(NETC_TMR_PCI_VENDOR, NETC_TMR_PCI_DEVID) },
> > +	{ 0, } /* End of table. */
> 
> just { }
> 
> needn't /* End of table. */
> 
> Frank
> > +};
> > +MODULE_DEVICE_TABLE(pci, netc_timer_id_table);
> > +
> > +static struct pci_driver netc_timer_driver = {
> > +	.name = KBUILD_MODNAME,
> > +	.id_table = netc_timer_id_table,
> > +	.probe = netc_timer_probe,
> > +	.remove = netc_timer_remove,
> > +};
> > +module_pci_driver(netc_timer_driver);
> > +
> > +MODULE_DESCRIPTION("NXP NETC Timer PTP Driver");
> > +MODULE_LICENSE("Dual BSD/GPL");
> > diff --git a/include/linux/fsl/netc_global.h b/include/linux/fsl/netc_global.h
> > index fdecca8c90f0..59c835e67ada 100644
> > --- a/include/linux/fsl/netc_global.h
> > +++ b/include/linux/fsl/netc_global.h
> > @@ -1,10 +1,11 @@
> >  /* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
> > -/* Copyright 2024 NXP
> > +/* Copyright 2024-2025 NXP
> >   */
> >  #ifndef __NETC_GLOBAL_H
> >  #define __NETC_GLOBAL_H
> >
> >  #include <linux/io.h>
> > +#include <linux/pci.h>
> >
> >  static inline u32 netc_read(void __iomem *reg)
> >  {
> > @@ -16,4 +17,13 @@ static inline void netc_write(void __iomem *reg, u32
> val)
> >  	iowrite32(val, reg);
> >  }
> >
> > +#if IS_ENABLED(CONFIG_PTP_1588_CLOCK_NETC)
> > +int netc_timer_get_phc_index(struct pci_dev *timer_pdev);
> > +#else
> > +static inline int netc_timer_get_phc_index(struct pci_dev *timer_pdev)
> > +{
> > +	return -ENODEV;
> > +}
> > +#endif
> > +
> >  #endif
> > --
> > 2.34.1
> >

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-17  8:30     ` Wei Fang
@ 2025-07-17  9:05       ` Vladimir Oltean
  2025-07-17  9:55         ` Wei Fang
  2025-07-17 10:04       ` Krzysztof Kozlowski
  1 sibling, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2025-07-17  9:05 UTC (permalink / raw)
  To: Wei Fang
  Cc: Krzysztof Kozlowski, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, richardcochran@gmail.com, Claudiu Manoil,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On Thu, Jul 17, 2025 at 11:30:14AM +0300, Wei Fang wrote:
> > On Wed, Jul 16, 2025 at 03:30:58PM +0800, Wei Fang wrote:
> > > +properties:
> > > +  compatible:
> > > +    enum:
> > > +      - pci1131,ee02
> > > +
> > > +  reg:
> > > +    maxItems: 1
> > > +
> > > +  clocks:
> > > +    maxItems: 1
> > > +    description:
> > > +      The reference clock of NETC Timer, if not present, indicates that
> > > +      the system clock of NETC IP is selected as the reference clock.
> > 
> > If not present...
> > 
> > > +
> > > +  clock-names:
> > 
> > ... this also is not present...
> > 
> > > +    description:
> > > +      NETC Timer has three reference clock sources, set TMR_CTRL[CK_SEL]
> > > +      by parsing clock name to select one of them as the reference clock.
> > > +      The "system" means that the system clock of NETC IP is used as the
> > > +      reference clock.
> > > +      The "ccm_timer" means another clock from CCM as the reference clock.
> > > +      The "ext_1588" means the reference clock comes from external IO pins.
> > > +    enum:
> > > +      - system
> > 
> > So what does system mean?
> > 
> 
> "system" is the system clock of the NETC subsystem, we can explicitly specify
> this clock as the PTP reference clock of the Timer in the DT node. Or do not
> add clock properties to the DT node, it implicitly indicates that the reference
> clock of the Timer is the "system" clock.

It's unusual to name the clock after the source rather than after the
destination. When "clock-names" takes any of the above 3 values, it's
still the same single IP clock, just taken from 3 different sources.

I see you need to update TMR_CTRL[CK_SEL] depending on where the IP
clock is sourced from. You use the "clock-names" for that. Whereas the
very similar ptp-qoriq uses a separate "fsl,cksel" property. Was that
not an acceptable solution, do we need a new way of achieving the same
thing?

Also, why are "clocks" and "clock-names" not required properties? The
Linux implementation fails probing if they are absent.

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-17  8:32     ` Wei Fang
@ 2025-07-17  9:12       ` Krzysztof Kozlowski
  2025-07-17  9:49         ` Wei Fang
  0 siblings, 1 reply; 64+ messages in thread
From: Krzysztof Kozlowski @ 2025-07-17  9:12 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On Thu, Jul 17, 2025 at 08:32:38AM +0000, Wei Fang wrote:
> > On Wed, Jul 16, 2025 at 03:30:59PM +0800, Wei Fang wrote:
> > > NETC is a multi-function PCIe Root Complex Integrated Endpoint (RCiEP)
> > > that contains multiple PCIe functions, such as ENETC and Timer. Timer
> > > provides PTP time synchronization functionality and ENETC provides the
> > > NIC functionality.
> > >
> > > For some platforms, such as i.MX95, it has only one timer instance, so
> > > the binding relationship between Timer and ENETC is fixed. But for some
> > > platforms, such as i.MX943, it has 3 Timer instances, by setting the
> > > EaTBCR registers of the IERB module, we can specify any Timer instance
> > > to be bound to the ENETC instance.
> > >
> > > Therefore, add "nxp,netc-timer" property to bind ENETC instance to a
> > > specified Timer instance so that ENETC can support PTP synchronization
> > > through Timer.
> > >
> > > Signed-off-by: Wei Fang <wei.fang@nxp.com>
> > >
> > > ---
> > > v2 changes:
> > > new patch
> > > ---
> > >  .../devicetree/bindings/net/fsl,enetc.yaml    | 23 +++++++++++++++++++
> > >  1 file changed, 23 insertions(+)
> > >
> > > diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > index ca70f0050171..ae05f2982653 100644
> > > --- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > +++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > @@ -44,6 +44,13 @@ properties:
> > >      unevaluatedProperties: false
> > >      description: Optional child node for ENETC instance, otherwise use
> > NETC EMDIO.
> > >
> > > +  nxp,netc-timer:
> > 
> > Heh, you got comments to use existing properties for PTP devices and
> > consumers. I also said to you to use cell arguments how existing
> > bindings use it.
> > 
> > You did not respond that you are not going to use existing properties.
> > 
> > So why existing timestamper is not correct? Is this not a timestamper?
> > If it is, why do we need to repeat the same discussion...
> > 
> 
> I do not think it is timestamper. Each ENETC has the ability to record the
> sending/receiving timestamp of the packets on the Tx/Rx BD, but the
> timestamp comes from the Timer. For platforms have multiple Timer

Isn't this exactly what timestamper is supposed to do?

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-17  9:12       ` Krzysztof Kozlowski
@ 2025-07-17  9:49         ` Wei Fang
  2025-07-17 10:06           ` Krzysztof Kozlowski
  0 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-17  9:49 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> On Thu, Jul 17, 2025 at 08:32:38AM +0000, Wei Fang wrote:
> > > On Wed, Jul 16, 2025 at 03:30:59PM +0800, Wei Fang wrote:
> > > > NETC is a multi-function PCIe Root Complex Integrated Endpoint
> > > > (RCiEP) that contains multiple PCIe functions, such as ENETC and
> > > > Timer. Timer provides PTP time synchronization functionality and
> > > > ENETC provides the NIC functionality.
> > > >
> > > > For some platforms, such as i.MX95, it has only one timer
> > > > instance, so the binding relationship between Timer and ENETC is
> > > > fixed. But for some platforms, such as i.MX943, it has 3 Timer
> > > > instances, by setting the EaTBCR registers of the IERB module, we
> > > > can specify any Timer instance to be bound to the ENETC instance.
> > > >
> > > > Therefore, add "nxp,netc-timer" property to bind ENETC instance to
> > > > a specified Timer instance so that ENETC can support PTP
> > > > synchronization through Timer.
> > > >
> > > > Signed-off-by: Wei Fang <wei.fang@nxp.com>
> > > >
> > > > ---
> > > > v2 changes:
> > > > new patch
> > > > ---
> > > >  .../devicetree/bindings/net/fsl,enetc.yaml    | 23
> +++++++++++++++++++
> > > >  1 file changed, 23 insertions(+)
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > > index ca70f0050171..ae05f2982653 100644
> > > > --- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > > +++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > > @@ -44,6 +44,13 @@ properties:
> > > >      unevaluatedProperties: false
> > > >      description: Optional child node for ENETC instance,
> > > > otherwise use
> > > NETC EMDIO.
> > > >
> > > > +  nxp,netc-timer:
> > >
> > > Heh, you got comments to use existing properties for PTP devices and
> > > consumers. I also said to you to use cell arguments how existing
> > > bindings use it.
> > >
> > > You did not respond that you are not going to use existing properties.
> > >
> > > So why existing timestamper is not correct? Is this not a timestamper?
> > > If it is, why do we need to repeat the same discussion...
> > >
> >
> > I do not think it is timestamper. Each ENETC has the ability to record
> > the sending/receiving timestamp of the packets on the Tx/Rx BD, but
> > the timestamp comes from the Timer. For platforms have multiple Timer
> 
> Isn't this exactly what timestamper is supposed to do?
> 
According to the definition, timestamper requires two parameters, one is
the node reference and the other is the port, and the timestamper is added
to the PHY node, and is used by the gerneric mdio driver. The PTP driver
provides the interfaces of mii_timestamping_ctrl. So this property is to
provide PTP support for PHY devices.


timestamper:	provides control node reference and
			the port channel within the IP core

The "timestamper" property lives in a phy node and links a time
stamping channel from the controller device to that phy's MII bus.

But for NETC, we only need the node parameter, and this property is
added to the MAC node.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-17  9:05       ` Vladimir Oltean
@ 2025-07-17  9:55         ` Wei Fang
  2025-07-17 12:42           ` Vladimir Oltean
  0 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-17  9:55 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Krzysztof Kozlowski, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, richardcochran@gmail.com, Claudiu Manoil,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> On Thu, Jul 17, 2025 at 11:30:14AM +0300, Wei Fang wrote:
> > > On Wed, Jul 16, 2025 at 03:30:58PM +0800, Wei Fang wrote:
> > > > +properties:
> > > > +  compatible:
> > > > +    enum:
> > > > +      - pci1131,ee02
> > > > +
> > > > +  reg:
> > > > +    maxItems: 1
> > > > +
> > > > +  clocks:
> > > > +    maxItems: 1
> > > > +    description:
> > > > +      The reference clock of NETC Timer, if not present, indicates that
> > > > +      the system clock of NETC IP is selected as the reference clock.
> > >
> > > If not present...
> > >
> > > > +
> > > > +  clock-names:
> > >
> > > ... this also is not present...
> > >
> > > > +    description:
> > > > +      NETC Timer has three reference clock sources, set
> TMR_CTRL[CK_SEL]
> > > > +      by parsing clock name to select one of them as the reference
> clock.
> > > > +      The "system" means that the system clock of NETC IP is used as
> the
> > > > +      reference clock.
> > > > +      The "ccm_timer" means another clock from CCM as the reference
> clock.
> > > > +      The "ext_1588" means the reference clock comes from external
> IO pins.
> > > > +    enum:
> > > > +      - system
> > >
> > > So what does system mean?
> > >
> >
> > "system" is the system clock of the NETC subsystem, we can explicitly specify
> > this clock as the PTP reference clock of the Timer in the DT node. Or do not
> > add clock properties to the DT node, it implicitly indicates that the reference
> > clock of the Timer is the "system" clock.
> 
> It's unusual to name the clock after the source rather than after the
> destination. When "clock-names" takes any of the above 3 values, it's
> still the same single IP clock, just taken from 3 different sources.
> 
> I see you need to update TMR_CTRL[CK_SEL] depending on where the IP
> clock is sourced from. You use the "clock-names" for that. Whereas the
> very similar ptp-qoriq uses a separate "fsl,cksel" property. Was that
> not an acceptable solution, do we need a new way of achieving the same
> thing?
> 

This an option, as I also mentioned in v1, either we have to parse the
clock-names or we need to add a new property.

> Also, why are "clocks" and "clock-names" not required properties? The
> Linux implementation fails probing if they are absent.

The current ptp_netc driver will not fail if they are absent, and it will always
use the NETC system clock by default, because the system clock of NETC is
always available to the Timer.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-17  8:30     ` Wei Fang
  2025-07-17  9:05       ` Vladimir Oltean
@ 2025-07-17 10:04       ` Krzysztof Kozlowski
  2025-07-17 10:28         ` Wei Fang
  1 sibling, 1 reply; 64+ messages in thread
From: Krzysztof Kozlowski @ 2025-07-17 10:04 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On 17/07/2025 10:30, Wei Fang wrote:
>> On Wed, Jul 16, 2025 at 03:30:58PM +0800, Wei Fang wrote:
>>> +properties:
>>> +  compatible:
>>> +    enum:
>>> +      - pci1131,ee02
>>> +
>>> +  reg:
>>> +    maxItems: 1
>>> +
>>> +  clocks:
>>> +    maxItems: 1
>>> +    description:
>>> +      The reference clock of NETC Timer, if not present, indicates that
>>> +      the system clock of NETC IP is selected as the reference clock.
>>
>> If not present...
>>
>>> +
>>> +  clock-names:
>>
>> ... this also is not present...
>>
>>> +    description:
>>> +      NETC Timer has three reference clock sources, set
>> TMR_CTRL[CK_SEL]
>>> +      by parsing clock name to select one of them as the reference clock.
>>> +      The "system" means that the system clock of NETC IP is used as the
>>> +      reference clock.
>>> +      The "ccm_timer" means another clock from CCM as the reference
>> clock.
>>> +      The "ext_1588" means the reference clock comes from external IO
>> pins.
>>> +    enum:
>>> +      - system
>>
>> So what does system mean?
>>
> 
> "system" is the system clock of the NETC subsystem, we can explicitly specify
> this clock as the PTP reference clock of the Timer in the DT node. Or do not
> add clock properties to the DT node, it implicitly indicates that the reference
> clock of the Timer is the "system" clock.
> 

Eh, no. If absence of clock input means you are using specific clock
input it is contradictory to the first. You cannot use some clock input
if you claim that it can be missing.

You define here clock inputs. This is what this property is about.


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-17  9:49         ` Wei Fang
@ 2025-07-17 10:06           ` Krzysztof Kozlowski
  2025-07-17 10:26             ` Wei Fang
  0 siblings, 1 reply; 64+ messages in thread
From: Krzysztof Kozlowski @ 2025-07-17 10:06 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On 17/07/2025 11:49, Wei Fang wrote:
>>>
>>> I do not think it is timestamper. Each ENETC has the ability to record
>>> the sending/receiving timestamp of the packets on the Tx/Rx BD, but
>>> the timestamp comes from the Timer. For platforms have multiple Timer
>>
>> Isn't this exactly what timestamper is supposed to do?
>>
> According to the definition, timestamper requires two parameters, one is
> the node reference and the other is the port, and the timestamper is added
> to the PHY node, and is used by the gerneric mdio driver. The PTP driver


We do not speak about drivers.

> provides the interfaces of mii_timestamping_ctrl. So this property is to
> provide PTP support for PHY devices.
> 
> 
> timestamper:	provides control node reference and
> 			the port channel within the IP core
> 
> The "timestamper" property lives in a phy node and links a time
> stamping channel from the controller device to that phy's MII bus.
> 
> But for NETC, we only need the node parameter, and this property is
> added to the MAC node.

I think we do not understand each other. I ask if this is the
timestamper and you explain about arguments of the phandle. The
arguments are not relevant.

What is this purpose/role/function of the timer device?

What is the purpose of this new property in the binding here?

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-17 10:06           ` Krzysztof Kozlowski
@ 2025-07-17 10:26             ` Wei Fang
  2025-07-18  7:46               ` Krzysztof Kozlowski
  0 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-17 10:26 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> On 17/07/2025 11:49, Wei Fang wrote:
> >>>
> >>> I do not think it is timestamper. Each ENETC has the ability to record
> >>> the sending/receiving timestamp of the packets on the Tx/Rx BD, but
> >>> the timestamp comes from the Timer. For platforms have multiple Timer
> >>
> >> Isn't this exactly what timestamper is supposed to do?
> >>
> > According to the definition, timestamper requires two parameters, one is
> > the node reference and the other is the port, and the timestamper is added
> > to the PHY node, and is used by the gerneric mdio driver. The PTP driver
> 
> 
> We do not speak about drivers.
> 
> > provides the interfaces of mii_timestamping_ctrl. So this property is to
> > provide PTP support for PHY devices.
> >
> >
> > timestamper:	provides control node reference and
> > 			the port channel within the IP core
> >
> > The "timestamper" property lives in a phy node and links a time
> > stamping channel from the controller device to that phy's MII bus.
> >
> > But for NETC, we only need the node parameter, and this property is
> > added to the MAC node.
> 
> I think we do not understand each other. I ask if this is the
> timestamper and you explain about arguments of the phandle. The
> arguments are not relevant.
> 
> What is this purpose/role/function of the timer device?

The timer device provides PHC with nanosecond resolution, so the
ptp_netc driver provides interfaces to adjust the PHC, and this PHC
is used by the ENETC device, so that the ENECT can capture the
timestamp of the packets.

> 
> What is the purpose of this new property in the binding here?
> 

This property is to allow the ENETC to find the timer device that is
physically bound to it. so that ENETC can perform PTP synchronization
with other network devices.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-17 10:04       ` Krzysztof Kozlowski
@ 2025-07-17 10:28         ` Wei Fang
  0 siblings, 0 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-17 10:28 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> On 17/07/2025 10:30, Wei Fang wrote:
> >> On Wed, Jul 16, 2025 at 03:30:58PM +0800, Wei Fang wrote:
> >>> +properties:
> >>> +  compatible:
> >>> +    enum:
> >>> +      - pci1131,ee02
> >>> +
> >>> +  reg:
> >>> +    maxItems: 1
> >>> +
> >>> +  clocks:
> >>> +    maxItems: 1
> >>> +    description:
> >>> +      The reference clock of NETC Timer, if not present, indicates that
> >>> +      the system clock of NETC IP is selected as the reference clock.
> >>
> >> If not present...
> >>
> >>> +
> >>> +  clock-names:
> >>
> >> ... this also is not present...
> >>
> >>> +    description:
> >>> +      NETC Timer has three reference clock sources, set
> >> TMR_CTRL[CK_SEL]
> >>> +      by parsing clock name to select one of them as the reference clock.
> >>> +      The "system" means that the system clock of NETC IP is used as the
> >>> +      reference clock.
> >>> +      The "ccm_timer" means another clock from CCM as the reference
> >> clock.
> >>> +      The "ext_1588" means the reference clock comes from external IO
> >> pins.
> >>> +    enum:
> >>> +      - system
> >>
> >> So what does system mean?
> >>
> >
> > "system" is the system clock of the NETC subsystem, we can explicitly specify
> > this clock as the PTP reference clock of the Timer in the DT node. Or do not
> > add clock properties to the DT node, it implicitly indicates that the reference
> > clock of the Timer is the "system" clock.
> >
> 
> Eh, no. If absence of clock input means you are using specific clock
> input it is contradictory to the first. You cannot use some clock input
> if you claim that it can be missing.
> 
> You define here clock inputs. This is what this property is about.
> 

Okay, I will remove "system", due to it is the system clock of NETC IP,
which is always available to Timer.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 04/14] ptp: netc: add PTP_CLK_REQ_PPS support
  2025-07-16 20:05   ` Frank Li
@ 2025-07-17 11:59     ` Wei Fang
  2025-07-17 15:15       ` Frank Li
  0 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-17 11:59 UTC (permalink / raw)
  To: Frank Li
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> > +static u32 netc_timer_calculate_fiper_pw(struct netc_timer *priv,
> > +					 u32 fiper)
> > +{
> > +	u64 divisor, pulse_width;
> > +
> > +	/* Set the FIPER pulse width to half FIPER interval by default.
> > +	 * pulse_width = (fiper / 2) / TMR_GCLK_period,
> > +	 * TMR_GCLK_period = NSEC_PER_SEC / TMR_GCLK_freq,
> > +	 * TMR_GCLK_freq = (clk_freq / oclk_prsc) Hz,
> > +	 * so pulse_width = fiper * clk_freq / (2 * NSEC_PER_SEC * oclk_prsc).
> > +	 */
> > +	divisor = mul_u32_u32(2000000000U, priv->oclk_prsc);
> 
> is it 2*PSEC_PER_SEC ?
> 

No, it is 2 * NSEC_PER_SEC, NSEC_PER_SEC is 1000000000.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 05/14] ptp: netc: add periodic pulse output support
  2025-07-16 20:26   ` Frank Li
@ 2025-07-17 12:11     ` Wei Fang
  0 siblings, 0 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-17 12:11 UTC (permalink / raw)
  To: Frank Li
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> On Wed, Jul 16, 2025 at 03:31:02PM +0800, Wei Fang wrote:
> > NETC Timer has three pulse channels, all of which support periodic pulse
> > output. Bind the channel to a ALARM register and then sets a future time
> > into the ALARM register. When the current time is greater than the ALARM
> > value, the FIPER register will be triggered to count down, and when the
> > count reaches 0, the pulse will be triggered. The PPS signal is also
> > implemented in this way. However, for i.MX95, only ALARM1 can be used for
> > periodic pulse output, and for i.MX943, ALARM1 and ALARM2 can be used
> for
> > periodic pulse output, but NETC Timer has three channels, so for i.MX95,
> > only one channel can work at the same time, and for i.MX943, at most two
> > channel can work at the same time. Otherwise, if multiple channels share
> > the same ALARM register, some channel pulses will not meet expectations.
> > Therefore, the current implementation does not allow multiple channels to
> > share the same ALARM register at the same time.
> 
> Can you simple said
> 
> NETC Timer has three pulse channels. i.MX95 have one ALARM1. i.MX943 have
> ALARM1 and ALARM2. So only one channel can work for i.MX95. Two channels
> for imx943 at most.

Actually, i.MX95 have ALARM2 as well, but ALARM2 cannot be used as an
indication to the TMR_FIPER register start down counting. Let me think
how to refine the commit.

> 
> Current (driver or IP) implementation don't allow multiple channels to
> share the same alarm register at the same time.
> 
> > +static int netc_timer_get_alarm_id(struct netc_timer *priv)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < priv->fs_alarm_num; i++) {
> > +		if (!(priv->fs_alarm_bitmap & BIT(i))) {
> 
> fnd_next_zero_bit()?
> 
> or use ffz();
> 

fs_alarm_num is 1 or 2, so fs_alarm_bitmap is u8 type, it fine
to use the current implementation, no need to change the type
of fs_alarm_bitmap to use the generic helper.


> > +			priv->fs_alarm_bitmap |= BIT(i);
> > +			break;
> > +		}
> > +	}
> > +
> > +	return i;
> > +}


^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 09/14] net: enetc: save the parsed information of PTP packet to skb->cb
  2025-07-16 20:46   ` Frank Li
@ 2025-07-17 12:20     ` Wei Fang
  0 siblings, 0 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-17 12:20 UTC (permalink / raw)
  To: Frank Li
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> > -				old_sec_h = *(__be16 *)(data + offset2);
> > +				old_sec_h = *(__be16 *)(data + tstamp_off);
> >  				inet_proto_csum_replace2(&uh->check, skb, old_sec_h,
> >  							 new_sec_h, false);
> >
> > -				old_sec_l = *(__be32 *)(data + offset2 + 2);
> > +				old_sec_l = *(__be32 *)(data + tstamp_off + 2);
> >  				inet_proto_csum_replace4(&uh->check, skb, old_sec_l,
> >  							 new_sec_l, false);
> >
> > -				old_nsec = *(__be32 *)(data + offset2 + 6);
> > +				old_nsec = *(__be32 *)(data + tstamp_off + 6);
> >  				inet_proto_csum_replace4(&uh->check, skb, old_nsec,
> >  							 new_nsec, false);
> >  			}
> >
> > -			*(__be16 *)(data + offset2) = new_sec_h;
> > -			*(__be32 *)(data + offset2 + 2) = new_sec_l;
> > -			*(__be32 *)(data + offset2 + 6) = new_nsec;
> > +			*(__be16 *)(data + tstamp_off) = new_sec_h;
> > ++			*(__be32 *)(data + tstamp_off + 2) = new_sec_l;
> > ++			*(__be32 *)(data + tstamp_off + 6) = new_nsec;
> 
> strange why there are two ++ here.

I do not know, I will fix it, thanks


^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 12/14] net: enetc: add PTP synchronization support for ENETC v4
  2025-07-16 21:01   ` Frank Li
@ 2025-07-17 12:35     ` Wei Fang
  2025-07-17 22:07       ` Frank Li
  0 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-17 12:35 UTC (permalink / raw)
  To: Frank Li
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> > +static void enetc_set_one_step_ts(struct enetc_si *si, bool udp, int
> > +offset) {
> > +	u32 val = ENETC_PM0_SINGLE_STEP_EN;
> > +
> > +	val |= ENETC_SET_SINGLE_STEP_OFFSET(offset);
> > +	if (udp)
> > +		val |= ENETC_PM0_SINGLE_STEP_CH;
> > +
> > +	/* the "Correction" field of a packet is updated based on the
> > +	 * current time and the timestamp provided
> > +	 */
> > +	enetc_port_mac_wr(si, ENETC_PM0_SINGLE_STEP, val); }
> > +
> > +static void enetc4_set_one_step_ts(struct enetc_si *si, bool udp, int
> > +offset) {
> > +	u32 val = PM_SINGLE_STEP_EN;
> > +
> > +	val |= PM_SINGLE_STEP_OFFSET_SET(offset);
> > +	if (udp)
> > +		val |= PM_SINGLE_STEP_CH;
> > +
> > +	enetc_port_mac_wr(si, ENETC4_PM_SINGLE_STEP(0), val); }
> > +
> >  static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
> >  				     struct sk_buff *skb)
> >  {
> > @@ -234,7 +259,6 @@ static u32 enetc_update_ptp_sync_msg(struct
> enetc_ndev_priv *priv,
> >  	u32 lo, hi, nsec;
> >  	u8 *data;
> >  	u64 sec;
> > -	u32 val;
> >
> >  	lo = enetc_rd_hot(hw, ENETC_SICTR0);
> >  	hi = enetc_rd_hot(hw, ENETC_SICTR1); @@ -279,12 +303,10 @@ static
> > u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
> >  	*(__be32 *)(data + tstamp_off + 6) = new_nsec;
> >
> >  	/* Configure single-step register */
> > -	val = ENETC_PM0_SINGLE_STEP_EN;
> > -	val |= ENETC_SET_SINGLE_STEP_OFFSET(corr_off);
> > -	if (enetc_cb->udp)
> > -		val |= ENETC_PM0_SINGLE_STEP_CH;
> > -
> > -	enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP, val);
> > +	if (is_enetc_rev1(si))
> > +		enetc_set_one_step_ts(si, enetc_cb->udp, corr_off);
> > +	else
> > +		enetc4_set_one_step_ts(si, enetc_cb->udp, corr_off);
> 
> Can you use callback function to avoid change this logic when new version
> appear in future?

According to Jakub's previous suggestion, there is no need to add callbacks
for such trivial things. 
https://lore.kernel.org/imx/20250115140042.63b99c4f@kernel.org/

If the differences between the two versions result in a lot of different
code, using a callback is more appropriate.

> 
> >
> >  	return lo & ENETC_TXBD_TSTAMP;
> >  }
> > @@ -303,6 +325,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr
> *tx_ring, struct sk_buff *skb)
> >  	unsigned int f;
> >  	dma_addr_t dma;
> >  	u8 flags = 0;
> > +	u32 tstamp;
> >
> >  	enetc_clear_tx_bd(&temp_bd);
> >  	if (skb->ip_summed == CHECKSUM_PARTIAL) { @@ -327,6 +350,13 @@
> > static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
> >  		}
> >  	}
> >
> > +	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
> > +		do_onestep_tstamp = true;
> > +		tstamp = enetc_update_ptp_sync_msg(priv, skb);
> > +	} else if (enetc_cb->flag & ENETC_F_TX_TSTAMP) {
> > +		do_twostep_tstamp = true;
> > +	}
> > +
> >  	i = tx_ring->next_to_use;
> >  	txbd = ENETC_TXBD(*tx_ring, i);
> >  	prefetchw(txbd);
> > @@ -346,11 +376,6 @@ static int enetc_map_tx_buffs(struct enetc_bdr
> *tx_ring, struct sk_buff *skb)
> >  	count++;
> >
> >  	do_vlan = skb_vlan_tag_present(skb);
> > -	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)
> > -		do_onestep_tstamp = true;
> > -	else if (enetc_cb->flag & ENETC_F_TX_TSTAMP)
> > -		do_twostep_tstamp = true;
> > -
> 
> why need move this block up?

Because we need check the flag to determine whether perform PTP
one-step, if yes, we need to call enetc_update_ptp_sync_msg() to
modify the sync packet before calling dma_map_single(). ENETCv4
do not support dma-coherent, I have explained in the commit message.

> 
> >  	tx_swbd->do_twostep_tstamp = do_twostep_tstamp;
> >  	tx_swbd->qbv_en = !!(priv->active_offloads & ENETC_F_QBV);
> >  	tx_swbd->check_wb = tx_swbd->do_twostep_tstamp ||
> tx_swbd->qbv_en;
> > @@ -393,8 +418,6 @@ static int enetc_map_tx_buffs(struct enetc_bdr
> *tx_ring, struct sk_buff *skb)
> >  		}
> >
> >  		if (do_onestep_tstamp) {
> > -			u32 tstamp = enetc_update_ptp_sync_msg(priv, skb);
> > -
> >  			/* Configure extension BD */
> >  			temp_bd.ext.tstamp = cpu_to_le32(tstamp);
> >  			e_flags |= ENETC_TXBD_E_FLAGS_ONE_STEP_PTP; @@ -3314,7
> +3337,7 @@
> > int enetc_hwtstamp_set(struct net_device *ndev,
> >  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
> >  	int err, new_offloads = priv->active_offloads;
> >
> > -	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
> > +	if (!enetc_ptp_clock_is_enabled(priv->si))
> >  		return -EOPNOTSUPP;
> >
> >  	switch (config->tx_type) {
> > @@ -3364,7 +3387,7 @@ int enetc_hwtstamp_get(struct net_device *ndev,
> > {
> >  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
> >
> > -	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
> > +	if (!enetc_ptp_clock_is_enabled(priv->si))
> >  		return -EOPNOTSUPP;
> >
> >  	if (priv->active_offloads & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) diff
> > --git a/drivers/net/ethernet/freescale/enetc/enetc.h
> > b/drivers/net/ethernet/freescale/enetc/enetc.h
> > index c65aa7b88122..6bacd851358c 100644
> > --- a/drivers/net/ethernet/freescale/enetc/enetc.h
> > +++ b/drivers/net/ethernet/freescale/enetc/enetc.h
> > @@ -598,6 +598,14 @@ static inline void enetc_cbd_free_data_mem(struct
> > enetc_si *si, int size,  void enetc_reset_ptcmsdur(struct enetc_hw
> > *hw);  void enetc_set_ptcmsdur(struct enetc_hw *hw, u32
> > *queue_max_sdu);
> >
> > +static inline bool enetc_ptp_clock_is_enabled(struct enetc_si *si) {
> > +	if (is_enetc_rev1(si))
> > +		return IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK);
> > +
> > +	return IS_ENABLED(CONFIG_PTP_1588_CLOCK_NETC);
> > +}
> > +
> 
> why v1 check CONFIG_FSL_ENETC_PTP_CLOCK and other check
> CONFIG_PTP_1588_CLOCK_NETC

Because they use different PTP drivers, so the configs are different.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-17  9:55         ` Wei Fang
@ 2025-07-17 12:42           ` Vladimir Oltean
  2025-07-17 15:06             ` Frank Li
  0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2025-07-17 12:42 UTC (permalink / raw)
  To: Wei Fang
  Cc: Krzysztof Kozlowski, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, richardcochran@gmail.com, Claudiu Manoil,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On Thu, Jul 17, 2025 at 12:55:27PM +0300, Wei Fang wrote:
> > > "system" is the system clock of the NETC subsystem, we can explicitly specify
> > > this clock as the PTP reference clock of the Timer in the DT node. Or do not
> > > add clock properties to the DT node, it implicitly indicates that the reference
> > > clock of the Timer is the "system" clock.
> > 
> > It's unusual to name the clock after the source rather than after the
> > destination. When "clock-names" takes any of the above 3 values, it's
> > still the same single IP clock, just taken from 3 different sources.
> > 
> > I see you need to update TMR_CTRL[CK_SEL] depending on where the IP
> > clock is sourced from. You use the "clock-names" for that. Whereas the
> > very similar ptp-qoriq uses a separate "fsl,cksel" property. Was that
> > not an acceptable solution, do we need a new way of achieving the same
> > thing?
> 
> This an option, as I also mentioned in v1, either we have to parse the
> clock-names or we need to add a new property.

I think a new property like "fsl,cksel" is preferable, due to the
arguments above: already used for ptp_qoriq, and the alternative of
parsing the clock-names implies going against the established convention
that the clock name should be from the perspective of this IP, not from
the perspective of the provider.

> > Also, why are "clocks" and "clock-names" not required properties? The
> > Linux implementation fails probing if they are absent.
> 
> The current ptp_netc driver will not fail if they are absent, and it will always
> use the NETC system clock by default, because the system clock of NETC is
> always available to the Timer.

Ok, sorry, I misinterpreted the code.

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-17 12:42           ` Vladimir Oltean
@ 2025-07-17 15:06             ` Frank Li
  2025-07-22 14:36               ` Vladimir Oltean
  0 siblings, 1 reply; 64+ messages in thread
From: Frank Li @ 2025-07-17 15:06 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Wei Fang, Krzysztof Kozlowski, robh@kernel.org,
	krzk+dt@kernel.org, conor+dt@kernel.org, richardcochran@gmail.com,
	Claudiu Manoil, Clark Wang, andrew+netdev@lunn.ch,
	davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
	pabeni@redhat.com, vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On Thu, Jul 17, 2025 at 03:42:41PM +0300, Vladimir Oltean wrote:
> On Thu, Jul 17, 2025 at 12:55:27PM +0300, Wei Fang wrote:
> > > > "system" is the system clock of the NETC subsystem, we can explicitly specify
> > > > this clock as the PTP reference clock of the Timer in the DT node. Or do not
> > > > add clock properties to the DT node, it implicitly indicates that the reference
> > > > clock of the Timer is the "system" clock.
> > >
> > > It's unusual to name the clock after the source rather than after the
> > > destination. When "clock-names" takes any of the above 3 values, it's
> > > still the same single IP clock, just taken from 3 different sources.
> > >
> > > I see you need to update TMR_CTRL[CK_SEL] depending on where the IP
> > > clock is sourced from. You use the "clock-names" for that. Whereas the
> > > very similar ptp-qoriq uses a separate "fsl,cksel" property. Was that
> > > not an acceptable solution, do we need a new way of achieving the same
> > > thing?
> >
> > This an option, as I also mentioned in v1, either we have to parse the
> > clock-names or we need to add a new property.
>
> I think a new property like "fsl,cksel" is preferable, due to the
> arguments above: already used for ptp_qoriq, and the alternative of
> parsing the clock-names implies going against the established convention
> that the clock name should be from the perspective of this IP, not from
> the perspective of the provider.

The similar problem already was discussed at
https://lore.kernel.org/imx/20250403103346.3064895-2-ciprianmarian.costea@oss.nxp.com/

Actually there are clock mux inside IP, which have some inputs. Only one
was chosen. Rob prefer use clock-names to distingish which one is used.

discuss thread in https://lore.kernel.org/imx/59261ba0-2086-4520-8429-6e3f08107077@oss.nxp.com/

Frank

>
> > > Also, why are "clocks" and "clock-names" not required properties? The
> > > Linux implementation fails probing if they are absent.
> >
> > The current ptp_netc driver will not fail if they are absent, and it will always
> > use the NETC system clock by default, because the system clock of NETC is
> > always available to the Timer.
>
> Ok, sorry`, I misinterpreted the code.

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 04/14] ptp: netc: add PTP_CLK_REQ_PPS support
  2025-07-17 11:59     ` Wei Fang
@ 2025-07-17 15:15       ` Frank Li
  2025-07-18  2:08         ` Wei Fang
  0 siblings, 1 reply; 64+ messages in thread
From: Frank Li @ 2025-07-17 15:15 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On Thu, Jul 17, 2025 at 11:59:30AM +0000, Wei Fang wrote:
> > > +static u32 netc_timer_calculate_fiper_pw(struct netc_timer *priv,
> > > +					 u32 fiper)
> > > +{
> > > +	u64 divisor, pulse_width;
> > > +
> > > +	/* Set the FIPER pulse width to half FIPER interval by default.
> > > +	 * pulse_width = (fiper / 2) / TMR_GCLK_period,
> > > +	 * TMR_GCLK_period = NSEC_PER_SEC / TMR_GCLK_freq,
> > > +	 * TMR_GCLK_freq = (clk_freq / oclk_prsc) Hz,
> > > +	 * so pulse_width = fiper * clk_freq / (2 * NSEC_PER_SEC * oclk_prsc).
> > > +	 */
> > > +	divisor = mul_u32_u32(2000000000U, priv->oclk_prsc);
> >
> > is it 2*PSEC_PER_SEC ?
> >
>
> No, it is 2 * NSEC_PER_SEC, NSEC_PER_SEC is 1000000000.

Use 2 * NSEC_PER_SEC, instead of hardcode number.

Frank

>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 12/14] net: enetc: add PTP synchronization support for ENETC v4
  2025-07-17 12:35     ` Wei Fang
@ 2025-07-17 22:07       ` Frank Li
  2025-07-18  2:08         ` Wei Fang
  0 siblings, 1 reply; 64+ messages in thread
From: Frank Li @ 2025-07-17 22:07 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On Thu, Jul 17, 2025 at 12:35:10PM +0000, Wei Fang wrote:
> > > +static void enetc_set_one_step_ts(struct enetc_si *si, bool udp, int
> > > +offset) {
> > > +	u32 val = ENETC_PM0_SINGLE_STEP_EN;
> > > +
> > > +	val |= ENETC_SET_SINGLE_STEP_OFFSET(offset);
> > > +	if (udp)
> > > +		val |= ENETC_PM0_SINGLE_STEP_CH;
> > > +
> > > +	/* the "Correction" field of a packet is updated based on the
> > > +	 * current time and the timestamp provided
> > > +	 */
> > > +	enetc_port_mac_wr(si, ENETC_PM0_SINGLE_STEP, val); }
> > > +
> > > +static void enetc4_set_one_step_ts(struct enetc_si *si, bool udp, int
> > > +offset) {
> > > +	u32 val = PM_SINGLE_STEP_EN;
> > > +
> > > +	val |= PM_SINGLE_STEP_OFFSET_SET(offset);
> > > +	if (udp)
> > > +		val |= PM_SINGLE_STEP_CH;
> > > +
> > > +	enetc_port_mac_wr(si, ENETC4_PM_SINGLE_STEP(0), val); }
> > > +
> > >  static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
> > >  				     struct sk_buff *skb)
> > >  {
> > > @@ -234,7 +259,6 @@ static u32 enetc_update_ptp_sync_msg(struct
> > enetc_ndev_priv *priv,
> > >  	u32 lo, hi, nsec;
> > >  	u8 *data;
> > >  	u64 sec;
> > > -	u32 val;
> > >
> > >  	lo = enetc_rd_hot(hw, ENETC_SICTR0);
> > >  	hi = enetc_rd_hot(hw, ENETC_SICTR1); @@ -279,12 +303,10 @@ static
> > > u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
> > >  	*(__be32 *)(data + tstamp_off + 6) = new_nsec;
> > >
> > >  	/* Configure single-step register */
> > > -	val = ENETC_PM0_SINGLE_STEP_EN;
> > > -	val |= ENETC_SET_SINGLE_STEP_OFFSET(corr_off);
> > > -	if (enetc_cb->udp)
> > > -		val |= ENETC_PM0_SINGLE_STEP_CH;
> > > -
> > > -	enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP, val);
> > > +	if (is_enetc_rev1(si))
> > > +		enetc_set_one_step_ts(si, enetc_cb->udp, corr_off);
> > > +	else
> > > +		enetc4_set_one_step_ts(si, enetc_cb->udp, corr_off);
> >
> > Can you use callback function to avoid change this logic when new version
> > appear in future?
>
> According to Jakub's previous suggestion, there is no need to add callbacks
> for such trivial things.
> https://lore.kernel.org/imx/20250115140042.63b99c4f@kernel.org/
>
> If the differences between the two versions result in a lot of different
> code, using a callback is more appropriate.
>
> >
> > >
> > >  	return lo & ENETC_TXBD_TSTAMP;
> > >  }
> > > @@ -303,6 +325,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr
> > *tx_ring, struct sk_buff *skb)
> > >  	unsigned int f;
> > >  	dma_addr_t dma;
> > >  	u8 flags = 0;
> > > +	u32 tstamp;
> > >
> > >  	enetc_clear_tx_bd(&temp_bd);
> > >  	if (skb->ip_summed == CHECKSUM_PARTIAL) { @@ -327,6 +350,13 @@
> > > static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
> > >  		}
> > >  	}
> > >
> > > +	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
> > > +		do_onestep_tstamp = true;
> > > +		tstamp = enetc_update_ptp_sync_msg(priv, skb);
> > > +	} else if (enetc_cb->flag & ENETC_F_TX_TSTAMP) {
> > > +		do_twostep_tstamp = true;
> > > +	}
> > > +
> > >  	i = tx_ring->next_to_use;
> > >  	txbd = ENETC_TXBD(*tx_ring, i);
> > >  	prefetchw(txbd);
> > > @@ -346,11 +376,6 @@ static int enetc_map_tx_buffs(struct enetc_bdr
> > *tx_ring, struct sk_buff *skb)
> > >  	count++;
> > >
> > >  	do_vlan = skb_vlan_tag_present(skb);
> > > -	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)
> > > -		do_onestep_tstamp = true;
> > > -	else if (enetc_cb->flag & ENETC_F_TX_TSTAMP)
> > > -		do_twostep_tstamp = true;
> > > -
> >
> > why need move this block up?
>
> Because we need check the flag to determine whether perform PTP
> one-step, if yes, we need to call enetc_update_ptp_sync_msg() to
> modify the sync packet before calling dma_map_single(). ENETCv4
> do not support dma-coherent, I have explained in the commit message.
>
> >
> > >  	tx_swbd->do_twostep_tstamp = do_twostep_tstamp;
> > >  	tx_swbd->qbv_en = !!(priv->active_offloads & ENETC_F_QBV);
> > >  	tx_swbd->check_wb = tx_swbd->do_twostep_tstamp ||
> > tx_swbd->qbv_en;
> > > @@ -393,8 +418,6 @@ static int enetc_map_tx_buffs(struct enetc_bdr
> > *tx_ring, struct sk_buff *skb)
> > >  		}
> > >
> > >  		if (do_onestep_tstamp) {
> > > -			u32 tstamp = enetc_update_ptp_sync_msg(priv, skb);
> > > -
> > >  			/* Configure extension BD */
> > >  			temp_bd.ext.tstamp = cpu_to_le32(tstamp);
> > >  			e_flags |= ENETC_TXBD_E_FLAGS_ONE_STEP_PTP; @@ -3314,7
> > +3337,7 @@
> > > int enetc_hwtstamp_set(struct net_device *ndev,
> > >  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
> > >  	int err, new_offloads = priv->active_offloads;
> > >
> > > -	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
> > > +	if (!enetc_ptp_clock_is_enabled(priv->si))
> > >  		return -EOPNOTSUPP;
> > >
> > >  	switch (config->tx_type) {
> > > @@ -3364,7 +3387,7 @@ int enetc_hwtstamp_get(struct net_device *ndev,
> > > {
> > >  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
> > >
> > > -	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
> > > +	if (!enetc_ptp_clock_is_enabled(priv->si))
> > >  		return -EOPNOTSUPP;
> > >
> > >  	if (priv->active_offloads & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) diff
> > > --git a/drivers/net/ethernet/freescale/enetc/enetc.h
> > > b/drivers/net/ethernet/freescale/enetc/enetc.h
> > > index c65aa7b88122..6bacd851358c 100644
> > > --- a/drivers/net/ethernet/freescale/enetc/enetc.h
> > > +++ b/drivers/net/ethernet/freescale/enetc/enetc.h
> > > @@ -598,6 +598,14 @@ static inline void enetc_cbd_free_data_mem(struct
> > > enetc_si *si, int size,  void enetc_reset_ptcmsdur(struct enetc_hw
> > > *hw);  void enetc_set_ptcmsdur(struct enetc_hw *hw, u32
> > > *queue_max_sdu);
> > >
> > > +static inline bool enetc_ptp_clock_is_enabled(struct enetc_si *si) {
> > > +	if (is_enetc_rev1(si))
> > > +		return IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK);
> > > +
> > > +	return IS_ENABLED(CONFIG_PTP_1588_CLOCK_NETC);
> > > +}
> > > +
> >
> > why v1 check CONFIG_FSL_ENETC_PTP_CLOCK and other check
> > CONFIG_PTP_1588_CLOCK_NETC
>
> Because they use different PTP drivers, so the configs are different.

But name CONFIG_FSL_ENETC_PTP_CLOCK and CONFIG_PTP_1588_CLOCK_NETC is quite
similar, suppose CONFIG_PTP_1588_CLOCK_NETC should be
CONFIG_PTP_1588_CLOCK_NETC_V4

Frank

>

^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 12/14] net: enetc: add PTP synchronization support for ENETC v4
  2025-07-17 22:07       ` Frank Li
@ 2025-07-18  2:08         ` Wei Fang
  2025-07-22 12:57           ` Vladimir Oltean
  0 siblings, 1 reply; 64+ messages in thread
From: Wei Fang @ 2025-07-18  2:08 UTC (permalink / raw)
  To: Frank Li
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> On Thu, Jul 17, 2025 at 12:35:10PM +0000, Wei Fang wrote:
> > > > +static void enetc_set_one_step_ts(struct enetc_si *si, bool udp, int
> > > > +offset) {
> > > > +	u32 val = ENETC_PM0_SINGLE_STEP_EN;
> > > > +
> > > > +	val |= ENETC_SET_SINGLE_STEP_OFFSET(offset);
> > > > +	if (udp)
> > > > +		val |= ENETC_PM0_SINGLE_STEP_CH;
> > > > +
> > > > +	/* the "Correction" field of a packet is updated based on the
> > > > +	 * current time and the timestamp provided
> > > > +	 */
> > > > +	enetc_port_mac_wr(si, ENETC_PM0_SINGLE_STEP, val); }
> > > > +
> > > > +static void enetc4_set_one_step_ts(struct enetc_si *si, bool udp, int
> > > > +offset) {
> > > > +	u32 val = PM_SINGLE_STEP_EN;
> > > > +
> > > > +	val |= PM_SINGLE_STEP_OFFSET_SET(offset);
> > > > +	if (udp)
> > > > +		val |= PM_SINGLE_STEP_CH;
> > > > +
> > > > +	enetc_port_mac_wr(si, ENETC4_PM_SINGLE_STEP(0), val); }
> > > > +
> > > >  static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
> > > >  				     struct sk_buff *skb)
> > > >  {
> > > > @@ -234,7 +259,6 @@ static u32 enetc_update_ptp_sync_msg(struct
> > > enetc_ndev_priv *priv,
> > > >  	u32 lo, hi, nsec;
> > > >  	u8 *data;
> > > >  	u64 sec;
> > > > -	u32 val;
> > > >
> > > >  	lo = enetc_rd_hot(hw, ENETC_SICTR0);
> > > >  	hi = enetc_rd_hot(hw, ENETC_SICTR1); @@ -279,12 +303,10 @@
> static
> > > > u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
> > > >  	*(__be32 *)(data + tstamp_off + 6) = new_nsec;
> > > >
> > > >  	/* Configure single-step register */
> > > > -	val = ENETC_PM0_SINGLE_STEP_EN;
> > > > -	val |= ENETC_SET_SINGLE_STEP_OFFSET(corr_off);
> > > > -	if (enetc_cb->udp)
> > > > -		val |= ENETC_PM0_SINGLE_STEP_CH;
> > > > -
> > > > -	enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP, val);
> > > > +	if (is_enetc_rev1(si))
> > > > +		enetc_set_one_step_ts(si, enetc_cb->udp, corr_off);
> > > > +	else
> > > > +		enetc4_set_one_step_ts(si, enetc_cb->udp, corr_off);
> > >
> > > Can you use callback function to avoid change this logic when new version
> > > appear in future?
> >
> > According to Jakub's previous suggestion, there is no need to add callbacks
> > for such trivial things.
> > https://lore.kernel.org/imx/20250115140042.63b99c4f@kernel.org/
> >
> > If the differences between the two versions result in a lot of different
> > code, using a callback is more appropriate.
> >
> > >
> > > >
> > > >  	return lo & ENETC_TXBD_TSTAMP;
> > > >  }
> > > > @@ -303,6 +325,7 @@ static int enetc_map_tx_buffs(struct enetc_bdr
> > > *tx_ring, struct sk_buff *skb)
> > > >  	unsigned int f;
> > > >  	dma_addr_t dma;
> > > >  	u8 flags = 0;
> > > > +	u32 tstamp;
> > > >
> > > >  	enetc_clear_tx_bd(&temp_bd);
> > > >  	if (skb->ip_summed == CHECKSUM_PARTIAL) { @@ -327,6 +350,13
> @@
> > > > static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff
> *skb)
> > > >  		}
> > > >  	}
> > > >
> > > > +	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
> > > > +		do_onestep_tstamp = true;
> > > > +		tstamp = enetc_update_ptp_sync_msg(priv, skb);
> > > > +	} else if (enetc_cb->flag & ENETC_F_TX_TSTAMP) {
> > > > +		do_twostep_tstamp = true;
> > > > +	}
> > > > +
> > > >  	i = tx_ring->next_to_use;
> > > >  	txbd = ENETC_TXBD(*tx_ring, i);
> > > >  	prefetchw(txbd);
> > > > @@ -346,11 +376,6 @@ static int enetc_map_tx_buffs(struct enetc_bdr
> > > *tx_ring, struct sk_buff *skb)
> > > >  	count++;
> > > >
> > > >  	do_vlan = skb_vlan_tag_present(skb);
> > > > -	if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)
> > > > -		do_onestep_tstamp = true;
> > > > -	else if (enetc_cb->flag & ENETC_F_TX_TSTAMP)
> > > > -		do_twostep_tstamp = true;
> > > > -
> > >
> > > why need move this block up?
> >
> > Because we need check the flag to determine whether perform PTP
> > one-step, if yes, we need to call enetc_update_ptp_sync_msg() to
> > modify the sync packet before calling dma_map_single(). ENETCv4
> > do not support dma-coherent, I have explained in the commit message.
> >
> > >
> > > >  	tx_swbd->do_twostep_tstamp = do_twostep_tstamp;
> > > >  	tx_swbd->qbv_en = !!(priv->active_offloads & ENETC_F_QBV);
> > > >  	tx_swbd->check_wb = tx_swbd->do_twostep_tstamp ||
> > > tx_swbd->qbv_en;
> > > > @@ -393,8 +418,6 @@ static int enetc_map_tx_buffs(struct enetc_bdr
> > > *tx_ring, struct sk_buff *skb)
> > > >  		}
> > > >
> > > >  		if (do_onestep_tstamp) {
> > > > -			u32 tstamp = enetc_update_ptp_sync_msg(priv, skb);
> > > > -
> > > >  			/* Configure extension BD */
> > > >  			temp_bd.ext.tstamp = cpu_to_le32(tstamp);
> > > >  			e_flags |= ENETC_TXBD_E_FLAGS_ONE_STEP_PTP; @@
> -3314,7
> > > +3337,7 @@
> > > > int enetc_hwtstamp_set(struct net_device *ndev,
> > > >  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
> > > >  	int err, new_offloads = priv->active_offloads;
> > > >
> > > > -	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
> > > > +	if (!enetc_ptp_clock_is_enabled(priv->si))
> > > >  		return -EOPNOTSUPP;
> > > >
> > > >  	switch (config->tx_type) {
> > > > @@ -3364,7 +3387,7 @@ int enetc_hwtstamp_get(struct net_device
> *ndev,
> > > > {
> > > >  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
> > > >
> > > > -	if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
> > > > +	if (!enetc_ptp_clock_is_enabled(priv->si))
> > > >  		return -EOPNOTSUPP;
> > > >
> > > >  	if (priv->active_offloads & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) diff
> > > > --git a/drivers/net/ethernet/freescale/enetc/enetc.h
> > > > b/drivers/net/ethernet/freescale/enetc/enetc.h
> > > > index c65aa7b88122..6bacd851358c 100644
> > > > --- a/drivers/net/ethernet/freescale/enetc/enetc.h
> > > > +++ b/drivers/net/ethernet/freescale/enetc/enetc.h
> > > > @@ -598,6 +598,14 @@ static inline void
> enetc_cbd_free_data_mem(struct
> > > > enetc_si *si, int size,  void enetc_reset_ptcmsdur(struct enetc_hw
> > > > *hw);  void enetc_set_ptcmsdur(struct enetc_hw *hw, u32
> > > > *queue_max_sdu);
> > > >
> > > > +static inline bool enetc_ptp_clock_is_enabled(struct enetc_si *si) {
> > > > +	if (is_enetc_rev1(si))
> > > > +		return IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK);
> > > > +
> > > > +	return IS_ENABLED(CONFIG_PTP_1588_CLOCK_NETC);
> > > > +}
> > > > +
> > >
> > > why v1 check CONFIG_FSL_ENETC_PTP_CLOCK and other check
> > > CONFIG_PTP_1588_CLOCK_NETC
> >
> > Because they use different PTP drivers, so the configs are different.
> 
> But name CONFIG_FSL_ENETC_PTP_CLOCK and
> CONFIG_PTP_1588_CLOCK_NETC is quite
> similar, suppose CONFIG_PTP_1588_CLOCK_NETC should be
> CONFIG_PTP_1588_CLOCK_NETC_V4
> 
Okay, it looks good


^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 04/14] ptp: netc: add PTP_CLK_REQ_PPS support
  2025-07-17 15:15       ` Frank Li
@ 2025-07-18  2:08         ` Wei Fang
  0 siblings, 0 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-18  2:08 UTC (permalink / raw)
  To: Frank Li
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> On Thu, Jul 17, 2025 at 11:59:30AM +0000, Wei Fang wrote:
> > > > +static u32 netc_timer_calculate_fiper_pw(struct netc_timer *priv,
> > > > +					 u32 fiper)
> > > > +{
> > > > +	u64 divisor, pulse_width;
> > > > +
> > > > +	/* Set the FIPER pulse width to half FIPER interval by default.
> > > > +	 * pulse_width = (fiper / 2) / TMR_GCLK_period,
> > > > +	 * TMR_GCLK_period = NSEC_PER_SEC / TMR_GCLK_freq,
> > > > +	 * TMR_GCLK_freq = (clk_freq / oclk_prsc) Hz,
> > > > +	 * so pulse_width = fiper * clk_freq / (2 * NSEC_PER_SEC * oclk_prsc).
> > > > +	 */
> > > > +	divisor = mul_u32_u32(2000000000U, priv->oclk_prsc);
> > >
> > > is it 2*PSEC_PER_SEC ?
> > >
> >
> > No, it is 2 * NSEC_PER_SEC, NSEC_PER_SEC is 1000000000.
> 
> Use 2 * NSEC_PER_SEC, instead of hardcode number.
> 
Sure, thanks.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-17 10:26             ` Wei Fang
@ 2025-07-18  7:46               ` Krzysztof Kozlowski
  2025-07-18  7:50                 ` Krzysztof Kozlowski
  2025-07-18 12:01                 ` Vladimir Oltean
  0 siblings, 2 replies; 64+ messages in thread
From: Krzysztof Kozlowski @ 2025-07-18  7:46 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On Thu, Jul 17, 2025 at 10:26:28AM +0000, Wei Fang wrote:
> > > timestamper:	provides control node reference and
> > > 			the port channel within the IP core
> > >
> > > The "timestamper" property lives in a phy node and links a time
> > > stamping channel from the controller device to that phy's MII bus.
> > >
> > > But for NETC, we only need the node parameter, and this property is
> > > added to the MAC node.
> > 
> > I think we do not understand each other. I ask if this is the
> > timestamper and you explain about arguments of the phandle. The
> > arguments are not relevant.
> > 
> > What is this purpose/role/function of the timer device?
> 
> The timer device provides PHC with nanosecond resolution, so the
> ptp_netc driver provides interfaces to adjust the PHC, and this PHC
> is used by the ENETC device, so that the ENECT can capture the
> timestamp of the packets.
> 
> > 
> > What is the purpose of this new property in the binding here?
> > 
> 
> This property is to allow the ENETC to find the timer device that is
> physically bound to it. so that ENETC can perform PTP synchronization
> with other network devices.


Looks exactly how existing timestamper property is described.

If this is not timestamper then probably someone with better domain
knowledge should explain it clearly, so I will understand why it is not
timestamper and what is the timestamper property. Then you should think
if you need new generic binding for it, IOW, whether this is typical
problem you solve here or not, and add such binding if needed.

Maybe there is another property describing a time provider in the
kernel or dtschema. Please look for it. This all looks like you are
implementing typical use case in non-typical, but vendor-like, way.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-18  7:46               ` Krzysztof Kozlowski
@ 2025-07-18  7:50                 ` Krzysztof Kozlowski
  2025-07-18 12:01                 ` Vladimir Oltean
  1 sibling, 0 replies; 64+ messages in thread
From: Krzysztof Kozlowski @ 2025-07-18  7:50 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Vladimir Oltean,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, Frank Li, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On 18/07/2025 09:46, Krzysztof Kozlowski wrote:
> On Thu, Jul 17, 2025 at 10:26:28AM +0000, Wei Fang wrote:
>>>> timestamper:	provides control node reference and
>>>> 			the port channel within the IP core
>>>>
>>>> The "timestamper" property lives in a phy node and links a time
>>>> stamping channel from the controller device to that phy's MII bus.
>>>>
>>>> But for NETC, we only need the node parameter, and this property is
>>>> added to the MAC node.
>>>
>>> I think we do not understand each other. I ask if this is the
>>> timestamper and you explain about arguments of the phandle. The
>>> arguments are not relevant.
>>>
>>> What is this purpose/role/function of the timer device?
>>
>> The timer device provides PHC with nanosecond resolution, so the
>> ptp_netc driver provides interfaces to adjust the PHC, and this PHC
>> is used by the ENETC device, so that the ENECT can capture the
>> timestamp of the packets.
>>
>>>
>>> What is the purpose of this new property in the binding here?
>>>
>>
>> This property is to allow the ENETC to find the timer device that is
>> physically bound to it. so that ENETC can perform PTP synchronization
>> with other network devices.
> 
> 
> Looks exactly how existing timestamper property is described.
> 
> If this is not timestamper then probably someone with better domain
> knowledge should explain it clearly, so I will understand why it is not
> timestamper and what is the timestamper property. Then you should think
> if you need new generic binding for it, IOW, whether this is typical
> problem you solve here or not, and add such binding if needed.
> 
> Maybe there is another property describing a time provider in the
> kernel or dtschema. Please look for it. This all looks like you are
> implementing typical use case in non-typical, but vendor-like, way.


BTW, I am also very disappointed that we had exactly that talk in v1,
for 10 or 20 emails (!!!) and then you send the same.

I feel like you do not want to actually solve this problem, but just
push whatever you have. Otherwise why unresolved comments in v1 would
result in the same code in v2 leading to the same lengthy discussion?

If you send the same v3, I will just NAK it without even discussing,
because 20 emails in v1 and 20 emails in v2 is way too much wasting my time.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-18  7:46               ` Krzysztof Kozlowski
  2025-07-18  7:50                 ` Krzysztof Kozlowski
@ 2025-07-18 12:01                 ` Vladimir Oltean
  2025-07-21  6:00                   ` Wei Fang
  2025-07-21 12:23                   ` Krzysztof Kozlowski
  1 sibling, 2 replies; 64+ messages in thread
From: Vladimir Oltean @ 2025-07-18 12:01 UTC (permalink / raw)
  To: Krzysztof Kozlowski, richardcochran@gmail.com
  Cc: Wei Fang, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, Claudiu Manoil, Clark Wang,
	andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
	kuba@kernel.org, pabeni@redhat.com, vadim.fedorenko@linux.dev,
	Frank Li, shawnguo@kernel.org, s.hauer@pengutronix.de,
	festevam@gmail.com, F.S. Peng, devicetree@vger.kernel.org,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	imx@lists.linux.dev, kernel@pengutronix.de

On Fri, Jul 18, 2025 at 09:46:14AM +0200, Krzysztof Kozlowski wrote:
> On Thu, Jul 17, 2025 at 10:26:28AM +0000, Wei Fang wrote:
> > > > timestamper:	provides control node reference and
> > > > 			the port channel within the IP core
> > > >
> > > > The "timestamper" property lives in a phy node and links a time
> > > > stamping channel from the controller device to that phy's MII bus.
> > > >
> > > > But for NETC, we only need the node parameter, and this property is
> > > > added to the MAC node.
> > > 
> > > I think we do not understand each other. I ask if this is the
> > > timestamper and you explain about arguments of the phandle. The
> > > arguments are not relevant.
> > > 
> > > What is this purpose/role/function of the timer device?
> > 
> > The timer device provides PHC with nanosecond resolution, so the
> > ptp_netc driver provides interfaces to adjust the PHC, and this PHC
> > is used by the ENETC device, so that the ENECT can capture the
> > timestamp of the packets.
> > 
> > > 
> > > What is the purpose of this new property in the binding here?
> > > 
> > 
> > This property is to allow the ENETC to find the timer device that is
> > physically bound to it. so that ENETC can perform PTP synchronization
> > with other network devices.
> 
> 
> Looks exactly how existing timestamper property is described.
> 
> If this is not timestamper then probably someone with better domain
> knowledge should explain it clearly, so I will understand why it is not
> timestamper and what is the timestamper property. Then you should think
> if you need new generic binding for it, IOW, whether this is typical
> problem you solve here or not, and add such binding if needed.
> 
> Maybe there is another property describing a time provider in the
> kernel or dtschema. Please look for it. This all looks like you are
> implementing typical use case in non-typical, but vendor-like, way.
> 
> Best regards,
> Krzysztof

An MII timestamper and a PTP clock (as integrated in a MAC or a PHY) are
similar but have some notable differences.

A timestamper is an external device with a free-running counter, which
sniffs the MII bus between the MAC and the PHY, and provides timestamps
when the first octet of a packet hits the wire.

A PTP clock is also a high precision counter, which can be free-running
or it can be precisely adjusted. It does not have packet timestamping
capabilities itself, instead the Ethernet MAC can snapshot this counter
when it places the first octet of a packet on the MII bus. PTP clocks
frequently have other auxiliary functions, like emitting external
signals based on the internal time, or snapshotting external signals.

The timestamper is not required to have these functions. In fact, I am
looking at ptp_ines.c, the only non-PHY MII timestamper supported by the
kernel, and I am noting the fact that it does not call ptp_clock_register()
at all, presumably because it has no controllable PTP clock to speak of.

That being said, my understanding is based on analyzing the public code
available to me, and I do not have practical experience with MII bus
snooping devices, so if Richard could chime in, it would be great.

I am also in favor of using the "ptp-timer" phandle to describe the link
between the MAC and the internal PTP clock that will be snapshot when
taking packet timestamps. The fman-dtsec.yaml schema also uses it for an
identical purpose.

^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-18 12:01                 ` Vladimir Oltean
@ 2025-07-21  6:00                   ` Wei Fang
  2025-07-21 12:23                   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-21  6:00 UTC (permalink / raw)
  To: Vladimir Oltean, Krzysztof Kozlowski, richardcochran@gmail.com
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	Claudiu Manoil, Clark Wang, andrew+netdev@lunn.ch,
	davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
	pabeni@redhat.com, vadim.fedorenko@linux.dev, Frank Li,
	shawnguo@kernel.org, s.hauer@pengutronix.de, festevam@gmail.com,
	F.S. Peng, devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> > > > > timestamper:	provides control node reference and
> > > > > 			the port channel within the IP core
> > > > >
> > > > > The "timestamper" property lives in a phy node and links a time
> > > > > stamping channel from the controller device to that phy's MII bus.
> > > > >
> > > > > But for NETC, we only need the node parameter, and this property is
> > > > > added to the MAC node.
> > > >
> > > > I think we do not understand each other. I ask if this is the
> > > > timestamper and you explain about arguments of the phandle. The
> > > > arguments are not relevant.
> > > >
> > > > What is this purpose/role/function of the timer device?
> > >
> > > The timer device provides PHC with nanosecond resolution, so the
> > > ptp_netc driver provides interfaces to adjust the PHC, and this PHC
> > > is used by the ENETC device, so that the ENECT can capture the
> > > timestamp of the packets.
> > >
> > > >
> > > > What is the purpose of this new property in the binding here?
> > > >
> > >
> > > This property is to allow the ENETC to find the timer device that is
> > > physically bound to it. so that ENETC can perform PTP synchronization
> > > with other network devices.
> >
> >
> > Looks exactly how existing timestamper property is described.
> >
> > If this is not timestamper then probably someone with better domain
> > knowledge should explain it clearly, so I will understand why it is not
> > timestamper and what is the timestamper property. Then you should think
> > if you need new generic binding for it, IOW, whether this is typical
> > problem you solve here or not, and add such binding if needed.
> >
> > Maybe there is another property describing a time provider in the
> > kernel or dtschema. Please look for it. This all looks like you are
> > implementing typical use case in non-typical, but vendor-like, way.
> >
> > Best regards,
> > Krzysztof
> 
> An MII timestamper and a PTP clock (as integrated in a MAC or a PHY) are
> similar but have some notable differences.
> 
> A timestamper is an external device with a free-running counter, which
> sniffs the MII bus between the MAC and the PHY, and provides timestamps
> when the first octet of a packet hits the wire.
> 
> A PTP clock is also a high precision counter, which can be free-running
> or it can be precisely adjusted. It does not have packet timestamping
> capabilities itself, instead the Ethernet MAC can snapshot this counter
> when it places the first octet of a packet on the MII bus. PTP clocks
> frequently have other auxiliary functions, like emitting external
> signals based on the internal time, or snapshotting external signals.
> 
> The timestamper is not required to have these functions. In fact, I am
> looking at ptp_ines.c, the only non-PHY MII timestamper supported by the
> kernel, and I am noting the fact that it does not call ptp_clock_register()
> at all, presumably because it has no controllable PTP clock to speak of.
> 
> That being said, my understanding is based on analyzing the public code
> available to me, and I do not have practical experience with MII bus
> snooping devices, so if Richard could chime in, it would be great.
> 
> I am also in favor of using the "ptp-timer" phandle to describe the link
> between the MAC and the internal PTP clock that will be snapshot when
> taking packet timestamps. The fman-dtsec.yaml schema also uses it for an
> identical purpose.

Hi Vladimir,

Thanks for the detailed explanation, I think so too, but my previous
explanation was not as convincing and professional as yours, many thanks.

I am not aware of any similar cases before. Thank you for pointing out the
"ptp-timer" property. And I also find the "ptimer-handle" in fsl,fman.yaml,
which has the same purpose as "ptp-timer". Actually, these two properties
are exactly the property I want.  And I think this is exactly what Krzysztof
said, use existing properties instead of adding new properties customized
by the vendor to implement a typical use case.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property
  2025-07-18 12:01                 ` Vladimir Oltean
  2025-07-21  6:00                   ` Wei Fang
@ 2025-07-21 12:23                   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 64+ messages in thread
From: Krzysztof Kozlowski @ 2025-07-21 12:23 UTC (permalink / raw)
  To: Vladimir Oltean, richardcochran@gmail.com
  Cc: Wei Fang, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, Claudiu Manoil, Clark Wang,
	andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
	kuba@kernel.org, pabeni@redhat.com, vadim.fedorenko@linux.dev,
	Frank Li, shawnguo@kernel.org, s.hauer@pengutronix.de,
	festevam@gmail.com, F.S. Peng, devicetree@vger.kernel.org,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	imx@lists.linux.dev, kernel@pengutronix.de

On 18/07/2025 14:01, Vladimir Oltean wrote:
>> Maybe there is another property describing a time provider in the
>> kernel or dtschema. Please look for it. This all looks like you are
>> implementing typical use case in non-typical, but vendor-like, way.
>>
>> Best regards,
>> Krzysztof
> 
> An MII timestamper and a PTP clock (as integrated in a MAC or a PHY) are
> similar but have some notable differences.
> 
> A timestamper is an external device with a free-running counter, which
> sniffs the MII bus between the MAC and the PHY, and provides timestamps
> when the first octet of a packet hits the wire.
> 
> A PTP clock is also a high precision counter, which can be free-running
> or it can be precisely adjusted. It does not have packet timestamping
> capabilities itself, instead the Ethernet MAC can snapshot this counter
> when it places the first octet of a packet on the MII bus. PTP clocks
> frequently have other auxiliary functions, like emitting external
> signals based on the internal time, or snapshotting external signals.
> 
> The timestamper is not required to have these functions. In fact, I am
> looking at ptp_ines.c, the only non-PHY MII timestamper supported by the
> kernel, and I am noting the fact that it does not call ptp_clock_register()
> at all, presumably because it has no controllable PTP clock to speak of.
> 
> That being said, my understanding is based on analyzing the public code
> available to me, and I do not have practical experience with MII bus
> snooping devices, so if Richard could chime in, it would be great.
> 
> I am also in favor of using the "ptp-timer" phandle to describe the link


And it is already used in other binding, so yes, that's the candidate.

> between the MAC and the internal PTP clock that will be snapshot when
> taking packet timestamps. The fman-dtsec.yaml schema also uses it for an
> identical purpose.


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 12/14] net: enetc: add PTP synchronization support for ENETC v4
  2025-07-18  2:08         ` Wei Fang
@ 2025-07-22 12:57           ` Vladimir Oltean
  2025-07-22 13:41             ` Wei Fang
  0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2025-07-22 12:57 UTC (permalink / raw)
  To: Wei Fang
  Cc: Frank Li, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, richardcochran@gmail.com, Claudiu Manoil,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On Fri, Jul 18, 2025 at 05:08:09AM +0300, Wei Fang wrote:
> > > > > +static inline bool enetc_ptp_clock_is_enabled(struct enetc_si *si) {
> > > > > +	if (is_enetc_rev1(si))
> > > > > +		return IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK);
> > > > > +
> > > > > +	return IS_ENABLED(CONFIG_PTP_1588_CLOCK_NETC);
> > > > > +}
> > > > > +
> > > >
> > > > why v1 check CONFIG_FSL_ENETC_PTP_CLOCK and other check
> > > > CONFIG_PTP_1588_CLOCK_NETC
> > >
> > > Because they use different PTP drivers, so the configs are different.
> > 
> > But name CONFIG_FSL_ENETC_PTP_CLOCK and
> > CONFIG_PTP_1588_CLOCK_NETC is quite
> > similar, suppose CONFIG_PTP_1588_CLOCK_NETC should be
> > CONFIG_PTP_1588_CLOCK_NETC_V4
> > 
> Okay, it looks good

The help text is also very confusing, nowhere is it specified that the
new driver is for NETCv4, the reader can just as well interpret it that
the LS1028A ENETC can use this PTP timer driver.

+         This driver adds support for using the NXP NETC Timer as a PTP
+         clock. This clock is used by ENETC MAC or NETC Switch for PTP
+         synchronization. It also supports periodic output signal (e.g.
+         PPS) and external trigger timestamping.

^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 12/14] net: enetc: add PTP synchronization support for ENETC v4
  2025-07-22 12:57           ` Vladimir Oltean
@ 2025-07-22 13:41             ` Wei Fang
  0 siblings, 0 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-22 13:41 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Frank Li, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, richardcochran@gmail.com, Claudiu Manoil,
	Clark Wang, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

> On Fri, Jul 18, 2025 at 05:08:09AM +0300, Wei Fang wrote:
> > > > > > +static inline bool enetc_ptp_clock_is_enabled(struct enetc_si *si) {
> > > > > > +	if (is_enetc_rev1(si))
> > > > > > +		return IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK);
> > > > > > +
> > > > > > +	return IS_ENABLED(CONFIG_PTP_1588_CLOCK_NETC);
> > > > > > +}
> > > > > > +
> > > > >
> > > > > why v1 check CONFIG_FSL_ENETC_PTP_CLOCK and other check
> > > > > CONFIG_PTP_1588_CLOCK_NETC
> > > >
> > > > Because they use different PTP drivers, so the configs are different.
> > >
> > > But name CONFIG_FSL_ENETC_PTP_CLOCK and
> > > CONFIG_PTP_1588_CLOCK_NETC is quite
> > > similar, suppose CONFIG_PTP_1588_CLOCK_NETC should be
> > > CONFIG_PTP_1588_CLOCK_NETC_V4
> > >
> > Okay, it looks good
> 
> The help text is also very confusing, nowhere is it specified that the
> new driver is for NETCv4, the reader can just as well interpret it that
> the LS1028A ENETC can use this PTP timer driver.
> 
> +         This driver adds support for using the NXP NETC Timer as a PTP
> +         clock. This clock is used by ENETC MAC or NETC Switch for PTP
> +         synchronization. It also supports periodic output signal (e.g.
> +         PPS) and external trigger timestamping.

Thanks for pointing out this, let me improve the description in the next
version.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-17 15:06             ` Frank Li
@ 2025-07-22 14:36               ` Vladimir Oltean
  2025-07-22 18:25                 ` Frank Li
  0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2025-07-22 14:36 UTC (permalink / raw)
  To: Frank Li
  Cc: Wei Fang, Krzysztof Kozlowski, robh@kernel.org,
	krzk+dt@kernel.org, conor+dt@kernel.org, richardcochran@gmail.com,
	Claudiu Manoil, Clark Wang, andrew+netdev@lunn.ch,
	davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
	pabeni@redhat.com, vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

Hi Frank,

On Thu, Jul 17, 2025 at 11:06:20AM -0400, Frank Li wrote:
> On Thu, Jul 17, 2025 at 03:42:41PM +0300, Vladimir Oltean wrote:
> > On Thu, Jul 17, 2025 at 12:55:27PM +0300, Wei Fang wrote:
> > > > > "system" is the system clock of the NETC subsystem, we can explicitly specify
> > > > > this clock as the PTP reference clock of the Timer in the DT node. Or do not
> > > > > add clock properties to the DT node, it implicitly indicates that the reference
> > > > > clock of the Timer is the "system" clock.
> > > >
> > > > It's unusual to name the clock after the source rather than after the
> > > > destination. When "clock-names" takes any of the above 3 values, it's
> > > > still the same single IP clock, just taken from 3 different sources.
> > > >
> > > > I see you need to update TMR_CTRL[CK_SEL] depending on where the IP
> > > > clock is sourced from. You use the "clock-names" for that. Whereas the
> > > > very similar ptp-qoriq uses a separate "fsl,cksel" property. Was that
> > > > not an acceptable solution, do we need a new way of achieving the same
> > > > thing?
> > >
> > > This an option, as I also mentioned in v1, either we have to parse the
> > > clock-names or we need to add a new property.
> >
> > I think a new property like "fsl,cksel" is preferable, due to the
> > arguments above: already used for ptp_qoriq, and the alternative of
> > parsing the clock-names implies going against the established convention
> > that the clock name should be from the perspective of this IP, not from
> > the perspective of the provider.
> 
> The similar problem already was discussed at
> https://lore.kernel.org/imx/20250403103346.3064895-2-ciprianmarian.costea@oss.nxp.com/
> 
> Actually there are clock mux inside IP, which have some inputs. Only one
> was chosen. Rob prefer use clock-names to distingish which one is used.
> 
> discuss thread in https://lore.kernel.org/imx/59261ba0-2086-4520-8429-6e3f08107077@oss.nxp.com/
> 
> Frank

Thanks for the reference. From the linked discussion you provided, I
am not able to draw the conclusion "Rob prefers to use clock-names to
distinguish which one is used". This seems to have been Ciprian Costea's
preference, and Rob just stated "Really, you probably should [ list all
possible clock sources ] no matter what, as you need to describe what's
in the h/w, not configuration".

Really, Rob just didn't object to the use of clock-names to identify the
source, but I don't see him expressing a preference for it.

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock
  2025-07-22 14:36               ` Vladimir Oltean
@ 2025-07-22 18:25                 ` Frank Li
  0 siblings, 0 replies; 64+ messages in thread
From: Frank Li @ 2025-07-22 18:25 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Wei Fang, Krzysztof Kozlowski, robh@kernel.org,
	krzk+dt@kernel.org, conor+dt@kernel.org, richardcochran@gmail.com,
	Claudiu Manoil, Clark Wang, andrew+netdev@lunn.ch,
	davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
	pabeni@redhat.com, vadim.fedorenko@linux.dev, shawnguo@kernel.org,
	s.hauer@pengutronix.de, festevam@gmail.com, F.S. Peng,
	devicetree@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	kernel@pengutronix.de

On Tue, Jul 22, 2025 at 05:36:38PM +0300, Vladimir Oltean wrote:
> Hi Frank,
>
> On Thu, Jul 17, 2025 at 11:06:20AM -0400, Frank Li wrote:
> > On Thu, Jul 17, 2025 at 03:42:41PM +0300, Vladimir Oltean wrote:
> > > On Thu, Jul 17, 2025 at 12:55:27PM +0300, Wei Fang wrote:
> > > > > > "system" is the system clock of the NETC subsystem, we can explicitly specify
> > > > > > this clock as the PTP reference clock of the Timer in the DT node. Or do not
> > > > > > add clock properties to the DT node, it implicitly indicates that the reference
> > > > > > clock of the Timer is the "system" clock.
> > > > >
> > > > > It's unusual to name the clock after the source rather than after the
> > > > > destination. When "clock-names" takes any of the above 3 values, it's
> > > > > still the same single IP clock, just taken from 3 different sources.
> > > > >
> > > > > I see you need to update TMR_CTRL[CK_SEL] depending on where the IP
> > > > > clock is sourced from. You use the "clock-names" for that. Whereas the
> > > > > very similar ptp-qoriq uses a separate "fsl,cksel" property. Was that
> > > > > not an acceptable solution, do we need a new way of achieving the same
> > > > > thing?
> > > >
> > > > This an option, as I also mentioned in v1, either we have to parse the
> > > > clock-names or we need to add a new property.
> > >
> > > I think a new property like "fsl,cksel" is preferable, due to the
> > > arguments above: already used for ptp_qoriq, and the alternative of
> > > parsing the clock-names implies going against the established convention
> > > that the clock name should be from the perspective of this IP, not from
> > > the perspective of the provider.
> >
> > The similar problem already was discussed at
> > https://lore.kernel.org/imx/20250403103346.3064895-2-ciprianmarian.costea@oss.nxp.com/
> >
> > Actually there are clock mux inside IP, which have some inputs. Only one
> > was chosen. Rob prefer use clock-names to distingish which one is used.
> >
> > discuss thread in https://lore.kernel.org/imx/59261ba0-2086-4520-8429-6e3f08107077@oss.nxp.com/
> >
> > Frank
>
> Thanks for the reference. From the linked discussion you provided, I
> am not able to draw the conclusion "Rob prefers to use clock-names to
> distinguish which one is used". This seems to have been Ciprian Costea's
> preference, and Rob just stated "Really, you probably should [ list all
> possible clock sources ] no matter what, as you need to describe what's
> in the h/w, not configuration".
>
> Really, Rob just didn't object to the use of clock-names to identify the
> source, but I don't see him expressing a preference for it.

Thank you carefull read again. Previously there are many discussion about
clk-sel

https://lore.kernel.org/all/2d870984-fd5b-469b-8157-ca5ad52a0e01@oss.nxp.com/#t

It is quite common for device, which are mux inside IP. we can continue
to work with DT team to figure out direction if s32 rtc's method is not
good enough.

Frank

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 net-next 03/14] ptp: netc: add NETC Timer PTP driver support
  2025-07-16  7:31 ` [PATCH v2 net-next 03/14] ptp: netc: add NETC Timer PTP driver support Wei Fang
  2025-07-16 19:58   ` Frank Li
@ 2025-07-23 16:09   ` Vladimir Oltean
  2025-07-24  2:36     ` Wei Fang
  1 sibling, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2025-07-23 16:09 UTC (permalink / raw)
  To: Wei Fang
  Cc: robh, krzk+dt, conor+dt, richardcochran, claudiu.manoil,
	xiaoning.wang, andrew+netdev, davem, edumazet, kuba, pabeni,
	vadim.fedorenko, Frank.Li, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Wed, Jul 16, 2025 at 03:31:00PM +0800, Wei Fang wrote:
> NETC Timer provides current time with nanosecond resolution, precise
> periodic pulse, pulse on timeout (alarm), and time capture on external
> pulse support. And it supports time synchronization as required for
> IEEE 1588 and IEEE 802.1AS-2020. The enetc v4 driver can implement PTP
> synchronization through the relevant interfaces provided by the driver.
> Note that the current driver does not support PEROUT, PPS and EXTTS yet,
> and support will be added one by one in subsequent patches.

Would you mind adding a paragraph justifying why you are introducing a
new driver, rather than extending the similar ptp_qoriq.c?

> 
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
> 
> ---
> v2 changes:
> 1. Rename netc_timer_get_source_clk() to
>    netc_timer_get_reference_clk_source() and refactor it
> 2. Remove the scaled_ppm check in netc_timer_adjfine()
> 3. Add a comment in netc_timer_cur_time_read()
> 4. Add linux/bitfield.h to fix the build errors
> ---
>  drivers/ptp/Kconfig             |  11 +
>  drivers/ptp/Makefile            |   1 +
>  drivers/ptp/ptp_netc.c          | 568 ++++++++++++++++++++++++++++++++
>  include/linux/fsl/netc_global.h |  12 +-
>  4 files changed, 591 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/ptp/ptp_netc.c
> 
> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
> index 204278eb215e..3e005b992aef 100644
> --- a/drivers/ptp/Kconfig
> +++ b/drivers/ptp/Kconfig
> @@ -252,4 +252,15 @@ config PTP_S390
>  	  driver provides the raw clock value without the delta to
>  	  userspace. That way userspace programs like chrony could steer
>  	  the kernel clock.
> +
> +config PTP_1588_CLOCK_NETC
> +	bool "NXP NETC Timer PTP Driver"
> +	depends on PTP_1588_CLOCK=y
> +	depends on PCI_MSI
> +	help
> +	  This driver adds support for using the NXP NETC Timer as a PTP
> +	  clock. This clock is used by ENETC MAC or NETC Switch for PTP
> +	  synchronization. It also supports periodic output signal (e.g.
> +	  PPS) and external trigger timestamping.
> +
>  endmenu
> diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
> index 25f846fe48c9..d48fe4009fa4 100644
> --- a/drivers/ptp/Makefile
> +++ b/drivers/ptp/Makefile
> @@ -23,3 +23,4 @@ obj-$(CONFIG_PTP_1588_CLOCK_VMW)	+= ptp_vmw.o
>  obj-$(CONFIG_PTP_1588_CLOCK_OCP)	+= ptp_ocp.o
>  obj-$(CONFIG_PTP_DFL_TOD)		+= ptp_dfl_tod.o
>  obj-$(CONFIG_PTP_S390)			+= ptp_s390.o
> +obj-$(CONFIG_PTP_1588_CLOCK_NETC)	+= ptp_netc.o
> diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> new file mode 100644
> index 000000000000..82cb1e6a0fe9
> --- /dev/null
> +++ b/drivers/ptp/ptp_netc.c
> @@ -0,0 +1,568 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
> +/*
> + * NXP NETC Timer driver
> + * Copyright 2025 NXP
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/fsl/netc_global.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/ptp_clock_kernel.h>
> +
> +#define NETC_TMR_PCI_VENDOR		0x1131
> +#define NETC_TMR_PCI_DEVID		0xee02
> +
> +#define NETC_TMR_CTRL			0x0080
> +#define  TMR_CTRL_CK_SEL		GENMASK(1, 0)
> +#define  TMR_CTRL_TE			BIT(2)
> +#define  TMR_COMP_MODE			BIT(15)
> +#define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
> +#define  TMR_CTRL_FS			BIT(28)
> +#define  TMR_ALARM1P			BIT(31)
> +
> +#define NETC_TMR_TEVENT			0x0084
> +#define  TMR_TEVENT_ALM1EN		BIT(16)
> +#define  TMR_TEVENT_ALM2EN		BIT(17)
> +
> +#define NETC_TMR_TEMASK			0x0088
> +#define NETC_TMR_CNT_L			0x0098
> +#define NETC_TMR_CNT_H			0x009c
> +#define NETC_TMR_ADD			0x00a0
> +#define NETC_TMR_PRSC			0x00a8
> +#define NETC_TMR_OFF_L			0x00b0
> +#define NETC_TMR_OFF_H			0x00b4
> +
> +/* i = 0, 1, i indicates the index of TMR_ALARM */
> +#define NETC_TMR_ALARM_L(i)		(0x00b8 + (i) * 8)
> +#define NETC_TMR_ALARM_H(i)		(0x00bc + (i) * 8)
> +
> +#define NETC_TMR_FIPER_CTRL		0x00dc
> +#define  FIPER_CTRL_DIS(i)		(BIT(7) << (i) * 8)
> +#define  FIPER_CTRL_PG(i)		(BIT(6) << (i) * 8)
> +
> +#define NETC_TMR_CUR_TIME_L		0x00f0
> +#define NETC_TMR_CUR_TIME_H		0x00f4
> +
> +#define NETC_TMR_REGS_BAR		0
> +
> +#define NETC_TMR_FIPER_NUM		3
> +#define NETC_TMR_DEFAULT_PRSC		2
> +#define NETC_TMR_DEFAULT_ALARM		GENMASK_ULL(63, 0)
> +
> +/* 1588 timer reference clock source select */
> +#define NETC_TMR_CCM_TIMER1		0 /* enet_timer1_clk_root, from CCM */
> +#define NETC_TMR_SYSTEM_CLK		1 /* enet_clk_root/2, from CCM */
> +#define NETC_TMR_EXT_OSC		2 /* tmr_1588_clk, from IO pins */
> +
> +#define NETC_TMR_SYSCLK_333M		333333333U
> +
> +struct netc_timer {
> +	void __iomem *base;
> +	struct pci_dev *pdev;
> +	spinlock_t lock; /* Prevent concurrent access to registers */
> +
> +	struct clk *src_clk;
> +	struct ptp_clock *clock;
> +	struct ptp_clock_info caps;
> +	int phc_index;
> +	u32 clk_select;
> +	u32 clk_freq;
> +	u32 oclk_prsc;
> +	/* High 32-bit is integer part, low 32-bit is fractional part */
> +	u64 period;
> +
> +	int irq;
> +};
> +
> +#define netc_timer_rd(p, o)		netc_read((p)->base + (o))
> +#define netc_timer_wr(p, o, v)		netc_write((p)->base + (o), v)
> +#define ptp_to_netc_timer(ptp)		container_of((ptp), struct netc_timer, caps)
> +
> +static u64 netc_timer_cnt_read(struct netc_timer *priv)
> +{
> +	u32 tmr_cnt_l, tmr_cnt_h;
> +	u64 ns;
> +
> +	/* The user must read the TMR_CNC_L register first to get
> +	 * correct 64-bit TMR_CNT_H/L counter values.
> +	 */
> +	tmr_cnt_l = netc_timer_rd(priv, NETC_TMR_CNT_L);
> +	tmr_cnt_h = netc_timer_rd(priv, NETC_TMR_CNT_H);
> +	ns = (((u64)tmr_cnt_h) << 32) | tmr_cnt_l;
> +
> +	return ns;
> +}
> +
> +static void netc_timer_cnt_write(struct netc_timer *priv, u64 ns)
> +{
> +	u32 tmr_cnt_h = upper_32_bits(ns);
> +	u32 tmr_cnt_l = lower_32_bits(ns);
> +
> +	/* The user must write to TMR_CNT_L register first. */
> +	netc_timer_wr(priv, NETC_TMR_CNT_L, tmr_cnt_l);
> +	netc_timer_wr(priv, NETC_TMR_CNT_H, tmr_cnt_h);
> +}
> +
> +static u64 netc_timer_offset_read(struct netc_timer *priv)
> +{
> +	u32 tmr_off_l, tmr_off_h;
> +	u64 offset;
> +
> +	tmr_off_l = netc_timer_rd(priv, NETC_TMR_OFF_L);
> +	tmr_off_h = netc_timer_rd(priv, NETC_TMR_OFF_H);
> +	offset = (((u64)tmr_off_h) << 32) | tmr_off_l;
> +
> +	return offset;
> +}
> +
> +static void netc_timer_offset_write(struct netc_timer *priv, u64 offset)
> +{
> +	u32 tmr_off_h = upper_32_bits(offset);
> +	u32 tmr_off_l = lower_32_bits(offset);
> +
> +	netc_timer_wr(priv, NETC_TMR_OFF_L, tmr_off_l);
> +	netc_timer_wr(priv, NETC_TMR_OFF_H, tmr_off_h);
> +}
> +
> +static u64 netc_timer_cur_time_read(struct netc_timer *priv)
> +{
> +	u32 time_h, time_l;
> +	u64 ns;
> +
> +	/* The user should read NETC_TMR_CUR_TIME_L first to
> +	 * get correct current time.
> +	 */
> +	time_l = netc_timer_rd(priv, NETC_TMR_CUR_TIME_L);
> +	time_h = netc_timer_rd(priv, NETC_TMR_CUR_TIME_H);
> +	ns = (u64)time_h << 32 | time_l;
> +
> +	return ns;
> +}
> +
> +static void netc_timer_alarm_write(struct netc_timer *priv,
> +				   u64 alarm, int index)
> +{
> +	u32 alarm_h = upper_32_bits(alarm);
> +	u32 alarm_l = lower_32_bits(alarm);
> +
> +	netc_timer_wr(priv, NETC_TMR_ALARM_L(index), alarm_l);
> +	netc_timer_wr(priv, NETC_TMR_ALARM_H(index), alarm_h);
> +}
> +
> +static void netc_timer_adjust_period(struct netc_timer *priv, u64 period)
> +{
> +	u32 fractional_period = lower_32_bits(period);
> +	u32 integral_period = upper_32_bits(period);
> +	u32 tmr_ctrl, old_tmr_ctrl;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	old_tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
> +	tmr_ctrl = u32_replace_bits(old_tmr_ctrl, integral_period,
> +				    TMR_CTRL_TCLK_PERIOD);
> +	if (tmr_ctrl != old_tmr_ctrl)
> +		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +
> +	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +}
> +
> +static int netc_timer_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
> +{
> +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> +	u64 new_period;
> +
> +	new_period = adjust_by_scaled_ppm(priv->period, scaled_ppm);
> +	netc_timer_adjust_period(priv, new_period);
> +
> +	return 0;
> +}
> +
> +static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
> +{
> +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> +	u64 tmr_cnt, tmr_off;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	tmr_off = netc_timer_offset_read(priv);
> +	if (delta < 0 && tmr_off < abs(delta)) {

You go to great lengths to avoid letting TMROFF become negative, but is
there any problem if you just let it do so, and delete the imprecise
"TMR_CNT += delta" code path altogether? An addition with the two's
complement of a number is the same as a subtraction.

Let's say delta=-10, and the current TMROFF value is 5.
Your condition deviates the adjustment through the imprecise method,
but if we write TMROFF = -5 = 0xffffffff_fffffffb, we should get the
correct result, no?

I thought about this a number of ways, and they all seem to be fine.
Like, the worst thing that can happen is a TMROFF value which became
negative by accident, due to too many netc_timer_adjtime() values with a
large (but positive) delta.

But even that should be fine, because an overflow on TMROFF is
indistinguishable from an overflow on TMR_CNT.

Anyway, _this_ is the time of logic which could really use a comment to
explain the intention behind it.

> +		delta += tmr_off;
> +		if (!tmr_off)
> +			netc_timer_offset_write(priv, 0);
> +
> +		tmr_cnt = netc_timer_cnt_read(priv);
> +		tmr_cnt += delta;
> +		netc_timer_cnt_write(priv, tmr_cnt);
> +	} else {
> +		tmr_off += delta;
> +		netc_timer_offset_write(priv, tmr_off);
> +	}
> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int netc_timer_gettimex64(struct ptp_clock_info *ptp,
> +				 struct timespec64 *ts,
> +				 struct ptp_system_timestamp *sts)
> +{
> +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> +	unsigned long flags;
> +	u64 ns;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	ptp_read_system_prets(sts);
> +	ns = netc_timer_cur_time_read(priv);
> +	ptp_read_system_postts(sts);
> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	*ts = ns_to_timespec64(ns);
> +
> +	return 0;
> +}
> +
> +static int netc_timer_settime64(struct ptp_clock_info *ptp,
> +				const struct timespec64 *ts)
> +{
> +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> +	u64 ns = timespec64_to_ns(ts);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +	netc_timer_offset_write(priv, 0);
> +	netc_timer_cnt_write(priv, ns);
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +
> +int netc_timer_get_phc_index(struct pci_dev *timer_pdev)
> +{
> +	struct netc_timer *priv;
> +
> +	if (!timer_pdev)
> +		return -ENODEV;
> +
> +	priv = pci_get_drvdata(timer_pdev);
> +	if (!priv)
> +		return -EINVAL;
> +
> +	return priv->phc_index;
> +}
> +EXPORT_SYMBOL_GPL(netc_timer_get_phc_index);
> +
> +static const struct ptp_clock_info netc_timer_ptp_caps = {
> +	.owner		= THIS_MODULE,
> +	.name		= "NETC Timer PTP clock",
> +	.max_adj	= 500000000,
> +	.n_alarm	= 2,

Is n_alarm functionally hooked with anything in the PTP core, other than
the "n_alarms" read-only sysfs? I didn't see anything.

> +	.n_pins		= 0,
> +	.adjfine	= netc_timer_adjfine,
> +	.adjtime	= netc_timer_adjtime,
> +	.gettimex64	= netc_timer_gettimex64,
> +	.settime64	= netc_timer_settime64,
> +};
> +
> +static void netc_timer_init(struct netc_timer *priv)
> +{
> +	u32 tmr_emask = TMR_TEVENT_ALM1EN | TMR_TEVENT_ALM2EN;
> +	u32 fractional_period = lower_32_bits(priv->period);
> +	u32 integral_period = upper_32_bits(priv->period);
> +	u32 tmr_ctrl, fiper_ctrl;
> +	struct timespec64 now;
> +	u64 ns;
> +	int i;
> +
> +	/* Software must enable timer first and the clock selected must be
> +	 * active, otherwise, the registers which are in the timer clock
> +	 * domain are not accessible.
> +	 */
> +	tmr_ctrl = (priv->clk_select & TMR_CTRL_CK_SEL) | TMR_CTRL_TE;

Candidate for FIELD_PREP()?

> +	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +	netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc);
> +
> +	/* Disable FIPER by default */
> +	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> +	for (i = 0; i < NETC_TMR_FIPER_NUM; i++) {
> +		fiper_ctrl |= FIPER_CTRL_DIS(i);
> +		fiper_ctrl &= ~FIPER_CTRL_PG(i);
> +	}
> +	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +
> +	ktime_get_real_ts64(&now);
> +	ns = timespec64_to_ns(&now);
> +	netc_timer_cnt_write(priv, ns);
> +
> +	/* Allow atomic writes to TCLK_PERIOD and TMR_ADD, An update to
> +	 * TCLK_PERIOD does not take effect until TMR_ADD is written.
> +	 */
> +	tmr_ctrl |= ((integral_period << 16) & TMR_CTRL_TCLK_PERIOD) |

Candidate for FIELD_PREP()?

> +		     TMR_COMP_MODE | TMR_CTRL_FS;
> +	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
> +	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
> +}
> +
> +static int netc_timer_pci_probe(struct pci_dev *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct netc_timer *priv;
> +	int err, len;
> +
> +	pcie_flr(pdev);
> +	err = pci_enable_device_mem(pdev);
> +	if (err)
> +		return dev_err_probe(dev, err, "Failed to enable device\n");
> +
> +	err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> +	if (err) {
> +		dev_err(dev, "dma_set_mask_and_coherent() failed, err:%pe\n",
> +			ERR_PTR(err));
> +		goto disable_dev;
> +	}
> +
> +	err = pci_request_mem_regions(pdev, KBUILD_MODNAME);
> +	if (err) {
> +		dev_err(dev, "pci_request_regions() failed, err:%pe\n",
> +			ERR_PTR(err));
> +		goto disable_dev;
> +	}
> +
> +	pci_set_master(pdev);
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv) {
> +		err = -ENOMEM;
> +		goto release_mem_regions;
> +	}
> +
> +	priv->pdev = pdev;
> +	len = pci_resource_len(pdev, NETC_TMR_REGS_BAR);
> +	priv->base = ioremap(pci_resource_start(pdev, NETC_TMR_REGS_BAR), len);
> +	if (!priv->base) {
> +		err = -ENXIO;
> +		dev_err(dev, "ioremap() failed\n");
> +		goto free_priv;
> +	}
> +
> +	pci_set_drvdata(pdev, priv);
> +
> +	return 0;
> +
> +free_priv:
> +	kfree(priv);
> +release_mem_regions:
> +	pci_release_mem_regions(pdev);
> +disable_dev:
> +	pci_disable_device(pdev);
> +
> +	return err;
> +}
> +
> +static void netc_timer_pci_remove(struct pci_dev *pdev)
> +{
> +	struct netc_timer *priv = pci_get_drvdata(pdev);
> +
> +	iounmap(priv->base);
> +	kfree(priv);
> +	pci_release_mem_regions(pdev);
> +	pci_disable_device(pdev);
> +}
> +
> +static int netc_timer_get_reference_clk_source(struct netc_timer *priv)
> +{
> +	struct device *dev = &priv->pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	const char *clk_name = NULL;
> +	u64 ns = NSEC_PER_SEC;

Nitpick: It's strange to keep a constant in a variable.

> +
> +	/* Select NETC system clock as the reference clock by default */
> +	priv->clk_select = NETC_TMR_SYSTEM_CLK;
> +	priv->clk_freq = NETC_TMR_SYSCLK_333M;
> +	priv->period = div_u64(ns << 32, priv->clk_freq);

When reviewing, I found "NSEC_PER_SEC << 32" deeply confusing, since it
has no physical meaning, and I was left wondering "Why is priv->period
equal to 4294967296 ns divided by the clock frequency?".

It would be helpful if you added a comment explaining that in order to
store the period in the desired 32-bit fixed-point format, you can
multiply the numerator of the fraction by 2^32.

> +
> +	if (!np)
> +		return 0;
> +
> +	of_property_read_string(np, "clock-names", &clk_name);
> +	if (!clk_name)
> +		return 0;
> +
> +	/* Update the clock source of the reference clock if the clock
> +	 * name is specified in DTS node.
> +	 */
> +	if (!strcmp(clk_name, "system"))
> +		priv->clk_select = NETC_TMR_SYSTEM_CLK;
> +	else if (!strcmp(clk_name, "ccm_timer"))
> +		priv->clk_select = NETC_TMR_CCM_TIMER1;
> +	else if (!strcmp(clk_name, "ext_1588"))
> +		priv->clk_select = NETC_TMR_EXT_OSC;
> +	else
> +		return -EINVAL;
> +
> +	priv->src_clk = devm_clk_get(dev, clk_name);
> +	if (IS_ERR(priv->src_clk)) {
> +		dev_err(dev, "Failed to get reference clock source\n");

Can this return -EPROBE_DEFER? Should you use dev_err_probe() instead,
to suppress error messages in that case?

> +		return PTR_ERR(priv->src_clk);
> +	}
> +
> +	priv->clk_freq = clk_get_rate(priv->src_clk);
> +	priv->period = div_u64(ns << 32, priv->clk_freq);
> +
> +	return 0;
> +}
> +
> +static int netc_timer_parse_dt(struct netc_timer *priv)
> +{
> +	return netc_timer_get_reference_clk_source(priv);
> +}
> +
> +static irqreturn_t netc_timer_isr(int irq, void *data)
> +{
> +	struct netc_timer *priv = data;
> +	u32 tmr_event, tmr_emask;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->lock, flags);

In hardirq context (this is not threaded) you don't need irqsave/irqrestore.

> +
> +	tmr_event = netc_timer_rd(priv, NETC_TMR_TEVENT);
> +	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);

The value of the NETC_TMR_TEMASK register is a runtime invariant, does
it make sense to cache it in the driver, to avoid a register read per
interrupt?

> +
> +	tmr_event &= tmr_emask;
> +	if (tmr_event & TMR_TEVENT_ALM1EN)
> +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> +
> +	if (tmr_event & TMR_TEVENT_ALM2EN)
> +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 1);

Writing GENMASK_ULL(63, 0) has the effect of disabling the alarm, right?
What is the functional need to have this logic wired up at this stage?
Somebody needs to have armed the alarm in the first place, yet I see no
such code.

> +
> +	/* Clear interrupts status */
> +	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return IRQ_HANDLED;
> +}
> +static int netc_timer_probe(struct pci_dev *pdev,
> +			    const struct pci_device_id *id)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct netc_timer *priv;
> +	int err;
> +
> +	err = netc_timer_pci_probe(pdev);
> +	if (err)
> +		return err;
> +
> +	priv = pci_get_drvdata(pdev);
> +	err = netc_timer_parse_dt(priv);
> +	if (err) {
> +		dev_err(dev, "Failed to parse DT node\n");
> +		goto timer_pci_remove;
> +	}
> +
> +	priv->caps = netc_timer_ptp_caps;
> +	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
> +	priv->phc_index = -1; /* initialize it as an invalid index */

A better use of the comment space would be to explain why, not just to
add obvious and unhelpful subtitles to the code.

When is the priv->phc_index value of -1 preserved (not overwritten with
the ptp_clock_index() result)? It seems to be when the driver fails to
probe.

But in that case, doesn't device_unbind_cleanup() call "dev_set_drvdata(dev, NULL);",
to prevent what would otherwise be a use-after-free?

> +	spin_lock_init(&priv->lock);
> +
> +	err = clk_prepare_enable(priv->src_clk);
> +	if (err) {
> +		dev_err(dev, "Failed to enable timer source clock\n");
> +		goto timer_pci_remove;
> +	}
> +
> +	err = netc_timer_init_msix_irq(priv);
> +	if (err)
> +		goto disable_clk;
> +
> +	netc_timer_init(priv);
> +	priv->clock = ptp_clock_register(&priv->caps, dev);
> +	if (IS_ERR(priv->clock)) {
> +		err = PTR_ERR(priv->clock);
> +		goto free_msix_irq;
> +	}
> +
> +	priv->phc_index = ptp_clock_index(priv->clock);
> +
> +	return 0;
> +
> +free_msix_irq:
> +	netc_timer_free_msix_irq(priv);
> +disable_clk:
> +	clk_disable_unprepare(priv->src_clk);
> +timer_pci_remove:
> +	netc_timer_pci_remove(pdev);
> +
> +	return err;
> +}

^ permalink raw reply	[flat|nested] 64+ messages in thread

* RE: [PATCH v2 net-next 03/14] ptp: netc: add NETC Timer PTP driver support
  2025-07-23 16:09   ` Vladimir Oltean
@ 2025-07-24  2:36     ` Wei Fang
  0 siblings, 0 replies; 64+ messages in thread
From: Wei Fang @ 2025-07-24  2:36 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	richardcochran@gmail.com, Claudiu Manoil, Clark Wang,
	andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
	kuba@kernel.org, pabeni@redhat.com, vadim.fedorenko@linux.dev,
	Frank Li, shawnguo@kernel.org, s.hauer@pengutronix.de,
	festevam@gmail.com, F.S. Peng, devicetree@vger.kernel.org,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	imx@lists.linux.dev, kernel@pengutronix.de

> On Wed, Jul 16, 2025 at 03:31:00PM +0800, Wei Fang wrote:
> > NETC Timer provides current time with nanosecond resolution, precise
> > periodic pulse, pulse on timeout (alarm), and time capture on external
> > pulse support. And it supports time synchronization as required for
> > IEEE 1588 and IEEE 802.1AS-2020. The enetc v4 driver can implement PTP
> > synchronization through the relevant interfaces provided by the driver.
> > Note that the current driver does not support PEROUT, PPS and EXTTS yet,
> > and support will be added one by one in subsequent patches.
> 
> Would you mind adding a paragraph justifying why you are introducing a
> new driver, rather than extending the similar ptp_qoriq.c?
> 

Sure, I will add paragraph to explain this. Thanks.

> >
> > Signed-off-by: Wei Fang <wei.fang@nxp.com>
> >
> > ---
> > v2 changes:
> > 1. Rename netc_timer_get_source_clk() to
> >    netc_timer_get_reference_clk_source() and refactor it
> > 2. Remove the scaled_ppm check in netc_timer_adjfine()
> > 3. Add a comment in netc_timer_cur_time_read()
> > 4. Add linux/bitfield.h to fix the build errors
> > ---
> >  drivers/ptp/Kconfig             |  11 +
> >  drivers/ptp/Makefile            |   1 +
> >  drivers/ptp/ptp_netc.c          | 568
> ++++++++++++++++++++++++++++++++
> >  include/linux/fsl/netc_global.h |  12 +-
> >  4 files changed, 591 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/ptp/ptp_netc.c
> >
> > diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
> > index 204278eb215e..3e005b992aef 100644
> > --- a/drivers/ptp/Kconfig
> > +++ b/drivers/ptp/Kconfig
> > @@ -252,4 +252,15 @@ config PTP_S390
> >  	  driver provides the raw clock value without the delta to
> >  	  userspace. That way userspace programs like chrony could steer
> >  	  the kernel clock.
> > +
> > +config PTP_1588_CLOCK_NETC
> > +	bool "NXP NETC Timer PTP Driver"
> > +	depends on PTP_1588_CLOCK=y
> > +	depends on PCI_MSI
> > +	help
> > +	  This driver adds support for using the NXP NETC Timer as a PTP
> > +	  clock. This clock is used by ENETC MAC or NETC Switch for PTP
> > +	  synchronization. It also supports periodic output signal (e.g.
> > +	  PPS) and external trigger timestamping.
> > +
> >  endmenu
> > diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
> > index 25f846fe48c9..d48fe4009fa4 100644
> > --- a/drivers/ptp/Makefile
> > +++ b/drivers/ptp/Makefile
> > @@ -23,3 +23,4 @@ obj-$(CONFIG_PTP_1588_CLOCK_VMW)	+= ptp_vmw.o
> >  obj-$(CONFIG_PTP_1588_CLOCK_OCP)	+= ptp_ocp.o
> >  obj-$(CONFIG_PTP_DFL_TOD)		+= ptp_dfl_tod.o
> >  obj-$(CONFIG_PTP_S390)			+= ptp_s390.o
> > +obj-$(CONFIG_PTP_1588_CLOCK_NETC)	+= ptp_netc.o
> > diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> > new file mode 100644
> > index 000000000000..82cb1e6a0fe9
> > --- /dev/null
> > +++ b/drivers/ptp/ptp_netc.c
> > @@ -0,0 +1,568 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
> > +/*
> > + * NXP NETC Timer driver
> > + * Copyright 2025 NXP
> > + */
> > +
> > +#include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/fsl/netc_global.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/ptp_clock_kernel.h>
> > +
> > +#define NETC_TMR_PCI_VENDOR		0x1131
> > +#define NETC_TMR_PCI_DEVID		0xee02
> > +
> > +#define NETC_TMR_CTRL			0x0080
> > +#define  TMR_CTRL_CK_SEL		GENMASK(1, 0)
> > +#define  TMR_CTRL_TE			BIT(2)
> > +#define  TMR_COMP_MODE			BIT(15)
> > +#define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
> > +#define  TMR_CTRL_FS			BIT(28)
> > +#define  TMR_ALARM1P			BIT(31)
> > +
> > +#define NETC_TMR_TEVENT			0x0084
> > +#define  TMR_TEVENT_ALM1EN		BIT(16)
> > +#define  TMR_TEVENT_ALM2EN		BIT(17)
> > +
> > +#define NETC_TMR_TEMASK			0x0088
> > +#define NETC_TMR_CNT_L			0x0098
> > +#define NETC_TMR_CNT_H			0x009c
> > +#define NETC_TMR_ADD			0x00a0
> > +#define NETC_TMR_PRSC			0x00a8
> > +#define NETC_TMR_OFF_L			0x00b0
> > +#define NETC_TMR_OFF_H			0x00b4
> > +
> > +/* i = 0, 1, i indicates the index of TMR_ALARM */
> > +#define NETC_TMR_ALARM_L(i)		(0x00b8 + (i) * 8)
> > +#define NETC_TMR_ALARM_H(i)		(0x00bc + (i) * 8)
> > +
> > +#define NETC_TMR_FIPER_CTRL		0x00dc
> > +#define  FIPER_CTRL_DIS(i)		(BIT(7) << (i) * 8)
> > +#define  FIPER_CTRL_PG(i)		(BIT(6) << (i) * 8)
> > +
> > +#define NETC_TMR_CUR_TIME_L		0x00f0
> > +#define NETC_TMR_CUR_TIME_H		0x00f4
> > +
> > +#define NETC_TMR_REGS_BAR		0
> > +
> > +#define NETC_TMR_FIPER_NUM		3
> > +#define NETC_TMR_DEFAULT_PRSC		2
> > +#define NETC_TMR_DEFAULT_ALARM		GENMASK_ULL(63, 0)
> > +
> > +/* 1588 timer reference clock source select */
> > +#define NETC_TMR_CCM_TIMER1		0 /* enet_timer1_clk_root, from
> CCM */
> > +#define NETC_TMR_SYSTEM_CLK		1 /* enet_clk_root/2, from CCM */
> > +#define NETC_TMR_EXT_OSC		2 /* tmr_1588_clk, from IO pins */
> > +
> > +#define NETC_TMR_SYSCLK_333M		333333333U
> > +
> > +struct netc_timer {
> > +	void __iomem *base;
> > +	struct pci_dev *pdev;
> > +	spinlock_t lock; /* Prevent concurrent access to registers */
> > +
> > +	struct clk *src_clk;
> > +	struct ptp_clock *clock;
> > +	struct ptp_clock_info caps;
> > +	int phc_index;
> > +	u32 clk_select;
> > +	u32 clk_freq;
> > +	u32 oclk_prsc;
> > +	/* High 32-bit is integer part, low 32-bit is fractional part */
> > +	u64 period;
> > +
> > +	int irq;
> > +};
> > +
> > +#define netc_timer_rd(p, o)		netc_read((p)->base + (o))
> > +#define netc_timer_wr(p, o, v)		netc_write((p)->base + (o), v)
> > +#define ptp_to_netc_timer(ptp)		container_of((ptp), struct
> netc_timer, caps)
> > +
> > +static u64 netc_timer_cnt_read(struct netc_timer *priv)
> > +{
> > +	u32 tmr_cnt_l, tmr_cnt_h;
> > +	u64 ns;
> > +
> > +	/* The user must read the TMR_CNC_L register first to get
> > +	 * correct 64-bit TMR_CNT_H/L counter values.
> > +	 */
> > +	tmr_cnt_l = netc_timer_rd(priv, NETC_TMR_CNT_L);
> > +	tmr_cnt_h = netc_timer_rd(priv, NETC_TMR_CNT_H);
> > +	ns = (((u64)tmr_cnt_h) << 32) | tmr_cnt_l;
> > +
> > +	return ns;
> > +}
> > +
> > +static void netc_timer_cnt_write(struct netc_timer *priv, u64 ns)
> > +{
> > +	u32 tmr_cnt_h = upper_32_bits(ns);
> > +	u32 tmr_cnt_l = lower_32_bits(ns);
> > +
> > +	/* The user must write to TMR_CNT_L register first. */
> > +	netc_timer_wr(priv, NETC_TMR_CNT_L, tmr_cnt_l);
> > +	netc_timer_wr(priv, NETC_TMR_CNT_H, tmr_cnt_h);
> > +}
> > +
> > +static u64 netc_timer_offset_read(struct netc_timer *priv)
> > +{
> > +	u32 tmr_off_l, tmr_off_h;
> > +	u64 offset;
> > +
> > +	tmr_off_l = netc_timer_rd(priv, NETC_TMR_OFF_L);
> > +	tmr_off_h = netc_timer_rd(priv, NETC_TMR_OFF_H);
> > +	offset = (((u64)tmr_off_h) << 32) | tmr_off_l;
> > +
> > +	return offset;
> > +}
> > +
> > +static void netc_timer_offset_write(struct netc_timer *priv, u64 offset)
> > +{
> > +	u32 tmr_off_h = upper_32_bits(offset);
> > +	u32 tmr_off_l = lower_32_bits(offset);
> > +
> > +	netc_timer_wr(priv, NETC_TMR_OFF_L, tmr_off_l);
> > +	netc_timer_wr(priv, NETC_TMR_OFF_H, tmr_off_h);
> > +}
> > +
> > +static u64 netc_timer_cur_time_read(struct netc_timer *priv)
> > +{
> > +	u32 time_h, time_l;
> > +	u64 ns;
> > +
> > +	/* The user should read NETC_TMR_CUR_TIME_L first to
> > +	 * get correct current time.
> > +	 */
> > +	time_l = netc_timer_rd(priv, NETC_TMR_CUR_TIME_L);
> > +	time_h = netc_timer_rd(priv, NETC_TMR_CUR_TIME_H);
> > +	ns = (u64)time_h << 32 | time_l;
> > +
> > +	return ns;
> > +}
> > +
> > +static void netc_timer_alarm_write(struct netc_timer *priv,
> > +				   u64 alarm, int index)
> > +{
> > +	u32 alarm_h = upper_32_bits(alarm);
> > +	u32 alarm_l = lower_32_bits(alarm);
> > +
> > +	netc_timer_wr(priv, NETC_TMR_ALARM_L(index), alarm_l);
> > +	netc_timer_wr(priv, NETC_TMR_ALARM_H(index), alarm_h);
> > +}
> > +
> > +static void netc_timer_adjust_period(struct netc_timer *priv, u64 period)
> > +{
> > +	u32 fractional_period = lower_32_bits(period);
> > +	u32 integral_period = upper_32_bits(period);
> > +	u32 tmr_ctrl, old_tmr_ctrl;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&priv->lock, flags);
> > +
> > +	old_tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
> > +	tmr_ctrl = u32_replace_bits(old_tmr_ctrl, integral_period,
> > +				    TMR_CTRL_TCLK_PERIOD);
> > +	if (tmr_ctrl != old_tmr_ctrl)
> > +		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> > +
> > +	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
> > +
> > +	spin_unlock_irqrestore(&priv->lock, flags);
> > +}
> > +
> > +static int netc_timer_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
> > +{
> > +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> > +	u64 new_period;
> > +
> > +	new_period = adjust_by_scaled_ppm(priv->period, scaled_ppm);
> > +	netc_timer_adjust_period(priv, new_period);
> > +
> > +	return 0;
> > +}
> > +
> > +static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
> > +{
> > +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> > +	u64 tmr_cnt, tmr_off;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&priv->lock, flags);
> > +
> > +	tmr_off = netc_timer_offset_read(priv);
> > +	if (delta < 0 && tmr_off < abs(delta)) {
> 
> You go to great lengths to avoid letting TMROFF become negative, but is
> there any problem if you just let it do so, and delete the imprecise
> "TMR_CNT += delta" code path altogether? An addition with the two's
> complement of a number is the same as a subtraction.

Because the RM does not specify that the TMROFF register is signed, I
thought it was unsigned at the time, so I came up with this logic. I should
do an experiment to prove whether it is signed. Thank you for your reminder.
I think I can do this experiment now. If it is signed, I will improve this logic.

> 
> Let's say delta=-10, and the current TMROFF value is 5.
> Your condition deviates the adjustment through the imprecise method,
> but if we write TMROFF = -5 = 0xffffffff_fffffffb, we should get the
> correct result, no?
> 
> I thought about this a number of ways, and they all seem to be fine.
> Like, the worst thing that can happen is a TMROFF value which became
> negative by accident, due to too many netc_timer_adjtime() values with a
> large (but positive) delta.
> 
> But even that should be fine, because an overflow on TMROFF is
> indistinguishable from an overflow on TMR_CNT.
> 
> Anyway, _this_ is the time of logic which could really use a comment to
> explain the intention behind it.

Yeah, I will add a comment.

> 
> > +		delta += tmr_off;
> > +		if (!tmr_off)
> > +			netc_timer_offset_write(priv, 0);
> > +
> > +		tmr_cnt = netc_timer_cnt_read(priv);
> > +		tmr_cnt += delta;
> > +		netc_timer_cnt_write(priv, tmr_cnt);
> > +	} else {
> > +		tmr_off += delta;
> > +		netc_timer_offset_write(priv, tmr_off);
> > +	}
> > +
> > +	spin_unlock_irqrestore(&priv->lock, flags);
> > +
> > +	return 0;
> > +}
> > +
> > +static int netc_timer_gettimex64(struct ptp_clock_info *ptp,
> > +				 struct timespec64 *ts,
> > +				 struct ptp_system_timestamp *sts)
> > +{
> > +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> > +	unsigned long flags;
> > +	u64 ns;
> > +
> > +	spin_lock_irqsave(&priv->lock, flags);
> > +
> > +	ptp_read_system_prets(sts);
> > +	ns = netc_timer_cur_time_read(priv);
> > +	ptp_read_system_postts(sts);
> > +
> > +	spin_unlock_irqrestore(&priv->lock, flags);
> > +
> > +	*ts = ns_to_timespec64(ns);
> > +
> > +	return 0;
> > +}
> > +
> > +static int netc_timer_settime64(struct ptp_clock_info *ptp,
> > +				const struct timespec64 *ts)
> > +{
> > +	struct netc_timer *priv = ptp_to_netc_timer(ptp);
> > +	u64 ns = timespec64_to_ns(ts);
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&priv->lock, flags);
> > +	netc_timer_offset_write(priv, 0);
> > +	netc_timer_cnt_write(priv, ns);
> > +	spin_unlock_irqrestore(&priv->lock, flags);
> > +
> > +	return 0;
> > +}
> > +
> > +int netc_timer_get_phc_index(struct pci_dev *timer_pdev)
> > +{
> > +	struct netc_timer *priv;
> > +
> > +	if (!timer_pdev)
> > +		return -ENODEV;
> > +
> > +	priv = pci_get_drvdata(timer_pdev);
> > +	if (!priv)
> > +		return -EINVAL;
> > +
> > +	return priv->phc_index;
> > +}
> > +EXPORT_SYMBOL_GPL(netc_timer_get_phc_index);
> > +
> > +static const struct ptp_clock_info netc_timer_ptp_caps = {
> > +	.owner		= THIS_MODULE,
> > +	.name		= "NETC Timer PTP clock",
> > +	.max_adj	= 500000000,
> > +	.n_alarm	= 2,
> 
> Is n_alarm functionally hooked with anything in the PTP core, other than
> the "n_alarms" read-only sysfs? I didn't see anything.
> 
> > +	.n_pins		= 0,
> > +	.adjfine	= netc_timer_adjfine,
> > +	.adjtime	= netc_timer_adjtime,
> > +	.gettimex64	= netc_timer_gettimex64,
> > +	.settime64	= netc_timer_settime64,
> > +};
> > +
> > +static void netc_timer_init(struct netc_timer *priv)
> > +{
> > +	u32 tmr_emask = TMR_TEVENT_ALM1EN | TMR_TEVENT_ALM2EN;
> > +	u32 fractional_period = lower_32_bits(priv->period);
> > +	u32 integral_period = upper_32_bits(priv->period);
> > +	u32 tmr_ctrl, fiper_ctrl;
> > +	struct timespec64 now;
> > +	u64 ns;
> > +	int i;
> > +
> > +	/* Software must enable timer first and the clock selected must be
> > +	 * active, otherwise, the registers which are in the timer clock
> > +	 * domain are not accessible.
> > +	 */
> > +	tmr_ctrl = (priv->clk_select & TMR_CTRL_CK_SEL) | TMR_CTRL_TE;
> 
> Candidate for FIELD_PREP()?
> 
> > +	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> > +	netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc);
> > +
> > +	/* Disable FIPER by default */
> > +	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> > +	for (i = 0; i < NETC_TMR_FIPER_NUM; i++) {
> > +		fiper_ctrl |= FIPER_CTRL_DIS(i);
> > +		fiper_ctrl &= ~FIPER_CTRL_PG(i);
> > +	}
> > +	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> > +
> > +	ktime_get_real_ts64(&now);
> > +	ns = timespec64_to_ns(&now);
> > +	netc_timer_cnt_write(priv, ns);
> > +
> > +	/* Allow atomic writes to TCLK_PERIOD and TMR_ADD, An update to
> > +	 * TCLK_PERIOD does not take effect until TMR_ADD is written.
> > +	 */
> > +	tmr_ctrl |= ((integral_period << 16) & TMR_CTRL_TCLK_PERIOD) |
> 
> Candidate for FIELD_PREP()?
> 
> > +		     TMR_COMP_MODE | TMR_CTRL_FS;
> > +	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> > +	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
> > +	netc_timer_wr(priv, NETC_TMR_TEMASK, tmr_emask);
> > +}
> > +
> > +static int netc_timer_pci_probe(struct pci_dev *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct netc_timer *priv;
> > +	int err, len;
> > +
> > +	pcie_flr(pdev);
> > +	err = pci_enable_device_mem(pdev);
> > +	if (err)
> > +		return dev_err_probe(dev, err, "Failed to enable device\n");
> > +
> > +	err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> > +	if (err) {
> > +		dev_err(dev, "dma_set_mask_and_coherent() failed, err:%pe\n",
> > +			ERR_PTR(err));
> > +		goto disable_dev;
> > +	}
> > +
> > +	err = pci_request_mem_regions(pdev, KBUILD_MODNAME);
> > +	if (err) {
> > +		dev_err(dev, "pci_request_regions() failed, err:%pe\n",
> > +			ERR_PTR(err));
> > +		goto disable_dev;
> > +	}
> > +
> > +	pci_set_master(pdev);
> > +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> > +	if (!priv) {
> > +		err = -ENOMEM;
> > +		goto release_mem_regions;
> > +	}
> > +
> > +	priv->pdev = pdev;
> > +	len = pci_resource_len(pdev, NETC_TMR_REGS_BAR);
> > +	priv->base = ioremap(pci_resource_start(pdev, NETC_TMR_REGS_BAR),
> len);
> > +	if (!priv->base) {
> > +		err = -ENXIO;
> > +		dev_err(dev, "ioremap() failed\n");
> > +		goto free_priv;
> > +	}
> > +
> > +	pci_set_drvdata(pdev, priv);
> > +
> > +	return 0;
> > +
> > +free_priv:
> > +	kfree(priv);
> > +release_mem_regions:
> > +	pci_release_mem_regions(pdev);
> > +disable_dev:
> > +	pci_disable_device(pdev);
> > +
> > +	return err;
> > +}
> > +
> > +static void netc_timer_pci_remove(struct pci_dev *pdev)
> > +{
> > +	struct netc_timer *priv = pci_get_drvdata(pdev);
> > +
> > +	iounmap(priv->base);
> > +	kfree(priv);
> > +	pci_release_mem_regions(pdev);
> > +	pci_disable_device(pdev);
> > +}
> > +
> > +static int netc_timer_get_reference_clk_source(struct netc_timer *priv)
> > +{
> > +	struct device *dev = &priv->pdev->dev;
> > +	struct device_node *np = dev->of_node;
> > +	const char *clk_name = NULL;
> > +	u64 ns = NSEC_PER_SEC;
> 
> Nitpick: It's strange to keep a constant in a variable.
> 
> > +
> > +	/* Select NETC system clock as the reference clock by default */
> > +	priv->clk_select = NETC_TMR_SYSTEM_CLK;
> > +	priv->clk_freq = NETC_TMR_SYSCLK_333M;
> > +	priv->period = div_u64(ns << 32, priv->clk_freq);
> 
> When reviewing, I found "NSEC_PER_SEC << 32" deeply confusing, since it
> has no physical meaning, and I was left wondering "Why is priv->period
> equal to 4294967296 ns divided by the clock frequency?".
> 
> It would be helpful if you added a comment explaining that in order to
> store the period in the desired 32-bit fixed-point format, you can
> multiply the numerator of the fraction by 2^32.
> 

Okay, I will add a comment

> > +
> > +	if (!np)
> > +		return 0;
> > +
> > +	of_property_read_string(np, "clock-names", &clk_name);
> > +	if (!clk_name)
> > +		return 0;
> > +
> > +	/* Update the clock source of the reference clock if the clock
> > +	 * name is specified in DTS node.
> > +	 */
> > +	if (!strcmp(clk_name, "system"))
> > +		priv->clk_select = NETC_TMR_SYSTEM_CLK;
> > +	else if (!strcmp(clk_name, "ccm_timer"))
> > +		priv->clk_select = NETC_TMR_CCM_TIMER1;
> > +	else if (!strcmp(clk_name, "ext_1588"))
> > +		priv->clk_select = NETC_TMR_EXT_OSC;
> > +	else
> > +		return -EINVAL;
> > +
> > +	priv->src_clk = devm_clk_get(dev, clk_name);
> > +	if (IS_ERR(priv->src_clk)) {
> > +		dev_err(dev, "Failed to get reference clock source\n");
> 
> Can this return -EPROBE_DEFER? Should you use dev_err_probe() instead,
> to suppress error messages in that case?
> 
> > +		return PTR_ERR(priv->src_clk);
> > +	}
> > +
> > +	priv->clk_freq = clk_get_rate(priv->src_clk);
> > +	priv->period = div_u64(ns << 32, priv->clk_freq);
> > +
> > +	return 0;
> > +}
> > +
> > +static int netc_timer_parse_dt(struct netc_timer *priv)
> > +{
> > +	return netc_timer_get_reference_clk_source(priv);
> > +}
> > +
> > +static irqreturn_t netc_timer_isr(int irq, void *data)
> > +{
> > +	struct netc_timer *priv = data;
> > +	u32 tmr_event, tmr_emask;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&priv->lock, flags);
> 
> In hardirq context (this is not threaded) you don't need irqsave/irqrestore.

You are right, I will improve this

> 
> > +
> > +	tmr_event = netc_timer_rd(priv, NETC_TMR_TEVENT);
> > +	tmr_emask = netc_timer_rd(priv, NETC_TMR_TEMASK);
> 
> The value of the NETC_TMR_TEMASK register is a runtime invariant, does
> it make sense to cache it in the driver, to avoid a register read per
> interrupt?
> 
> > +
> > +	tmr_event &= tmr_emask;
> > +	if (tmr_event & TMR_TEVENT_ALM1EN)
> > +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> > +
> > +	if (tmr_event & TMR_TEVENT_ALM2EN)
> > +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 1);
> 
> Writing GENMASK_ULL(63, 0) has the effect of disabling the alarm, right?
> What is the functional need to have this logic wired up at this stage?
> Somebody needs to have armed the alarm in the first place, yet I see no
> such code.

Hmm, I am sorry, I will remove the alarm logic to PPS patch.
> 
> > +
> > +	/* Clear interrupts status */
> > +	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
> > +
> > +	spin_unlock_irqrestore(&priv->lock, flags);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +static int netc_timer_probe(struct pci_dev *pdev,
> > +			    const struct pci_device_id *id)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct netc_timer *priv;
> > +	int err;
> > +
> > +	err = netc_timer_pci_probe(pdev);
> > +	if (err)
> > +		return err;
> > +
> > +	priv = pci_get_drvdata(pdev);
> > +	err = netc_timer_parse_dt(priv);
> > +	if (err) {
> > +		dev_err(dev, "Failed to parse DT node\n");
> > +		goto timer_pci_remove;
> > +	}
> > +
> > +	priv->caps = netc_timer_ptp_caps;
> > +	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
> > +	priv->phc_index = -1; /* initialize it as an invalid index */
> 
> A better use of the comment space would be to explain why, not just to
> add obvious and unhelpful subtitles to the code.
> 
> When is the priv->phc_index value of -1 preserved (not overwritten with
> the ptp_clock_index() result)? It seems to be when the driver fails to
> probe.
> 
> But in that case, doesn't device_unbind_cleanup() call "dev_set_drvdata(dev,
> NULL);",
> to prevent what would otherwise be a use-after-free?
> 

My bad, this line is not reasonable, I will remove it.

> > +	spin_lock_init(&priv->lock);
> > +
> > +	err = clk_prepare_enable(priv->src_clk);
> > +	if (err) {
> > +		dev_err(dev, "Failed to enable timer source clock\n");
> > +		goto timer_pci_remove;
> > +	}
> > +
> > +	err = netc_timer_init_msix_irq(priv);
> > +	if (err)
> > +		goto disable_clk;
> > +
> > +	netc_timer_init(priv);
> > +	priv->clock = ptp_clock_register(&priv->caps, dev);
> > +	if (IS_ERR(priv->clock)) {
> > +		err = PTR_ERR(priv->clock);
> > +		goto free_msix_irq;
> > +	}
> > +
> > +	priv->phc_index = ptp_clock_index(priv->clock);
> > +
> > +	return 0;
> > +
> > +free_msix_irq:
> > +	netc_timer_free_msix_irq(priv);
> > +disable_clk:
> > +	clk_disable_unprepare(priv->src_clk);
> > +timer_pci_remove:
> > +	netc_timer_pci_remove(pdev);
> > +
> > +	return err;
> > +}

^ permalink raw reply	[flat|nested] 64+ messages in thread

end of thread, other threads:[~2025-07-24  2:36 UTC | newest]

Thread overview: 64+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-16  7:30 [PATCH v2 net-next 00/14] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
2025-07-16  7:30 ` [PATCH v2 net-next 01/14] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
2025-07-16 19:19   ` Frank Li
2025-07-17  7:40   ` Krzysztof Kozlowski
2025-07-17  8:30     ` Wei Fang
2025-07-17  9:05       ` Vladimir Oltean
2025-07-17  9:55         ` Wei Fang
2025-07-17 12:42           ` Vladimir Oltean
2025-07-17 15:06             ` Frank Li
2025-07-22 14:36               ` Vladimir Oltean
2025-07-22 18:25                 ` Frank Li
2025-07-17 10:04       ` Krzysztof Kozlowski
2025-07-17 10:28         ` Wei Fang
2025-07-16  7:30 ` [PATCH v2 net-next 02/14] dt-bindings: net: add nxp,netc-timer property Wei Fang
2025-07-16 19:28   ` Frank Li
2025-07-17  3:23     ` Wei Fang
2025-07-17  7:42   ` Krzysztof Kozlowski
2025-07-17  8:32     ` Wei Fang
2025-07-17  9:12       ` Krzysztof Kozlowski
2025-07-17  9:49         ` Wei Fang
2025-07-17 10:06           ` Krzysztof Kozlowski
2025-07-17 10:26             ` Wei Fang
2025-07-18  7:46               ` Krzysztof Kozlowski
2025-07-18  7:50                 ` Krzysztof Kozlowski
2025-07-18 12:01                 ` Vladimir Oltean
2025-07-21  6:00                   ` Wei Fang
2025-07-21 12:23                   ` Krzysztof Kozlowski
2025-07-16  7:31 ` [PATCH v2 net-next 03/14] ptp: netc: add NETC Timer PTP driver support Wei Fang
2025-07-16 19:58   ` Frank Li
2025-07-17  8:42     ` Wei Fang
2025-07-23 16:09   ` Vladimir Oltean
2025-07-24  2:36     ` Wei Fang
2025-07-16  7:31 ` [PATCH v2 net-next 04/14] ptp: netc: add PTP_CLK_REQ_PPS support Wei Fang
2025-07-16 20:05   ` Frank Li
2025-07-17 11:59     ` Wei Fang
2025-07-17 15:15       ` Frank Li
2025-07-18  2:08         ` Wei Fang
2025-07-16  7:31 ` [PATCH v2 net-next 05/14] ptp: netc: add periodic pulse output support Wei Fang
2025-07-16 20:26   ` Frank Li
2025-07-17 12:11     ` Wei Fang
2025-07-16  7:31 ` [PATCH v2 net-next 06/14] ptp: netc: add external trigger stamp support Wei Fang
2025-07-16 20:30   ` Frank Li
2025-07-16  7:31 ` [PATCH v2 net-next 07/14] ptp: netc: add debugfs support to loop back pulse signal Wei Fang
2025-07-16 20:32   ` Frank Li
2025-07-16  7:31 ` [PATCH v2 net-next 08/14] MAINTAINERS: add NETC Timer PTP clock driver section Wei Fang
2025-07-16 20:33   ` Frank Li
2025-07-16  7:31 ` [PATCH v2 net-next 09/14] net: enetc: save the parsed information of PTP packet to skb->cb Wei Fang
2025-07-16 20:46   ` Frank Li
2025-07-17 12:20     ` Wei Fang
2025-07-16  7:31 ` [PATCH v2 net-next 10/14] net: enetc: Add enetc_update_ptp_sync_msg() to process PTP sync packet Wei Fang
2025-07-16 20:49   ` Frank Li
2025-07-16  7:31 ` [PATCH v2 net-next 11/14] net: enetc: remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check Wei Fang
2025-07-16 20:50   ` Frank Li
2025-07-16  7:31 ` [PATCH v2 net-next 12/14] net: enetc: add PTP synchronization support for ENETC v4 Wei Fang
2025-07-16 21:01   ` Frank Li
2025-07-17 12:35     ` Wei Fang
2025-07-17 22:07       ` Frank Li
2025-07-18  2:08         ` Wei Fang
2025-07-22 12:57           ` Vladimir Oltean
2025-07-22 13:41             ` Wei Fang
2025-07-16  7:31 ` [PATCH v2 net-next 13/14] net: enetc: don't update sync packet checksum if checksum offload is used Wei Fang
2025-07-16 21:03   ` Frank Li
2025-07-16  7:31 ` [PATCH v2 14/14] arm64: dts: imx95: Add NETC Timer support Wei Fang
2025-07-16 21:04   ` Frank Li

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).