netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95
@ 2025-08-12  9:46 Wei Fang
  2025-08-12  9:46 ` [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
                   ` (14 more replies)
  0 siblings, 15 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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/
v2 link: https://lore.kernel.org/imx/20250716073111.367382-1-wei.fang@nxp.com/
---

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

Wei Fang (14):
  dt-bindings: ptp: add NETC Timer PTP clock
  dt-bindings: net: add ptp-timer property
  dt-bindings: net: add an example for ENETC v4
  ptp: netc: add NETC V4 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: extract enetc_update_ptp_sync_msg() to handle PTP Sync
    packets
  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 standard PCI device compatible string to NETC
    Timer

 .../bindings/net/ethernet-controller.yaml     |    5 +
 .../devicetree/bindings/net/fsl,enetc.yaml    |   15 +
 .../devicetree/bindings/ptp/nxp,ptp-netc.yaml |   63 +
 MAINTAINERS                                   |    9 +
 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                        | 1127 +++++++++++++++++
 include/linux/fsl/netc_global.h               |   12 +-
 15 files changed, 1474 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] 53+ messages in thread

* [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 14:36   ` Frank Li
  2025-08-14  8:25   ` Krzysztof Kozlowski
  2025-08-12  9:46 ` [PATCH v3 net-next 02/15] dt-bindings: net: add ptp-timer property Wei Fang
                   ` (13 subsequent siblings)
  14 siblings, 2 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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"
v3 changes:
1. Remove the "system" clock from clock-names
---
 .../devicetree/bindings/ptp/nxp,ptp-netc.yaml | 63 +++++++++++++++++++
 1 file changed, 63 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..60fb2513fd76
--- /dev/null
+++ b/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
@@ -0,0 +1,63 @@
+# 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 V4 Timer PTP clock
+
+description:
+  NETC V4 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:
+      The "ccm_timer" means the reference clock comes from CCM of SoC.
+      The "ext_1588" means the reference clock comes from external IO
+      pins.
+    enum:
+      - 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] 53+ messages in thread

* [PATCH v3 net-next 02/15] dt-bindings: net: add ptp-timer property
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
  2025-08-12  9:46 ` [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 11:18   ` Vladimir Oltean
                     ` (2 more replies)
  2025-08-12  9:46 ` [PATCH v3 net-next 03/15] dt-bindings: net: add an example for ENETC v4 Wei Fang
                   ` (12 subsequent siblings)
  14 siblings, 3 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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 some Ethernet controllers, the PTP timer function is not integrated.
Instead, the PTP timer is a separate device and provides PTP Hardware
Clock (PHC) to the Ethernet controller to use, such as NXP FMan MAC,
ENETC, etc. Therefore, a property is needed to indicate this hardware
relationship between the Ethernet controller and the PTP timer.

Since this use case is also very common, it is better to add a generic
property to ethernet-controller.yaml. According to the existing binding
docs, there are two good candidates, one is the "ptp-timer" defined in
fsl,fman-dtsec.yaml, and the other is the "ptimer-handle" defined in
fsl,fman.yaml. From the perspective of the name, the former is more
straightforward, so add the "ptp-timer" property.

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

---
v3 changes:
New patch, add a generic property instead of adding a property to
fsl,enetc.yaml
---
 .../devicetree/bindings/net/ethernet-controller.yaml         | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
index 66b1cfbbfe22..2c924d296a8f 100644
--- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml
+++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
@@ -108,6 +108,11 @@ properties:
     $ref: "#/properties/phy-handle"
     deprecated: true
 
+  ptp-timer:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Specifies a reference to a node representing an IEEE 1588 PTP device.
+
   rx-fifo-depth:
     $ref: /schemas/types.yaml#/definitions/uint32
     description:
-- 
2.34.1


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

* [PATCH v3 net-next 03/15] dt-bindings: net: add an example for ENETC v4
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
  2025-08-12  9:46 ` [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
  2025-08-12  9:46 ` [PATCH v3 net-next 02/15] dt-bindings: net: add ptp-timer property Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 14:38   ` Frank Li
  2025-08-12  9:46 ` [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support Wei Fang
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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 DT node example for ENETC v4 device.

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

---
v2 changes:
new patch
v3 changes:
1. Rename the subject
2. Remove nxp,netc-timer property and use ptp-timer in the example
---
 .../devicetree/bindings/net/fsl,enetc.yaml        | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
index ca70f0050171..a545b54c9e5d 100644
--- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
+++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
@@ -86,3 +86,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";
+          phy-handle = <&ethphy0>;
+          phy-mode = "rgmii-id";
+          ptp-timer = <&netc_timer>;
+      };
+    };
-- 
2.34.1


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

* [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (2 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 03/15] dt-bindings: net: add an example for ENETC v4 Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 13:49   ` Vladimir Oltean
                     ` (2 more replies)
  2025-08-12  9:46 ` [PATCH v3 net-next 05/15] ptp: netc: add PTP_CLK_REQ_PPS support Wei Fang
                   ` (10 subsequent siblings)
  14 siblings, 3 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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 V4 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.

Inside NETC, ENETC can capture the timestamp of the sent/received packet
through the PHC provided by the Timer and record it on the Tx/Rx BD. And
through the relevant PHC interfaces provided by the driver, the enetc V4
driver can support PTP time synchronization.

In addition, NETC V4 Timer is similar to the QorIQ 1588 timer, but it is
not exactly the same. The current ptp-qoriq driver is not compatible with
NETC V4 Timer, most of the code cannot be reused, see below reasons.

1. The architecture of ptp-qoriq driver makes the register offset fixed,
however, the offsets of all the high registers and low registers of V4
are swapped, and V4 also adds some new registers. so extending ptp-qoriq
to make it compatible with V4 Timer is tantamount to completely rewriting
ptp-qoriq driver.

2. The usage of some functions is somewhat different from QorIQ timer,
such as the setting of TCLK_PERIOD and TMR_ADD, the logic of configuring
PPS, etc., so making the driver compatible with V4 Timer will undoubtedly
increase the complexity of the code and reduce readability.

3. QorIQ is an expired brand. It is difficult for us to verify whether
it works stably on the QorIQ platforms if we refactor the driver, and
this will make maintenance difficult, so refactoring the driver obviously
does not bring any benefits.

Therefore, add this new driver for NETC V4 Timer. Note that the missing
features like PEROUT, PPS and EXTTS will be added 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
v3 changes:
1. Refactor netc_timer_adjtime() and remove netc_timer_cnt_read()
2. Remove the check of dma_set_mask_and_coherent()
3. Use devm_kzalloc() and pci_ioremap_bar()
4. Move alarm related logic including irq handler to the next patch
5. Improve the commit message
6. Refactor netc_timer_get_reference_clk_source() and remove
   clk_prepare_enable()
7. Use FIELD_PREP() helper
8. Rename PTP_1588_CLOCK_NETC to PTP_NETC_V4_TIMER and improve the
   help text.
9. Refine netc_timer_adjtime(), change tmr_off to s64 type as we
   confirmed TMR_OFF is a signed register.
---
 drivers/ptp/Kconfig             |  11 +
 drivers/ptp/Makefile            |   1 +
 drivers/ptp/ptp_netc.c          | 438 ++++++++++++++++++++++++++++++++
 include/linux/fsl/netc_global.h |  12 +-
 4 files changed, 461 insertions(+), 1 deletion(-)
 create mode 100644 drivers/ptp/ptp_netc.c

diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 204278eb215e..0ac31a20096c 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_NETC_V4_TIMER
+	bool "NXP NETC V4 Timer PTP Driver"
+	depends on PTP_1588_CLOCK=y
+	depends on PCI_MSI
+	help
+	  This driver adds support for using the NXP NETC V4 Timer as a PTP
+	  clock, the clock is used by ENETC V4 or NETC V4 Switch for PTP time
+	  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..8985d723d29c 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_NETC_V4_TIMER)		+= ptp_netc.o
diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
new file mode 100644
index 000000000000..cbe2a64d1ced
--- /dev/null
+++ b/drivers/ptp/ptp_netc.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * NXP NETC V4 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 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
+
+#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
+
+/* 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 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;
+};
+
+#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 const char *const timer_clk_src[] = {
+	"ccm_timer",
+	"ext_1588"
+};
+
+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);
+
+	/* Writes to the TMR_CNT_L register copies the written value
+	 * into the shadow TMR_CNT_L register. Writes to the TMR_CNT_H
+	 * register copies the values written into the shadow TMR_CNT_H
+	 * register. Contents of the shadow registers are copied into
+	 * the TMR_CNT_L and TMR_CNT_H registers following a write into
+	 * the TMR_CNT_H register. So the user must writes 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_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);
+	unsigned long flags;
+	s64 tmr_off;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* Adjusting TMROFF instead of TMR_CNT is that the timer
+	 * counter keeps increasing during reading and writing
+	 * TMR_CNT, which will cause latency.
+	 */
+	tmr_off = netc_timer_offset_read(priv);
+	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_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 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 = FIELD_PREP(TMR_CTRL_CK_SEL, priv->clk_select) |
+		   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 |= FIELD_PREP(TMR_CTRL_TCLK_PERIOD, integral_period) |
+		    TMR_COMP_MODE;
+	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
+	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
+}
+
+static int netc_timer_pci_probe(struct pci_dev *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct netc_timer *priv;
+	int err;
+
+	pcie_flr(pdev);
+	err = pci_enable_device_mem(pdev);
+	if (err)
+		return dev_err_probe(dev, err, "Failed to enable device\n");
+
+	dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+	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 = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		err = -ENOMEM;
+		goto release_mem_regions;
+	}
+
+	priv->pdev = pdev;
+	priv->base = pci_ioremap_bar(pdev, NETC_TMR_REGS_BAR);
+	if (!priv->base) {
+		err = -ENOMEM;
+		goto release_mem_regions;
+	}
+
+	pci_set_drvdata(pdev, priv);
+
+	return 0;
+
+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);
+	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 clk *clk;
+	int i;
+
+	/* Select NETC system clock as the reference clock by default */
+	priv->clk_select = NETC_TMR_SYSTEM_CLK;
+	priv->clk_freq = NETC_TMR_SYSCLK_333M;
+
+	/* Update the clock source of the reference clock if the clock
+	 * is specified in DT node.
+	 */
+	for (i = 0; i < ARRAY_SIZE(timer_clk_src); i++) {
+		clk = devm_clk_get_optional_enabled(dev, timer_clk_src[i]);
+		if (IS_ERR(clk))
+			return PTR_ERR(clk);
+
+		if (clk) {
+			priv->clk_freq = clk_get_rate(clk);
+			priv->clk_select = i ? NETC_TMR_EXT_OSC :
+					       NETC_TMR_CCM_TIMER1;
+			break;
+		}
+	}
+
+	/* The period is a 64-bit number, the high 32-bit is the integer
+	 * part of the period, the low 32-bit is the fractional part of
+	 * the period. In order to get the desired 32-bit fixed-point
+	 * format, multiply the numerator of the fraction by 2^32.
+	 */
+	priv->period = div_u64(NSEC_PER_SEC << 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 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) {
+		if (err != -EPROBE_DEFER)
+			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;
+	spin_lock_init(&priv->lock);
+
+	netc_timer_init(priv);
+	priv->clock = ptp_clock_register(&priv->caps, dev);
+	if (IS_ERR(priv->clock)) {
+		err = PTR_ERR(priv->clock);
+		goto timer_pci_remove;
+	}
+
+	priv->phc_index = ptp_clock_index(priv->clock);
+
+	return 0;
+
+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_pci_remove(pdev);
+}
+
+static const struct pci_device_id netc_timer_id_table[] = {
+	{ PCI_DEVICE(NETC_TMR_PCI_VENDOR, NETC_TMR_PCI_DEVID) },
+	{ }
+};
+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..17c19c8d3f93 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_NETC_V4_TIMER)
+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] 53+ messages in thread

* [PATCH v3 net-next 05/15] ptp: netc: add PTP_CLK_REQ_PPS support
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (3 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 15:11   ` Frank Li
  2025-08-12  9:46 ` [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support Wei Fang
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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 is capable of generating a PPS interrupt to the host. To
support this feature, a 64-bit alarm time (which is a integral second
of PHC in the future) is set to TMR_ALARM, and the period is set to
TMR_FIPER. The alarm time is compared to the current time on each update,
then the alarm trigger is used as an indication to the TMR_FIPER starts
down counting. After the period has passed, the PPS event is generated.

According to the NETC block guide, the Timer has three FIPERs, any of
which can be used to generate the PPS events, but in the current
implementation, we only need one of them to implement the PPS feature,
so FIPER 0 is used as the default PPS generator. Also, the Timer has
2 ALARMs, currently, ALARM 0 is used as the default time comparator.

However, if there is a time drift when PPS is enabled, the PPS event will
not be generated at an integral second of PHC. The suggested steps from
IP team if time drift happens:

1. Disable FIPER before adjusting the hardware time
2. Rearm ALARM after the time adjustment to make the next PPS event be
generated at an integral second of PHC.
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
v3 changes:
1. Use "2 * NSEC_PER_SEC" to instead of "2000000000U"
2. Improve the commit message
3. Add alarm related logic and the irq handler
4. Add tmr_emask to struct netc_timer to save the irq masks instead of
   reading TMR_EMASK register
5. Remove pps_channel from struct netc_timer and remove
   NETC_TMR_DEFAULT_PPS_CHANNEL
---
 drivers/ptp/ptp_netc.c | 260 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 257 insertions(+), 3 deletions(-)

diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
index cbe2a64d1ced..9026a967a5fe 100644
--- a/drivers/ptp/ptp_netc.c
+++ b/drivers/ptp/ptp_netc.c
@@ -20,7 +20,14 @@
 #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 NETC_TMR_TEVENT			0x0084
+#define  TMR_TEVNET_PPEN(i)		BIT(7 - (i))
+#define  TMR_TEVENT_PPEN_ALL		GENMASK(7, 5)
+#define  TMR_TEVENT_ALMEN(i)		BIT(16 + (i))
+
+#define NETC_TMR_TEMASK			0x0088
 #define NETC_TMR_CNT_L			0x0098
 #define NETC_TMR_CNT_H			0x009c
 #define NETC_TMR_ADD			0x00a0
@@ -28,9 +35,19 @@
 #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)
+
+/* 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
@@ -39,6 +56,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_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 */
@@ -60,6 +80,10 @@ struct netc_timer {
 	u32 oclk_prsc;
 	/* High 32-bit is integer part, low 32-bit is fractional part */
 	u64 period;
+
+	int irq;
+	u32 tmr_emask;
+	bool pps_enabled;
 };
 
 #define netc_timer_rd(p, o)		netc_read((p)->base + (o))
@@ -124,6 +148,155 @@ static u64 netc_timer_cur_time_read(struct netc_timer *priv)
 	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 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(2 * NSEC_PER_SEC, 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 fiper, fiper_ctrl;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	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(0) | FIPER_CTRL_PW(0) |
+				FIPER_CTRL_FS_ALARM(0));
+		fiper_ctrl |= FIPER_CTRL_SET_PW(0, fiper_pw);
+		priv->tmr_emask |= TMR_TEVNET_PPEN(0) | TMR_TEVENT_ALMEN(0);
+		priv->pps_enabled = true;
+		netc_timer_set_pps_alarm(priv, 0, integral_period);
+	} else {
+		if (!priv->pps_enabled)
+			goto unlock_spinlock;
+
+		fiper = NETC_TMR_DEFAULT_FIPER;
+		priv->tmr_emask &= ~(TMR_TEVNET_PPEN(0) |
+				     TMR_TEVENT_ALMEN(0));
+		fiper_ctrl |= FIPER_CTRL_DIS(0);
+		priv->pps_enabled = false;
+		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
+	}
+
+	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
+	netc_timer_wr(priv, NETC_TMR_FIPER(0), 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_ctrl;
+
+	if (!priv->pps_enabled)
+		return;
+
+	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
+	fiper_ctrl |= FIPER_CTRL_DIS(0);
+	netc_timer_wr(priv, NETC_TMR_FIPER(0), NETC_TMR_DEFAULT_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;
+
+	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(0);
+	fiper = NSEC_PER_SEC - integral_period;
+
+	netc_timer_set_pps_alarm(priv, 0, integral_period);
+	netc_timer_wr(priv, NETC_TMR_FIPER(0), 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);
@@ -136,8 +309,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);
 
@@ -163,6 +339,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);
+
 	/* Adjusting TMROFF instead of TMR_CNT is that the timer
 	 * counter keeps increasing during reading and writing
 	 * TMR_CNT, which will cause latency.
@@ -171,6 +349,8 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
 	tmr_off += delta;
 	netc_timer_offset_write(priv, tmr_off);
 
+	netc_timer_enable_pps_fiper(priv);
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
@@ -205,8 +385,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;
@@ -232,10 +416,13 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
 	.name		= "NETC Timer PTP clock",
 	.max_adj	= 500000000,
 	.n_pins		= 0,
+	.n_alarm	= 2,
+	.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)
@@ -252,7 +439,7 @@ static void netc_timer_init(struct netc_timer *priv)
 	 * domain are not accessible.
 	 */
 	tmr_ctrl = FIELD_PREP(TMR_CTRL_CK_SEL, priv->clk_select) |
-		   TMR_CTRL_TE;
+		   TMR_CTRL_TE | TMR_CTRL_FS;
 	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
 	netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc);
 
@@ -372,6 +559,66 @@ 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;
+	struct ptp_clock_event event;
+	u32 tmr_event;
+
+	spin_lock(&priv->lock);
+
+	tmr_event = netc_timer_rd(priv, NETC_TMR_TEVENT);
+	tmr_event &= priv->tmr_emask;
+	/* Clear interrupts status */
+	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
+
+	if (tmr_event & TMR_TEVENT_ALMEN(0))
+		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
+
+	if (tmr_event & TMR_TEVENT_PPEN_ALL) {
+		event.type = PTP_CLOCK_PPS;
+		ptp_clock_event(priv->clock, &event);
+	}
+
+	spin_unlock(&priv->lock);
+
+	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)
 {
@@ -395,17 +642,23 @@ static int netc_timer_probe(struct pci_dev *pdev,
 	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
 	spin_lock_init(&priv->lock);
 
+	err = netc_timer_init_msix_irq(priv);
+	if (err)
+		goto timer_pci_remove;
+
 	netc_timer_init(priv);
 	priv->clock = ptp_clock_register(&priv->caps, dev);
 	if (IS_ERR(priv->clock)) {
 		err = PTR_ERR(priv->clock);
-		goto timer_pci_remove;
+		goto free_msix_irq;
 	}
 
 	priv->phc_index = ptp_clock_index(priv->clock);
 
 	return 0;
 
+free_msix_irq:
+	netc_timer_free_msix_irq(priv);
 timer_pci_remove:
 	netc_timer_pci_remove(pdev);
 
@@ -417,6 +670,7 @@ 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);
 	netc_timer_pci_remove(pdev);
 }
 
-- 
2.34.1


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

* [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (4 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 05/15] ptp: netc: add PTP_CLK_REQ_PPS support Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 12:03   ` Vladimir Oltean
                     ` (2 more replies)
  2025-08-12  9:46 ` [PATCH v3 net-next 07/15] ptp: netc: add external trigger stamp support Wei Fang
                   ` (8 subsequent siblings)
  14 siblings, 3 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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.

For i.MX95, it only has ALARM1 can be used as an indication to the FIPER
start down counting, but i.MX943 has ALARM1 and ALARM2 can be used. That
is to say, only one channel can work for i.MX95, two channels for i.MX943
as most. Current implementation does not allow multiple channels to share
the same alarm register at the same time.

In addition, because of the introduction of PTP_CLK_REQ_PEROUT support,
the PPS channel is changed from being fixed to 0 to being dynamically
selected.

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

---
v2: no changes
v3 changes:
1. Improve the commit message
2. Add revision to struct netc_timer
3. Use priv->tmr_emask to instead of reading TMR_EMASK register
4. Add pps_channel to struct netc_timer and NETC_TMR_INVALID_CHANNEL
5. Add some helper functions: netc_timer_enable/disable_periodic_pulse(),
   and netc_timer_select_pps_channel()
6. Dynamically select PPS channel instead of fixed to channel 0.
---
 drivers/ptp/ptp_netc.c | 356 +++++++++++++++++++++++++++++++++++------
 1 file changed, 306 insertions(+), 50 deletions(-)

diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
index 9026a967a5fe..aa88767f8355 100644
--- a/drivers/ptp/ptp_netc.c
+++ b/drivers/ptp/ptp_netc.c
@@ -53,12 +53,18 @@
 #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_INVALID_CHANNEL	NETC_TMR_FIPER_NUM
 #define NETC_TMR_DEFAULT_PRSC		2
 #define NETC_TMR_DEFAULT_ALARM		GENMASK_ULL(63, 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 */
@@ -67,6 +73,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;
@@ -82,8 +101,12 @@ struct netc_timer {
 	u64 period;
 
 	int irq;
+	int revision;
 	u32 tmr_emask;
-	bool pps_enabled;
+	u8 pps_channel;
+	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))
@@ -192,6 +215,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 */
@@ -199,7 +223,116 @@ 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);
+}
+
+static void netc_timer_enable_periodic_pulse(struct netc_timer *priv,
+					     u8 channel)
+{
+	u32 fiper_pw, fiper, fiper_ctrl, integral_period;
+	struct netc_pp *pp = &priv->pp[channel];
+	int alarm_id = pp->alarm_id;
+
+	integral_period = netc_timer_get_integral_period(priv);
+	/* Set to desired FIPER interval in ns - TCLK_PERIOD */
+	fiper = pp->period - integral_period;
+	fiper_pw = netc_timer_calculate_fiper_pw(priv, fiper);
+
+	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
+	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;
+
+	priv->tmr_emask |= TMR_TEVNET_PPEN(channel) |
+			   TMR_TEVENT_ALMEN(alarm_id);
+
+	if (pp->type == NETC_PP_PPS)
+		netc_timer_set_pps_alarm(priv, channel, integral_period);
+	else
+		netc_timer_set_perout_alarm(priv, channel, integral_period);
+
+	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
+	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
+	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
+}
+
+static void netc_timer_disable_periodic_pulse(struct netc_timer *priv,
+					      u8 channel)
+{
+	struct netc_pp *pp = &priv->pp[channel];
+	int alarm_id = pp->alarm_id;
+	u32 fiper_ctrl;
+
+	if (!pp->enabled)
+		return;
+
+	priv->tmr_emask &= ~(TMR_TEVNET_PPEN(channel) |
+			     TMR_TEVENT_ALMEN(alarm_id));
+
+	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
+	fiper_ctrl |= FIPER_CTRL_DIS(channel);
+
+	netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, alarm_id);
+	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
+	netc_timer_wr(priv, NETC_TMR_FIPER(channel), NETC_TMR_DEFAULT_FIPER);
+	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
+}
+
+static u8 netc_timer_select_pps_channel(struct netc_timer *priv)
+{
+	int i;
+
+	for (i = 0; i < NETC_TMR_FIPER_NUM; i++) {
+		if (!priv->pp[i].enabled)
+			return i;
+	}
+
+	return NETC_TMR_INVALID_CHANNEL;
 }
 
 /* Note that users should not use this API to output PPS signal on
@@ -210,77 +343,178 @@ 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)
 {
-	u32 fiper, fiper_ctrl;
+	struct device *dev = &priv->pdev->dev;
 	unsigned long flags;
+	struct netc_pp *pp;
+	int err = 0;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
-
 	if (on) {
-		u32 integral_period, fiper_pw;
+		int alarm_id;
+		u8 channel;
+
+		if (priv->pps_channel < NETC_TMR_FIPER_NUM) {
+			channel = priv->pps_channel;
+		} else {
+			channel = netc_timer_select_pps_channel(priv);
+			if (channel == NETC_TMR_INVALID_CHANNEL) {
+				dev_err(dev, "No available FIPERs\n");
+				err = -EBUSY;
+				goto unlock_spinlock;
+			}
+		}
 
-		if (priv->pps_enabled)
+		pp = &priv->pp[channel];
+		if (pp->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(0) | FIPER_CTRL_PW(0) |
-				FIPER_CTRL_FS_ALARM(0));
-		fiper_ctrl |= FIPER_CTRL_SET_PW(0, fiper_pw);
-		priv->tmr_emask |= TMR_TEVNET_PPEN(0) | TMR_TEVENT_ALMEN(0);
-		priv->pps_enabled = true;
-		netc_timer_set_pps_alarm(priv, 0, integral_period);
+		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;
+		priv->pps_channel = channel;
+
+		netc_timer_enable_periodic_pulse(priv, channel);
 	} else {
-		if (!priv->pps_enabled)
+		/* pps_channel is invalid if PPS is not enabled, so no
+		 * processing is needed.
+		 */
+		if (priv->pps_channel >= NETC_TMR_FIPER_NUM)
 			goto unlock_spinlock;
 
-		fiper = NETC_TMR_DEFAULT_FIPER;
-		priv->tmr_emask &= ~(TMR_TEVNET_PPEN(0) |
-				     TMR_TEVENT_ALMEN(0));
-		fiper_ctrl |= FIPER_CTRL_DIS(0);
-		priv->pps_enabled = false;
-		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
+		netc_timer_disable_periodic_pulse(priv, priv->pps_channel);
+		priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id);
+		pp = &priv->pp[priv->pps_channel];
+		memset(pp, 0, sizeof(*pp));
+		priv->pps_channel = NETC_TMR_INVALID_CHANNEL;
 	}
 
-	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
-	netc_timer_wr(priv, NETC_TMR_FIPER(0), fiper);
-	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
+unlock_spinlock:
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return err;
+}
+
+static int net_timer_enable_perout(struct netc_timer *priv,
+				   struct ptp_clock_request *rq, int on)
+{
+	struct device *dev = &priv->pdev->dev;
+	u32 channel = rq->perout.index;
+	unsigned long flags;
+	struct netc_pp *pp;
+	int err = 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	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;
+	}
+
+	if (on) {
+		u64 period_ns, gclk_period, max_period, min_period;
+		struct timespec64 period, stime;
+		u32 integral_period;
+		int alarm_id;
+
+		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;
+		}
+
+		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;
+		}
+
+		stime.tv_sec = rq->perout.start.sec;
+		stime.tv_nsec = rq->perout.start.nsec;
+		pp->stime = timespec64_to_ns(&stime);
+		pp->period = period_ns;
+
+		netc_timer_enable_periodic_pulse(priv, channel);
+	} else {
+		netc_timer_disable_periodic_pulse(priv, channel);
+		priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id);
+		memset(pp, 0, sizeof(*pp));
+	}
 
 unlock_spinlock:
 	spin_unlock_irqrestore(&priv->lock, flags);
 
-	return 0;
+	return err;
 }
 
-static void netc_timer_disable_pps_fiper(struct netc_timer *priv)
+static void netc_timer_disable_fiper(struct netc_timer *priv)
 {
-	u32 fiper_ctrl;
+	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++) {
+		if (!priv->pp[i].enabled)
+			continue;
+
+		fiper_ctrl |= FIPER_CTRL_DIS(i);
+		netc_timer_wr(priv, NETC_TMR_FIPER(i), NETC_TMR_DEFAULT_FIPER);
+	}
 
-	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
-	fiper_ctrl |= FIPER_CTRL_DIS(0);
-	netc_timer_wr(priv, NETC_TMR_FIPER(0), NETC_TMR_DEFAULT_FIPER);
 	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
 }
 
-static void netc_timer_enable_pps_fiper(struct netc_timer *priv)
+static void netc_timer_enable_fiper(struct netc_timer *priv)
 {
-	u32 fiper_ctrl, integral_period, fiper;
+	u32 integral_period = netc_timer_get_integral_period(priv);
+	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];
+		u32 fiper;
 
-	integral_period = netc_timer_get_integral_period(priv);
-	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
-	fiper_ctrl &= ~FIPER_CTRL_DIS(0);
-	fiper = NSEC_PER_SEC - integral_period;
+		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);
+	}
 
-	netc_timer_set_pps_alarm(priv, 0, integral_period);
-	netc_timer_wr(priv, NETC_TMR_FIPER(0), fiper);
 	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
 }
 
@@ -292,6 +526,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;
 	}
@@ -310,9 +546,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);
@@ -339,7 +575,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);
 
 	/* Adjusting TMROFF instead of TMR_CNT is that the timer
 	 * counter keeps increasing during reading and writing
@@ -349,7 +585,7 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
 	tmr_off += 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);
 
@@ -386,10 +622,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);
 
@@ -418,6 +654,7 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
 	.n_pins		= 0,
 	.n_alarm	= 2,
 	.pps		= 1,
+	.n_per_out	= 3,
 	.adjfine	= netc_timer_adjfine,
 	.adjtime	= netc_timer_adjtime,
 	.gettimex64	= netc_timer_gettimex64,
@@ -575,6 +812,9 @@ static irqreturn_t netc_timer_isr(int irq, void *data)
 	if (tmr_event & TMR_TEVENT_ALMEN(0))
 		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
 
+	if (tmr_event & TMR_TEVENT_ALMEN(1))
+		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);
@@ -619,6 +859,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)
 {
@@ -631,6 +880,12 @@ static int netc_timer_probe(struct pci_dev *pdev,
 		return err;
 
 	priv = pci_get_drvdata(pdev);
+	priv->revision = netc_timer_get_global_ip_rev(priv);
+	if (priv->revision == NETC_REV_4_1)
+		priv->fs_alarm_num = 1;
+	else
+		priv->fs_alarm_num = NETC_TMR_ALARM_NUM;
+
 	err = netc_timer_parse_dt(priv);
 	if (err) {
 		if (err != -EPROBE_DEFER)
@@ -640,6 +895,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_INVALID_CHANNEL;
 	spin_lock_init(&priv->lock);
 
 	err = netc_timer_init_msix_irq(priv);
-- 
2.34.1


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

* [PATCH v3 net-next 07/15] ptp: netc: add external trigger stamp support
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (5 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 15:27   ` Frank Li
  2025-08-12  9:46 ` [PATCH v3 net-next 08/15] ptp: netc: add debugfs support to loop back pulse signal Wei Fang
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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>

---
v3 changes:
1. Rebase this patch and use priv->tmr_emask instead of reading
   TMR_EMASK register
2. Rename related macros
3. Remove the switch statement from netc_timer_enable_extts() and
   netc_timer_handle_etts_event()
---
 drivers/ptp/ptp_netc.c | 85 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)

diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
index aa88767f8355..45d60ad46b68 100644
--- a/drivers/ptp/ptp_netc.c
+++ b/drivers/ptp/ptp_netc.c
@@ -18,6 +18,7 @@
 #define NETC_TMR_CTRL			0x0080
 #define  TMR_CTRL_CK_SEL		GENMASK(1, 0)
 #define  TMR_CTRL_TE			BIT(2)
+#define  TMR_ETEP(i)			BIT(8 + (i))
 #define  TMR_COMP_MODE			BIT(15)
 #define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
 #define  TMR_CTRL_FS			BIT(28)
@@ -26,12 +27,22 @@
 #define  TMR_TEVNET_PPEN(i)		BIT(7 - (i))
 #define  TMR_TEVENT_PPEN_ALL		GENMASK(7, 5)
 #define  TMR_TEVENT_ALMEN(i)		BIT(16 + (i))
+#define  TMR_TEVENT_ETS_THREN(i)	BIT(20 + (i))
+#define  TMR_TEVENT_ETSEN(i)		BIT(24 + (i))
+#define  TMR_TEVENT_ETS_OVEN(i)		BIT(28 + (i))
+#define  TMR_TEVENT_ETS(i)		(TMR_TEVENT_ETS_THREN(i) | \
+					 TMR_TEVENT_ETSEN(i) | \
+					 TMR_TEVENT_ETS_OVEN(i))
 
 #define NETC_TMR_TEMASK			0x0088
+#define NETC_TMR_STAT			0x0094
+#define  TMR_STAT_ETS_VLD(i)		BIT(24 + (i))
+
 #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
 
@@ -49,6 +60,9 @@
 #define  FIPER_CTRL_PW(i)		(GENMASK(4, 0) << (i) * 8)
 #define  FIPER_CTRL_SET_PW(i, v)	(((v) & GENMASK(4, 0)) << 8 * (i))
 
+/* i = 0, 1, i indicates the index of TMR_ETTS */
+#define NETC_TMR_ETTS_L(i)		(0x00e0 + (i) * 8)
+#define NETC_TMR_ETTS_H(i)		(0x00e4 + (i) * 8)
 #define NETC_TMR_CUR_TIME_L		0x00f0
 #define NETC_TMR_CUR_TIME_H		0x00f4
 
@@ -65,6 +79,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 */
@@ -475,6 +490,64 @@ 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)
+{
+	struct ptp_clock_event event;
+	u32 etts_l = 0, etts_h = 0;
+
+	while (netc_timer_rd(priv, NETC_TMR_STAT) & TMR_STAT_ETS_VLD(index)) {
+		etts_l = netc_timer_rd(priv, NETC_TMR_ETTS_L(index));
+		etts_h = netc_timer_rd(priv, NETC_TMR_ETTS_H(index));
+	}
+
+	/* Invalid time stamp */
+	if (!etts_l && !etts_h)
+		return;
+
+	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)
+{
+	int index = rq->extts.index;
+	unsigned long flags;
+	u32 tmr_ctrl;
+
+	/* Reject requests to enable time stamping on both edges */
+	if ((rq->extts.flags & PTP_EXTTS_EDGES) == PTP_EXTTS_EDGES)
+		return -EOPNOTSUPP;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	netc_timer_handle_etts_event(priv, rq->extts.index, false);
+	if (on) {
+		tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
+		if (rq->extts.flags & PTP_FALLING_EDGE)
+			tmr_ctrl |= TMR_ETEP(index);
+		else
+			tmr_ctrl &= ~TMR_ETEP(index);
+
+		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
+		priv->tmr_emask |= TMR_TEVENT_ETS(index);
+	} else {
+		priv->tmr_emask &= ~TMR_TEVENT_ETS(index);
+	}
+
+	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->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);
@@ -528,6 +601,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;
 	}
@@ -655,6 +730,9 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
 	.n_alarm	= 2,
 	.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,
@@ -687,6 +765,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);
@@ -820,6 +899,12 @@ static irqreturn_t netc_timer_isr(int irq, void *data)
 		ptp_clock_event(priv->clock, &event);
 	}
 
+	if (tmr_event & TMR_TEVENT_ETS(0))
+		netc_timer_handle_etts_event(priv, 0, true);
+
+	if (tmr_event & TMR_TEVENT_ETS(1))
+		netc_timer_handle_etts_event(priv, 1, true);
+
 	spin_unlock(&priv->lock);
 
 	return IRQ_HANDLED;
-- 
2.34.1


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

* [PATCH v3 net-next 08/15] ptp: netc: add debugfs support to loop back pulse signal
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (6 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 07/15] ptp: netc: add external trigger stamp support Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 15:42   ` Frank Li
  2025-08-12  9:46 ` [PATCH v3 net-next 09/15] MAINTAINERS: add NETC Timer PTP clock driver section Wei Fang
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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()
v3 changes:
1. Rename TMR_CTRL_PP1L and TMR_CTRL_PP2L to TMR_CTRL_PPL(i)
2. Remove switch statement from netc_timer_get_fiper_loopback() and
   netc_timer_set_fiper_loopback()
---
 drivers/ptp/ptp_netc.c | 94 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)

diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
index 45d60ad46b68..d483fad51c66 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>
@@ -21,6 +22,7 @@
 #define  TMR_ETEP(i)			BIT(8 + (i))
 #define  TMR_COMP_MODE			BIT(15)
 #define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
+#define  TMR_CTRL_PPL(i)		BIT(27 - (i))
 #define  TMR_CTRL_FS			BIT(28)
 
 #define NETC_TMR_TEVENT			0x0084
@@ -122,6 +124,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))
@@ -953,6 +956,95 @@ 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);
+
+	*val = (tmr_ctrl & TMR_CTRL_PPL(fiper)) ? 1 : 0;
+
+	return 0;
+}
+
+static int netc_timer_set_fiper_loopback(struct netc_timer *priv,
+					 int fiper, bool en)
+{
+	unsigned long flags;
+	u32 tmr_ctrl;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
+	if (en)
+		tmr_ctrl |= TMR_CTRL_PPL(fiper);
+	else
+		tmr_ctrl &= ~TMR_CTRL_PPL(fiper);
+
+	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+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)
 {
@@ -995,6 +1087,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;
 
@@ -1010,6 +1103,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);
 	netc_timer_pci_remove(pdev);
-- 
2.34.1


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

* [PATCH v3 net-next 09/15] MAINTAINERS: add NETC Timer PTP clock driver section
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (7 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 08/15] ptp: netc: add debugfs support to loop back pulse signal Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12  9:46 ` [PATCH v3 net-next 10/15] net: enetc: save the parsed information of PTP packet to skb->cb Wei Fang
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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>
Reviewed-by: Frank Li <Frank.Li@nxp.com>

---
v2,v3: no changes, just collect Reviewed-by tag
---
 MAINTAINERS | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index bd62ad58a47f..5384bfbbc3e3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18264,6 +18264,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] 53+ messages in thread

* [PATCH v3 net-next 10/15] net: enetc: save the parsed information of PTP packet to skb->cb
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (8 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 09/15] MAINTAINERS: add NETC Timer PTP clock driver section Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 15:45   ` Frank Li
  2025-08-12  9:46 ` [PATCH v3 net-next 11/15] net: enetc: extract enetc_update_ptp_sync_msg() to handle PTP Sync packets Wei Fang
                   ` (4 subsequent siblings)
  14 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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, 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 parsing data again.

In addition, 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.
v3 changes:
1. Improve the commit message
2. Fix the error the patch, there were two "++" in the patch
---
 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..54ccd7c57961 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] 53+ messages in thread

* [PATCH v3 net-next 11/15] net: enetc: extract enetc_update_ptp_sync_msg() to handle PTP Sync packets
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (9 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 10/15] net: enetc: save the parsed information of PTP packet to skb->cb Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 15:52   ` Frank Li
  2025-08-12  9:46 ` [PATCH v3 net-next 12/15] net: enetc: remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check Wei Fang
                   ` (3 subsequent siblings)
  14 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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

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.

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

---
v2: no changes
v3: Change the subject and improve the commit message
---
 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 54ccd7c57961..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] 53+ messages in thread

* [PATCH v3 net-next 12/15] net: enetc: remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (10 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 11/15] net: enetc: extract enetc_update_ptp_sync_msg() to handle PTP Sync packets Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12  9:46 ` [PATCH v3 net-next 13/15] net: enetc: add PTP synchronization support for ENETC v4 Wei Fang
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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>
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 related	[flat|nested] 53+ messages in thread

* [PATCH v3 net-next 13/15] net: enetc: add PTP synchronization support for ENETC v4
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (11 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 12/15] net: enetc: remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 15:58   ` Frank Li
  2025-08-12  9:46 ` [PATCH v3 net-next 14/15] net: enetc: don't update sync packet checksum if checksum offload is used Wei Fang
  2025-08-12  9:46 ` [PATCH v3 15/15] arm64: dts: imx95: add standard PCI device compatible string to NETC Timer Wei Fang
  14 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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.
v3 changes:
1. Change CONFIG_PTP_1588_CLOCK_NETC to CONFIG_PTP_NETC_V4_TIMER
2. Change "nxp,netc-timer" to "ptp-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..6dbc9cc811a0 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..815afdc2ec23 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_NETC_V4_TIMER);
+}
+
 #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..b6014b1069de 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, "ptp-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] 53+ messages in thread

* [PATCH v3 net-next 14/15] net: enetc: don't update sync packet checksum if checksum offload is used
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (12 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 13/15] net: enetc: add PTP synchronization support for ENETC v4 Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12  9:46 ` [PATCH v3 15/15] arm64: dts: imx95: add standard PCI device compatible string to NETC Timer Wei Fang
  14 siblings, 0 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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>
Reviewed-by: Frank Li <Frank.Li@nxp.com>

---
v3: no changes, just collect Reviewed-by tag
---
 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 6dbc9cc811a0..aae462a0cf5a 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] 53+ messages in thread

* [PATCH v3 15/15] arm64: dts: imx95: add standard PCI device compatible string to NETC Timer
  2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
                   ` (13 preceding siblings ...)
  2025-08-12  9:46 ` [PATCH v3 net-next 14/15] net: enetc: don't update sync packet checksum if checksum offload is used Wei Fang
@ 2025-08-12  9:46 ` Wei Fang
  2025-08-12 15:59   ` Frank Li
  14 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-12  9:46 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

PCI devices should have a compatible string based on the vendor and
device IDs. So add this compatible string to NETC Timer.

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

---
v2 changes:
new patch
v3 changes:
Since the commit 02b7adb791e1 ("arm64: dts: imx95-19x19-evk: add adc0
flexcan[1,2] i2c[2,3] uart5 spi3 and tpm3") has enabled NETC Timer, so
rebase this patch and change the title and commit message.
---
 arch/arm64/boot/dts/freescale/imx95.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi
index 4ca6a7ea586e..605f14d8fa25 100644
--- a/arch/arm64/boot/dts/freescale/imx95.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx95.dtsi
@@ -1948,6 +1948,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] 53+ messages in thread

* Re: [PATCH v3 net-next 02/15] dt-bindings: net: add ptp-timer property
  2025-08-12  9:46 ` [PATCH v3 net-next 02/15] dt-bindings: net: add ptp-timer property Wei Fang
@ 2025-08-12 11:18   ` Vladimir Oltean
  2025-08-12 14:40   ` Frank Li
  2025-08-14  8:27   ` Krzysztof Kozlowski
  2 siblings, 0 replies; 53+ messages in thread
From: Vladimir Oltean @ 2025-08-12 11:18 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 Tue, Aug 12, 2025 at 05:46:21PM +0800, Wei Fang wrote:
> For some Ethernet controllers, the PTP timer function is not integrated.
> Instead, the PTP timer is a separate device and provides PTP Hardware
> Clock (PHC) to the Ethernet controller to use, such as NXP FMan MAC,
> ENETC, etc. Therefore, a property is needed to indicate this hardware
> relationship between the Ethernet controller and the PTP timer.
> 
> Since this use case is also very common, it is better to add a generic
> property to ethernet-controller.yaml. According to the existing binding
> docs, there are two good candidates, one is the "ptp-timer" defined in
> fsl,fman-dtsec.yaml, and the other is the "ptimer-handle" defined in
> fsl,fman.yaml. From the perspective of the name, the former is more
> straightforward, so add the "ptp-timer" property.
> 
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
> 
> ---
> v3 changes:
> New patch, add a generic property instead of adding a property to
> fsl,enetc.yaml
> ---

Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>

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

* Re: [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support
  2025-08-12  9:46 ` [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support Wei Fang
@ 2025-08-12 12:03   ` Vladimir Oltean
  2025-08-13  1:42     ` Wei Fang
  2025-08-12 15:24   ` Frank Li
  2025-08-13  2:35   ` kernel test robot
  2 siblings, 1 reply; 53+ messages in thread
From: Vladimir Oltean @ 2025-08-12 12:03 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 Tue, Aug 12, 2025 at 05:46:25PM +0800, Wei Fang wrote:
> @@ -210,77 +343,178 @@ 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)
>  {
> -	u32 fiper, fiper_ctrl;
> +	struct device *dev = &priv->pdev->dev;
>  	unsigned long flags;
> +	struct netc_pp *pp;
> +	int err = 0;
>  
>  	spin_lock_irqsave(&priv->lock, flags);
>  
> -	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> -
>  	if (on) {
		...
>  	} else {
> -		if (!priv->pps_enabled)
> +		/* pps_channel is invalid if PPS is not enabled, so no
> +		 * processing is needed.
> +		 */
> +		if (priv->pps_channel >= NETC_TMR_FIPER_NUM)
>  			goto unlock_spinlock;
>  
> -		fiper = NETC_TMR_DEFAULT_FIPER;
> -		priv->tmr_emask &= ~(TMR_TEVNET_PPEN(0) |
> -				     TMR_TEVENT_ALMEN(0));
> -		fiper_ctrl |= FIPER_CTRL_DIS(0);
> -		priv->pps_enabled = false;
> -		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> +		netc_timer_disable_periodic_pulse(priv, priv->pps_channel);
> +		priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id);

You dereference "pp"->alarm_id before assigning "pp" one line below.

> +		pp = &priv->pp[priv->pps_channel];
> +		memset(pp, 0, sizeof(*pp));
> +		priv->pps_channel = NETC_TMR_INVALID_CHANNEL;
>  	}
>  
> -	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
> -	netc_timer_wr(priv, NETC_TMR_FIPER(0), fiper);
> -	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +unlock_spinlock:
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return err;
> +}

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

* Re: [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support
  2025-08-12  9:46 ` [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support Wei Fang
@ 2025-08-12 13:49   ` Vladimir Oltean
  2025-08-13  2:21     ` Wei Fang
  2025-08-12 15:01   ` Frank Li
  2025-08-13  4:45   ` kernel test robot
  2 siblings, 1 reply; 53+ messages in thread
From: Vladimir Oltean @ 2025-08-12 13:49 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 Tue, Aug 12, 2025 at 05:46:23PM +0800, Wei Fang wrote:
> +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);
...
> @@ -16,4 +17,13 @@ static inline void netc_write(void __iomem *reg, u32 val)
>  	iowrite32(val, reg);
>  }
>  
> +#if IS_ENABLED(CONFIG_PTP_NETC_V4_TIMER)
> +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
> +

I was expecting that with the generic ptp-timer phandle you'd also offer
a generic mechanism of retrieving the PHC index, instead of cooking up a
custom API convention between the NETC MAC and the NETC timer.

Something like below, completely untested:

struct ptp_clock_fwnode_match {
	struct fwnode_handle *fwnode;
	struct ptp_clock *clock;
};

static int ptp_clock_fwnode_match(struct device *dev, void *data)
{
	struct ptp_clock_fwnode_match *match = data;

	if (!dev->parent || dev_fwnode(dev->parent) != match->fwnode)
		return 0;

	match->clock = dev_get_drvdata(dev);
	return 1;
}

static struct ptp_clock *ptp_clock_find_by_fwnode(struct fwnode_handle *fwnode)
{
	struct ptp_clock_fwnode_match match = { .fwnode = fwnode };

	class_for_each_device(&ptp_class, NULL, &match, ptp_clock_fwnode_match);

	return match.clock;
}

int ptp_clock_index_by_fwnode_handle(struct fwnode_handle *fwnode)
{
	struct fwnode_handle *ptp_fwnode;
	struct ptp_clock *clock;
	int phc_index;

	ptp_fwnode = fwnode_find_reference(fwnode, "ptp-timer", 0);
	if (!ptp_fwnode)
		return -1;

	clock = ptp_clock_find_by_fwnode(ptp_fwnode);
	fwnode_handle_put(ptp_fwnode);
	if (!clock)
		return -1;

	phc_index = ptp_clock_index(clock);
	put_device(&clock->dev);

	return phc_index;
}
EXPORT_SYMBOL_GPL(ptp_clock_index_by_fwnode_handle);

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

* Re: [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock
  2025-08-12  9:46 ` [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
@ 2025-08-12 14:36   ` Frank Li
  2025-08-14  8:25   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 53+ messages in thread
From: Frank Li @ 2025-08-12 14:36 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 Tue, Aug 12, 2025 at 05:46:20PM +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"
> v3 changes:
> 1. Remove the "system" clock from clock-names
> ---
>  .../devicetree/bindings/ptp/nxp,ptp-netc.yaml | 63 +++++++++++++++++++
>  1 file changed, 63 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..60fb2513fd76
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
> @@ -0,0 +1,63 @@
> +# 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 V4 Timer PTP clock
> +
> +description:
> +  NETC V4 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:
> +      The "ccm_timer" means the reference clock comes from CCM of SoC.
> +      The "ext_1588" means the reference clock comes from external IO
> +      pins.
> +    enum:
> +      - 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] 53+ messages in thread

* Re: [PATCH v3 net-next 03/15] dt-bindings: net: add an example for ENETC v4
  2025-08-12  9:46 ` [PATCH v3 net-next 03/15] dt-bindings: net: add an example for ENETC v4 Wei Fang
@ 2025-08-12 14:38   ` Frank Li
  2025-08-13  1:38     ` Wei Fang
  0 siblings, 1 reply; 53+ messages in thread
From: Frank Li @ 2025-08-12 14:38 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 Tue, Aug 12, 2025 at 05:46:22PM +0800, Wei Fang wrote:
> Add a DT node example for ENETC v4 device.

Not sure why need add examples here? Any big difference with existed
example?

Frank

>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
>
> ---
> v2 changes:
> new patch
> v3 changes:
> 1. Rename the subject
> 2. Remove nxp,netc-timer property and use ptp-timer in the example
> ---
>  .../devicetree/bindings/net/fsl,enetc.yaml        | 15 +++++++++++++++
>  1 file changed, 15 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> index ca70f0050171..a545b54c9e5d 100644
> --- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> +++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> @@ -86,3 +86,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";
> +          phy-handle = <&ethphy0>;
> +          phy-mode = "rgmii-id";
> +          ptp-timer = <&netc_timer>;
> +      };
> +    };
> --
> 2.34.1
>

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

* Re: [PATCH v3 net-next 02/15] dt-bindings: net: add ptp-timer property
  2025-08-12  9:46 ` [PATCH v3 net-next 02/15] dt-bindings: net: add ptp-timer property Wei Fang
  2025-08-12 11:18   ` Vladimir Oltean
@ 2025-08-12 14:40   ` Frank Li
  2025-08-14  8:27   ` Krzysztof Kozlowski
  2 siblings, 0 replies; 53+ messages in thread
From: Frank Li @ 2025-08-12 14: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, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Tue, Aug 12, 2025 at 05:46:21PM +0800, Wei Fang wrote:
> For some Ethernet controllers, the PTP timer function is not integrated.
> Instead, the PTP timer is a separate device and provides PTP Hardware
> Clock (PHC) to the Ethernet controller to use, such as NXP FMan MAC,
> ENETC, etc. Therefore, a property is needed to indicate this hardware
> relationship between the Ethernet controller and the PTP timer.
>
> Since this use case is also very common, it is better to add a generic
> property to ethernet-controller.yaml. According to the existing binding
> docs, there are two good candidates, one is the "ptp-timer" defined in
> fsl,fman-dtsec.yaml, and the other is the "ptimer-handle" defined in
> fsl,fman.yaml. From the perspective of the name, the former is more
> straightforward, so add the "ptp-timer" property.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>

Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
> ---
> v3 changes:
> New patch, add a generic property instead of adding a property to
> fsl,enetc.yaml
> ---
>  .../devicetree/bindings/net/ethernet-controller.yaml         | 5 +++++
>  1 file changed, 5 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
> index 66b1cfbbfe22..2c924d296a8f 100644
> --- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml
> +++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
> @@ -108,6 +108,11 @@ properties:
>      $ref: "#/properties/phy-handle"
>      deprecated: true
>
> +  ptp-timer:
> +    $ref: /schemas/types.yaml#/definitions/phandle
> +    description:
> +      Specifies a reference to a node representing an IEEE 1588 PTP device.
> +
>    rx-fifo-depth:
>      $ref: /schemas/types.yaml#/definitions/uint32
>      description:
> --
> 2.34.1
>

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

* Re: [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support
  2025-08-12  9:46 ` [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support Wei Fang
  2025-08-12 13:49   ` Vladimir Oltean
@ 2025-08-12 15:01   ` Frank Li
  2025-08-13  1:46     ` Wei Fang
  2025-08-13  4:45   ` kernel test robot
  2 siblings, 1 reply; 53+ messages in thread
From: Frank Li @ 2025-08-12 15: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 Tue, Aug 12, 2025 at 05:46:23PM +0800, Wei Fang wrote:
> NETC V4 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.
>
> Inside NETC, ENETC can capture the timestamp of the sent/received packet
> through the PHC provided by the Timer and record it on the Tx/Rx BD. And
> through the relevant PHC interfaces provided by the driver, the enetc V4
> driver can support PTP time synchronization.
>
> In addition, NETC V4 Timer is similar to the QorIQ 1588 timer, but it is
> not exactly the same. The current ptp-qoriq driver is not compatible with
> NETC V4 Timer, most of the code cannot be reused, see below reasons.
>
> 1. The architecture of ptp-qoriq driver makes the register offset fixed,
> however, the offsets of all the high registers and low registers of V4
> are swapped, and V4 also adds some new registers. so extending ptp-qoriq
> to make it compatible with V4 Timer is tantamount to completely rewriting
> ptp-qoriq driver.
>
> 2. The usage of some functions is somewhat different from QorIQ timer,
> such as the setting of TCLK_PERIOD and TMR_ADD, the logic of configuring
> PPS, etc., so making the driver compatible with V4 Timer will undoubtedly
> increase the complexity of the code and reduce readability.
>
> 3. QorIQ is an expired brand. It is difficult for us to verify whether
> it works stably on the QorIQ platforms if we refactor the driver, and
> this will make maintenance difficult, so refactoring the driver obviously
> does not bring any benefits.
>
> Therefore, add this new driver for NETC V4 Timer. Note that the missing
> features like PEROUT, PPS and EXTTS will be added 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
> v3 changes:
> 1. Refactor netc_timer_adjtime() and remove netc_timer_cnt_read()
> 2. Remove the check of dma_set_mask_and_coherent()
> 3. Use devm_kzalloc() and pci_ioremap_bar()
> 4. Move alarm related logic including irq handler to the next patch
> 5. Improve the commit message
> 6. Refactor netc_timer_get_reference_clk_source() and remove
>    clk_prepare_enable()
> 7. Use FIELD_PREP() helper
> 8. Rename PTP_1588_CLOCK_NETC to PTP_NETC_V4_TIMER and improve the
>    help text.
> 9. Refine netc_timer_adjtime(), change tmr_off to s64 type as we
>    confirmed TMR_OFF is a signed register.
> ---
>  drivers/ptp/Kconfig             |  11 +
>  drivers/ptp/Makefile            |   1 +
>  drivers/ptp/ptp_netc.c          | 438 ++++++++++++++++++++++++++++++++
>  include/linux/fsl/netc_global.h |  12 +-
>  4 files changed, 461 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/ptp/ptp_netc.c
>
> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
> index 204278eb215e..0ac31a20096c 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_NETC_V4_TIMER
> +	bool "NXP NETC V4 Timer PTP Driver"
> +	depends on PTP_1588_CLOCK=y
> +	depends on PCI_MSI
> +	help
> +	  This driver adds support for using the NXP NETC V4 Timer as a PTP
> +	  clock, the clock is used by ENETC V4 or NETC V4 Switch for PTP time
> +	  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..8985d723d29c 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_NETC_V4_TIMER)		+= ptp_netc.o
> diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> new file mode 100644
> index 000000000000..cbe2a64d1ced
> --- /dev/null
> +++ b/drivers/ptp/ptp_netc.c
> @@ -0,0 +1,438 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
> +/*
> + * NXP NETC V4 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

Nit: Like this only use once constant, needn't macro, espcial DEVID.

> +
> +#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 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
> +
> +#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
> +
> +/* 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 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;
> +};
> +
> +#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 const char *const timer_clk_src[] = {
> +	"ccm_timer",
> +	"ext_1588"
> +};
> +
> +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);
> +
> +	/* Writes to the TMR_CNT_L register copies the written value
> +	 * into the shadow TMR_CNT_L register. Writes to the TMR_CNT_H
> +	 * register copies the values written into the shadow TMR_CNT_H
> +	 * register. Contents of the shadow registers are copied into
> +	 * the TMR_CNT_L and TMR_CNT_H registers following a write into
> +	 * the TMR_CNT_H register. So the user must writes to TMR_CNT_L
> +	 * register first.
> +	 */

Is all other register the same? like OFF_L, OFF_H?

And read have similar behavor?

> +	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_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);
> +	unsigned long flags;
> +	s64 tmr_off;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	/* Adjusting TMROFF instead of TMR_CNT is that the timer
> +	 * counter keeps increasing during reading and writing
> +	 * TMR_CNT, which will cause latency.
> +	 */
> +	tmr_off = netc_timer_offset_read(priv);
> +	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_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 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 = FIELD_PREP(TMR_CTRL_CK_SEL, priv->clk_select) |
> +		   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 |= FIELD_PREP(TMR_CTRL_TCLK_PERIOD, integral_period) |
> +		    TMR_COMP_MODE;
> +	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period);
> +}
> +
> +static int netc_timer_pci_probe(struct pci_dev *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct netc_timer *priv;
> +	int err;
> +
> +	pcie_flr(pdev);
> +	err = pci_enable_device_mem(pdev);
> +	if (err)
> +		return dev_err_probe(dev, err, "Failed to enable device\n");
> +
> +	dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> +	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 = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv) {
> +		err = -ENOMEM;
> +		goto release_mem_regions;
> +	}

move devm_kzalloc() before pci_enable_device_mem() to reduce a goto

> +
> +	priv->pdev = pdev;
> +	priv->base = pci_ioremap_bar(pdev, NETC_TMR_REGS_BAR);
> +	if (!priv->base) {
> +		err = -ENOMEM;
> +		goto release_mem_regions;
> +	}
> +
> +	pci_set_drvdata(pdev, priv);
> +
> +	return 0;
> +
> +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);
> +	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 clk *clk;
> +	int i;
> +
> +	/* Select NETC system clock as the reference clock by default */
> +	priv->clk_select = NETC_TMR_SYSTEM_CLK;
> +	priv->clk_freq = NETC_TMR_SYSCLK_333M;
> +
> +	/* Update the clock source of the reference clock if the clock
> +	 * is specified in DT node.
> +	 */
> +	for (i = 0; i < ARRAY_SIZE(timer_clk_src); i++) {
> +		clk = devm_clk_get_optional_enabled(dev, timer_clk_src[i]);
> +		if (IS_ERR(clk))
> +			return PTR_ERR(clk);
> +
> +		if (clk) {
> +			priv->clk_freq = clk_get_rate(clk);
> +			priv->clk_select = i ? NETC_TMR_EXT_OSC :
> +					       NETC_TMR_CCM_TIMER1;
> +			break;
> +		}
> +	}
> +
> +	/* The period is a 64-bit number, the high 32-bit is the integer
> +	 * part of the period, the low 32-bit is the fractional part of
> +	 * the period. In order to get the desired 32-bit fixed-point
> +	 * format, multiply the numerator of the fraction by 2^32.
> +	 */
> +	priv->period = div_u64(NSEC_PER_SEC << 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 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) {
> +		if (err != -EPROBE_DEFER)
> +			dev_err(dev, "Failed to parse DT node\n");
> +		goto timer_pci_remove;
> +	}

move netc_timer_parse_dt() before netc_timer_pci_probe()

you can use return dev_err_probe(), which already handle -EPROBE_DEFER
case.


> +
> +	priv->caps = netc_timer_ptp_caps;
> +	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
> +	spin_lock_init(&priv->lock);
> +
> +	netc_timer_init(priv);
> +	priv->clock = ptp_clock_register(&priv->caps, dev);
> +	if (IS_ERR(priv->clock)) {
> +		err = PTR_ERR(priv->clock);
> +		goto timer_pci_remove;
> +	}
> +
> +	priv->phc_index = ptp_clock_index(priv->clock);
> +
> +	return 0;
> +
> +timer_pci_remove:
> +	netc_timer_pci_remove(pdev);
> +
> +	return err;
> +}
> +
> +static void netc_timer_remove(struct pci_dev *pdev)
> +{

use devm_add_action_or_reset() can simpify your error handle.

Frank
> +	struct netc_timer *priv = pci_get_drvdata(pdev);
> +
> +	ptp_clock_unregister(priv->clock);
> +	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) },
> +	{ }
> +};
> +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..17c19c8d3f93 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_NETC_V4_TIMER)
> +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] 53+ messages in thread

* Re: [PATCH v3 net-next 05/15] ptp: netc: add PTP_CLK_REQ_PPS support
  2025-08-12  9:46 ` [PATCH v3 net-next 05/15] ptp: netc: add PTP_CLK_REQ_PPS support Wei Fang
@ 2025-08-12 15:11   ` Frank Li
  2025-08-13  1:59     ` Wei Fang
  0 siblings, 1 reply; 53+ messages in thread
From: Frank Li @ 2025-08-12 15:11 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 Tue, Aug 12, 2025 at 05:46:24PM +0800, Wei Fang wrote:
> The NETC Timer is capable of generating a PPS interrupt to the host. To
> support this feature, a 64-bit alarm time (which is a integral second
> of PHC in the future) is set to TMR_ALARM, and the period is set to
> TMR_FIPER. The alarm time is compared to the current time on each update,
> then the alarm trigger is used as an indication to the TMR_FIPER starts
> down counting. After the period has passed, the PPS event is generated.
>
> According to the NETC block guide, the Timer has three FIPERs, any of
> which can be used to generate the PPS events, but in the current
> implementation, we only need one of them to implement the PPS feature,
> so FIPER 0 is used as the default PPS generator. Also, the Timer has
> 2 ALARMs, currently, ALARM 0 is used as the default time comparator.
>
> However, if there is a time drift when PPS is enabled, the PPS event will
> not be generated at an integral second of PHC. The suggested steps from
> IP team if time drift happens:

according to patch, "drift" means timer adjust period?
netc_timer_adjust_period()

generally, netc_timer_adjust_period() happen 4 times every second, does
disable/re-enable impact pps accurate?

Frank
>
> 1. Disable FIPER before adjusting the hardware time
> 2. Rearm ALARM after the time adjustment to make the next PPS event be
> generated at an integral second of PHC.
> 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
> v3 changes:
> 1. Use "2 * NSEC_PER_SEC" to instead of "2000000000U"
> 2. Improve the commit message
> 3. Add alarm related logic and the irq handler
> 4. Add tmr_emask to struct netc_timer to save the irq masks instead of
>    reading TMR_EMASK register
> 5. Remove pps_channel from struct netc_timer and remove
>    NETC_TMR_DEFAULT_PPS_CHANNEL
> ---
>  drivers/ptp/ptp_netc.c | 260 ++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 257 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> index cbe2a64d1ced..9026a967a5fe 100644
> --- a/drivers/ptp/ptp_netc.c
> +++ b/drivers/ptp/ptp_netc.c
> @@ -20,7 +20,14 @@
>  #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 NETC_TMR_TEVENT			0x0084
> +#define  TMR_TEVNET_PPEN(i)		BIT(7 - (i))
> +#define  TMR_TEVENT_PPEN_ALL		GENMASK(7, 5)
> +#define  TMR_TEVENT_ALMEN(i)		BIT(16 + (i))
> +
> +#define NETC_TMR_TEMASK			0x0088
>  #define NETC_TMR_CNT_L			0x0098
>  #define NETC_TMR_CNT_H			0x009c
>  #define NETC_TMR_ADD			0x00a0
> @@ -28,9 +35,19 @@
>  #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)
> +
> +/* 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
> @@ -39,6 +56,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_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 */
> @@ -60,6 +80,10 @@ struct netc_timer {
>  	u32 oclk_prsc;
>  	/* High 32-bit is integer part, low 32-bit is fractional part */
>  	u64 period;
> +
> +	int irq;
> +	u32 tmr_emask;
> +	bool pps_enabled;
>  };
>
>  #define netc_timer_rd(p, o)		netc_read((p)->base + (o))
> @@ -124,6 +148,155 @@ static u64 netc_timer_cur_time_read(struct netc_timer *priv)
>  	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 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(2 * NSEC_PER_SEC, 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 fiper, fiper_ctrl;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	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(0) | FIPER_CTRL_PW(0) |
> +				FIPER_CTRL_FS_ALARM(0));
> +		fiper_ctrl |= FIPER_CTRL_SET_PW(0, fiper_pw);
> +		priv->tmr_emask |= TMR_TEVNET_PPEN(0) | TMR_TEVENT_ALMEN(0);
> +		priv->pps_enabled = true;
> +		netc_timer_set_pps_alarm(priv, 0, integral_period);
> +	} else {
> +		if (!priv->pps_enabled)
> +			goto unlock_spinlock;
> +
> +		fiper = NETC_TMR_DEFAULT_FIPER;
> +		priv->tmr_emask &= ~(TMR_TEVNET_PPEN(0) |
> +				     TMR_TEVENT_ALMEN(0));
> +		fiper_ctrl |= FIPER_CTRL_DIS(0);
> +		priv->pps_enabled = false;
> +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> +	}
> +
> +	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
> +	netc_timer_wr(priv, NETC_TMR_FIPER(0), 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_ctrl;
> +
> +	if (!priv->pps_enabled)
> +		return;
> +
> +	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> +	fiper_ctrl |= FIPER_CTRL_DIS(0);
> +	netc_timer_wr(priv, NETC_TMR_FIPER(0), NETC_TMR_DEFAULT_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;
> +
> +	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(0);
> +	fiper = NSEC_PER_SEC - integral_period;
> +
> +	netc_timer_set_pps_alarm(priv, 0, integral_period);
> +	netc_timer_wr(priv, NETC_TMR_FIPER(0), 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);
> @@ -136,8 +309,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);
>
> @@ -163,6 +339,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);
> +
>  	/* Adjusting TMROFF instead of TMR_CNT is that the timer
>  	 * counter keeps increasing during reading and writing
>  	 * TMR_CNT, which will cause latency.
> @@ -171,6 +349,8 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
>  	tmr_off += delta;
>  	netc_timer_offset_write(priv, tmr_off);
>
> +	netc_timer_enable_pps_fiper(priv);
> +
>  	spin_unlock_irqrestore(&priv->lock, flags);
>
>  	return 0;
> @@ -205,8 +385,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;
> @@ -232,10 +416,13 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
>  	.name		= "NETC Timer PTP clock",
>  	.max_adj	= 500000000,
>  	.n_pins		= 0,
> +	.n_alarm	= 2,
> +	.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)
> @@ -252,7 +439,7 @@ static void netc_timer_init(struct netc_timer *priv)
>  	 * domain are not accessible.
>  	 */
>  	tmr_ctrl = FIELD_PREP(TMR_CTRL_CK_SEL, priv->clk_select) |
> -		   TMR_CTRL_TE;
> +		   TMR_CTRL_TE | TMR_CTRL_FS;
>  	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
>  	netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc);
>
> @@ -372,6 +559,66 @@ 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;
> +	struct ptp_clock_event event;
> +	u32 tmr_event;
> +
> +	spin_lock(&priv->lock);
> +
> +	tmr_event = netc_timer_rd(priv, NETC_TMR_TEVENT);
> +	tmr_event &= priv->tmr_emask;
> +	/* Clear interrupts status */
> +	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
> +
> +	if (tmr_event & TMR_TEVENT_ALMEN(0))
> +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> +
> +	if (tmr_event & TMR_TEVENT_PPEN_ALL) {
> +		event.type = PTP_CLOCK_PPS;
> +		ptp_clock_event(priv->clock, &event);
> +	}
> +
> +	spin_unlock(&priv->lock);
> +
> +	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)
>  {
> @@ -395,17 +642,23 @@ static int netc_timer_probe(struct pci_dev *pdev,
>  	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
>  	spin_lock_init(&priv->lock);
>
> +	err = netc_timer_init_msix_irq(priv);
> +	if (err)
> +		goto timer_pci_remove;
> +
>  	netc_timer_init(priv);
>  	priv->clock = ptp_clock_register(&priv->caps, dev);
>  	if (IS_ERR(priv->clock)) {
>  		err = PTR_ERR(priv->clock);
> -		goto timer_pci_remove;
> +		goto free_msix_irq;
>  	}
>
>  	priv->phc_index = ptp_clock_index(priv->clock);
>
>  	return 0;
>
> +free_msix_irq:
> +	netc_timer_free_msix_irq(priv);
>  timer_pci_remove:
>  	netc_timer_pci_remove(pdev);
>
> @@ -417,6 +670,7 @@ 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);
>  	netc_timer_pci_remove(pdev);
>  }
>
> --
> 2.34.1
>

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

* Re: [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support
  2025-08-12  9:46 ` [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support Wei Fang
  2025-08-12 12:03   ` Vladimir Oltean
@ 2025-08-12 15:24   ` Frank Li
  2025-08-13  2:35   ` kernel test robot
  2 siblings, 0 replies; 53+ messages in thread
From: Frank Li @ 2025-08-12 15:24 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 Tue, Aug 12, 2025 at 05:46:25PM +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.
>
> For i.MX95, it only has ALARM1 can be used as an indication to the FIPER
> start down counting, but i.MX943 has ALARM1 and ALARM2 can be used. That
> is to say, only one channel can work for i.MX95, two channels for i.MX943
> as most. Current implementation does not allow multiple channels to share
> the same alarm register at the same time.

Keep short and simple.

i.MX96 have only ALARM1. i.MX943 have ALARM1 and ALARM2. So only one channel
for i.MX95, two channels for i.MX943 since channel sharing are not supported
yet.

>
> In addition, because of the introduction of PTP_CLK_REQ_PEROUT support,
> the PPS channel is changed from being fixed to 0 to being dynamically
> selected.

Nit: Change the PPS channel to be dynamically selected from fixed number (0)
because add PTP_CLK_REQ_PEROUT support.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
>
> ---
> v2: no changes
> v3 changes:
> 1. Improve the commit message
> 2. Add revision to struct netc_timer
> 3. Use priv->tmr_emask to instead of reading TMR_EMASK register
> 4. Add pps_channel to struct netc_timer and NETC_TMR_INVALID_CHANNEL
> 5. Add some helper functions: netc_timer_enable/disable_periodic_pulse(),
>    and netc_timer_select_pps_channel()
> 6. Dynamically select PPS channel instead of fixed to channel 0.
> ---
>  drivers/ptp/ptp_netc.c | 356 +++++++++++++++++++++++++++++++++++------
>  1 file changed, 306 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> index 9026a967a5fe..aa88767f8355 100644
> --- a/drivers/ptp/ptp_netc.c
> +++ b/drivers/ptp/ptp_netc.c
> @@ -53,12 +53,18 @@
>  #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_INVALID_CHANNEL	NETC_TMR_FIPER_NUM
>  #define NETC_TMR_DEFAULT_PRSC		2
>  #define NETC_TMR_DEFAULT_ALARM		GENMASK_ULL(63, 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 */
> @@ -67,6 +73,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;
> @@ -82,8 +101,12 @@ struct netc_timer {
>  	u64 period;
>
>  	int irq;
> +	int revision;
>  	u32 tmr_emask;
> -	bool pps_enabled;
> +	u8 pps_channel;
> +	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))
> @@ -192,6 +215,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 */
> @@ -199,7 +223,116 @@ 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);
> +}
> +
> +static void netc_timer_enable_periodic_pulse(struct netc_timer *priv,
> +					     u8 channel)
> +{
> +	u32 fiper_pw, fiper, fiper_ctrl, integral_period;
> +	struct netc_pp *pp = &priv->pp[channel];
> +	int alarm_id = pp->alarm_id;
> +
> +	integral_period = netc_timer_get_integral_period(priv);
> +	/* Set to desired FIPER interval in ns - TCLK_PERIOD */
> +	fiper = pp->period - integral_period;
> +	fiper_pw = netc_timer_calculate_fiper_pw(priv, fiper);
> +
> +	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> +	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;
> +
> +	priv->tmr_emask |= TMR_TEVNET_PPEN(channel) |
> +			   TMR_TEVENT_ALMEN(alarm_id);
> +
> +	if (pp->type == NETC_PP_PPS)
> +		netc_timer_set_pps_alarm(priv, channel, integral_period);
> +	else
> +		netc_timer_set_perout_alarm(priv, channel, integral_period);
> +
> +	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
> +	netc_timer_wr(priv, NETC_TMR_FIPER(channel), fiper);
> +	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +}
> +
> +static void netc_timer_disable_periodic_pulse(struct netc_timer *priv,
> +					      u8 channel)
> +{
> +	struct netc_pp *pp = &priv->pp[channel];
> +	int alarm_id = pp->alarm_id;
> +	u32 fiper_ctrl;
> +
> +	if (!pp->enabled)
> +		return;
> +
> +	priv->tmr_emask &= ~(TMR_TEVNET_PPEN(channel) |
> +			     TMR_TEVENT_ALMEN(alarm_id));
> +
> +	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> +	fiper_ctrl |= FIPER_CTRL_DIS(channel);
> +
> +	netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, alarm_id);
> +	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
> +	netc_timer_wr(priv, NETC_TMR_FIPER(channel), NETC_TMR_DEFAULT_FIPER);
> +	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +}
> +
> +static u8 netc_timer_select_pps_channel(struct netc_timer *priv)
> +{
> +	int i;
> +
> +	for (i = 0; i < NETC_TMR_FIPER_NUM; i++) {
> +		if (!priv->pp[i].enabled)
> +			return i;
> +	}
> +
> +	return NETC_TMR_INVALID_CHANNEL;
>  }
>
>  /* Note that users should not use this API to output PPS signal on
> @@ -210,77 +343,178 @@ 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)
>  {
> -	u32 fiper, fiper_ctrl;
> +	struct device *dev = &priv->pdev->dev;
>  	unsigned long flags;
> +	struct netc_pp *pp;
> +	int err = 0;
>
>  	spin_lock_irqsave(&priv->lock, flags);
>
> -	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> -
>  	if (on) {
> -		u32 integral_period, fiper_pw;
> +		int alarm_id;
> +		u8 channel;
> +
> +		if (priv->pps_channel < NETC_TMR_FIPER_NUM) {
> +			channel = priv->pps_channel;
> +		} else {
> +			channel = netc_timer_select_pps_channel(priv);
> +			if (channel == NETC_TMR_INVALID_CHANNEL) {
> +				dev_err(dev, "No available FIPERs\n");
> +				err = -EBUSY;
> +				goto unlock_spinlock;
> +			}
> +		}
>
> -		if (priv->pps_enabled)
> +		pp = &priv->pp[channel];
> +		if (pp->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(0) | FIPER_CTRL_PW(0) |
> -				FIPER_CTRL_FS_ALARM(0));
> -		fiper_ctrl |= FIPER_CTRL_SET_PW(0, fiper_pw);
> -		priv->tmr_emask |= TMR_TEVNET_PPEN(0) | TMR_TEVENT_ALMEN(0);
> -		priv->pps_enabled = true;
> -		netc_timer_set_pps_alarm(priv, 0, integral_period);
> +		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;
> +		priv->pps_channel = channel;
> +
> +		netc_timer_enable_periodic_pulse(priv, channel);
>  	} else {
> -		if (!priv->pps_enabled)
> +		/* pps_channel is invalid if PPS is not enabled, so no
> +		 * processing is needed.
> +		 */
> +		if (priv->pps_channel >= NETC_TMR_FIPER_NUM)
>  			goto unlock_spinlock;
>
> -		fiper = NETC_TMR_DEFAULT_FIPER;
> -		priv->tmr_emask &= ~(TMR_TEVNET_PPEN(0) |
> -				     TMR_TEVENT_ALMEN(0));
> -		fiper_ctrl |= FIPER_CTRL_DIS(0);
> -		priv->pps_enabled = false;
> -		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> +		netc_timer_disable_periodic_pulse(priv, priv->pps_channel);
> +		priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id);
> +		pp = &priv->pp[priv->pps_channel];
> +		memset(pp, 0, sizeof(*pp));
> +		priv->pps_channel = NETC_TMR_INVALID_CHANNEL;
>  	}
>
> -	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
> -	netc_timer_wr(priv, NETC_TMR_FIPER(0), fiper);
> -	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> +unlock_spinlock:
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return err;
> +}
> +
> +static int net_timer_enable_perout(struct netc_timer *priv,
> +				   struct ptp_clock_request *rq, int on)
> +{
> +	struct device *dev = &priv->pdev->dev;
> +	u32 channel = rq->perout.index;
> +	unsigned long flags;
> +	struct netc_pp *pp;
> +	int err = 0;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	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;
> +	}
> +
> +	if (on) {
> +		u64 period_ns, gclk_period, max_period, min_period;
> +		struct timespec64 period, stime;
> +		u32 integral_period;
> +		int alarm_id;
> +
> +		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;
> +		}
> +
> +		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;
> +		}
> +
> +		stime.tv_sec = rq->perout.start.sec;
> +		stime.tv_nsec = rq->perout.start.nsec;
> +		pp->stime = timespec64_to_ns(&stime);
> +		pp->period = period_ns;
> +
> +		netc_timer_enable_periodic_pulse(priv, channel);
> +	} else {
> +		netc_timer_disable_periodic_pulse(priv, channel);
> +		priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id);
> +		memset(pp, 0, sizeof(*pp));
> +	}
>
>  unlock_spinlock:
>  	spin_unlock_irqrestore(&priv->lock, flags);
>
> -	return 0;
> +	return err;
>  }
>
> -static void netc_timer_disable_pps_fiper(struct netc_timer *priv)
> +static void netc_timer_disable_fiper(struct netc_timer *priv)
>  {
> -	u32 fiper_ctrl;
> +	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++) {
> +		if (!priv->pp[i].enabled)
> +			continue;
> +
> +		fiper_ctrl |= FIPER_CTRL_DIS(i);
> +		netc_timer_wr(priv, NETC_TMR_FIPER(i), NETC_TMR_DEFAULT_FIPER);
> +	}
>
> -	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> -	fiper_ctrl |= FIPER_CTRL_DIS(0);
> -	netc_timer_wr(priv, NETC_TMR_FIPER(0), NETC_TMR_DEFAULT_FIPER);
>  	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
>  }
>
> -static void netc_timer_enable_pps_fiper(struct netc_timer *priv)
> +static void netc_timer_enable_fiper(struct netc_timer *priv)
>  {
> -	u32 fiper_ctrl, integral_period, fiper;
> +	u32 integral_period = netc_timer_get_integral_period(priv);
> +	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];
> +		u32 fiper;
>
> -	integral_period = netc_timer_get_integral_period(priv);
> -	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> -	fiper_ctrl &= ~FIPER_CTRL_DIS(0);
> -	fiper = NSEC_PER_SEC - integral_period;
> +		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);
> +	}
>
> -	netc_timer_set_pps_alarm(priv, 0, integral_period);
> -	netc_timer_wr(priv, NETC_TMR_FIPER(0), fiper);
>  	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
>  }
>
> @@ -292,6 +526,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;
>  	}
> @@ -310,9 +546,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);
> @@ -339,7 +575,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);
>
>  	/* Adjusting TMROFF instead of TMR_CNT is that the timer
>  	 * counter keeps increasing during reading and writing
> @@ -349,7 +585,7 @@ static int netc_timer_adjtime(struct ptp_clock_info *ptp, s64 delta)
>  	tmr_off += 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);
>
> @@ -386,10 +622,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);
>
> @@ -418,6 +654,7 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
>  	.n_pins		= 0,
>  	.n_alarm	= 2,
>  	.pps		= 1,
> +	.n_per_out	= 3,
>  	.adjfine	= netc_timer_adjfine,
>  	.adjtime	= netc_timer_adjtime,
>  	.gettimex64	= netc_timer_gettimex64,
> @@ -575,6 +812,9 @@ static irqreturn_t netc_timer_isr(int irq, void *data)
>  	if (tmr_event & TMR_TEVENT_ALMEN(0))
>  		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
>
> +	if (tmr_event & TMR_TEVENT_ALMEN(1))
> +		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);
> @@ -619,6 +859,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)
>  {
> @@ -631,6 +880,12 @@ static int netc_timer_probe(struct pci_dev *pdev,
>  		return err;
>
>  	priv = pci_get_drvdata(pdev);
> +	priv->revision = netc_timer_get_global_ip_rev(priv);
> +	if (priv->revision == NETC_REV_4_1)
> +		priv->fs_alarm_num = 1;
> +	else
> +		priv->fs_alarm_num = NETC_TMR_ALARM_NUM;
> +
>  	err = netc_timer_parse_dt(priv);
>  	if (err) {
>  		if (err != -EPROBE_DEFER)
> @@ -640,6 +895,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_INVALID_CHANNEL;
>  	spin_lock_init(&priv->lock);
>
>  	err = netc_timer_init_msix_irq(priv);
> --
> 2.34.1
>

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

* Re: [PATCH v3 net-next 07/15] ptp: netc: add external trigger stamp support
  2025-08-12  9:46 ` [PATCH v3 net-next 07/15] ptp: netc: add external trigger stamp support Wei Fang
@ 2025-08-12 15:27   ` Frank Li
  0 siblings, 0 replies; 53+ messages in thread
From: Frank Li @ 2025-08-12 15:27 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 Tue, Aug 12, 2025 at 05:46:26PM +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.
>

Nit: when the FIFO reaches a threshold or overflows.

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

> Signed-off-by: F.S. Peng <fushi.peng@nxp.com>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
>
> ---
> v3 changes:
> 1. Rebase this patch and use priv->tmr_emask instead of reading
>    TMR_EMASK register
> 2. Rename related macros
> 3. Remove the switch statement from netc_timer_enable_extts() and
>    netc_timer_handle_etts_event()
> ---
>  drivers/ptp/ptp_netc.c | 85 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 85 insertions(+)
>
> diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> index aa88767f8355..45d60ad46b68 100644
> --- a/drivers/ptp/ptp_netc.c
> +++ b/drivers/ptp/ptp_netc.c
> @@ -18,6 +18,7 @@
>  #define NETC_TMR_CTRL			0x0080
>  #define  TMR_CTRL_CK_SEL		GENMASK(1, 0)
>  #define  TMR_CTRL_TE			BIT(2)
> +#define  TMR_ETEP(i)			BIT(8 + (i))
>  #define  TMR_COMP_MODE			BIT(15)
>  #define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
>  #define  TMR_CTRL_FS			BIT(28)
> @@ -26,12 +27,22 @@
>  #define  TMR_TEVNET_PPEN(i)		BIT(7 - (i))
>  #define  TMR_TEVENT_PPEN_ALL		GENMASK(7, 5)
>  #define  TMR_TEVENT_ALMEN(i)		BIT(16 + (i))
> +#define  TMR_TEVENT_ETS_THREN(i)	BIT(20 + (i))
> +#define  TMR_TEVENT_ETSEN(i)		BIT(24 + (i))
> +#define  TMR_TEVENT_ETS_OVEN(i)		BIT(28 + (i))
> +#define  TMR_TEVENT_ETS(i)		(TMR_TEVENT_ETS_THREN(i) | \
> +					 TMR_TEVENT_ETSEN(i) | \
> +					 TMR_TEVENT_ETS_OVEN(i))
>
>  #define NETC_TMR_TEMASK			0x0088
> +#define NETC_TMR_STAT			0x0094
> +#define  TMR_STAT_ETS_VLD(i)		BIT(24 + (i))
> +
>  #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
>
> @@ -49,6 +60,9 @@
>  #define  FIPER_CTRL_PW(i)		(GENMASK(4, 0) << (i) * 8)
>  #define  FIPER_CTRL_SET_PW(i, v)	(((v) & GENMASK(4, 0)) << 8 * (i))
>
> +/* i = 0, 1, i indicates the index of TMR_ETTS */
> +#define NETC_TMR_ETTS_L(i)		(0x00e0 + (i) * 8)
> +#define NETC_TMR_ETTS_H(i)		(0x00e4 + (i) * 8)
>  #define NETC_TMR_CUR_TIME_L		0x00f0
>  #define NETC_TMR_CUR_TIME_H		0x00f4
>
> @@ -65,6 +79,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 */
> @@ -475,6 +490,64 @@ 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)
> +{
> +	struct ptp_clock_event event;
> +	u32 etts_l = 0, etts_h = 0;
> +
> +	while (netc_timer_rd(priv, NETC_TMR_STAT) & TMR_STAT_ETS_VLD(index)) {
> +		etts_l = netc_timer_rd(priv, NETC_TMR_ETTS_L(index));
> +		etts_h = netc_timer_rd(priv, NETC_TMR_ETTS_H(index));
> +	}
> +
> +	/* Invalid time stamp */
> +	if (!etts_l && !etts_h)
> +		return;
> +
> +	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)
> +{
> +	int index = rq->extts.index;
> +	unsigned long flags;
> +	u32 tmr_ctrl;
> +
> +	/* Reject requests to enable time stamping on both edges */
> +	if ((rq->extts.flags & PTP_EXTTS_EDGES) == PTP_EXTTS_EDGES)
> +		return -EOPNOTSUPP;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	netc_timer_handle_etts_event(priv, rq->extts.index, false);
> +	if (on) {
> +		tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
> +		if (rq->extts.flags & PTP_FALLING_EDGE)
> +			tmr_ctrl |= TMR_ETEP(index);
> +		else
> +			tmr_ctrl &= ~TMR_ETEP(index);
> +
> +		netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +		priv->tmr_emask |= TMR_TEVENT_ETS(index);
> +	} else {
> +		priv->tmr_emask &= ~TMR_TEVENT_ETS(index);
> +	}
> +
> +	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->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);
> @@ -528,6 +601,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;
>  	}
> @@ -655,6 +730,9 @@ static const struct ptp_clock_info netc_timer_ptp_caps = {
>  	.n_alarm	= 2,
>  	.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,
> @@ -687,6 +765,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);
> @@ -820,6 +899,12 @@ static irqreturn_t netc_timer_isr(int irq, void *data)
>  		ptp_clock_event(priv->clock, &event);
>  	}
>
> +	if (tmr_event & TMR_TEVENT_ETS(0))
> +		netc_timer_handle_etts_event(priv, 0, true);
> +
> +	if (tmr_event & TMR_TEVENT_ETS(1))
> +		netc_timer_handle_etts_event(priv, 1, true);
> +
>  	spin_unlock(&priv->lock);
>
>  	return IRQ_HANDLED;
> --
> 2.34.1
>

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

* Re: [PATCH v3 net-next 08/15] ptp: netc: add debugfs support to loop back pulse signal
  2025-08-12  9:46 ` [PATCH v3 net-next 08/15] ptp: netc: add debugfs support to loop back pulse signal Wei Fang
@ 2025-08-12 15:42   ` Frank Li
  0 siblings, 0 replies; 53+ messages in thread
From: Frank Li @ 2025-08-12 15: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, shawnguo, s.hauer, festevam,
	fushi.peng, devicetree, netdev, linux-kernel, imx, kernel

On Tue, Aug 12, 2025 at 05:46:27PM +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.

The NETC Timer supports to loop back the output pulse signal of Fiper-n
into Trigger-n input. Add debugfs interfaces to enable loopback for PTP
feature validation without external hardware support.

See below typical user case:

>
> An example to test the generation time of PPS event.

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.

Test generation time of the periodic output signal.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
> $ 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()
> v3 changes:
> 1. Rename TMR_CTRL_PP1L and TMR_CTRL_PP2L to TMR_CTRL_PPL(i)
> 2. Remove switch statement from netc_timer_get_fiper_loopback() and
>    netc_timer_set_fiper_loopback()
> ---
>  drivers/ptp/ptp_netc.c | 94 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 94 insertions(+)
>
> diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> index 45d60ad46b68..d483fad51c66 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>
> @@ -21,6 +22,7 @@
>  #define  TMR_ETEP(i)			BIT(8 + (i))
>  #define  TMR_COMP_MODE			BIT(15)
>  #define  TMR_CTRL_TCLK_PERIOD		GENMASK(25, 16)
> +#define  TMR_CTRL_PPL(i)		BIT(27 - (i))
>  #define  TMR_CTRL_FS			BIT(28)
>
>  #define NETC_TMR_TEVENT			0x0084
> @@ -122,6 +124,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))
> @@ -953,6 +956,95 @@ 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);
> +
> +	*val = (tmr_ctrl & TMR_CTRL_PPL(fiper)) ? 1 : 0;
> +
> +	return 0;
> +}
> +
> +static int netc_timer_set_fiper_loopback(struct netc_timer *priv,
> +					 int fiper, bool en)
> +{
> +	unsigned long flags;
> +	u32 tmr_ctrl;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +
> +	tmr_ctrl = netc_timer_rd(priv, NETC_TMR_CTRL);
> +	if (en)
> +		tmr_ctrl |= TMR_CTRL_PPL(fiper);
> +	else
> +		tmr_ctrl &= ~TMR_CTRL_PPL(fiper);
> +
> +	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> +
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +
> +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)
>  {
> @@ -995,6 +1087,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;
>
> @@ -1010,6 +1103,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);
>  	netc_timer_pci_remove(pdev);
> --
> 2.34.1
>

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

* Re: [PATCH v3 net-next 10/15] net: enetc: save the parsed information of PTP packet to skb->cb
  2025-08-12  9:46 ` [PATCH v3 net-next 10/15] net: enetc: save the parsed information of PTP packet to skb->cb Wei Fang
@ 2025-08-12 15:45   ` Frank Li
  0 siblings, 0 replies; 53+ messages in thread
From: Frank Li @ 2025-08-12 15:45 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 Tue, Aug 12, 2025 at 05:46:29PM +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, 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 parsing data again.
>
> In addition, 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>

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

>
> ---
> v2 changes:
> 1. Add description of offset1 and offset2 being renamed in the commit
> message.
> v3 changes:
> 1. Improve the commit message
> 2. Fix the error the patch, there were two "++" in the patch
> ---
>  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..54ccd7c57961 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	[flat|nested] 53+ messages in thread

* Re: [PATCH v3 net-next 11/15] net: enetc: extract enetc_update_ptp_sync_msg() to handle PTP Sync packets
  2025-08-12  9:46 ` [PATCH v3 net-next 11/15] net: enetc: extract enetc_update_ptp_sync_msg() to handle PTP Sync packets Wei Fang
@ 2025-08-12 15:52   ` Frank Li
  2025-08-13  2:04     ` Wei Fang
  0 siblings, 1 reply; 53+ messages in thread
From: Frank Li @ 2025-08-12 15:52 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 Tue, Aug 12, 2025 at 05:46:30PM +0800, Wei Fang wrote:
> 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.

Add "no functional change".

>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
>
> ---
> v2: no changes
> v3: Change the subject and improve the commit message
> ---
>  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 54ccd7c57961..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)

Suppose this type patch no any addtional change to reduce review efforts.

Or you need say replace 0x3fffffff with ENETC_TXBD_TSTAMP.

Frank

>
>  static inline __le32 enetc_txbd_set_tx_start(u64 tx_start, u8 flags)
>  {
> --
> 2.34.1
>

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

* Re: [PATCH v3 net-next 13/15] net: enetc: add PTP synchronization support for ENETC v4
  2025-08-12  9:46 ` [PATCH v3 net-next 13/15] net: enetc: add PTP synchronization support for ENETC v4 Wei Fang
@ 2025-08-12 15:58   ` Frank Li
  2025-08-13  2:17     ` Wei Fang
  0 siblings, 1 reply; 53+ messages in thread
From: Frank Li @ 2025-08-12 15: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 Tue, Aug 12, 2025 at 05:46:32PM +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.

In patch, I have not find dma_map_single(), is it in enetc_map_tx_buffs()?

This move should be fix, even dma-coherent, it also should be before
dma_map_single().  just hardware dma-coherent hidden the problem.

Frank
>
> 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.
> v3 changes:
> 1. Change CONFIG_PTP_1588_CLOCK_NETC to CONFIG_PTP_NETC_V4_TIMER
> 2. Change "nxp,netc-timer" to "ptp-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..6dbc9cc811a0 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..815afdc2ec23 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_NETC_V4_TIMER);
> +}
> +
>  #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..b6014b1069de 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, "ptp-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] 53+ messages in thread

* Re: [PATCH v3 15/15] arm64: dts: imx95: add standard PCI device compatible string to NETC Timer
  2025-08-12  9:46 ` [PATCH v3 15/15] arm64: dts: imx95: add standard PCI device compatible string to NETC Timer Wei Fang
@ 2025-08-12 15:59   ` Frank Li
  0 siblings, 0 replies; 53+ messages in thread
From: Frank Li @ 2025-08-12 15:59 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 Tue, Aug 12, 2025 at 05:46:34PM +0800, Wei Fang wrote:
> PCI devices should have a compatible string based on the vendor and
> device IDs. So add this compatible string to NETC Timer.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>

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

>
> ---
> v2 changes:
> new patch
> v3 changes:
> Since the commit 02b7adb791e1 ("arm64: dts: imx95-19x19-evk: add adc0
> flexcan[1,2] i2c[2,3] uart5 spi3 and tpm3") has enabled NETC Timer, so
> rebase this patch and change the title and commit message.
> ---
>  arch/arm64/boot/dts/freescale/imx95.dtsi | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi
> index 4ca6a7ea586e..605f14d8fa25 100644
> --- a/arch/arm64/boot/dts/freescale/imx95.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx95.dtsi
> @@ -1948,6 +1948,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] 53+ messages in thread

* RE: [PATCH v3 net-next 03/15] dt-bindings: net: add an example for ENETC v4
  2025-08-12 14:38   ` Frank Li
@ 2025-08-13  1:38     ` Wei Fang
  2025-08-13 15:10       ` Frank Li
  0 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-13  1:38 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 Tue, Aug 12, 2025 at 05:46:22PM +0800, Wei Fang wrote:
> > Add a DT node example for ENETC v4 device.
> 
> Not sure why need add examples here? Any big difference with existed
> example?
> 

For enetc v4, we have added clocks, and it also supports ptp-timer
property, these are different from enetc v1, so I think it is better to
add an example for v4.

> 
> >
> > Signed-off-by: Wei Fang <wei.fang@nxp.com>
> >
> > ---
> > v2 changes:
> > new patch
> > v3 changes:
> > 1. Rename the subject
> > 2. Remove nxp,netc-timer property and use ptp-timer in the example
> > ---
> >  .../devicetree/bindings/net/fsl,enetc.yaml        | 15 +++++++++++++++
> >  1 file changed, 15 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > index ca70f0050171..a545b54c9e5d 100644
> > --- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > +++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > @@ -86,3 +86,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";
> > +          phy-handle = <&ethphy0>;
> > +          phy-mode = "rgmii-id";
> > +          ptp-timer = <&netc_timer>;
> > +      };
> > +    };
> > --
> > 2.34.1
> >

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

* RE: [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support
  2025-08-12 12:03   ` Vladimir Oltean
@ 2025-08-13  1:42     ` Wei Fang
  0 siblings, 0 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-13  1:42 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 Tue, Aug 12, 2025 at 05:46:25PM +0800, Wei Fang wrote:
> > @@ -210,77 +343,178 @@ 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)  {
> > -	u32 fiper, fiper_ctrl;
> > +	struct device *dev = &priv->pdev->dev;
> >  	unsigned long flags;
> > +	struct netc_pp *pp;
> > +	int err = 0;
> >
> >  	spin_lock_irqsave(&priv->lock, flags);
> >
> > -	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> > -
> >  	if (on) {
> 		...
> >  	} else {
> > -		if (!priv->pps_enabled)
> > +		/* pps_channel is invalid if PPS is not enabled, so no
> > +		 * processing is needed.
> > +		 */
> > +		if (priv->pps_channel >= NETC_TMR_FIPER_NUM)
> >  			goto unlock_spinlock;
> >
> > -		fiper = NETC_TMR_DEFAULT_FIPER;
> > -		priv->tmr_emask &= ~(TMR_TEVNET_PPEN(0) |
> > -				     TMR_TEVENT_ALMEN(0));
> > -		fiper_ctrl |= FIPER_CTRL_DIS(0);
> > -		priv->pps_enabled = false;
> > -		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> > +		netc_timer_disable_periodic_pulse(priv, priv->pps_channel);
> > +		priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id);
> 
> You dereference "pp"->alarm_id before assigning "pp" one line below.

oh, sorry, I will fix it in next version.

> 
> > +		pp = &priv->pp[priv->pps_channel];
> > +		memset(pp, 0, sizeof(*pp));
> > +		priv->pps_channel = NETC_TMR_INVALID_CHANNEL;
> >  	}
> >
> > -	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
> > -	netc_timer_wr(priv, NETC_TMR_FIPER(0), fiper);
> > -	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
> > +unlock_spinlock:
> > +	spin_unlock_irqrestore(&priv->lock, flags);
> > +
> > +	return err;
> > +}

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

* RE: [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support
  2025-08-12 15:01   ` Frank Li
@ 2025-08-13  1:46     ` Wei Fang
  2025-08-15 18:13       ` Frank Li
  0 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-13  1:46 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 Tue, Aug 12, 2025 at 05:46:23PM +0800, Wei Fang wrote:
> > NETC V4 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.
> >
> > Inside NETC, ENETC can capture the timestamp of the sent/received
> > packet through the PHC provided by the Timer and record it on the
> > Tx/Rx BD. And through the relevant PHC interfaces provided by the
> > driver, the enetc V4 driver can support PTP time synchronization.
> >
> > In addition, NETC V4 Timer is similar to the QorIQ 1588 timer, but it
> > is not exactly the same. The current ptp-qoriq driver is not
> > compatible with NETC V4 Timer, most of the code cannot be reused, see below
> reasons.
> >
> > 1. The architecture of ptp-qoriq driver makes the register offset
> > fixed, however, the offsets of all the high registers and low
> > registers of V4 are swapped, and V4 also adds some new registers. so
> > extending ptp-qoriq to make it compatible with V4 Timer is tantamount
> > to completely rewriting ptp-qoriq driver.
> >
> > 2. The usage of some functions is somewhat different from QorIQ timer,
> > such as the setting of TCLK_PERIOD and TMR_ADD, the logic of
> > configuring PPS, etc., so making the driver compatible with V4 Timer
> > will undoubtedly increase the complexity of the code and reduce readability.
> >
> > 3. QorIQ is an expired brand. It is difficult for us to verify whether
> > it works stably on the QorIQ platforms if we refactor the driver, and
> > this will make maintenance difficult, so refactoring the driver
> > obviously does not bring any benefits.
> >
> > Therefore, add this new driver for NETC V4 Timer. Note that the
> > missing features like PEROUT, PPS and EXTTS will be added 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
> > v3 changes:
> > 1. Refactor netc_timer_adjtime() and remove netc_timer_cnt_read() 2.
> > Remove the check of dma_set_mask_and_coherent() 3. Use devm_kzalloc()
> > and pci_ioremap_bar() 4. Move alarm related logic including irq
> > handler to the next patch 5. Improve the commit message 6. Refactor
> > netc_timer_get_reference_clk_source() and remove
> >    clk_prepare_enable()
> > 7. Use FIELD_PREP() helper
> > 8. Rename PTP_1588_CLOCK_NETC to PTP_NETC_V4_TIMER and improve the
> >    help text.
> > 9. Refine netc_timer_adjtime(), change tmr_off to s64 type as we
> >    confirmed TMR_OFF is a signed register.
> > ---
> >  drivers/ptp/Kconfig             |  11 +
> >  drivers/ptp/Makefile            |   1 +
> >  drivers/ptp/ptp_netc.c          | 438
> ++++++++++++++++++++++++++++++++
> >  include/linux/fsl/netc_global.h |  12 +-
> >  4 files changed, 461 insertions(+), 1 deletion(-)  create mode 100644
> > drivers/ptp/ptp_netc.c
> >
> > diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig index
> > 204278eb215e..0ac31a20096c 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_NETC_V4_TIMER
> > +	bool "NXP NETC V4 Timer PTP Driver"
> > +	depends on PTP_1588_CLOCK=y
> > +	depends on PCI_MSI
> > +	help
> > +	  This driver adds support for using the NXP NETC V4 Timer as a PTP
> > +	  clock, the clock is used by ENETC V4 or NETC V4 Switch for PTP time
> > +	  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..8985d723d29c 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_NETC_V4_TIMER)		+= ptp_netc.o
> > diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c new file
> > mode 100644 index 000000000000..cbe2a64d1ced
> > --- /dev/null
> > +++ b/drivers/ptp/ptp_netc.c
> > @@ -0,0 +1,438 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
> > +/*
> > + * NXP NETC V4 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
> 
> Nit: Like this only use once constant, needn't macro, espcial DEVID.

So you mean directly use PCI_DEVICE(0x1131,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 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
> > +
> > +#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
> > +
> > +/* 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 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;
> > +};
> > +
> > +#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 const char *const timer_clk_src[] = {
> > +	"ccm_timer",
> > +	"ext_1588"
> > +};
> > +
> > +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);
> > +
> > +	/* Writes to the TMR_CNT_L register copies the written value
> > +	 * into the shadow TMR_CNT_L register. Writes to the TMR_CNT_H
> > +	 * register copies the values written into the shadow TMR_CNT_H
> > +	 * register. Contents of the shadow registers are copied into
> > +	 * the TMR_CNT_L and TMR_CNT_H registers following a write into
> > +	 * the TMR_CNT_H register. So the user must writes to TMR_CNT_L
> > +	 * register first.
> > +	 */
> 
> Is all other register the same? like OFF_L, OFF_H?
> 
> And read have similar behavor?
> 
> > +	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_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);
> > +	unsigned long flags;
> > +	s64 tmr_off;
> > +
> > +	spin_lock_irqsave(&priv->lock, flags);
> > +
> > +	/* Adjusting TMROFF instead of TMR_CNT is that the timer
> > +	 * counter keeps increasing during reading and writing
> > +	 * TMR_CNT, which will cause latency.
> > +	 */
> > +	tmr_off = netc_timer_offset_read(priv);
> > +	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_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 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 = FIELD_PREP(TMR_CTRL_CK_SEL, priv->clk_select) |
> > +		   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 |= FIELD_PREP(TMR_CTRL_TCLK_PERIOD, integral_period) |
> > +		    TMR_COMP_MODE;
> > +	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> > +	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period); }
> > +
> > +static int netc_timer_pci_probe(struct pci_dev *pdev) {
> > +	struct device *dev = &pdev->dev;
> > +	struct netc_timer *priv;
> > +	int err;
> > +
> > +	pcie_flr(pdev);
> > +	err = pci_enable_device_mem(pdev);
> > +	if (err)
> > +		return dev_err_probe(dev, err, "Failed to enable device\n");
> > +
> > +	dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> > +	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 = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv) {
> > +		err = -ENOMEM;
> > +		goto release_mem_regions;
> > +	}
> 
> move devm_kzalloc() before pci_enable_device_mem() to reduce a goto
> 
> > +
> > +	priv->pdev = pdev;
> > +	priv->base = pci_ioremap_bar(pdev, NETC_TMR_REGS_BAR);
> > +	if (!priv->base) {
> > +		err = -ENOMEM;
> > +		goto release_mem_regions;
> > +	}
> > +
> > +	pci_set_drvdata(pdev, priv);
> > +
> > +	return 0;
> > +
> > +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);
> > +	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 clk *clk;
> > +	int i;
> > +
> > +	/* Select NETC system clock as the reference clock by default */
> > +	priv->clk_select = NETC_TMR_SYSTEM_CLK;
> > +	priv->clk_freq = NETC_TMR_SYSCLK_333M;
> > +
> > +	/* Update the clock source of the reference clock if the clock
> > +	 * is specified in DT node.
> > +	 */
> > +	for (i = 0; i < ARRAY_SIZE(timer_clk_src); i++) {
> > +		clk = devm_clk_get_optional_enabled(dev, timer_clk_src[i]);
> > +		if (IS_ERR(clk))
> > +			return PTR_ERR(clk);
> > +
> > +		if (clk) {
> > +			priv->clk_freq = clk_get_rate(clk);
> > +			priv->clk_select = i ? NETC_TMR_EXT_OSC :
> > +					       NETC_TMR_CCM_TIMER1;
> > +			break;
> > +		}
> > +	}
> > +
> > +	/* The period is a 64-bit number, the high 32-bit is the integer
> > +	 * part of the period, the low 32-bit is the fractional part of
> > +	 * the period. In order to get the desired 32-bit fixed-point
> > +	 * format, multiply the numerator of the fraction by 2^32.
> > +	 */
> > +	priv->period = div_u64(NSEC_PER_SEC << 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 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) {
> > +		if (err != -EPROBE_DEFER)
> > +			dev_err(dev, "Failed to parse DT node\n");
> > +		goto timer_pci_remove;
> > +	}
> 
> move netc_timer_parse_dt() before netc_timer_pci_probe()
> 
> you can use return dev_err_probe(), which already handle -EPROBE_DEFER
> case.
> 
> 
> > +
> > +	priv->caps = netc_timer_ptp_caps;
> > +	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
> > +	spin_lock_init(&priv->lock);
> > +
> > +	netc_timer_init(priv);
> > +	priv->clock = ptp_clock_register(&priv->caps, dev);
> > +	if (IS_ERR(priv->clock)) {
> > +		err = PTR_ERR(priv->clock);
> > +		goto timer_pci_remove;
> > +	}
> > +
> > +	priv->phc_index = ptp_clock_index(priv->clock);
> > +
> > +	return 0;
> > +
> > +timer_pci_remove:
> > +	netc_timer_pci_remove(pdev);
> > +
> > +	return err;
> > +}
> > +
> > +static void netc_timer_remove(struct pci_dev *pdev) {
> 
> use devm_add_action_or_reset() can simpify your error handle.
> 
> Frank
> > +	struct netc_timer *priv = pci_get_drvdata(pdev);
> > +
> > +	ptp_clock_unregister(priv->clock);
> > +	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) },
> > +	{ }
> > +};
> > +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..17c19c8d3f93
> > 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_NETC_V4_TIMER)
> > +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] 53+ messages in thread

* RE: [PATCH v3 net-next 05/15] ptp: netc: add PTP_CLK_REQ_PPS support
  2025-08-12 15:11   ` Frank Li
@ 2025-08-13  1:59     ` Wei Fang
  2025-08-13 15:06       ` Frank Li
  0 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-13  1: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

> On Tue, Aug 12, 2025 at 05:46:24PM +0800, Wei Fang wrote:
> > The NETC Timer is capable of generating a PPS interrupt to the host. To
> > support this feature, a 64-bit alarm time (which is a integral second
> > of PHC in the future) is set to TMR_ALARM, and the period is set to
> > TMR_FIPER. The alarm time is compared to the current time on each update,
> > then the alarm trigger is used as an indication to the TMR_FIPER starts
> > down counting. After the period has passed, the PPS event is generated.
> >
> > According to the NETC block guide, the Timer has three FIPERs, any of
> > which can be used to generate the PPS events, but in the current
> > implementation, we only need one of them to implement the PPS feature,
> > so FIPER 0 is used as the default PPS generator. Also, the Timer has
> > 2 ALARMs, currently, ALARM 0 is used as the default time comparator.
> >
> > However, if there is a time drift when PPS is enabled, the PPS event will
> > not be generated at an integral second of PHC. The suggested steps from
> > IP team if time drift happens:
> 
> according to patch, "drift" means timer adjust period?

No only adjust period, but also including adjust time.

> netc_timer_adjust_period()
> 
> generally, netc_timer_adjust_period() happen 4 times every second, does
> disable/re-enable impact pps accurate?

PPS needs to be re-enabled only when the integer part of the period changes.
In this case, re-enabling PPS will result in a loss of PPS signal for 1 ~ 2 seconds.
In most cases, only the fractional part of the period is adjusted, so there is no
need to re-enable PPS.

> 
> >
> > 1. Disable FIPER before adjusting the hardware time
> > 2. Rearm ALARM after the time adjustment to make the next PPS event be
> > generated at an integral second of PHC.
> > 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
> > v3 changes:
> > 1. Use "2 * NSEC_PER_SEC" to instead of "2000000000U"
> > 2. Improve the commit message
> > 3. Add alarm related logic and the irq handler
> > 4. Add tmr_emask to struct netc_timer to save the irq masks instead of
> >    reading TMR_EMASK register
> > 5. Remove pps_channel from struct netc_timer and remove
> >    NETC_TMR_DEFAULT_PPS_CHANNEL
> > ---
> >  drivers/ptp/ptp_netc.c | 260
> ++++++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 257 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> > index cbe2a64d1ced..9026a967a5fe 100644
> > --- a/drivers/ptp/ptp_netc.c
> > +++ b/drivers/ptp/ptp_netc.c
> > @@ -20,7 +20,14 @@
> >  #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 NETC_TMR_TEVENT			0x0084
> > +#define  TMR_TEVNET_PPEN(i)		BIT(7 - (i))
> > +#define  TMR_TEVENT_PPEN_ALL		GENMASK(7, 5)
> > +#define  TMR_TEVENT_ALMEN(i)		BIT(16 + (i))
> > +
> > +#define NETC_TMR_TEMASK			0x0088
> >  #define NETC_TMR_CNT_L			0x0098
> >  #define NETC_TMR_CNT_H			0x009c
> >  #define NETC_TMR_ADD			0x00a0
> > @@ -28,9 +35,19 @@
> >  #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)
> > +
> > +/* 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
> > @@ -39,6 +56,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_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 */
> > @@ -60,6 +80,10 @@ struct netc_timer {
> >  	u32 oclk_prsc;
> >  	/* High 32-bit is integer part, low 32-bit is fractional part */
> >  	u64 period;
> > +
> > +	int irq;
> > +	u32 tmr_emask;
> > +	bool pps_enabled;
> >  };
> >
> >  #define netc_timer_rd(p, o)		netc_read((p)->base + (o))
> > @@ -124,6 +148,155 @@ static u64 netc_timer_cur_time_read(struct
> netc_timer *priv)
> >  	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 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(2 * NSEC_PER_SEC, 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 fiper, fiper_ctrl;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&priv->lock, flags);
> > +
> > +	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(0) | FIPER_CTRL_PW(0) |
> > +				FIPER_CTRL_FS_ALARM(0));
> > +		fiper_ctrl |= FIPER_CTRL_SET_PW(0, fiper_pw);
> > +		priv->tmr_emask |= TMR_TEVNET_PPEN(0) |
> TMR_TEVENT_ALMEN(0);
> > +		priv->pps_enabled = true;
> > +		netc_timer_set_pps_alarm(priv, 0, integral_period);
> > +	} else {
> > +		if (!priv->pps_enabled)
> > +			goto unlock_spinlock;
> > +
> > +		fiper = NETC_TMR_DEFAULT_FIPER;
> > +		priv->tmr_emask &= ~(TMR_TEVNET_PPEN(0) |
> > +				     TMR_TEVENT_ALMEN(0));
> > +		fiper_ctrl |= FIPER_CTRL_DIS(0);
> > +		priv->pps_enabled = false;
> > +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> > +	}
> > +
> > +	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
> > +	netc_timer_wr(priv, NETC_TMR_FIPER(0), 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_ctrl;
> > +
> > +	if (!priv->pps_enabled)
> > +		return;
> > +
> > +	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> > +	fiper_ctrl |= FIPER_CTRL_DIS(0);
> > +	netc_timer_wr(priv, NETC_TMR_FIPER(0), NETC_TMR_DEFAULT_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;
> > +
> > +	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(0);
> > +	fiper = NSEC_PER_SEC - integral_period;
> > +
> > +	netc_timer_set_pps_alarm(priv, 0, integral_period);
> > +	netc_timer_wr(priv, NETC_TMR_FIPER(0), 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);
> > @@ -136,8 +309,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);
> >
> > @@ -163,6 +339,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);
> > +
> >  	/* Adjusting TMROFF instead of TMR_CNT is that the timer
> >  	 * counter keeps increasing during reading and writing
> >  	 * TMR_CNT, which will cause latency.
> > @@ -171,6 +349,8 @@ static int netc_timer_adjtime(struct ptp_clock_info
> *ptp, s64 delta)
> >  	tmr_off += delta;
> >  	netc_timer_offset_write(priv, tmr_off);
> >
> > +	netc_timer_enable_pps_fiper(priv);
> > +
> >  	spin_unlock_irqrestore(&priv->lock, flags);
> >
> >  	return 0;
> > @@ -205,8 +385,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;
> > @@ -232,10 +416,13 @@ static const struct ptp_clock_info
> netc_timer_ptp_caps = {
> >  	.name		= "NETC Timer PTP clock",
> >  	.max_adj	= 500000000,
> >  	.n_pins		= 0,
> > +	.n_alarm	= 2,
> > +	.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)
> > @@ -252,7 +439,7 @@ static void netc_timer_init(struct netc_timer *priv)
> >  	 * domain are not accessible.
> >  	 */
> >  	tmr_ctrl = FIELD_PREP(TMR_CTRL_CK_SEL, priv->clk_select) |
> > -		   TMR_CTRL_TE;
> > +		   TMR_CTRL_TE | TMR_CTRL_FS;
> >  	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> >  	netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc);
> >
> > @@ -372,6 +559,66 @@ 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;
> > +	struct ptp_clock_event event;
> > +	u32 tmr_event;
> > +
> > +	spin_lock(&priv->lock);
> > +
> > +	tmr_event = netc_timer_rd(priv, NETC_TMR_TEVENT);
> > +	tmr_event &= priv->tmr_emask;
> > +	/* Clear interrupts status */
> > +	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
> > +
> > +	if (tmr_event & TMR_TEVENT_ALMEN(0))
> > +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> > +
> > +	if (tmr_event & TMR_TEVENT_PPEN_ALL) {
> > +		event.type = PTP_CLOCK_PPS;
> > +		ptp_clock_event(priv->clock, &event);
> > +	}
> > +
> > +	spin_unlock(&priv->lock);
> > +
> > +	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)
> >  {
> > @@ -395,17 +642,23 @@ static int netc_timer_probe(struct pci_dev *pdev,
> >  	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
> >  	spin_lock_init(&priv->lock);
> >
> > +	err = netc_timer_init_msix_irq(priv);
> > +	if (err)
> > +		goto timer_pci_remove;
> > +
> >  	netc_timer_init(priv);
> >  	priv->clock = ptp_clock_register(&priv->caps, dev);
> >  	if (IS_ERR(priv->clock)) {
> >  		err = PTR_ERR(priv->clock);
> > -		goto timer_pci_remove;
> > +		goto free_msix_irq;
> >  	}
> >
> >  	priv->phc_index = ptp_clock_index(priv->clock);
> >
> >  	return 0;
> >
> > +free_msix_irq:
> > +	netc_timer_free_msix_irq(priv);
> >  timer_pci_remove:
> >  	netc_timer_pci_remove(pdev);
> >
> > @@ -417,6 +670,7 @@ 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);
> >  	netc_timer_pci_remove(pdev);
> >  }
> >
> > --
> > 2.34.1
> >

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

* RE: [PATCH v3 net-next 11/15] net: enetc: extract enetc_update_ptp_sync_msg() to handle PTP Sync packets
  2025-08-12 15:52   ` Frank Li
@ 2025-08-13  2:04     ` Wei Fang
  0 siblings, 0 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-13  2:04 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 Tue, Aug 12, 2025 at 05:46:30PM +0800, Wei Fang wrote:
> > 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.
> 
> Add "no functional change".
> 
> >
> > Signed-off-by: Wei Fang <wei.fang@nxp.com>
> >
> > ---
> > v2: no changes
> > v3: Change the subject and improve the commit message
> > ---
> >  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 54ccd7c57961..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)
> 
> Suppose this type patch no any addtional change to reduce review efforts.
> 
> Or you need say replace 0x3fffffff with ENETC_TXBD_TSTAMP.
> 

Okay, I will describe this change in the commit message, thanks

> 
> >
> >  static inline __le32 enetc_txbd_set_tx_start(u64 tx_start, u8 flags)
> > {
> > --
> > 2.34.1
> >

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

* RE: [PATCH v3 net-next 13/15] net: enetc: add PTP synchronization support for ENETC v4
  2025-08-12 15:58   ` Frank Li
@ 2025-08-13  2:17     ` Wei Fang
  2025-08-13 14:55       ` Frank Li
  0 siblings, 1 reply; 53+ messages in thread
From: Wei Fang @ 2025-08-13  2:17 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 Tue, Aug 12, 2025 at 05:46:32PM +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.
> 
> In patch, I have not find dma_map_single(), is it in enetc_map_tx_buffs()?
> 

Yes, see below code snippet.

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, csum_offload);
	} 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);

	dma = dma_map_single(tx_ring->dev, skb->data, len, DMA_TO_DEVICE);
	if (unlikely(dma_mapping_error(tx_ring->dev, dma)))
		goto dma_err;

	temp_bd.addr = cpu_to_le64(dma);
	temp_bd.buf_len = cpu_to_le16(len);
	[...]
}

> This move should be fix, even dma-coherent, it also should be before
> dma_map_single().  just hardware dma-coherent hidden the problem.
> 

There are no visible issues with enetc v1, if it is considered a fix, it is only
a logical fix and have no effect on v1 . So this patch is fine to target to
net-next tree.

> >
> > 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.
> > v3 changes:
> > 1. Change CONFIG_PTP_1588_CLOCK_NETC to CONFIG_PTP_NETC_V4_TIMER
> > 2. Change "nxp,netc-timer" to "ptp-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..6dbc9cc811a0 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..815afdc2ec23 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_NETC_V4_TIMER);
> > +}
> > +
> >  #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..b6014b1069de 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, "ptp-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] 53+ messages in thread

* RE: [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support
  2025-08-12 13:49   ` Vladimir Oltean
@ 2025-08-13  2:21     ` Wei Fang
  0 siblings, 0 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-13  2:21 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 Tue, Aug 12, 2025 at 05:46:23PM +0800, Wei Fang wrote:
> > +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);
> ...
> > @@ -16,4 +17,13 @@ static inline void netc_write(void __iomem *reg, u32
> val)
> >  	iowrite32(val, reg);
> >  }
> >
> > +#if IS_ENABLED(CONFIG_PTP_NETC_V4_TIMER)
> > +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
> > +
> 
> I was expecting that with the generic ptp-timer phandle you'd also offer
> a generic mechanism of retrieving the PHC index, instead of cooking up a
> custom API convention between the NETC MAC and the NETC timer.
> 

Good advice, I did not realize that, I will try to apply your proposal,
many thanks.

> Something like below, completely untested:
> 
> struct ptp_clock_fwnode_match {
> 	struct fwnode_handle *fwnode;
> 	struct ptp_clock *clock;
> };
> 
> static int ptp_clock_fwnode_match(struct device *dev, void *data)
> {
> 	struct ptp_clock_fwnode_match *match = data;
> 
> 	if (!dev->parent || dev_fwnode(dev->parent) != match->fwnode)
> 		return 0;
> 
> 	match->clock = dev_get_drvdata(dev);
> 	return 1;
> }
> 
> static struct ptp_clock *ptp_clock_find_by_fwnode(struct fwnode_handle
> *fwnode)
> {
> 	struct ptp_clock_fwnode_match match = { .fwnode = fwnode };
> 
> 	class_for_each_device(&ptp_class, NULL, &match,
> ptp_clock_fwnode_match);
> 
> 	return match.clock;
> }
> 
> int ptp_clock_index_by_fwnode_handle(struct fwnode_handle *fwnode)
> {
> 	struct fwnode_handle *ptp_fwnode;
> 	struct ptp_clock *clock;
> 	int phc_index;
> 
> 	ptp_fwnode = fwnode_find_reference(fwnode, "ptp-timer", 0);
> 	if (!ptp_fwnode)
> 		return -1;
> 
> 	clock = ptp_clock_find_by_fwnode(ptp_fwnode);
> 	fwnode_handle_put(ptp_fwnode);
> 	if (!clock)
> 		return -1;
> 
> 	phc_index = ptp_clock_index(clock);
> 	put_device(&clock->dev);
> 
> 	return phc_index;
> }
> EXPORT_SYMBOL_GPL(ptp_clock_index_by_fwnode_handle);

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

* Re: [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support
  2025-08-12  9:46 ` [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support Wei Fang
  2025-08-12 12:03   ` Vladimir Oltean
  2025-08-12 15:24   ` Frank Li
@ 2025-08-13  2:35   ` kernel test robot
  2 siblings, 0 replies; 53+ messages in thread
From: kernel test robot @ 2025-08-13  2:35 UTC (permalink / raw)
  To: Wei Fang, 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: oe-kbuild-all, fushi.peng, devicetree, netdev, linux-kernel, imx,
	kernel

Hi Wei,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Wei-Fang/dt-bindings-ptp-add-NETC-Timer-PTP-clock/20250812-181510
base:   net-next/main
patch link:    https://lore.kernel.org/r/20250812094634.489901-7-wei.fang%40nxp.com
patch subject: [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support
config: loongarch-allyesconfig (https://download.01.org/0day-ci/archive/20250813/202508131027.y3pyBEJQ-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 3769ce013be2879bf0b329c14a16f5cb766f26ce)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250813/202508131027.y3pyBEJQ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202508131027.y3pyBEJQ-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/ptp/ptp_netc.c:394:33: warning: variable 'pp' is uninitialized when used here [-Wuninitialized]
     394 |                 priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id);
         |                                               ^~
   drivers/ptp/ptp_netc.c:348:20: note: initialize the variable 'pp' to silence this warning
     348 |         struct netc_pp *pp;
         |                           ^
         |                            = NULL
   1 warning generated.


vim +/pp +394 drivers/ptp/ptp_netc.c

   337	
   338	/* Note that users should not use this API to output PPS signal on
   339	 * external pins, because PTP_CLK_REQ_PPS trigger internal PPS event
   340	 * for input into kernel PPS subsystem. See:
   341	 * https://lore.kernel.org/r/20201117213826.18235-1-a.fatoum@pengutronix.de
   342	 */
   343	static int netc_timer_enable_pps(struct netc_timer *priv,
   344					 struct ptp_clock_request *rq, int on)
   345	{
   346		struct device *dev = &priv->pdev->dev;
   347		unsigned long flags;
   348		struct netc_pp *pp;
   349		int err = 0;
   350	
   351		spin_lock_irqsave(&priv->lock, flags);
   352	
   353		if (on) {
   354			int alarm_id;
   355			u8 channel;
   356	
   357			if (priv->pps_channel < NETC_TMR_FIPER_NUM) {
   358				channel = priv->pps_channel;
   359			} else {
   360				channel = netc_timer_select_pps_channel(priv);
   361				if (channel == NETC_TMR_INVALID_CHANNEL) {
   362					dev_err(dev, "No available FIPERs\n");
   363					err = -EBUSY;
   364					goto unlock_spinlock;
   365				}
   366			}
   367	
   368			pp = &priv->pp[channel];
   369			if (pp->enabled)
   370				goto unlock_spinlock;
   371	
   372			alarm_id = netc_timer_get_alarm_id(priv);
   373			if (alarm_id == priv->fs_alarm_num) {
   374				dev_err(dev, "No available ALARMs\n");
   375				err = -EBUSY;
   376				goto unlock_spinlock;
   377			}
   378	
   379			pp->enabled = true;
   380			pp->type = NETC_PP_PPS;
   381			pp->alarm_id = alarm_id;
   382			pp->period = NSEC_PER_SEC;
   383			priv->pps_channel = channel;
   384	
   385			netc_timer_enable_periodic_pulse(priv, channel);
   386		} else {
   387			/* pps_channel is invalid if PPS is not enabled, so no
   388			 * processing is needed.
   389			 */
   390			if (priv->pps_channel >= NETC_TMR_FIPER_NUM)
   391				goto unlock_spinlock;
   392	
   393			netc_timer_disable_periodic_pulse(priv, priv->pps_channel);
 > 394			priv->fs_alarm_bitmap &= ~BIT(pp->alarm_id);
   395			pp = &priv->pp[priv->pps_channel];
   396			memset(pp, 0, sizeof(*pp));
   397			priv->pps_channel = NETC_TMR_INVALID_CHANNEL;
   398		}
   399	
   400	unlock_spinlock:
   401		spin_unlock_irqrestore(&priv->lock, flags);
   402	
   403		return err;
   404	}
   405	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support
  2025-08-12  9:46 ` [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support Wei Fang
  2025-08-12 13:49   ` Vladimir Oltean
  2025-08-12 15:01   ` Frank Li
@ 2025-08-13  4:45   ` kernel test robot
  2 siblings, 0 replies; 53+ messages in thread
From: kernel test robot @ 2025-08-13  4:45 UTC (permalink / raw)
  To: Wei Fang, 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: oe-kbuild-all, fushi.peng, devicetree, netdev, linux-kernel, imx,
	kernel

Hi Wei,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Wei-Fang/dt-bindings-ptp-add-NETC-Timer-PTP-clock/20250812-181510
base:   net-next/main
patch link:    https://lore.kernel.org/r/20250812094634.489901-5-wei.fang%40nxp.com
patch subject: [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20250813/202508131032.BQJ5m6ky-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250813/202508131032.BQJ5m6ky-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202508131032.BQJ5m6ky-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/ptp/ptp_netc.c: In function 'netc_timer_get_reference_clk_source':
>> drivers/ptp/ptp_netc.c:365:45: warning: left shift count >= width of type [-Wshift-count-overflow]
     365 |         priv->period = div_u64(NSEC_PER_SEC << 32, priv->clk_freq);
         |                                             ^~


vim +365 drivers/ptp/ptp_netc.c

   333	
   334	static int netc_timer_get_reference_clk_source(struct netc_timer *priv)
   335	{
   336		struct device *dev = &priv->pdev->dev;
   337		struct clk *clk;
   338		int i;
   339	
   340		/* Select NETC system clock as the reference clock by default */
   341		priv->clk_select = NETC_TMR_SYSTEM_CLK;
   342		priv->clk_freq = NETC_TMR_SYSCLK_333M;
   343	
   344		/* Update the clock source of the reference clock if the clock
   345		 * is specified in DT node.
   346		 */
   347		for (i = 0; i < ARRAY_SIZE(timer_clk_src); i++) {
   348			clk = devm_clk_get_optional_enabled(dev, timer_clk_src[i]);
   349			if (IS_ERR(clk))
   350				return PTR_ERR(clk);
   351	
   352			if (clk) {
   353				priv->clk_freq = clk_get_rate(clk);
   354				priv->clk_select = i ? NETC_TMR_EXT_OSC :
   355						       NETC_TMR_CCM_TIMER1;
   356				break;
   357			}
   358		}
   359	
   360		/* The period is a 64-bit number, the high 32-bit is the integer
   361		 * part of the period, the low 32-bit is the fractional part of
   362		 * the period. In order to get the desired 32-bit fixed-point
   363		 * format, multiply the numerator of the fraction by 2^32.
   364		 */
 > 365		priv->period = div_u64(NSEC_PER_SEC << 32, priv->clk_freq);
   366	
   367		return 0;
   368	}
   369	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH v3 net-next 13/15] net: enetc: add PTP synchronization support for ENETC v4
  2025-08-13  2:17     ` Wei Fang
@ 2025-08-13 14:55       ` Frank Li
  0 siblings, 0 replies; 53+ messages in thread
From: Frank Li @ 2025-08-13 14:55 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 Wed, Aug 13, 2025 at 02:17:46AM +0000, Wei Fang wrote:
> > On Tue, Aug 12, 2025 at 05:46:32PM +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.
> >
> > In patch, I have not find dma_map_single(), is it in enetc_map_tx_buffs()?
> >
>
> Yes, see below code snippet.
>
> 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, csum_offload);
> 	} 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);
>
> 	dma = dma_map_single(tx_ring->dev, skb->data, len, DMA_TO_DEVICE);
> 	if (unlikely(dma_mapping_error(tx_ring->dev, dma)))
> 		goto dma_err;
>
> 	temp_bd.addr = cpu_to_le64(dma);
> 	temp_bd.buf_len = cpu_to_le16(len);
> 	[...]
> }
>
> > This move should be fix, even dma-coherent, it also should be before
> > dma_map_single().  just hardware dma-coherent hidden the problem.
> >
>
> There are no visible issues with enetc v1, if it is considered a fix, it is only
> a logical fix and have no effect on v1 . So this patch is fine to target to
> net-next tree.

I agree, is below more clear?

Move sync package content modification before dma_map_single() to follow
correct DMA usage process, even though the previous sequence worked due
to hardware DMA-coherence support.

Frank

>
> > >
> > > 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.
> > > v3 changes:
> > > 1. Change CONFIG_PTP_1588_CLOCK_NETC to CONFIG_PTP_NETC_V4_TIMER
> > > 2. Change "nxp,netc-timer" to "ptp-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..6dbc9cc811a0 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..815afdc2ec23 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_NETC_V4_TIMER);
> > > +}
> > > +
> > >  #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..b6014b1069de 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, "ptp-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] 53+ messages in thread

* Re: [PATCH v3 net-next 05/15] ptp: netc: add PTP_CLK_REQ_PPS support
  2025-08-13  1:59     ` Wei Fang
@ 2025-08-13 15:06       ` Frank Li
  2025-08-14  2:22         ` Wei Fang
  0 siblings, 1 reply; 53+ messages in thread
From: Frank Li @ 2025-08-13 15: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, 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, Aug 13, 2025 at 01:59:01AM +0000, Wei Fang wrote:
> > On Tue, Aug 12, 2025 at 05:46:24PM +0800, Wei Fang wrote:
> > > The NETC Timer is capable of generating a PPS interrupt to the host. To
> > > support this feature, a 64-bit alarm time (which is a integral second
> > > of PHC in the future) is set to TMR_ALARM, and the period is set to
> > > TMR_FIPER. The alarm time is compared to the current time on each update,
> > > then the alarm trigger is used as an indication to the TMR_FIPER starts
> > > down counting. After the period has passed, the PPS event is generated.
> > >
> > > According to the NETC block guide, the Timer has three FIPERs, any of
> > > which can be used to generate the PPS events, but in the current
> > > implementation, we only need one of them to implement the PPS feature,
> > > so FIPER 0 is used as the default PPS generator. Also, the Timer has
> > > 2 ALARMs, currently, ALARM 0 is used as the default time comparator.
> > >
> > > However, if there is a time drift when PPS is enabled, the PPS event will
> > > not be generated at an integral second of PHC. The suggested steps from
> > > IP team if time drift happens:
> >
> > according to patch, "drift" means timer adjust period?
>
> No only adjust period, but also including adjust time.

I think 'adjust period and time' is more accurate then drift.  drift always
happen. The problem should happen only at adjust.

>
> > netc_timer_adjust_period()
> >
> > generally, netc_timer_adjust_period() happen 4 times every second, does
> > disable/re-enable impact pps accurate?
>
> PPS needs to be re-enabled only when the integer part of the period changes.
> In this case, re-enabling PPS will result in a loss of PPS signal for 1 ~ 2 seconds.
> In most cases, only the fractional part of the period is adjusted, so there is no
> need to re-enable PPS.

Lost 1-2 second should be okay when adjust time, which only happen at
beginning of sync.

Does software enable/disable impact PPS accurate? For example:

suppose PPS plus at 10000ns position.

enable/disable software take 112ns, does PPS plus at 10112ns Or still at
10000ns position.

Frank

>
> >
> > >
> > > 1. Disable FIPER before adjusting the hardware time
> > > 2. Rearm ALARM after the time adjustment to make the next PPS event be
> > > generated at an integral second of PHC.
> > > 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
> > > v3 changes:
> > > 1. Use "2 * NSEC_PER_SEC" to instead of "2000000000U"
> > > 2. Improve the commit message
> > > 3. Add alarm related logic and the irq handler
> > > 4. Add tmr_emask to struct netc_timer to save the irq masks instead of
> > >    reading TMR_EMASK register
> > > 5. Remove pps_channel from struct netc_timer and remove
> > >    NETC_TMR_DEFAULT_PPS_CHANNEL
> > > ---
> > >  drivers/ptp/ptp_netc.c | 260
> > ++++++++++++++++++++++++++++++++++++++++-
> > >  1 file changed, 257 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
> > > index cbe2a64d1ced..9026a967a5fe 100644
> > > --- a/drivers/ptp/ptp_netc.c
> > > +++ b/drivers/ptp/ptp_netc.c
> > > @@ -20,7 +20,14 @@
> > >  #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 NETC_TMR_TEVENT			0x0084
> > > +#define  TMR_TEVNET_PPEN(i)		BIT(7 - (i))
> > > +#define  TMR_TEVENT_PPEN_ALL		GENMASK(7, 5)
> > > +#define  TMR_TEVENT_ALMEN(i)		BIT(16 + (i))
> > > +
> > > +#define NETC_TMR_TEMASK			0x0088
> > >  #define NETC_TMR_CNT_L			0x0098
> > >  #define NETC_TMR_CNT_H			0x009c
> > >  #define NETC_TMR_ADD			0x00a0
> > > @@ -28,9 +35,19 @@
> > >  #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)
> > > +
> > > +/* 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
> > > @@ -39,6 +56,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_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 */
> > > @@ -60,6 +80,10 @@ struct netc_timer {
> > >  	u32 oclk_prsc;
> > >  	/* High 32-bit is integer part, low 32-bit is fractional part */
> > >  	u64 period;
> > > +
> > > +	int irq;
> > > +	u32 tmr_emask;
> > > +	bool pps_enabled;
> > >  };
> > >
> > >  #define netc_timer_rd(p, o)		netc_read((p)->base + (o))
> > > @@ -124,6 +148,155 @@ static u64 netc_timer_cur_time_read(struct
> > netc_timer *priv)
> > >  	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 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(2 * NSEC_PER_SEC, 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 fiper, fiper_ctrl;
> > > +	unsigned long flags;
> > > +
> > > +	spin_lock_irqsave(&priv->lock, flags);
> > > +
> > > +	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(0) | FIPER_CTRL_PW(0) |
> > > +				FIPER_CTRL_FS_ALARM(0));
> > > +		fiper_ctrl |= FIPER_CTRL_SET_PW(0, fiper_pw);
> > > +		priv->tmr_emask |= TMR_TEVNET_PPEN(0) |
> > TMR_TEVENT_ALMEN(0);
> > > +		priv->pps_enabled = true;
> > > +		netc_timer_set_pps_alarm(priv, 0, integral_period);
> > > +	} else {
> > > +		if (!priv->pps_enabled)
> > > +			goto unlock_spinlock;
> > > +
> > > +		fiper = NETC_TMR_DEFAULT_FIPER;
> > > +		priv->tmr_emask &= ~(TMR_TEVNET_PPEN(0) |
> > > +				     TMR_TEVENT_ALMEN(0));
> > > +		fiper_ctrl |= FIPER_CTRL_DIS(0);
> > > +		priv->pps_enabled = false;
> > > +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> > > +	}
> > > +
> > > +	netc_timer_wr(priv, NETC_TMR_TEMASK, priv->tmr_emask);
> > > +	netc_timer_wr(priv, NETC_TMR_FIPER(0), 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_ctrl;
> > > +
> > > +	if (!priv->pps_enabled)
> > > +		return;
> > > +
> > > +	fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL);
> > > +	fiper_ctrl |= FIPER_CTRL_DIS(0);
> > > +	netc_timer_wr(priv, NETC_TMR_FIPER(0), NETC_TMR_DEFAULT_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;
> > > +
> > > +	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(0);
> > > +	fiper = NSEC_PER_SEC - integral_period;
> > > +
> > > +	netc_timer_set_pps_alarm(priv, 0, integral_period);
> > > +	netc_timer_wr(priv, NETC_TMR_FIPER(0), 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);
> > > @@ -136,8 +309,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);
> > >
> > > @@ -163,6 +339,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);
> > > +
> > >  	/* Adjusting TMROFF instead of TMR_CNT is that the timer
> > >  	 * counter keeps increasing during reading and writing
> > >  	 * TMR_CNT, which will cause latency.
> > > @@ -171,6 +349,8 @@ static int netc_timer_adjtime(struct ptp_clock_info
> > *ptp, s64 delta)
> > >  	tmr_off += delta;
> > >  	netc_timer_offset_write(priv, tmr_off);
> > >
> > > +	netc_timer_enable_pps_fiper(priv);
> > > +
> > >  	spin_unlock_irqrestore(&priv->lock, flags);
> > >
> > >  	return 0;
> > > @@ -205,8 +385,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;
> > > @@ -232,10 +416,13 @@ static const struct ptp_clock_info
> > netc_timer_ptp_caps = {
> > >  	.name		= "NETC Timer PTP clock",
> > >  	.max_adj	= 500000000,
> > >  	.n_pins		= 0,
> > > +	.n_alarm	= 2,
> > > +	.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)
> > > @@ -252,7 +439,7 @@ static void netc_timer_init(struct netc_timer *priv)
> > >  	 * domain are not accessible.
> > >  	 */
> > >  	tmr_ctrl = FIELD_PREP(TMR_CTRL_CK_SEL, priv->clk_select) |
> > > -		   TMR_CTRL_TE;
> > > +		   TMR_CTRL_TE | TMR_CTRL_FS;
> > >  	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> > >  	netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc);
> > >
> > > @@ -372,6 +559,66 @@ 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;
> > > +	struct ptp_clock_event event;
> > > +	u32 tmr_event;
> > > +
> > > +	spin_lock(&priv->lock);
> > > +
> > > +	tmr_event = netc_timer_rd(priv, NETC_TMR_TEVENT);
> > > +	tmr_event &= priv->tmr_emask;
> > > +	/* Clear interrupts status */
> > > +	netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event);
> > > +
> > > +	if (tmr_event & TMR_TEVENT_ALMEN(0))
> > > +		netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0);
> > > +
> > > +	if (tmr_event & TMR_TEVENT_PPEN_ALL) {
> > > +		event.type = PTP_CLOCK_PPS;
> > > +		ptp_clock_event(priv->clock, &event);
> > > +	}
> > > +
> > > +	spin_unlock(&priv->lock);
> > > +
> > > +	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)
> > >  {
> > > @@ -395,17 +642,23 @@ static int netc_timer_probe(struct pci_dev *pdev,
> > >  	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
> > >  	spin_lock_init(&priv->lock);
> > >
> > > +	err = netc_timer_init_msix_irq(priv);
> > > +	if (err)
> > > +		goto timer_pci_remove;
> > > +
> > >  	netc_timer_init(priv);
> > >  	priv->clock = ptp_clock_register(&priv->caps, dev);
> > >  	if (IS_ERR(priv->clock)) {
> > >  		err = PTR_ERR(priv->clock);
> > > -		goto timer_pci_remove;
> > > +		goto free_msix_irq;
> > >  	}
> > >
> > >  	priv->phc_index = ptp_clock_index(priv->clock);
> > >
> > >  	return 0;
> > >
> > > +free_msix_irq:
> > > +	netc_timer_free_msix_irq(priv);
> > >  timer_pci_remove:
> > >  	netc_timer_pci_remove(pdev);
> > >
> > > @@ -417,6 +670,7 @@ 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);
> > >  	netc_timer_pci_remove(pdev);
> > >  }
> > >
> > > --
> > > 2.34.1
> > >

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

* Re: [PATCH v3 net-next 03/15] dt-bindings: net: add an example for ENETC v4
  2025-08-13  1:38     ` Wei Fang
@ 2025-08-13 15:10       ` Frank Li
  2025-08-14  1:34         ` Wei Fang
  2025-08-14  8:29         ` Krzysztof Kozlowski
  0 siblings, 2 replies; 53+ messages in thread
From: Frank Li @ 2025-08-13 15:10 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 Wed, Aug 13, 2025 at 01:38:55AM +0000, Wei Fang wrote:
> > On Tue, Aug 12, 2025 at 05:46:22PM +0800, Wei Fang wrote:
> > > Add a DT node example for ENETC v4 device.
> >
> > Not sure why need add examples here? Any big difference with existed
> > example?
> >
>
> For enetc v4, we have added clocks, and it also supports ptp-timer
> property, these are different from enetc v1, so I think it is better to
> add an example for v4.

If there are not big change, needn't duplicate one example at yaml file,
the content should be in dts file already. Pass DTB_CHECK should be okay.

Frank
>
> >
> > >
> > > Signed-off-by: Wei Fang <wei.fang@nxp.com>
> > >
> > > ---
> > > v2 changes:
> > > new patch
> > > v3 changes:
> > > 1. Rename the subject
> > > 2. Remove nxp,netc-timer property and use ptp-timer in the example
> > > ---
> > >  .../devicetree/bindings/net/fsl,enetc.yaml        | 15 +++++++++++++++
> > >  1 file changed, 15 insertions(+)
> > >
> > > diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > index ca70f0050171..a545b54c9e5d 100644
> > > --- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > +++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > @@ -86,3 +86,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";
> > > +          phy-handle = <&ethphy0>;
> > > +          phy-mode = "rgmii-id";
> > > +          ptp-timer = <&netc_timer>;
> > > +      };
> > > +    };
> > > --
> > > 2.34.1
> > >

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

* RE: [PATCH v3 net-next 03/15] dt-bindings: net: add an example for ENETC v4
  2025-08-13 15:10       ` Frank Li
@ 2025-08-14  1:34         ` Wei Fang
  2025-08-14  8:29         ` Krzysztof Kozlowski
  1 sibling, 0 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-14  1:34 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, Aug 13, 2025 at 01:38:55AM +0000, Wei Fang wrote:
> > > On Tue, Aug 12, 2025 at 05:46:22PM +0800, Wei Fang wrote:
> > > > Add a DT node example for ENETC v4 device.
> > >
> > > Not sure why need add examples here? Any big difference with existed
> > > example?
> > >
> >
> > For enetc v4, we have added clocks, and it also supports ptp-timer
> > property, these are different from enetc v1, so I think it is better to
> > add an example for v4.
> 
> If there are not big change, needn't duplicate one example at yaml file,
> the content should be in dts file already. Pass DTB_CHECK should be okay.
>

Okay, I can remove this patch in next version

 
> >
> > >
> > > >
> > > > Signed-off-by: Wei Fang <wei.fang@nxp.com>
> > > >
> > > > ---
> > > > v2 changes:
> > > > new patch
> > > > v3 changes:
> > > > 1. Rename the subject
> > > > 2. Remove nxp,netc-timer property and use ptp-timer in the example
> > > > ---
> > > >  .../devicetree/bindings/net/fsl,enetc.yaml        | 15
> +++++++++++++++
> > > >  1 file changed, 15 insertions(+)
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > > b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > > index ca70f0050171..a545b54c9e5d 100644
> > > > --- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > > +++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
> > > > @@ -86,3 +86,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";
> > > > +          phy-handle = <&ethphy0>;
> > > > +          phy-mode = "rgmii-id";
> > > > +          ptp-timer = <&netc_timer>;
> > > > +      };
> > > > +    };
> > > > --
> > > > 2.34.1
> > > >

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

* RE: [PATCH v3 net-next 05/15] ptp: netc: add PTP_CLK_REQ_PPS support
  2025-08-13 15:06       ` Frank Li
@ 2025-08-14  2:22         ` Wei Fang
  0 siblings, 0 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-14  2:22 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, Aug 13, 2025 at 01:59:01AM +0000, Wei Fang wrote:
> > > On Tue, Aug 12, 2025 at 05:46:24PM +0800, Wei Fang wrote:
> > > > The NETC Timer is capable of generating a PPS interrupt to the host. To
> > > > support this feature, a 64-bit alarm time (which is a integral second
> > > > of PHC in the future) is set to TMR_ALARM, and the period is set to
> > > > TMR_FIPER. The alarm time is compared to the current time on each
> update,
> > > > then the alarm trigger is used as an indication to the TMR_FIPER starts
> > > > down counting. After the period has passed, the PPS event is generated.
> > > >
> > > > According to the NETC block guide, the Timer has three FIPERs, any of
> > > > which can be used to generate the PPS events, but in the current
> > > > implementation, we only need one of them to implement the PPS feature,
> > > > so FIPER 0 is used as the default PPS generator. Also, the Timer has
> > > > 2 ALARMs, currently, ALARM 0 is used as the default time comparator.
> > > >
> > > > However, if there is a time drift when PPS is enabled, the PPS event will
> > > > not be generated at an integral second of PHC. The suggested steps from
> > > > IP team if time drift happens:
> > >
> > > according to patch, "drift" means timer adjust period?
> >
> > No only adjust period, but also including adjust time.
> 
> I think 'adjust period and time' is more accurate then drift.  drift always
> happen. The problem should happen only at adjust.
> 
> >
> > > netc_timer_adjust_period()
> > >
> > > generally, netc_timer_adjust_period() happen 4 times every second, does
> > > disable/re-enable impact pps accurate?
> >
> > PPS needs to be re-enabled only when the integer part of the period changes.
> > In this case, re-enabling PPS will result in a loss of PPS signal for 1 ~ 2 seconds.
> > In most cases, only the fractional part of the period is adjusted, so there is no
> > need to re-enable PPS.
> 
> Lost 1-2 second should be okay when adjust time, which only happen at
> beginning of sync.
> 
> Does software enable/disable impact PPS accurate? For example:
> 
> suppose PPS plus at 10000ns position.
> 
> enable/disable software take 112ns, does PPS plus at 10112ns Or still at
> 10000ns position.
> 

The purpose is to make the PPS signal always be generated at an integral
second of PHC. Below are the results without and with PPS re-enabled
when adjusting the integer part of the period.

Without PPS re-enabled:

#before adjustment
event index 0 at 39.000000017
event index 0 at 40.000000017

#adjust period
./testptp -d /dev/ptp0 -f -1000000

#after adjustment, the generation time of the PPS signal gradually drifts,
and the deviation from the integer second becomes larger and larger.
event index 0 at 41.000000017
event index 0 at 42.000000017
[...]
event index 0 at 128.999999931
event index 0 at 129.999999928

With PPS re-enabled:

#before adjustment
event index 0 at 86.000000017
event index 0 at 87.000000017

./testptp -d /dev/ptp0 -f -1000000

#after adjustment, a PPS signal will be lost at 88 second, but the generation
time of the PPS signal does not drift.
event index 0 at 89.000000017
event index 0 at 90.000000017
[...]
event index 0 at 161.000000017
event index 0 at 162.000000017


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

* Re: [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock
  2025-08-12  9:46 ` [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
  2025-08-12 14:36   ` Frank Li
@ 2025-08-14  8:25   ` Krzysztof Kozlowski
  2025-08-14 18:50     ` Frank Li
  2025-08-15  2:05     ` Wei Fang
  1 sibling, 2 replies; 53+ messages in thread
From: Krzysztof Kozlowski @ 2025-08-14  8:25 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 Tue, Aug 12, 2025 at 05:46:20PM +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>
> 
> ---
> v2 changes:
> 1. Refine the subject and the commit message
> 2. Remove "nxp,pps-channel"
> 3. Add description to "clocks" and "clock-names"
> v3 changes:
> 1. Remove the "system" clock from clock-names
> ---
>  .../devicetree/bindings/ptp/nxp,ptp-netc.yaml | 63 +++++++++++++++++++
>  1 file changed, 63 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..60fb2513fd76
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
> @@ -0,0 +1,63 @@
> +# 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 V4 Timer PTP clock
> +
> +description:
> +  NETC V4 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:
> +      The "ccm_timer" means the reference clock comes from CCM of SoC.
> +      The "ext_1588" means the reference clock comes from external IO
> +      pins.
> +    enum:
> +      - ccm_timer

You should name here how the input pin is called, not the source. Pin is
"ref"?

> +      - ext_1588

This should be just "ext"? We probably talked about this, but this feels
like you describe one input in different ways.

You will get the same questions in the future, if commit msg does not
reflect previous talks.

> +
> +required:
> +  - compatible
> +  - reg
> +
> +allOf:
> +  - $ref: /schemas/pci/pci-device.yaml
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    pcie {
> +        #address-cells = <3>;
> +        #size-cells = <2>;
> +
> +        ethernet@18,0 {

That's rather timer or ptp-timer or your binding is incorrect. Please
describe COMPLETE device in your binding.

> +            compatible = "pci1131,ee02";
> +            reg = <0x00c000 0 0 0 0>;

Best regards,
Krzysztof


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

* Re: [PATCH v3 net-next 02/15] dt-bindings: net: add ptp-timer property
  2025-08-12  9:46 ` [PATCH v3 net-next 02/15] dt-bindings: net: add ptp-timer property Wei Fang
  2025-08-12 11:18   ` Vladimir Oltean
  2025-08-12 14:40   ` Frank Li
@ 2025-08-14  8:27   ` Krzysztof Kozlowski
  2 siblings, 0 replies; 53+ messages in thread
From: Krzysztof Kozlowski @ 2025-08-14  8:27 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 Tue, Aug 12, 2025 at 05:46:21PM +0800, Wei Fang wrote:
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
> 
> ---
> v3 changes:
> New patch, add a generic property instead of adding a property to
> fsl,enetc.yaml
> ---
>  .../devicetree/bindings/net/ethernet-controller.yaml         | 5 +++++
>  1 file changed, 5 insertions(+)

In the same patch please remove redundancies: fsl,fman-dtsec.yaml and
maybe more. That's always one logical change. Otherwise you introduce
redundant or duplicated code.

Best regards,
Krzysztof


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

* Re: [PATCH v3 net-next 03/15] dt-bindings: net: add an example for ENETC v4
  2025-08-13 15:10       ` Frank Li
  2025-08-14  1:34         ` Wei Fang
@ 2025-08-14  8:29         ` Krzysztof Kozlowski
  1 sibling, 0 replies; 53+ messages in thread
From: Krzysztof Kozlowski @ 2025-08-14  8:29 UTC (permalink / raw)
  To: Frank Li
  Cc: Wei Fang, 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, Aug 13, 2025 at 11:10:18AM -0400, Frank Li wrote:
> On Wed, Aug 13, 2025 at 01:38:55AM +0000, Wei Fang wrote:
> > > On Tue, Aug 12, 2025 at 05:46:22PM +0800, Wei Fang wrote:
> > > > Add a DT node example for ENETC v4 device.
> > >
> > > Not sure why need add examples here? Any big difference with existed
> > > example?
> > >
> >
> > For enetc v4, we have added clocks, and it also supports ptp-timer
> > property, these are different from enetc v1, so I think it is better to
> > add an example for v4.
> 
> If there are not big change, needn't duplicate one example at yaml file,
> the content should be in dts file already. Pass DTB_CHECK should be okay.

Two new properties is on the edge of justification of new example. I am
fine with both - having this patch and dropping it.

Best regards,
Krzysztof


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

* Re: [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock
  2025-08-14  8:25   ` Krzysztof Kozlowski
@ 2025-08-14 18:50     ` Frank Li
  2025-08-15  6:05       ` Krzysztof Kozlowski
  2025-08-15  2:05     ` Wei Fang
  1 sibling, 1 reply; 53+ messages in thread
From: Frank Li @ 2025-08-14 18:50 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Wei Fang, 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 Thu, Aug 14, 2025 at 10:25:14AM +0200, Krzysztof Kozlowski wrote:
> On Tue, Aug 12, 2025 at 05:46:20PM +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>
> >
> > ---
> > v2 changes:
> > 1. Refine the subject and the commit message
> > 2. Remove "nxp,pps-channel"
> > 3. Add description to "clocks" and "clock-names"
> > v3 changes:
> > 1. Remove the "system" clock from clock-names
> > ---
> >  .../devicetree/bindings/ptp/nxp,ptp-netc.yaml | 63 +++++++++++++++++++
> >  1 file changed, 63 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..60fb2513fd76
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
> > @@ -0,0 +1,63 @@
> > +# 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 V4 Timer PTP clock
> > +
> > +description:
> > +  NETC V4 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:
> > +      The "ccm_timer" means the reference clock comes from CCM of SoC.
> > +      The "ext_1588" means the reference clock comes from external IO
> > +      pins.
> > +    enum:
> > +      - ccm_timer
>
> You should name here how the input pin is called, not the source. Pin is
> "ref"?
>
> > +      - ext_1588
>
> This should be just "ext"? We probably talked about this, but this feels
> like you describe one input in different ways.
>
> You will get the same questions in the future, if commit msg does not
> reflect previous talks.
>
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +
> > +allOf:
> > +  - $ref: /schemas/pci/pci-device.yaml
> > +
> > +unevaluatedProperties: false
> > +
> > +examples:
> > +  - |
> > +    pcie {
> > +        #address-cells = <3>;
> > +        #size-cells = <2>;
> > +
> > +        ethernet@18,0 {
>
> That's rather timer or ptp-timer or your binding is incorrect. Please
> describe COMPLETE device in your binding.

Krzysztof:

	I have question about "COMPLETE" here. For some MFD/syscon, I know
need descript all children nodes to make MFD/syscon complete.

	But here it is PCIe device.

pcie_4ca00000: pcie@4ca00000 {
	compatible = "pci-host-ecam-generic";
	...

	enetc_port0: ethernet@0,0 {
        	compatible = "fsl,imx95-enetc", "...";
		...

	ptp-timer@18,0 {
		compatible = "pci1131,ee02";
	}
};

	parent "pci-host-ecam-generic" is common pci binding, each children
is indepentant part.

	I am not sure how to decript COMPLETE device for PCI devices.

	Of course, we make complete example here, which include ptp-timer's
consumer nodes.

Frank

>
> > +            compatible = "pci1131,ee02";
> > +            reg = <0x00c000 0 0 0 0>;
>
> Best regards,
> Krzysztof
>

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

* RE: [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock
  2025-08-14  8:25   ` Krzysztof Kozlowski
  2025-08-14 18:50     ` Frank Li
@ 2025-08-15  2:05     ` Wei Fang
  1 sibling, 0 replies; 53+ messages in thread
From: Wei Fang @ 2025-08-15  2:05 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

> > +title: NXP NETC V4 Timer PTP clock
> > +
> > +description:
> > +  NETC V4 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:
> > +      The "ccm_timer" means the reference clock comes from CCM of SoC.
> > +      The "ext_1588" means the reference clock comes from external IO
> > +      pins.
> > +    enum:
> > +      - ccm_timer
> 
> You should name here how the input pin is called, not the source. Pin is
> "ref"?
> 
> > +      - ext_1588
> 
> This should be just "ext"? We probably talked about this, but this feels
> like you describe one input in different ways.

The NETC Timer has 3 reference clock sources, "ccm_timer" and "ext_1588"
are two of them, but the clock mux is inside the IP, so we want to set this
mux to select the desired clock source by parsing the clock name. This
method is the same as https://lore.kernel.org/imx/20250403103346.3064895-2-ciprianmarian.costea@oss.nxp.com/

> 
> You will get the same questions in the future, if commit msg does not
> reflect previous talks.

Okay, I will add more information to the description or the commit message

> 
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +
> > +allOf:
> > +  - $ref: /schemas/pci/pci-device.yaml
> > +
> > +unevaluatedProperties: false
> > +
> > +examples:
> > +  - |
> > +    pcie {
> > +        #address-cells = <3>;
> > +        #size-cells = <2>;
> > +
> > +        ethernet@18,0 {
> 
> That's rather timer or ptp-timer or your binding is incorrect. Please
> describe COMPLETE device in your binding.
> 
> > +            compatible = "pci1131,ee02";
> > +            reg = <0x00c000 0 0 0 0>;
> 
> Best regards,
> Krzysztof


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

* Re: [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock
  2025-08-14 18:50     ` Frank Li
@ 2025-08-15  6:05       ` Krzysztof Kozlowski
  2025-08-15 18:11         ` Frank Li
  0 siblings, 1 reply; 53+ messages in thread
From: Krzysztof Kozlowski @ 2025-08-15  6:05 UTC (permalink / raw)
  To: Frank Li
  Cc: Wei Fang, 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 14/08/2025 20:50, Frank Li wrote:
> On Thu, Aug 14, 2025 at 10:25:14AM +0200, Krzysztof Kozlowski wrote:
>> On Tue, Aug 12, 2025 at 05:46:20PM +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>
>>>
>>> ---
>>> v2 changes:
>>> 1. Refine the subject and the commit message
>>> 2. Remove "nxp,pps-channel"
>>> 3. Add description to "clocks" and "clock-names"
>>> v3 changes:
>>> 1. Remove the "system" clock from clock-names
>>> ---
>>>  .../devicetree/bindings/ptp/nxp,ptp-netc.yaml | 63 +++++++++++++++++++
>>>  1 file changed, 63 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..60fb2513fd76
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
>>> @@ -0,0 +1,63 @@
>>> +# 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 V4 Timer PTP clock
>>> +
>>> +description:
>>> +  NETC V4 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:
>>> +      The "ccm_timer" means the reference clock comes from CCM of SoC.
>>> +      The "ext_1588" means the reference clock comes from external IO
>>> +      pins.
>>> +    enum:
>>> +      - ccm_timer
>>
>> You should name here how the input pin is called, not the source. Pin is
>> "ref"?
>>
>>> +      - ext_1588
>>
>> This should be just "ext"? We probably talked about this, but this feels
>> like you describe one input in different ways.
>>
>> You will get the same questions in the future, if commit msg does not
>> reflect previous talks.
>>
>>> +
>>> +required:
>>> +  - compatible
>>> +  - reg
>>> +
>>> +allOf:
>>> +  - $ref: /schemas/pci/pci-device.yaml
>>> +
>>> +unevaluatedProperties: false
>>> +
>>> +examples:
>>> +  - |
>>> +    pcie {
>>> +        #address-cells = <3>;
>>> +        #size-cells = <2>;
>>> +
>>> +        ethernet@18,0 {
>>
>> That's rather timer or ptp-timer or your binding is incorrect. Please
>> describe COMPLETE device in your binding.
> 
> Krzysztof:
> 
> 	I have question about "COMPLETE" here. For some MFD/syscon, I know
> need descript all children nodes to make MFD/syscon complete.
> 
> 	But here it is PCIe device.
> 
> pcie_4ca00000: pcie@4ca00000 {
> 	compatible = "pci-host-ecam-generic";
> 	...
> 
> 	enetc_port0: ethernet@0,0 {
>         	compatible = "fsl,imx95-enetc", "...";
> 		...
> 
> 	ptp-timer@18,0 {
> 		compatible = "pci1131,ee02";
> 	}
> };
> 
> 	parent "pci-host-ecam-generic" is common pci binding, each children
> is indepentant part.
> 
> 	I am not sure how to decript COMPLETE device for PCI devices.

I don't know what is missing here, but naming it ethernet suggested
there are other functions not being described in the binding.



Best regards,
Krzysztof

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

* Re: [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock
  2025-08-15  6:05       ` Krzysztof Kozlowski
@ 2025-08-15 18:11         ` Frank Li
  0 siblings, 0 replies; 53+ messages in thread
From: Frank Li @ 2025-08-15 18:11 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Wei Fang, 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 Fri, Aug 15, 2025 at 08:05:12AM +0200, Krzysztof Kozlowski wrote:
> On 14/08/2025 20:50, Frank Li wrote:
> > On Thu, Aug 14, 2025 at 10:25:14AM +0200, Krzysztof Kozlowski wrote:
> >> On Tue, Aug 12, 2025 at 05:46:20PM +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>
> >>>
> >>> ---
> >>> v2 changes:
> >>> 1. Refine the subject and the commit message
> >>> 2. Remove "nxp,pps-channel"
> >>> 3. Add description to "clocks" and "clock-names"
> >>> v3 changes:
> >>> 1. Remove the "system" clock from clock-names
> >>> ---
> >>>  .../devicetree/bindings/ptp/nxp,ptp-netc.yaml | 63 +++++++++++++++++++
> >>>  1 file changed, 63 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..60fb2513fd76
> >>> --- /dev/null
> >>> +++ b/Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
> >>> @@ -0,0 +1,63 @@
> >>> +# 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 V4 Timer PTP clock
> >>> +
> >>> +description:
> >>> +  NETC V4 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:
> >>> +      The "ccm_timer" means the reference clock comes from CCM of SoC.
> >>> +      The "ext_1588" means the reference clock comes from external IO
> >>> +      pins.
> >>> +    enum:
> >>> +      - ccm_timer
> >>
> >> You should name here how the input pin is called, not the source. Pin is
> >> "ref"?
> >>
> >>> +      - ext_1588
> >>
> >> This should be just "ext"? We probably talked about this, but this feels
> >> like you describe one input in different ways.
> >>
> >> You will get the same questions in the future, if commit msg does not
> >> reflect previous talks.
> >>
> >>> +
> >>> +required:
> >>> +  - compatible
> >>> +  - reg
> >>> +
> >>> +allOf:
> >>> +  - $ref: /schemas/pci/pci-device.yaml
> >>> +
> >>> +unevaluatedProperties: false
> >>> +
> >>> +examples:
> >>> +  - |
> >>> +    pcie {
> >>> +        #address-cells = <3>;
> >>> +        #size-cells = <2>;
> >>> +
> >>> +        ethernet@18,0 {
> >>
> >> That's rather timer or ptp-timer or your binding is incorrect. Please
> >> describe COMPLETE device in your binding.
> >
> > Krzysztof:
> >
> > 	I have question about "COMPLETE" here. For some MFD/syscon, I know
> > need descript all children nodes to make MFD/syscon complete.
> >
> > 	But here it is PCIe device.
> >
> > pcie_4ca00000: pcie@4ca00000 {
> > 	compatible = "pci-host-ecam-generic";
> > 	...
> >
> > 	enetc_port0: ethernet@0,0 {
> >         	compatible = "fsl,imx95-enetc", "...";
> > 		...
> >
> > 	ptp-timer@18,0 {
> > 		compatible = "pci1131,ee02";
> > 	}
> > };
> >
> > 	parent "pci-host-ecam-generic" is common pci binding, each children
> > is indepentant part.
> >
> > 	I am not sure how to decript COMPLETE device for PCI devices.
>
> I don't know what is missing here, but naming it ethernet suggested
> there are other functions not being described in the binding.

name 'ethernet@18,0' is wrong, which should be fixed as 'ptp-timer@18,0'.

which is independence device. This file descript its's bindings.

Other embedded pcie devices located in ENETC submodule is descript by other
binding files.

Such as MIDO: Documentation/devicetree/bindings/net/fsl,enetc-mdio.yaml
NIC: Documentation/devicetree/bindings/net/fsl,enetc.yaml

For ptp-timer, I think it should be complete enough. Do we need descirpt
its's consumer? An example

https://lore.kernel.org/imx/20250812094634.489901-4-wei.fang@nxp.com/

+    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";
+          phy-handle = <&ethphy0>;
+          phy-mode = "rgmii-id";
+          ptp-timer = <&netc_timer>;

This phandle point to ptp-timer@18,0

+      };
+    };

Frank
>
>
>
> Best regards,
> Krzysztof

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

* Re: [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support
  2025-08-13  1:46     ` Wei Fang
@ 2025-08-15 18:13       ` Frank Li
  0 siblings, 0 replies; 53+ messages in thread
From: Frank Li @ 2025-08-15 18:13 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 Wed, Aug 13, 2025 at 01:46:10AM +0000, Wei Fang wrote:
> > On Tue, Aug 12, 2025 at 05:46:23PM +0800, Wei Fang wrote:
> > > NETC V4 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.
> > >
> > > Inside NETC, ENETC can capture the timestamp of the sent/received
> > > packet through the PHC provided by the Timer and record it on the
> > > Tx/Rx BD. And through the relevant PHC interfaces provided by the
> > > driver, the enetc V4 driver can support PTP time synchronization.
> > >
> > > In addition, NETC V4 Timer is similar to the QorIQ 1588 timer, but it
> > > is not exactly the same. The current ptp-qoriq driver is not
> > > compatible with NETC V4 Timer, most of the code cannot be reused, see below
> > reasons.
> > >
> > > 1. The architecture of ptp-qoriq driver makes the register offset
> > > fixed, however, the offsets of all the high registers and low
> > > registers of V4 are swapped, and V4 also adds some new registers. so
> > > extending ptp-qoriq to make it compatible with V4 Timer is tantamount
> > > to completely rewriting ptp-qoriq driver.
> > >
> > > 2. The usage of some functions is somewhat different from QorIQ timer,
> > > such as the setting of TCLK_PERIOD and TMR_ADD, the logic of
> > > configuring PPS, etc., so making the driver compatible with V4 Timer
> > > will undoubtedly increase the complexity of the code and reduce readability.
> > >
> > > 3. QorIQ is an expired brand. It is difficult for us to verify whether
> > > it works stably on the QorIQ platforms if we refactor the driver, and
> > > this will make maintenance difficult, so refactoring the driver
> > > obviously does not bring any benefits.
> > >
> > > Therefore, add this new driver for NETC V4 Timer. Note that the
> > > missing features like PEROUT, PPS and EXTTS will be added 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
> > > v3 changes:
> > > 1. Refactor netc_timer_adjtime() and remove netc_timer_cnt_read() 2.
> > > Remove the check of dma_set_mask_and_coherent() 3. Use devm_kzalloc()
> > > and pci_ioremap_bar() 4. Move alarm related logic including irq
> > > handler to the next patch 5. Improve the commit message 6. Refactor
> > > netc_timer_get_reference_clk_source() and remove
> > >    clk_prepare_enable()
> > > 7. Use FIELD_PREP() helper
> > > 8. Rename PTP_1588_CLOCK_NETC to PTP_NETC_V4_TIMER and improve the
> > >    help text.
> > > 9. Refine netc_timer_adjtime(), change tmr_off to s64 type as we
> > >    confirmed TMR_OFF is a signed register.
> > > ---
> > >  drivers/ptp/Kconfig             |  11 +
> > >  drivers/ptp/Makefile            |   1 +
> > >  drivers/ptp/ptp_netc.c          | 438
> > ++++++++++++++++++++++++++++++++
> > >  include/linux/fsl/netc_global.h |  12 +-
> > >  4 files changed, 461 insertions(+), 1 deletion(-)  create mode 100644
> > > drivers/ptp/ptp_netc.c
> > >
> > > diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig index
> > > 204278eb215e..0ac31a20096c 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_NETC_V4_TIMER
> > > +	bool "NXP NETC V4 Timer PTP Driver"
> > > +	depends on PTP_1588_CLOCK=y
> > > +	depends on PCI_MSI
> > > +	help
> > > +	  This driver adds support for using the NXP NETC V4 Timer as a PTP
> > > +	  clock, the clock is used by ENETC V4 or NETC V4 Switch for PTP time
> > > +	  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..8985d723d29c 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_NETC_V4_TIMER)		+= ptp_netc.o
> > > diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c new file
> > > mode 100644 index 000000000000..cbe2a64d1ced
> > > --- /dev/null
> > > +++ b/drivers/ptp/ptp_netc.c
> > > @@ -0,0 +1,438 @@
> > > +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
> > > +/*
> > > + * NXP NETC V4 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
> >
> > Nit: Like this only use once constant, needn't macro, espcial DEVID.
>
> So you mean directly use PCI_DEVICE(0x1131,0xee02)?


Yes, or
PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, 0xee02);

Frank

>
> >
> > > +
> > > +#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 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
> > > +
> > > +#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
> > > +
> > > +/* 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 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;
> > > +};
> > > +
> > > +#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 const char *const timer_clk_src[] = {
> > > +	"ccm_timer",
> > > +	"ext_1588"
> > > +};
> > > +
> > > +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);
> > > +
> > > +	/* Writes to the TMR_CNT_L register copies the written value
> > > +	 * into the shadow TMR_CNT_L register. Writes to the TMR_CNT_H
> > > +	 * register copies the values written into the shadow TMR_CNT_H
> > > +	 * register. Contents of the shadow registers are copied into
> > > +	 * the TMR_CNT_L and TMR_CNT_H registers following a write into
> > > +	 * the TMR_CNT_H register. So the user must writes to TMR_CNT_L
> > > +	 * register first.
> > > +	 */
> >
> > Is all other register the same? like OFF_L, OFF_H?
> >
> > And read have similar behavor?
> >
> > > +	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_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);
> > > +	unsigned long flags;
> > > +	s64 tmr_off;
> > > +
> > > +	spin_lock_irqsave(&priv->lock, flags);
> > > +
> > > +	/* Adjusting TMROFF instead of TMR_CNT is that the timer
> > > +	 * counter keeps increasing during reading and writing
> > > +	 * TMR_CNT, which will cause latency.
> > > +	 */
> > > +	tmr_off = netc_timer_offset_read(priv);
> > > +	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_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 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 = FIELD_PREP(TMR_CTRL_CK_SEL, priv->clk_select) |
> > > +		   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 |= FIELD_PREP(TMR_CTRL_TCLK_PERIOD, integral_period) |
> > > +		    TMR_COMP_MODE;
> > > +	netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl);
> > > +	netc_timer_wr(priv, NETC_TMR_ADD, fractional_period); }
> > > +
> > > +static int netc_timer_pci_probe(struct pci_dev *pdev) {
> > > +	struct device *dev = &pdev->dev;
> > > +	struct netc_timer *priv;
> > > +	int err;
> > > +
> > > +	pcie_flr(pdev);
> > > +	err = pci_enable_device_mem(pdev);
> > > +	if (err)
> > > +		return dev_err_probe(dev, err, "Failed to enable device\n");
> > > +
> > > +	dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> > > +	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 = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > > +	if (!priv) {
> > > +		err = -ENOMEM;
> > > +		goto release_mem_regions;
> > > +	}
> >
> > move devm_kzalloc() before pci_enable_device_mem() to reduce a goto
> >
> > > +
> > > +	priv->pdev = pdev;
> > > +	priv->base = pci_ioremap_bar(pdev, NETC_TMR_REGS_BAR);
> > > +	if (!priv->base) {
> > > +		err = -ENOMEM;
> > > +		goto release_mem_regions;
> > > +	}
> > > +
> > > +	pci_set_drvdata(pdev, priv);
> > > +
> > > +	return 0;
> > > +
> > > +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);
> > > +	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 clk *clk;
> > > +	int i;
> > > +
> > > +	/* Select NETC system clock as the reference clock by default */
> > > +	priv->clk_select = NETC_TMR_SYSTEM_CLK;
> > > +	priv->clk_freq = NETC_TMR_SYSCLK_333M;
> > > +
> > > +	/* Update the clock source of the reference clock if the clock
> > > +	 * is specified in DT node.
> > > +	 */
> > > +	for (i = 0; i < ARRAY_SIZE(timer_clk_src); i++) {
> > > +		clk = devm_clk_get_optional_enabled(dev, timer_clk_src[i]);
> > > +		if (IS_ERR(clk))
> > > +			return PTR_ERR(clk);
> > > +
> > > +		if (clk) {
> > > +			priv->clk_freq = clk_get_rate(clk);
> > > +			priv->clk_select = i ? NETC_TMR_EXT_OSC :
> > > +					       NETC_TMR_CCM_TIMER1;
> > > +			break;
> > > +		}
> > > +	}
> > > +
> > > +	/* The period is a 64-bit number, the high 32-bit is the integer
> > > +	 * part of the period, the low 32-bit is the fractional part of
> > > +	 * the period. In order to get the desired 32-bit fixed-point
> > > +	 * format, multiply the numerator of the fraction by 2^32.
> > > +	 */
> > > +	priv->period = div_u64(NSEC_PER_SEC << 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 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) {
> > > +		if (err != -EPROBE_DEFER)
> > > +			dev_err(dev, "Failed to parse DT node\n");
> > > +		goto timer_pci_remove;
> > > +	}
> >
> > move netc_timer_parse_dt() before netc_timer_pci_probe()
> >
> > you can use return dev_err_probe(), which already handle -EPROBE_DEFER
> > case.
> >
> >
> > > +
> > > +	priv->caps = netc_timer_ptp_caps;
> > > +	priv->oclk_prsc = NETC_TMR_DEFAULT_PRSC;
> > > +	spin_lock_init(&priv->lock);
> > > +
> > > +	netc_timer_init(priv);
> > > +	priv->clock = ptp_clock_register(&priv->caps, dev);
> > > +	if (IS_ERR(priv->clock)) {
> > > +		err = PTR_ERR(priv->clock);
> > > +		goto timer_pci_remove;
> > > +	}
> > > +
> > > +	priv->phc_index = ptp_clock_index(priv->clock);
> > > +
> > > +	return 0;
> > > +
> > > +timer_pci_remove:
> > > +	netc_timer_pci_remove(pdev);
> > > +
> > > +	return err;
> > > +}
> > > +
> > > +static void netc_timer_remove(struct pci_dev *pdev) {
> >
> > use devm_add_action_or_reset() can simpify your error handle.
> >
> > Frank
> > > +	struct netc_timer *priv = pci_get_drvdata(pdev);
> > > +
> > > +	ptp_clock_unregister(priv->clock);
> > > +	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) },
> > > +	{ }
> > > +};
> > > +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..17c19c8d3f93
> > > 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_NETC_V4_TIMER)
> > > +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] 53+ messages in thread

end of thread, other threads:[~2025-08-15 18:13 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-12  9:46 [PATCH v3 net-next 00/15] Add NETC Timer PTP driver and add PTP support for i.MX95 Wei Fang
2025-08-12  9:46 ` [PATCH v3 net-next 01/15] dt-bindings: ptp: add NETC Timer PTP clock Wei Fang
2025-08-12 14:36   ` Frank Li
2025-08-14  8:25   ` Krzysztof Kozlowski
2025-08-14 18:50     ` Frank Li
2025-08-15  6:05       ` Krzysztof Kozlowski
2025-08-15 18:11         ` Frank Li
2025-08-15  2:05     ` Wei Fang
2025-08-12  9:46 ` [PATCH v3 net-next 02/15] dt-bindings: net: add ptp-timer property Wei Fang
2025-08-12 11:18   ` Vladimir Oltean
2025-08-12 14:40   ` Frank Li
2025-08-14  8:27   ` Krzysztof Kozlowski
2025-08-12  9:46 ` [PATCH v3 net-next 03/15] dt-bindings: net: add an example for ENETC v4 Wei Fang
2025-08-12 14:38   ` Frank Li
2025-08-13  1:38     ` Wei Fang
2025-08-13 15:10       ` Frank Li
2025-08-14  1:34         ` Wei Fang
2025-08-14  8:29         ` Krzysztof Kozlowski
2025-08-12  9:46 ` [PATCH v3 net-next 04/15] ptp: netc: add NETC V4 Timer PTP driver support Wei Fang
2025-08-12 13:49   ` Vladimir Oltean
2025-08-13  2:21     ` Wei Fang
2025-08-12 15:01   ` Frank Li
2025-08-13  1:46     ` Wei Fang
2025-08-15 18:13       ` Frank Li
2025-08-13  4:45   ` kernel test robot
2025-08-12  9:46 ` [PATCH v3 net-next 05/15] ptp: netc: add PTP_CLK_REQ_PPS support Wei Fang
2025-08-12 15:11   ` Frank Li
2025-08-13  1:59     ` Wei Fang
2025-08-13 15:06       ` Frank Li
2025-08-14  2:22         ` Wei Fang
2025-08-12  9:46 ` [PATCH v3 net-next 06/15] ptp: netc: add periodic pulse output support Wei Fang
2025-08-12 12:03   ` Vladimir Oltean
2025-08-13  1:42     ` Wei Fang
2025-08-12 15:24   ` Frank Li
2025-08-13  2:35   ` kernel test robot
2025-08-12  9:46 ` [PATCH v3 net-next 07/15] ptp: netc: add external trigger stamp support Wei Fang
2025-08-12 15:27   ` Frank Li
2025-08-12  9:46 ` [PATCH v3 net-next 08/15] ptp: netc: add debugfs support to loop back pulse signal Wei Fang
2025-08-12 15:42   ` Frank Li
2025-08-12  9:46 ` [PATCH v3 net-next 09/15] MAINTAINERS: add NETC Timer PTP clock driver section Wei Fang
2025-08-12  9:46 ` [PATCH v3 net-next 10/15] net: enetc: save the parsed information of PTP packet to skb->cb Wei Fang
2025-08-12 15:45   ` Frank Li
2025-08-12  9:46 ` [PATCH v3 net-next 11/15] net: enetc: extract enetc_update_ptp_sync_msg() to handle PTP Sync packets Wei Fang
2025-08-12 15:52   ` Frank Li
2025-08-13  2:04     ` Wei Fang
2025-08-12  9:46 ` [PATCH v3 net-next 12/15] net: enetc: remove unnecessary CONFIG_FSL_ENETC_PTP_CLOCK check Wei Fang
2025-08-12  9:46 ` [PATCH v3 net-next 13/15] net: enetc: add PTP synchronization support for ENETC v4 Wei Fang
2025-08-12 15:58   ` Frank Li
2025-08-13  2:17     ` Wei Fang
2025-08-13 14:55       ` Frank Li
2025-08-12  9:46 ` [PATCH v3 net-next 14/15] net: enetc: don't update sync packet checksum if checksum offload is used Wei Fang
2025-08-12  9:46 ` [PATCH v3 15/15] arm64: dts: imx95: add standard PCI device compatible string to NETC Timer Wei Fang
2025-08-12 15:59   ` 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).