mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user