]> www.infradead.org Git - users/hch/misc.git/commitdiff
net: enetc: add PTP synchronization support for ENETC v4
authorWei Fang <wei.fang@nxp.com>
Fri, 29 Aug 2025 05:06:14 +0000 (13:06 +0800)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 2 Sep 2025 11:13:34 +0000 (13:13 +0200)
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 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.

3. Since the generic helper functions from ptp_clock are used to get
the PHC index of the PTP clock, so FSL_ENETC_CORE depends on Kconfig
symbol "PTP_1588_CLOCK_OPTIONAL". But FSL_ENETC_CORE can only be
selected, so add the dependency to FSL_ENETC, FSL_ENETC_VF and
NXP_ENETC4. Perhaps the best approach would be to change FSL_ENETC_CORE
to a visible menu entry. Then make FSL_ENETC, FSL_ENETC_VF, and
NXP_ENETC4 depend on it, but this is not the goal of this patch, so this
may be changed in the future.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20250829050615.1247468-14-wei.fang@nxp.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/freescale/enetc/Kconfig
drivers/net/ethernet/freescale/enetc/enetc.c
drivers/net/ethernet/freescale/enetc/enetc.h
drivers/net/ethernet/freescale/enetc/enetc4_hw.h
drivers/net/ethernet/freescale/enetc/enetc4_pf.c
drivers/net/ethernet/freescale/enetc/enetc_ethtool.c

index 54b0f0a5a6bb12161fff590e864d315042eebd1b..117038104b699176acef51dd215cc2b543337706 100644 (file)
@@ -28,6 +28,7 @@ config NXP_NTMP
 
 config FSL_ENETC
        tristate "ENETC PF driver"
+       depends on PTP_1588_CLOCK_OPTIONAL
        depends on PCI_MSI
        select FSL_ENETC_CORE
        select FSL_ENETC_IERB
@@ -45,6 +46,7 @@ config FSL_ENETC
 
 config NXP_ENETC4
        tristate "ENETC4 PF driver"
+       depends on PTP_1588_CLOCK_OPTIONAL
        depends on PCI_MSI
        select FSL_ENETC_CORE
        select FSL_ENETC_MDIO
@@ -62,6 +64,7 @@ config NXP_ENETC4
 
 config FSL_ENETC_VF
        tristate "ENETC VF driver"
+       depends on PTP_1588_CLOCK_OPTIONAL
        depends on PCI_MSI
        select FSL_ENETC_CORE
        select FSL_ENETC_MDIO
index 25379ac7d69d63b1285b8750ec543aa5aed34c0e..6dbc9cc811a0723e677fb457a0c412d545df4a6b 100644 (file)
@@ -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;
 }
@@ -3315,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) {
@@ -3365,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)
index c65aa7b8812208fa5943abae431a8b8f766f00f7..815afdc2ec239704c442551f68bfa7436cc0e28e 100644 (file)
@@ -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);
index aa25b445d301875e2d40c916bcdf049f33519c5a..19bf0e89cdc2589ba7d615cdf458a56e82879548 100644 (file)
 /* 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)
index 38fb81db48c2f9c472fc60df32fbb1ab6ff63356..2e07b9b746e1ad18295de19401188cd3b5370a64 100644 (file)
@@ -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 *
index 961e76cd84898f459320c7c75cf74329ffa0046a..6215e9c68fc5b1df4a5803a76a9586fcb2ea4732 100644 (file)
@@ -4,6 +4,9 @@
 #include <linux/ethtool_netlink.h>
 #include <linux/net_tstamp.h>
 #include <linux/module.h>
+#include <linux/of.h>
+#include <linux/ptp_clock_kernel.h>
+
 #include "enetc.h"
 
 static const u32 enetc_si_regs[] = {
@@ -877,23 +880,54 @@ 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 int enetc4_get_phc_index_by_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;
+       struct pci_dev *timer_pdev;
+       unsigned int devfn;
+       int phc_index;
+
+       switch (si->revision) {
+       case ENETC_REV_4_1:
+               devfn = PCI_DEVFN(24, 0);
+               break;
+       default:
+               return -1;
        }
 
-       if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK)) {
-               info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE;
+       timer_pdev = pci_get_slot(bus, devfn);
+       if (!timer_pdev)
+               return -1;
 
-               return 0;
-       }
+       phc_index = ptp_clock_index_by_dev(&timer_pdev->dev);
+       pci_dev_put(timer_pdev);
+
+       return phc_index;
+}
+
+static int enetc4_get_phc_index(struct enetc_si *si)
+{
+       struct device_node *np = si->pdev->dev.of_node;
+       struct device_node *timer_np;
+       int phc_index;
+
+       if (!np)
+               return enetc4_get_phc_index_by_pdev(si);
+
+       timer_np = of_parse_phandle(np, "ptp-timer", 0);
+       if (!timer_np)
+               return enetc4_get_phc_index_by_pdev(si);
+
+       phc_index = ptp_clock_index_by_of_node(timer_np);
+       of_node_put(timer_np);
+
+       return phc_index;
+}
+
+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 +942,36 @@ 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;
+       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 {
+               info->phc_index = enetc4_get_phc_index(si);
+               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 +1360,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)