net: enetc: add PTP synchronization support for ENETC v4

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>
This commit is contained in:
Wei Fang
2025-08-29 13:06:14 +08:00
committed by Paolo Abeni
parent 7776d5e6e3
commit f5b9a1cde0
6 changed files with 131 additions and 24 deletions

View 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

View 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)

View 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);

View File

@@ -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)

View 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 *

View 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 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;
}
timer_pdev = pci_get_slot(bus, devfn);
if (!timer_pdev)
return -1;
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);
int *phc_idx;
phc_idx = symbol_get(enetc_phc_index);
if (phc_idx) {
info->phc_index = *phc_idx;
symbol_put(enetc_phc_index);
}
if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK)) {
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE;
return 0;
}
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)