* [PATCH net-next 0/4] Support PTP clock for Wangxun NICs
@ 2025-01-02 10:30 Jiawen Wu
2025-01-02 10:30 ` [PATCH net-next 1/4] net: wangxun: Add support for PTP clock Jiawen Wu
` (3 more replies)
0 siblings, 4 replies; 20+ messages in thread
From: Jiawen Wu @ 2025-01-02 10:30 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, jacob.e.keller, netdev
Cc: mengyuanlou, Jiawen Wu
Implement support for PTP clock on Wangxun NICs.
Jiawen Wu (4):
net: wangxun: Add support for PTP clock
net: wangxun: Implement get_ts_info
net: wangxun: Add watchdog task for PTP clock
net: ngbe: Add support for 1PPS and TOD
drivers/net/ethernet/wangxun/libwx/Makefile | 2 +-
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 40 +
.../net/ethernet/wangxun/libwx/wx_ethtool.h | 2 +
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 19 +
drivers/net/ethernet/wangxun/libwx/wx_hw.h | 1 +
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 121 +-
drivers/net/ethernet/wangxun/libwx/wx_lib.h | 3 +
drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 1013 +++++++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 20 +
drivers/net/ethernet/wangxun/libwx/wx_type.h | 107 ++
.../net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 1 +
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 27 +-
drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c | 11 +
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 5 +
.../ethernet/wangxun/txgbe/txgbe_ethtool.c | 1 +
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 17 +
.../net/ethernet/wangxun/txgbe/txgbe_phy.c | 10 +
17 files changed, 1393 insertions(+), 7 deletions(-)
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_ptp.c
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_ptp.h
--
2.27.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-02 10:30 [PATCH net-next 0/4] Support PTP clock for Wangxun NICs Jiawen Wu
@ 2025-01-02 10:30 ` Jiawen Wu
2025-01-02 14:13 ` Andrew Lunn
` (3 more replies)
2025-01-02 10:30 ` [PATCH net-next 2/4] net: wangxun: Implement get_ts_info Jiawen Wu
` (2 subsequent siblings)
3 siblings, 4 replies; 20+ messages in thread
From: Jiawen Wu @ 2025-01-02 10:30 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, jacob.e.keller, netdev
Cc: mengyuanlou, Jiawen Wu
Implement support for PTP clock on Wangxun NICs.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/Makefile | 2 +-
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 3 +
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 64 +-
drivers/net/ethernet/wangxun/libwx/wx_lib.h | 1 +
drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 719 ++++++++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 16 +
drivers/net/ethernet/wangxun/libwx/wx_type.h | 67 ++
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 7 +
drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c | 10 +
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 10 +
.../net/ethernet/wangxun/txgbe/txgbe_phy.c | 9 +
11 files changed, 902 insertions(+), 6 deletions(-)
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_ptp.c
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_ptp.h
diff --git a/drivers/net/ethernet/wangxun/libwx/Makefile b/drivers/net/ethernet/wangxun/libwx/Makefile
index 42ccd6e4052e..e9f0f1f2309b 100644
--- a/drivers/net/ethernet/wangxun/libwx/Makefile
+++ b/drivers/net/ethernet/wangxun/libwx/Makefile
@@ -4,4 +4,4 @@
obj-$(CONFIG_LIBWX) += libwx.o
-libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o
+libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index abe5921dde02..c4b3b00b0926 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -41,6 +41,9 @@ static const struct wx_stats wx_gstrings_stats[] = {
WX_STAT("rx_csum_offload_good_count", hw_csum_rx_good),
WX_STAT("rx_csum_offload_errors", hw_csum_rx_error),
WX_STAT("alloc_rx_buff_failed", alloc_rx_buff_failed),
+ WX_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
+ WX_STAT("tx_hwtstamp_skipped", tx_hwtstamp_skipped),
+ WX_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
};
static const struct wx_stats wx_gstrings_fdir_stats[] = {
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index 2b3d6586f44a..be1dcc278612 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -13,6 +13,7 @@
#include "wx_type.h"
#include "wx_lib.h"
+#include "wx_ptp.h"
#include "wx_hw.h"
/* Lookup table mapping the HW PTYPE to the bit field for decoding */
@@ -597,8 +598,17 @@ static void wx_process_skb_fields(struct wx_ring *rx_ring,
union wx_rx_desc *rx_desc,
struct sk_buff *skb)
{
+ struct wx *wx = netdev_priv(rx_ring->netdev);
+
wx_rx_hash(rx_ring, rx_desc, skb);
wx_rx_checksum(rx_ring, rx_desc, skb);
+
+ if (unlikely(test_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, wx->flags)) &&
+ unlikely(wx_test_staterr(rx_desc, WX_RXD_STAT_TS))) {
+ wx_ptp_rx_hwtstamp(rx_ring->q_vector->wx, skb);
+ rx_ring->last_rx_timestamp = jiffies;
+ }
+
wx_rx_vlan(rx_ring, rx_desc, skb);
skb_record_rx_queue(skb, rx_ring->queue_index);
skb->protocol = eth_type_trans(skb, rx_ring->netdev);
@@ -932,9 +942,9 @@ static void wx_tx_olinfo_status(union wx_tx_desc *tx_desc,
tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status);
}
-static void wx_tx_map(struct wx_ring *tx_ring,
- struct wx_tx_buffer *first,
- const u8 hdr_len)
+static int wx_tx_map(struct wx_ring *tx_ring,
+ struct wx_tx_buffer *first,
+ const u8 hdr_len)
{
struct sk_buff *skb = first->skb;
struct wx_tx_buffer *tx_buffer;
@@ -1013,6 +1023,8 @@ static void wx_tx_map(struct wx_ring *tx_ring,
netdev_tx_sent_queue(wx_txring_txq(tx_ring), first->bytecount);
+ /* set the timestamp */
+ first->time_stamp = jiffies;
skb_tx_timestamp(skb);
/* Force memory writes to complete before letting h/w know there
@@ -1038,7 +1050,7 @@ static void wx_tx_map(struct wx_ring *tx_ring,
if (netif_xmit_stopped(wx_txring_txq(tx_ring)) || !netdev_xmit_more())
writel(i, tx_ring->tail);
- return;
+ return 0;
dma_error:
dev_err(tx_ring->dev, "TX DMA map failed\n");
@@ -1062,6 +1074,8 @@ static void wx_tx_map(struct wx_ring *tx_ring,
first->skb = NULL;
tx_ring->next_to_use = i;
+
+ return -EPERM;
}
static void wx_tx_ctxtdesc(struct wx_ring *tx_ring, u32 vlan_macip_lens,
@@ -1486,6 +1500,23 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
tx_flags |= WX_TX_FLAGS_HW_VLAN;
}
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+ wx->ptp_clock) {
+ if (wx->tstamp_config.tx_type == HWTSTAMP_TX_ON &&
+ !test_and_set_bit_lock(WX_STATE_PTP_TX_IN_PROGRESS,
+ wx->state)) {
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ tx_flags |= WX_TX_FLAGS_TSTAMP;
+
+ /* schedule check for Tx timestamp */
+ wx->ptp_tx_skb = skb_get(skb);
+ wx->ptp_tx_start = jiffies;
+ schedule_work(&wx->ptp_tx_work);
+ } else {
+ wx->tx_hwtstamp_skipped++;
+ }
+ }
+
/* record initial flags and protocol */
first->tx_flags = tx_flags;
first->protocol = vlan_get_protocol(skb);
@@ -1501,12 +1532,20 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags) && tx_ring->atr_sample_rate)
wx->atr(tx_ring, first, ptype);
- wx_tx_map(tx_ring, first, hdr_len);
+ if (wx_tx_map(tx_ring, first, hdr_len))
+ goto cleanup_tx_tstamp;
return NETDEV_TX_OK;
out_drop:
dev_kfree_skb_any(first->skb);
first->skb = NULL;
+cleanup_tx_tstamp:
+ if (unlikely(tx_flags & WX_TX_FLAGS_TSTAMP)) {
+ dev_kfree_skb_any(wx->ptp_tx_skb);
+ wx->ptp_tx_skb = NULL;
+ cancel_work_sync(&wx->ptp_tx_work);
+ clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
+ }
return NETDEV_TX_OK;
}
@@ -2870,5 +2909,20 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count,
}
EXPORT_SYMBOL(wx_set_ring);
+int wx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ struct wx *wx = netdev_priv(netdev);
+
+ switch (cmd) {
+ case SIOCGHWTSTAMP:
+ return wx_ptp_get_ts_config(wx, ifr);
+ case SIOCSHWTSTAMP:
+ return wx_ptp_set_ts_config(wx, ifr);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+EXPORT_SYMBOL(wx_ioctl);
+
MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers.");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
index fdeb0c315b75..d67a33216811 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
@@ -35,5 +35,6 @@ netdev_features_t wx_fix_features(struct net_device *netdev,
netdev_features_t features);
void wx_set_ring(struct wx *wx, u32 new_tx_count,
u32 new_rx_count, struct wx_ring *temp_ring);
+int wx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
#endif /* _NGBE_LIB_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
new file mode 100644
index 000000000000..d78f99cf4a10
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
@@ -0,0 +1,719 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
+
+#include <linux/ptp_classify.h>
+#include <linux/clocksource.h>
+#include <linux/pci.h>
+
+#include "wx_type.h"
+#include "wx_ptp.h"
+#include "wx_hw.h"
+
+#define WX_INCVAL_10GB 0xCCCCCC
+#define WX_INCVAL_1GB 0x800000
+#define WX_INCVAL_100 0xA00000
+#define WX_INCVAL_10 0xC7F380
+#define WX_INCVAL_EM 0x2000000
+
+#define WX_INCVAL_SHIFT_10GB 20
+#define WX_INCVAL_SHIFT_1GB 18
+#define WX_INCVAL_SHIFT_100 15
+#define WX_INCVAL_SHIFT_10 12
+#define WX_INCVAL_SHIFT_EM 22
+
+#define WX_OVERFLOW_PERIOD (HZ * 30)
+#define WX_PTP_TX_TIMEOUT (HZ)
+
+#define WX_1588_PPS_WIDTH_EM 120
+
+#define WX_NS_PER_SEC 1000000000ULL
+#define WX_NS_PER_MSEC 1000000ULL
+
+/**
+ * wx_ptp_adjfine
+ * @ptp: the ptp clock structure
+ * @ppb: parts per billion adjustment from base
+ * Returns 0 on success
+ *
+ * Adjust the frequency of the ptp cycle counter by the
+ * indicated ppb from the base frequency.
+ */
+static int wx_ptp_adjfine(struct ptp_clock_info *ptp, long ppb)
+{
+ struct wx *wx = container_of(ptp, struct wx, ptp_caps);
+ u64 incval, mask;
+
+ smp_mb(); /* Force any pending update before accessing. */
+ incval = READ_ONCE(wx->base_incval);
+ incval = adjust_by_scaled_ppm(incval, ppb);
+
+ mask = (wx->mac.type == wx_mac_em) ? 0x7FFFFFF : 0xFFFFFF;
+ if (incval > mask)
+ dev_warn(&wx->pdev->dev,
+ "PTP ppb adjusted SYSTIME rate overflowed!\n");
+
+ incval &= mask;
+ if (wx->mac.type != wx_mac_em)
+ incval |= 2 << 24;
+
+ wr32ptp(wx, WX_TSC_1588_INC, incval);
+
+ return 0;
+}
+
+/**
+ * wx_ptp_adjtime
+ * @ptp: the ptp clock structure
+ * @delta: offset to adjust the cycle counter by ns
+ * Returns 0 on success
+ *
+ * Adjust the timer by resetting the timecounter structure.
+ */
+static int wx_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct wx *wx = container_of(ptp, struct wx, ptp_caps);
+ unsigned long flags;
+
+ spin_lock_irqsave(&wx->tmreg_lock, flags);
+ timecounter_adjtime(&wx->hw_tc, delta);
+ spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+
+ return 0;
+}
+
+/**
+ * wx_ptp_gettimex64
+ * @ptp: the ptp clock structure
+ * @ts: timespec structure to hold the current time value
+ * @sts: structure to hold the system time before and after reading the PHC
+ * Returns 0 on success
+ *
+ * Read the timecounter and return the correct value on ns,
+ * after converting it into a struct timespec64.
+ */
+static int wx_ptp_gettimex64(struct ptp_clock_info *ptp,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ struct wx *wx = container_of(ptp, struct wx, ptp_caps);
+ unsigned long flags;
+ u64 ns, stamp;
+
+ spin_lock_irqsave(&wx->tmreg_lock, flags);
+
+ ptp_read_system_prets(sts);
+ stamp = (u64)rd32ptp(wx, WX_TSC_1588_SYSTIML);
+ ptp_read_system_postts(sts);
+ stamp |= (u64)rd32ptp(wx, WX_TSC_1588_SYSTIMH) << 32;
+ ns = timecounter_cyc2time(&wx->hw_tc, stamp);
+
+ spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+
+ *ts = ns_to_timespec64(ns);
+
+ return 0;
+}
+
+/**
+ * wx_ptp_settime64
+ * @ptp: the ptp clock structure
+ * @ts: the timespec64 containing the new time for the cycle counter
+ * Returns 0 on success
+ *
+ * Reset the timecounter to use a new base value instead of the kernel
+ * wall timer value.
+ */
+static int wx_ptp_settime64(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct wx *wx = container_of(ptp, struct wx, ptp_caps);
+ unsigned long flags;
+ u64 ns;
+
+ ns = timespec64_to_ns(ts);
+
+ /* reset the timecounter */
+ spin_lock_irqsave(&wx->tmreg_lock, flags);
+ timecounter_init(&wx->hw_tc, &wx->hw_cc, ns);
+ spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+
+ return 0;
+}
+
+/**
+ * wx_ptp_create_clock
+ * @wx: the private board structure
+ *
+ * Returns 0 on success, negative value on failure
+ *
+ * This function performs setup of the user entry point function table and
+ * initalizes the PTP clock device used by userspace to access the clock-like
+ * features of the PTP core. It will be called by wx_ptp_init, and may
+ * re-use a previously initialized clock (such as during a suspend/resume
+ * cycle).
+ */
+static long wx_ptp_create_clock(struct wx *wx)
+{
+ struct net_device *netdev = wx->netdev;
+ long err;
+
+ /* do nothing if we already have a clock device */
+ if (!IS_ERR_OR_NULL(wx->ptp_clock))
+ return 0;
+
+ snprintf(wx->ptp_caps.name, sizeof(wx->ptp_caps.name),
+ "%s", netdev->name);
+ wx->ptp_caps.owner = THIS_MODULE;
+ wx->ptp_caps.n_alarm = 0;
+ wx->ptp_caps.n_ext_ts = 0;
+ wx->ptp_caps.n_per_out = 0;
+ wx->ptp_caps.pps = 0;
+ wx->ptp_caps.adjfine = wx_ptp_adjfine;
+ wx->ptp_caps.adjtime = wx_ptp_adjtime;
+ wx->ptp_caps.gettimex64 = wx_ptp_gettimex64;
+ wx->ptp_caps.settime64 = wx_ptp_settime64;
+ if (wx->mac.type == wx_mac_em)
+ wx->ptp_caps.max_adj = 500000000;
+ else
+ wx->ptp_caps.max_adj = 250000000;
+
+ wx->ptp_clock = ptp_clock_register(&wx->ptp_caps, &wx->pdev->dev);
+ if (IS_ERR(wx->ptp_clock)) {
+ err = PTR_ERR(wx->ptp_clock);
+ wx->ptp_clock = NULL;
+ wx_err(wx, "ptp clock register failed\n");
+ return err;
+ } else if (wx->ptp_clock) {
+ dev_info(&wx->pdev->dev, "registered PHC device on %s\n",
+ netdev->name);
+ }
+
+ /* Set the default timestamp mode to disabled here. We do this in
+ * create_clock instead of initialization, because we don't want to
+ * override the previous settings during a suspend/resume cycle.
+ */
+ wx->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
+ wx->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
+
+ return 0;
+}
+
+/**
+ * wx_ptp_clear_tx_timestamp - utility function to clear Tx timestamp state
+ * @wx: the private board structure
+ *
+ * This function should be called whenever the state related to a Tx timestamp
+ * needs to be cleared. This helps ensure that all related bits are reset for
+ * the next Tx timestamp event.
+ */
+static void wx_ptp_clear_tx_timestamp(struct wx *wx)
+{
+ rd32ptp(wx, WX_TSC_1588_STMPH);
+ if (wx->ptp_tx_skb) {
+ dev_kfree_skb_any(wx->ptp_tx_skb);
+ wx->ptp_tx_skb = NULL;
+ }
+ clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
+}
+
+/**
+ * wx_ptp_convert_to_hwtstamp - convert register value to hw timestamp
+ * @wx: private board structure
+ * @hwtstamp: stack timestamp structure
+ * @timestamp: unsigned 64bit system time value
+ *
+ * We need to convert the adapter's RX/TXSTMP registers into a hwtstamp value
+ * which can be used by the stack's ptp functions.
+ *
+ * The lock is used to protect consistency of the cyclecounter and the SYSTIME
+ * registers. However, it does not need to protect against the Rx or Tx
+ * timestamp registers, as there can't be a new timestamp until the old one is
+ * unlatched by reading.
+ *
+ * In addition to the timestamp in hardware, some controllers need a software
+ * overflow cyclecounter, and this function takes this into account as well.
+ **/
+static void wx_ptp_convert_to_hwtstamp(struct wx *wx,
+ struct skb_shared_hwtstamps *hwtstamp,
+ u64 timestamp)
+{
+ unsigned long flags;
+ u64 ns;
+
+ memset(hwtstamp, 0, sizeof(*hwtstamp));
+
+ spin_lock_irqsave(&wx->tmreg_lock, flags);
+ ns = timecounter_cyc2time(&wx->hw_tc, timestamp);
+ spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+
+ hwtstamp->hwtstamp = ns_to_ktime(ns);
+}
+
+/**
+ * wx_ptp_tx_hwtstamp - utility function which checks for TX time stamp
+ * @wx: the private board struct
+ *
+ * if the timestamp is valid, we convert it into the timecounter ns
+ * value, then store that result into the shhwtstamps structure which
+ * is passed up the network stack
+ */
+static void wx_ptp_tx_hwtstamp(struct wx *wx)
+{
+ struct skb_shared_hwtstamps shhwtstamps;
+ struct sk_buff *skb = wx->ptp_tx_skb;
+ u64 regval = 0;
+
+ regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPL);
+ regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPH) << 32;
+
+ wx_ptp_convert_to_hwtstamp(wx, &shhwtstamps, regval);
+
+ wx->ptp_tx_skb = NULL;
+ clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
+ skb_tstamp_tx(skb, &shhwtstamps);
+ dev_kfree_skb_any(skb);
+}
+
+/**
+ * wx_ptp_tx_hwtstamp_work
+ * @work: pointer to the work struct
+ *
+ * This work item polls TSYNCTXCTL valid bit to determine when a Tx hardware
+ * timestamp has been taken for the current skb. It is necessary, because the
+ * descriptor's "done" bit does not correlate with the timestamp event.
+ */
+static void wx_ptp_tx_hwtstamp_work(struct work_struct *work)
+{
+ struct wx *wx = container_of(work, struct wx, ptp_tx_work);
+ u32 tsynctxctl;
+ bool timeout;
+
+ /* we have to have a valid skb to poll for a timestamp */
+ if (!wx->ptp_tx_skb) {
+ wx_ptp_clear_tx_timestamp(wx);
+ return;
+ }
+
+ /* stop polling once we have a valid timestamp */
+ tsynctxctl = rd32ptp(wx, WX_TSC_1588_CTL);
+ if (tsynctxctl & WX_TSC_1588_CTL_VALID) {
+ wx_ptp_tx_hwtstamp(wx);
+ return;
+ }
+
+ timeout = time_is_before_jiffies(wx->ptp_tx_start + WX_PTP_TX_TIMEOUT);
+ /* check timeout last in case timestamp event just occurred */
+ if (timeout) {
+ wx_ptp_clear_tx_timestamp(wx);
+ wx->tx_hwtstamp_timeouts++;
+ dev_warn(&wx->pdev->dev, "clearing Tx Timestamp hang\n");
+ } else {
+ /* reschedule to keep checking until we timeout */
+ schedule_work(&wx->ptp_tx_work);
+ }
+}
+
+/**
+ * wx_ptp_set_timestamp_mode - setup the hardware for the requested mode
+ * @wx: the private board structure
+ * @config: the hwtstamp configuration requested
+ *
+ * Returns 0 on success, negative on failure
+ *
+ * Outgoing time stamping can be enabled and disabled. Play nice and
+ * disable it when requested, although it shouldn't cause any overhead
+ * when no packet needs it. At most one packet in the queue may be
+ * marked for time stamping, otherwise it would be impossible to tell
+ * for sure to which packet the hardware time stamp belongs.
+ *
+ * Incoming time stamping has to be configured via the hardware
+ * filters. Not all combinations are supported, in particular event
+ * type has to be specified. Matching the kind of event packet is
+ * not supported, with the exception of "all V2 events regardless of
+ * level 2 or 4".
+ *
+ * Since hardware always timestamps Path delay packets when timestamping V2
+ * packets, regardless of the type specified in the register, only use V2
+ * Event mode. This more accurately tells the user what the hardware is going
+ * to do anyways.
+ *
+ * Note: this may modify the hwtstamp configuration towards a more general
+ * mode, if required to support the specifically requested mode.
+ */
+static int wx_ptp_set_timestamp_mode(struct wx *wx,
+ struct hwtstamp_config *config)
+{
+ u32 tsync_tx_ctl = WX_TSC_1588_CTL_ENABLED;
+ u32 tsync_rx_ctl = WX_PSR_1588_CTL_ENABLED;
+ DECLARE_BITMAP(flags, WX_PF_FLAGS_NBITS);
+ u32 tsync_rx_mtrl = PTP_EV_PORT << 16;
+ bool is_l2 = false;
+ u32 regval;
+
+ memcpy(flags, wx->flags, sizeof(wx->flags));
+
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ tsync_tx_ctl = 0;
+ case HWTSTAMP_TX_ON:
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (config->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ tsync_rx_ctl = 0;
+ tsync_rx_mtrl = 0;
+ clear_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
+ clear_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_L4_V1;
+ tsync_rx_mtrl |= WX_PSR_1588_MSG_V1_SYNC;
+ set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
+ set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_L4_V1;
+ tsync_rx_mtrl |= WX_PSR_1588_MSG_V1_DELAY_REQ;
+ set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
+ set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_EVENT_V2;
+ is_l2 = true;
+ config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+ set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
+ set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
+ break;
+ default:
+ /* register RXMTRL must be set in order to do V1 packets,
+ * therefore it is not possible to time stamp both V1 Sync and
+ * Delay_Req messages unless hardware supports timestamping all
+ * packets => return error
+ */
+ clear_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, wx->flags);
+ clear_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, wx->flags);
+ config->rx_filter = HWTSTAMP_FILTER_NONE;
+ return -ERANGE;
+ }
+
+ /* define ethertype filter for timestamping L2 packets */
+ if (is_l2)
+ wr32(wx, WX_PSR_ETYPE_SWC(WX_PSR_ETYPE_SWC_FILTER_1588),
+ (WX_PSR_ETYPE_SWC_FILTER_EN | /* enable filter */
+ WX_PSR_ETYPE_SWC_1588 | /* enable timestamping */
+ ETH_P_1588)); /* 1588 eth protocol type */
+ else
+ wr32(wx, WX_PSR_ETYPE_SWC(WX_PSR_ETYPE_SWC_FILTER_1588), 0);
+
+ /* enable/disable TX */
+ regval = rd32ptp(wx, WX_TSC_1588_CTL);
+ regval &= ~WX_TSC_1588_CTL_ENABLED;
+ regval |= tsync_tx_ctl;
+ wr32ptp(wx, WX_TSC_1588_CTL, regval);
+
+ /* enable/disable RX */
+ regval = rd32(wx, WX_PSR_1588_CTL);
+ regval &= ~(WX_PSR_1588_CTL_ENABLED | WX_PSR_1588_CTL_TYPE_MASK);
+ regval |= tsync_rx_ctl;
+ wr32(wx, WX_PSR_1588_CTL, regval);
+
+ /* define which PTP packets are time stamped */
+ wr32(wx, WX_PSR_1588_MSG, tsync_rx_mtrl);
+
+ WX_WRITE_FLUSH(wx);
+
+ /* configure adapter flags only when HW is actually configured */
+ memcpy(wx->flags, flags, sizeof(wx->flags));
+
+ /* clear TX/RX timestamp state, just to be sure */
+ wx_ptp_clear_tx_timestamp(wx);
+ rd32(wx, WX_PSR_1588_STMPH);
+
+ return 0;
+}
+
+/**
+ * wx_ptp_read - read raw cycle counter (to be used by time counter)
+ * @hw_cc: the cyclecounter structure
+ *
+ * this function reads the cyclecounter registers and is called by the
+ * cyclecounter structure used to construct a ns counter from the
+ * arbitrary fixed point registers
+ */
+static u64 wx_ptp_read(const struct cyclecounter *hw_cc)
+{
+ struct wx *wx = container_of(hw_cc, struct wx, hw_cc);
+ u64 stamp = 0;
+
+ stamp |= (u64)rd32ptp(wx, WX_TSC_1588_SYSTIML);
+ stamp |= (u64)rd32ptp(wx, WX_TSC_1588_SYSTIMH) << 32;
+
+ return stamp;
+}
+
+static void wx_ptp_link_speed_adjust(struct wx *wx, u32 *shift, u32 *incval)
+{
+ if (wx->mac.type == wx_mac_em) {
+ *shift = WX_INCVAL_SHIFT_EM;
+ *incval = WX_INCVAL_EM;
+ return;
+ }
+
+ switch (wx->speed) {
+ case SPEED_10:
+ *shift = WX_INCVAL_SHIFT_10;
+ *incval = WX_INCVAL_10;
+ break;
+ case SPEED_100:
+ *shift = WX_INCVAL_SHIFT_100;
+ *incval = WX_INCVAL_100;
+ break;
+ case SPEED_1000:
+ *shift = WX_INCVAL_SHIFT_1GB;
+ *incval = WX_INCVAL_1GB;
+ break;
+ case SPEED_10000:
+ default:
+ *shift = WX_INCVAL_SHIFT_10GB;
+ *incval = WX_INCVAL_10GB;
+ break;
+ }
+}
+
+/**
+ * wx_ptp_start_cyclecounter - create the cycle counter from hw
+ * @wx: pointer to the wx structure
+ *
+ * This function should be called to set the proper values for the TIMINCA
+ * register and tell the cyclecounter structure what the tick rate of SYSTIME
+ * is. It does not directly modify SYSTIME registers or the timecounter
+ * structure. It should be called whenever a new TIMINCA value is necessary,
+ * such as during initialization or when the link speed changes.
+ */
+void wx_ptp_start_cyclecounter(struct wx *wx)
+{
+ u32 incval = 0, mask = 0;
+ struct cyclecounter cc;
+ unsigned long flags;
+
+ /* For some of the boards below this mask is technically incorrect.
+ * The timestamp mask overflows at approximately 61bits. However the
+ * particular hardware does not overflow on an even bitmask value.
+ * Instead, it overflows due to conversion of upper 32bits billions of
+ * cycles. Timecounters are not really intended for this purpose so
+ * they do not properly function if the overflow point isn't 2^N-1.
+ * However, the actual SYSTIME values in question take ~138 years to
+ * overflow. In practice this means they won't actually overflow. A
+ * proper fix to this problem would require modification of the
+ * timecounter delta calculations.
+ */
+ cc.mask = CLOCKSOURCE_MASK(64);
+ cc.mult = 1;
+ cc.shift = 0;
+
+ cc.read = wx_ptp_read;
+ wx_ptp_link_speed_adjust(wx, &cc.shift, &incval);
+
+ /* update the base incval used to calculate frequency adjustment */
+ WRITE_ONCE(wx->base_incval, incval);
+
+ mask = (wx->mac.type == wx_mac_em) ? 0x7FFFFFF : 0xFFFFFF;
+ incval &= mask;
+ if (wx->mac.type != wx_mac_em)
+ incval |= 2 << 24;
+ wr32ptp(wx, WX_TSC_1588_INC, incval);
+
+ smp_mb(); /* Force the above update. */
+
+ /* need lock to prevent incorrect read while modifying cyclecounter */
+ spin_lock_irqsave(&wx->tmreg_lock, flags);
+ memcpy(&wx->hw_cc, &cc, sizeof(wx->hw_cc));
+ spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+}
+EXPORT_SYMBOL(wx_ptp_start_cyclecounter);
+
+/**
+ * wx_ptp_reset
+ * @wx: the private board structure
+ *
+ * When the MAC resets, all of the hardware configuration for timesync is
+ * reset. This function should be called to re-enable the device for PTP,
+ * using the last known settings. However, we do lose the current clock time,
+ * so we fallback to resetting it based on the kernel's realtime clock.
+ *
+ * This function will maintain the hwtstamp_config settings, and it retriggers
+ * the SDP output if it's enabled.
+ */
+void wx_ptp_reset(struct wx *wx)
+{
+ unsigned long flags;
+
+ /* reset the hardware timestamping mode */
+ wx_ptp_set_timestamp_mode(wx, &wx->tstamp_config);
+ wx_ptp_start_cyclecounter(wx);
+
+ wr32ptp(wx, WX_TSC_1588_SYSTIML, 0);
+ wr32ptp(wx, WX_TSC_1588_SYSTIMH, 0);
+ WX_WRITE_FLUSH(wx);
+
+ spin_lock_irqsave(&wx->tmreg_lock, flags);
+ timecounter_init(&wx->hw_tc, &wx->hw_cc,
+ ktime_to_ns(ktime_get_real()));
+ spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+}
+EXPORT_SYMBOL(wx_ptp_reset);
+
+/**
+ * wx_ptp_init
+ * @wx: the private board structure
+ *
+ * This function performs the required steps for enabling ptp
+ * support. If ptp support has already been loaded it simply calls the
+ * cyclecounter init routine and exits.
+ */
+void wx_ptp_init(struct wx *wx)
+{
+ /* Initialize the spin lock first, since the user might call the clock
+ * functions any time after we've initialized the ptp clock device.
+ */
+ spin_lock_init(&wx->tmreg_lock);
+
+ /* obtain a ptp clock device, or re-use an existing device */
+ if (wx_ptp_create_clock(wx))
+ return;
+
+ /* we have a clock, so we can initialize work for timestamps now */
+ INIT_WORK(&wx->ptp_tx_work, wx_ptp_tx_hwtstamp_work);
+
+ wx->tx_hwtstamp_timeouts = 0;
+ wx->tx_hwtstamp_skipped = 0;
+ wx->rx_hwtstamp_cleared = 0;
+ /* reset the ptp related hardware bits */
+ wx_ptp_reset(wx);
+
+ /* enter the WX_STATE_PTP_RUNNING state */
+ set_bit(WX_STATE_PTP_RUNNING, wx->state);
+}
+EXPORT_SYMBOL(wx_ptp_init);
+
+/**
+ * wx_ptp_suspend - stop ptp work items
+ * @wx: pointer to wx struct
+ *
+ * This function suspends ptp activity, and prevents more work from being
+ * generated, but does not destroy the clock device.
+ */
+void wx_ptp_suspend(struct wx *wx)
+{
+ /* leave the TXGBE_PTP_RUNNING STATE */
+ if (!test_and_clear_bit(WX_STATE_PTP_RUNNING, wx->state))
+ return;
+
+ cancel_work_sync(&wx->ptp_tx_work);
+ wx_ptp_clear_tx_timestamp(wx);
+}
+EXPORT_SYMBOL(wx_ptp_suspend);
+
+/**
+ * wx_ptp_stop - destroy the ptp_clock device
+ * @wx: pointer to wx struct
+ *
+ * Completely destroy the ptp_clock device, and disable all PTP related
+ * features. Intended to be run when the device is being closed.
+ */
+void wx_ptp_stop(struct wx *wx)
+{
+ /* first, suspend ptp activity */
+ wx_ptp_suspend(wx);
+
+ /* now destroy the ptp clock device */
+ if (wx->ptp_clock) {
+ ptp_clock_unregister(wx->ptp_clock);
+ wx->ptp_clock = NULL;
+ dev_info(&wx->pdev->dev, "removed PHC on %s\n", wx->netdev->name);
+ }
+}
+EXPORT_SYMBOL(wx_ptp_stop);
+
+/**
+ * wx_ptp_rx_hwtstamp - utility function which checks for RX time stamp
+ * @wx: pointer to wx struct
+ * @skb: particular skb to send timestamp with
+ *
+ * if the timestamp is valid, we convert it into the timecounter ns
+ * value, then store that result into the shhwtstamps structure which
+ * is passed up the network stack
+ */
+void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb)
+{
+ u64 regval = 0;
+ u32 tsyncrxctl;
+
+ /* Read the tsyncrxctl register afterwards in order to prevent taking an
+ * I/O hit on every packet.
+ */
+ tsyncrxctl = rd32(wx, WX_PSR_1588_CTL);
+ if (!(tsyncrxctl & WX_PSR_1588_CTL_VALID))
+ return;
+
+ regval |= (u64)rd32(wx, WX_PSR_1588_STMPL);
+ regval |= (u64)rd32(wx, WX_PSR_1588_STMPH) << 32;
+
+ wx_ptp_convert_to_hwtstamp(wx, skb_hwtstamps(skb), regval);
+}
+
+/**
+ * wx_ptp_get_ts_config - get current hardware timestamping configuration
+ * @wx: pointer to wx structure
+ * @ifr: ioctl data
+ *
+ * This function returns the current timestamping settings. Rather than
+ * attempt to deconstruct registers to fill in the values, simply keep a copy
+ * of the old settings around, and return a copy when requested.
+ */
+int wx_ptp_get_ts_config(struct wx *wx, struct ifreq *ifr)
+{
+ struct hwtstamp_config *config = &wx->tstamp_config;
+
+ return copy_to_user(ifr->ifr_data, config,
+ sizeof(*config)) ? -EFAULT : 0;
+}
+
+/**
+ * wx_ptp_set_ts_config - user entry point for timestamp mode
+ * @wx: pointer to wx struct
+ * @ifr: ioctl data
+ *
+ * Set hardware to requested mode. If unsupported, return an error with no
+ * changes. Otherwise, store the mode for future reference.
+ */
+int wx_ptp_set_ts_config(struct wx *wx, struct ifreq *ifr)
+{
+ struct hwtstamp_config config;
+ int err;
+
+ if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+ return -EFAULT;
+
+ err = wx_ptp_set_timestamp_mode(wx, &config);
+ if (err)
+ return err;
+
+ /* save these settings for future reference */
+ memcpy(&wx->tstamp_config, &config,
+ sizeof(wx->tstamp_config));
+
+ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+ -EFAULT : 0;
+}
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
new file mode 100644
index 000000000000..0fdc4f808636
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2019 - 2025 Beijing WangXun Technology Co., Ltd. */
+
+#ifndef _WX_PTP_H_
+#define _WX_PTP_H_
+
+void wx_ptp_start_cyclecounter(struct wx *wx);
+void wx_ptp_reset(struct wx *wx);
+void wx_ptp_init(struct wx *wx);
+void wx_ptp_suspend(struct wx *wx);
+void wx_ptp_stop(struct wx *wx);
+void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb);
+int wx_ptp_get_ts_config(struct wx *wx, struct ifreq *ifr);
+int wx_ptp_set_ts_config(struct wx *wx, struct ifreq *ifr);
+
+#endif /* _WX_PTP_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index b54bffda027b..4fa817f55c0a 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -4,6 +4,8 @@
#ifndef _WX_TYPE_H_
#define _WX_TYPE_H_
+#include <linux/ptp_clock_kernel.h>
+#include <linux/timecounter.h>
#include <linux/bitfield.h>
#include <linux/netdevice.h>
#include <linux/if_vlan.h>
@@ -180,6 +182,23 @@
#define WX_PSR_VLAN_CTL 0x15088
#define WX_PSR_VLAN_CTL_CFIEN BIT(29) /* bit 29 */
#define WX_PSR_VLAN_CTL_VFE BIT(30) /* bit 30 */
+/* EType Queue Filter */
+#define WX_PSR_ETYPE_SWC(_i) (0x15128 + ((_i) * 4))
+#define WX_PSR_ETYPE_SWC_FILTER_1588 3
+#define WX_PSR_ETYPE_SWC_FILTER_EN BIT(31)
+#define WX_PSR_ETYPE_SWC_1588 BIT(30)
+/* 1588 */
+#define WX_PSR_1588_MSG 0x15120
+#define WX_PSR_1588_MSG_V1_SYNC FIELD_PREP(GENMASK(7, 0), 0)
+#define WX_PSR_1588_MSG_V1_DELAY_REQ FIELD_PREP(GENMASK(7, 0), 1)
+#define WX_PSR_1588_STMPL 0x151E8
+#define WX_PSR_1588_STMPH 0x151A4
+#define WX_PSR_1588_CTL 0x15188
+#define WX_PSR_1588_CTL_ENABLED BIT(4)
+#define WX_PSR_1588_CTL_TYPE_MASK GENMASK(3, 1)
+#define WX_PSR_1588_CTL_TYPE_L4_V1 FIELD_PREP(GENMASK(3, 1), 1)
+#define WX_PSR_1588_CTL_TYPE_EVENT_V2 FIELD_PREP(GENMASK(3, 1), 5)
+#define WX_PSR_1588_CTL_VALID BIT(0)
/* mcasst/ucast overflow tbl */
#define WX_PSR_MC_TBL(_i) (0x15200 + ((_i) * 4))
#define WX_PSR_UC_TBL(_i) (0x15400 + ((_i) * 4))
@@ -253,6 +272,15 @@
#define WX_TSC_ST_SECTX_RDY BIT(0)
#define WX_TSC_BUF_AE 0x1D00C
#define WX_TSC_BUF_AE_THR GENMASK(9, 0)
+/* 1588 */
+#define WX_TSC_1588_CTL 0x11F00
+#define WX_TSC_1588_CTL_ENABLED BIT(4)
+#define WX_TSC_1588_CTL_VALID BIT(0)
+#define WX_TSC_1588_STMPL 0x11F04
+#define WX_TSC_1588_STMPH 0x11F08
+#define WX_TSC_1588_SYSTIML 0x11F0C
+#define WX_TSC_1588_SYSTIMH 0x11F10
+#define WX_TSC_1588_INC 0x11F14
/************************************** MNG ********************************/
#define WX_MNG_SWFW_SYNC 0x1E008
@@ -460,6 +488,7 @@ enum WX_MSCA_CMD_value {
#define WX_RXD_STAT_L4CS BIT(7) /* L4 xsum calculated */
#define WX_RXD_STAT_IPCS BIT(8) /* IP xsum calculated */
#define WX_RXD_STAT_OUTERIPCS BIT(10) /* Cloud IP xsum calculated*/
+#define WX_RXD_STAT_TS BIT(14) /* IEEE1588 Time Stamp */
#define WX_RXD_ERR_OUTERIPER BIT(26) /* CRC IP Header error */
#define WX_RXD_ERR_RXE BIT(29) /* Any MAC Error */
@@ -863,6 +892,7 @@ struct wx_tx_context_desc {
*/
struct wx_tx_buffer {
union wx_tx_desc *next_to_watch;
+ unsigned long time_stamp;
struct sk_buff *skb;
unsigned int bytecount;
unsigned short gso_segs;
@@ -924,6 +954,7 @@ struct wx_ring {
unsigned int size; /* length in bytes */
u16 count; /* amount of descriptors */
+ unsigned long last_rx_timestamp;
u8 queue_index; /* needed for multiqueue queue management */
u8 reg_idx; /* holds the special value that gets
@@ -1026,6 +1057,8 @@ struct wx_hw_stats {
enum wx_state {
WX_STATE_RESETTING,
+ WX_STATE_PTP_RUNNING,
+ WX_STATE_PTP_TX_IN_PROGRESS,
WX_STATE_NBITS, /* must be last */
};
@@ -1033,6 +1066,8 @@ enum wx_pf_flags {
WX_FLAG_FDIR_CAPABLE,
WX_FLAG_FDIR_HASH,
WX_FLAG_FDIR_PERFECT,
+ WX_FLAG_RX_HWTSTAMP_ENABLED,
+ WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
WX_PF_FLAGS_NBITS /* must be last */
};
@@ -1133,6 +1168,20 @@ struct wx {
void (*atr)(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype);
void (*configure_fdir)(struct wx *wx);
void (*do_reset)(struct net_device *netdev);
+
+ u32 base_incval;
+ u32 tx_hwtstamp_timeouts;
+ u32 tx_hwtstamp_skipped;
+ u32 rx_hwtstamp_cleared;
+ unsigned long ptp_tx_start;
+ spinlock_t tmreg_lock; /* spinlock for ptp */
+ struct cyclecounter hw_cc;
+ struct timecounter hw_tc;
+ struct ptp_clock *ptp_clock;
+ struct ptp_clock_info ptp_caps;
+ struct hwtstamp_config tstamp_config;
+ struct work_struct ptp_tx_work;
+ struct sk_buff *ptp_tx_skb;
};
#define WX_INTR_ALL (~0ULL)
@@ -1177,6 +1226,24 @@ rd64(struct wx *wx, u32 reg)
return (lsb | msb << 32);
}
+static inline u32
+rd32ptp(struct wx *wx, u32 reg)
+{
+ if (wx->mac.type == wx_mac_em)
+ return rd32(wx, reg);
+
+ return rd32(wx, reg + 0xB500);
+}
+
+static inline void
+wr32ptp(struct wx *wx, u32 reg, u32 value)
+{
+ if (wx->mac.type == wx_mac_em)
+ return wr32(wx, reg, value);
+
+ return wr32(wx, reg + 0xB500, value);
+}
+
/* On some domestic CPU platforms, sometimes IO is not synchronized with
* flushing memory, here use readl() to flush PCI read and write.
*/
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index 53aeae2f884b..655433bd5545 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -14,6 +14,7 @@
#include "../libwx/wx_type.h"
#include "../libwx/wx_hw.h"
#include "../libwx/wx_lib.h"
+#include "../libwx/wx_ptp.h"
#include "ngbe_type.h"
#include "ngbe_mdio.h"
#include "ngbe_hw.h"
@@ -317,6 +318,8 @@ void ngbe_down(struct wx *wx)
{
phylink_stop(wx->phylink);
ngbe_disable_device(wx);
+ if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
+ wx_ptp_reset(wx);
wx_clean_all_tx_rings(wx);
wx_clean_all_rx_rings(wx);
}
@@ -379,6 +382,8 @@ static int ngbe_open(struct net_device *netdev)
if (err)
goto err_dis_phy;
+ wx_ptp_init(wx);
+
ngbe_up(wx);
return 0;
@@ -407,6 +412,7 @@ static int ngbe_close(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
+ wx_ptp_stop(wx);
ngbe_down(wx);
wx_free_irq(wx);
wx_free_isb_resources(wx);
@@ -507,6 +513,7 @@ static const struct net_device_ops ngbe_netdev_ops = {
.ndo_get_stats64 = wx_get_stats64,
.ndo_vlan_rx_add_vid = wx_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = wx_vlan_rx_kill_vid,
+ .ndo_eth_ioctl = wx_ioctl,
};
/**
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c
index a5e9b779c44d..7282ca53d834 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c
@@ -7,6 +7,7 @@
#include <linux/phy.h>
#include "../libwx/wx_type.h"
+#include "../libwx/wx_ptp.h"
#include "../libwx/wx_hw.h"
#include "ngbe_type.h"
#include "ngbe_mdio.h"
@@ -64,6 +65,11 @@ static void ngbe_mac_config(struct phylink_config *config, unsigned int mode,
static void ngbe_mac_link_down(struct phylink_config *config,
unsigned int mode, phy_interface_t interface)
{
+ struct wx *wx = phylink_to_wx(config);
+
+ wx->speed = SPEED_UNKNOWN;
+ if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
+ wx_ptp_start_cyclecounter(wx);
}
static void ngbe_mac_link_up(struct phylink_config *config,
@@ -103,6 +109,10 @@ static void ngbe_mac_link_up(struct phylink_config *config,
wr32(wx, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR);
reg = rd32(wx, WX_MAC_WDG_TIMEOUT);
wr32(wx, WX_MAC_WDG_TIMEOUT, reg);
+
+ wx->speed = speed;
+ if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
+ wx_ptp_start_cyclecounter(wx);
}
static const struct phylink_mac_ops ngbe_mac_ops = {
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index f77450268036..c0e800d0f66b 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -13,6 +13,7 @@
#include "../libwx/wx_type.h"
#include "../libwx/wx_lib.h"
+#include "../libwx/wx_ptp.h"
#include "../libwx/wx_hw.h"
#include "txgbe_type.h"
#include "txgbe_hw.h"
@@ -116,6 +117,9 @@ static void txgbe_reset(struct wx *wx)
memcpy(old_addr, &wx->mac_table[0].addr, netdev->addr_len);
wx_flush_sw_mac_table(wx);
wx_mac_set_default_filter(wx, old_addr);
+
+ if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
+ wx_ptp_reset(wx);
}
static void txgbe_disable_device(struct wx *wx)
@@ -176,6 +180,7 @@ void txgbe_down(struct wx *wx)
void txgbe_up(struct wx *wx)
{
wx_configure(wx);
+ wx_ptp_init(wx);
txgbe_up_complete(wx);
}
@@ -321,6 +326,8 @@ static int txgbe_open(struct net_device *netdev)
if (err)
goto err_free_irq;
+ wx_ptp_init(wx);
+
txgbe_up_complete(wx);
return 0;
@@ -344,6 +351,7 @@ static int txgbe_open(struct net_device *netdev)
*/
static void txgbe_close_suspend(struct wx *wx)
{
+ wx_ptp_suspend(wx);
txgbe_disable_device(wx);
wx_free_resources(wx);
}
@@ -363,6 +371,7 @@ static int txgbe_close(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
+ wx_ptp_stop(wx);
txgbe_down(wx);
wx_free_irq(wx);
wx_free_resources(wx);
@@ -479,6 +488,7 @@ static const struct net_device_ops txgbe_netdev_ops = {
.ndo_get_stats64 = wx_get_stats64,
.ndo_vlan_rx_add_vid = wx_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = wx_vlan_rx_kill_vid,
+ .ndo_eth_ioctl = wx_ioctl,
};
/**
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index 1ae68f94dd49..f6f3c94de97a 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -15,6 +15,7 @@
#include "../libwx/wx_type.h"
#include "../libwx/wx_lib.h"
+#include "../libwx/wx_ptp.h"
#include "../libwx/wx_hw.h"
#include "txgbe_type.h"
#include "txgbe_phy.h"
@@ -179,6 +180,10 @@ static void txgbe_mac_link_down(struct phylink_config *config,
struct wx *wx = phylink_to_wx(config);
wr32m(wx, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0);
+
+ wx->speed = SPEED_UNKNOWN;
+ if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
+ wx_ptp_start_cyclecounter(wx);
}
static void txgbe_mac_link_up(struct phylink_config *config,
@@ -215,6 +220,10 @@ static void txgbe_mac_link_up(struct phylink_config *config,
wr32(wx, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR);
wdg = rd32(wx, WX_MAC_WDG_TIMEOUT);
wr32(wx, WX_MAC_WDG_TIMEOUT, wdg);
+
+ wx->speed = speed;
+ if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
+ wx_ptp_start_cyclecounter(wx);
}
static int txgbe_mac_prepare(struct phylink_config *config, unsigned int mode,
--
2.27.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH net-next 2/4] net: wangxun: Implement get_ts_info
2025-01-02 10:30 [PATCH net-next 0/4] Support PTP clock for Wangxun NICs Jiawen Wu
2025-01-02 10:30 ` [PATCH net-next 1/4] net: wangxun: Add support for PTP clock Jiawen Wu
@ 2025-01-02 10:30 ` Jiawen Wu
2025-01-02 17:44 ` Vadim Fedorenko
2025-01-02 10:30 ` [PATCH net-next 3/4] net: wangxun: Add watchdog task for PTP clock Jiawen Wu
2025-01-02 10:30 ` [PATCH net-next 4/4] net: ngbe: Add support for 1PPS and TOD Jiawen Wu
3 siblings, 1 reply; 20+ messages in thread
From: Jiawen Wu @ 2025-01-02 10:30 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, jacob.e.keller, netdev
Cc: mengyuanlou, Jiawen Wu
Implement the function get_ts_info in ethtool_ops which is needed to get
the HW capabilities for timestamping.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 37 +++++++++++++++++++
.../net/ethernet/wangxun/libwx/wx_ethtool.h | 2 +
.../net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 1 +
.../ethernet/wangxun/txgbe/txgbe_ethtool.c | 1 +
4 files changed, 41 insertions(+)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index c4b3b00b0926..2a228faf0c26 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -455,3 +455,40 @@ void wx_set_msglevel(struct net_device *netdev, u32 data)
wx->msg_enable = data;
}
EXPORT_SYMBOL(wx_set_msglevel);
+
+int wx_get_ts_info(struct net_device *dev,
+ struct kernel_ethtool_ts_info *info)
+{
+ struct wx *wx = netdev_priv(dev);
+
+ info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
+ BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
+ BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_SYNC) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
+
+ info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
+ SOF_TIMESTAMPING_RX_SOFTWARE |
+ SOF_TIMESTAMPING_SOFTWARE |
+ SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ if (wx->ptp_clock)
+ info->phc_index = ptp_clock_index(wx->ptp_clock);
+ else
+ info->phc_index = -1;
+
+ info->tx_types = BIT(HWTSTAMP_TX_OFF) |
+ BIT(HWTSTAMP_TX_ON);
+
+ return 0;
+}
+EXPORT_SYMBOL(wx_get_ts_info);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
index 600c3b597d1a..7c3630e3e187 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
@@ -40,4 +40,6 @@ int wx_set_channels(struct net_device *dev,
struct ethtool_channels *ch);
u32 wx_get_msglevel(struct net_device *netdev);
void wx_set_msglevel(struct net_device *netdev, u32 data);
+int wx_get_ts_info(struct net_device *dev,
+ struct kernel_ethtool_ts_info *info);
#endif /* _WX_ETHTOOL_H_ */
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
index e868f7ef4920..9270cf8e5bc7 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
@@ -138,6 +138,7 @@ static const struct ethtool_ops ngbe_ethtool_ops = {
.set_channels = ngbe_set_channels,
.get_msglevel = wx_get_msglevel,
.set_msglevel = wx_set_msglevel,
+ .get_ts_info = wx_get_ts_info,
};
void ngbe_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
index d98314b26c19..9f8df5b3aee0 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
@@ -529,6 +529,7 @@ static const struct ethtool_ops txgbe_ethtool_ops = {
.set_rxnfc = txgbe_set_rxnfc,
.get_msglevel = wx_get_msglevel,
.set_msglevel = wx_set_msglevel,
+ .get_ts_info = wx_get_ts_info,
};
void txgbe_set_ethtool_ops(struct net_device *netdev)
--
2.27.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH net-next 3/4] net: wangxun: Add watchdog task for PTP clock
2025-01-02 10:30 [PATCH net-next 0/4] Support PTP clock for Wangxun NICs Jiawen Wu
2025-01-02 10:30 ` [PATCH net-next 1/4] net: wangxun: Add support for PTP clock Jiawen Wu
2025-01-02 10:30 ` [PATCH net-next 2/4] net: wangxun: Implement get_ts_info Jiawen Wu
@ 2025-01-02 10:30 ` Jiawen Wu
2025-01-02 17:53 ` Vadim Fedorenko
2025-01-02 10:30 ` [PATCH net-next 4/4] net: ngbe: Add support for 1PPS and TOD Jiawen Wu
3 siblings, 1 reply; 20+ messages in thread
From: Jiawen Wu @ 2025-01-02 10:30 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, jacob.e.keller, netdev
Cc: mengyuanlou, Jiawen Wu
Implement watchdog task to detect SYSTIME overflow and error cases of
Rx/Tx timestamp.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 57 +++++++++++
drivers/net/ethernet/wangxun/libwx/wx_lib.h | 2 +
drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 99 +++++++++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 3 +
drivers/net/ethernet/wangxun/libwx/wx_type.h | 6 ++
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 8 ++
drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c | 1 +
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 7 ++
.../net/ethernet/wangxun/txgbe/txgbe_phy.c | 1 +
9 files changed, 184 insertions(+)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index be1dcc278612..c6db69406f15 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -2,6 +2,7 @@
/* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */
#include <linux/etherdevice.h>
+#include <linux/workqueue.h>
#include <net/ip6_checksum.h>
#include <net/page_pool/helpers.h>
#include <net/inet_ecn.h>
@@ -2909,6 +2910,62 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count,
}
EXPORT_SYMBOL(wx_set_ring);
+void wx_service_event_schedule(struct wx *wx)
+{
+ if (netif_carrier_ok(wx->netdev) &&
+ !test_and_set_bit(WX_STATE_SERVICE_SCHED, wx->state))
+ queue_work(system_power_efficient_wq, &wx->service_task);
+}
+EXPORT_SYMBOL(wx_service_event_schedule);
+
+static void wx_service_event_complete(struct wx *wx)
+{
+ if (WARN_ON(!test_bit(WX_STATE_SERVICE_SCHED, wx->state)))
+ return;
+
+ /* flush memory to make sure state is correct before next watchdog */
+ smp_mb__before_atomic();
+ clear_bit(WX_STATE_SERVICE_SCHED, wx->state);
+}
+
+static void wx_service_timer(struct timer_list *t)
+{
+ struct wx *wx = from_timer(wx, t, service_timer);
+ unsigned long next_event_offset = HZ * 2;
+
+ /* Reset the timer */
+ mod_timer(&wx->service_timer, next_event_offset + jiffies);
+
+ wx_service_event_schedule(wx);
+}
+
+/**
+ * wx_service_task - manages and runs subtasks
+ * @work: pointer to work_struct containing our data
+ **/
+static void wx_service_task(struct work_struct *work)
+{
+ struct wx *wx = container_of(work, struct wx, service_task);
+
+ if (test_bit(WX_STATE_PTP_RUNNING, wx->state)) {
+ wx_ptp_overflow_check(wx);
+ if (unlikely(test_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
+ wx->flags)))
+ wx_ptp_rx_hang(wx);
+ wx_ptp_tx_hang(wx);
+ }
+
+ wx_service_event_complete(wx);
+}
+
+void wx_init_service(struct wx *wx)
+{
+ timer_setup(&wx->service_timer, wx_service_timer, 0);
+ INIT_WORK(&wx->service_task, wx_service_task);
+ clear_bit(WX_STATE_SERVICE_SCHED, wx->state);
+}
+EXPORT_SYMBOL(wx_init_service);
+
int wx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
struct wx *wx = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
index d67a33216811..77c4ea4baabf 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
@@ -35,6 +35,8 @@ netdev_features_t wx_fix_features(struct net_device *netdev,
netdev_features_t features);
void wx_set_ring(struct wx *wx, u32 new_tx_count,
u32 new_rx_count, struct wx_ring *temp_ring);
+void wx_service_event_schedule(struct wx *wx);
+void wx_init_service(struct wx *wx);
int wx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
#endif /* _NGBE_LIB_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
index d78f99cf4a10..2047667ce0fe 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
@@ -571,6 +571,8 @@ void wx_ptp_reset(struct wx *wx)
timecounter_init(&wx->hw_tc, &wx->hw_cc,
ktime_to_ns(ktime_get_real()));
spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+
+ wx->last_overflow_check = jiffies;
}
EXPORT_SYMBOL(wx_ptp_reset);
@@ -717,3 +719,100 @@ int wx_ptp_set_ts_config(struct wx *wx, struct ifreq *ifr)
return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
-EFAULT : 0;
}
+
+/**
+ * wx_ptp_overflow_check - watchdog task to detect SYSTIME overflow
+ * @wx: pointer to wx struct
+ *
+ * this watchdog task periodically reads the timecounter
+ * in order to prevent missing when the system time registers wrap
+ * around. This needs to be run approximately twice a minute for the fastest
+ * overflowing hardware. We run it for all hardware since it shouldn't have a
+ * large impact.
+ */
+void wx_ptp_overflow_check(struct wx *wx)
+{
+ bool timeout = time_is_before_jiffies(wx->last_overflow_check +
+ WX_OVERFLOW_PERIOD);
+ unsigned long flags;
+
+ if (timeout) {
+ /* Update the timecounter */
+ spin_lock_irqsave(&wx->tmreg_lock, flags);
+ timecounter_read(&wx->hw_tc);
+ spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+
+ wx->last_overflow_check = jiffies;
+ }
+}
+
+/**
+ * wx_ptp_rx_hang - detect error case when Rx timestamp registers latched
+ * @wx: pointer to wx struct
+ *
+ * this watchdog task is scheduled to detect error case where hardware has
+ * dropped an Rx packet that was timestamped when the ring is full. The
+ * particular error is rare but leaves the device in a state unable to
+ * timestamp any future packets.
+ */
+void wx_ptp_rx_hang(struct wx *wx)
+{
+ struct wx_ring *rx_ring;
+ unsigned long rx_event;
+ u32 tsyncrxctl;
+ int n;
+
+ tsyncrxctl = rd32(wx, WX_PSR_1588_CTL);
+
+ /* if we don't have a valid timestamp in the registers, just update the
+ * timeout counter and exit
+ */
+ if (!(tsyncrxctl & WX_PSR_1588_CTL_VALID)) {
+ wx->last_rx_ptp_check = jiffies;
+ return;
+ }
+
+ /* determine the most recent watchdog or rx_timestamp event */
+ rx_event = wx->last_rx_ptp_check;
+ for (n = 0; n < wx->num_rx_queues; n++) {
+ rx_ring = wx->rx_ring[n];
+ if (time_after(rx_ring->last_rx_timestamp, rx_event))
+ rx_event = rx_ring->last_rx_timestamp;
+ }
+
+ /* only need to read the high RXSTMP register to clear the lock */
+ if (time_is_before_jiffies(rx_event + 5 * HZ)) {
+ rd32(wx, WX_PSR_1588_STMPH);
+ wx->last_rx_ptp_check = jiffies;
+
+ wx->rx_hwtstamp_cleared++;
+ dev_warn(&wx->pdev->dev, "clearing RX Timestamp hang");
+ }
+}
+
+/**
+ * wx_ptp_tx_hang - detect error case where Tx timestamp never finishes
+ * @wx: private network wx structure
+ */
+void wx_ptp_tx_hang(struct wx *wx)
+{
+ bool timeout = time_is_before_jiffies(wx->ptp_tx_start +
+ WX_PTP_TX_TIMEOUT);
+
+ if (!wx->ptp_tx_skb)
+ return;
+
+ if (!test_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state))
+ return;
+
+ /* If we haven't received a timestamp within the timeout, it is
+ * reasonable to assume that it will never occur, so we can unlock the
+ * timestamp bit when this occurs.
+ */
+ if (timeout) {
+ cancel_work_sync(&wx->ptp_tx_work);
+ wx_ptp_clear_tx_timestamp(wx);
+ wx->tx_hwtstamp_timeouts++;
+ dev_warn(&wx->pdev->dev, "clearing Tx timestamp hang\n");
+ }
+}
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
index 0fdc4f808636..4a7a9cda2a35 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
@@ -9,6 +9,9 @@ void wx_ptp_reset(struct wx *wx);
void wx_ptp_init(struct wx *wx);
void wx_ptp_suspend(struct wx *wx);
void wx_ptp_stop(struct wx *wx);
+void wx_ptp_overflow_check(struct wx *wx);
+void wx_ptp_rx_hang(struct wx *wx);
+void wx_ptp_tx_hang(struct wx *wx);
void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb);
int wx_ptp_get_ts_config(struct wx *wx, struct ifreq *ifr);
int wx_ptp_set_ts_config(struct wx *wx, struct ifreq *ifr);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index 4fa817f55c0a..706ee8dd99c1 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -1057,6 +1057,7 @@ struct wx_hw_stats {
enum wx_state {
WX_STATE_RESETTING,
+ WX_STATE_SERVICE_SCHED,
WX_STATE_PTP_RUNNING,
WX_STATE_PTP_TX_IN_PROGRESS,
WX_STATE_NBITS, /* must be last */
@@ -1169,10 +1170,15 @@ struct wx {
void (*configure_fdir)(struct wx *wx);
void (*do_reset)(struct net_device *netdev);
+ struct timer_list service_timer;
+ struct work_struct service_task;
+
u32 base_incval;
u32 tx_hwtstamp_timeouts;
u32 tx_hwtstamp_skipped;
u32 rx_hwtstamp_cleared;
+ unsigned long last_overflow_check;
+ unsigned long last_rx_ptp_check;
unsigned long ptp_tx_start;
spinlock_t tmreg_lock; /* spinlock for ptp */
struct cyclecounter hw_cc;
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index 655433bd5545..83bc0100fb3e 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -304,6 +304,9 @@ static void ngbe_disable_device(struct wx *wx)
if (wx->gpio_ctrl)
ngbe_sfp_modules_txrx_powerctl(wx, false);
wx_irq_disable(wx);
+
+ del_timer_sync(&wx->service_timer);
+
/* disable transmits in the hardware now that interrupts are off */
for (i = 0; i < wx->num_tx_queues; i++) {
u8 reg_idx = wx->tx_ring[i]->reg_idx;
@@ -342,6 +345,7 @@ void ngbe_up(struct wx *wx)
ngbe_sfp_modules_txrx_powerctl(wx, true);
phylink_start(wx->phylink);
+ mod_timer(&wx->service_timer, jiffies);
}
/**
@@ -685,6 +689,8 @@ static int ngbe_probe(struct pci_dev *pdev,
eth_hw_addr_set(netdev, wx->mac.perm_addr);
wx_mac_set_default_filter(wx, wx->mac.perm_addr);
+ wx_init_service(wx);
+
err = wx_init_interrupt_scheme(wx);
if (err)
goto err_free_mac_table;
@@ -731,6 +737,8 @@ static void ngbe_remove(struct pci_dev *pdev)
struct wx *wx = pci_get_drvdata(pdev);
struct net_device *netdev;
+ cancel_work_sync(&wx->service_task);
+
netdev = wx->netdev;
unregister_netdev(netdev);
phylink_destroy(wx->phylink);
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c
index 7282ca53d834..623c615361a9 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c
@@ -111,6 +111,7 @@ static void ngbe_mac_link_up(struct phylink_config *config,
wr32(wx, WX_MAC_WDG_TIMEOUT, reg);
wx->speed = speed;
+ wx->last_rx_ptp_check = jiffies;
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
wx_ptp_start_cyclecounter(wx);
}
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index c0e800d0f66b..fccfd02b0e54 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -100,6 +100,7 @@ static void txgbe_up_complete(struct wx *wx)
/* enable transmits */
netif_tx_start_all_queues(netdev);
+ mod_timer(&wx->service_timer, jiffies);
}
static void txgbe_reset(struct wx *wx)
@@ -142,6 +143,8 @@ static void txgbe_disable_device(struct wx *wx)
wx_irq_disable(wx);
wx_napi_disable_all(wx);
+ del_timer_sync(&wx->service_timer);
+
if (wx->bus.func < 2)
wr32m(wx, TXGBE_MIS_PRB_CTL, TXGBE_MIS_PRB_CTL_LAN_UP(wx->bus.func), 0);
else
@@ -631,6 +634,8 @@ static int txgbe_probe(struct pci_dev *pdev,
eth_hw_addr_set(netdev, wx->mac.perm_addr);
wx_mac_set_default_filter(wx, wx->mac.perm_addr);
+ wx_init_service(wx);
+
err = wx_init_interrupt_scheme(wx);
if (err)
goto err_free_mac_table;
@@ -751,6 +756,8 @@ static void txgbe_remove(struct pci_dev *pdev)
struct txgbe *txgbe = wx->priv;
struct net_device *netdev;
+ cancel_work_sync(&wx->service_task);
+
netdev = wx->netdev;
unregister_netdev(netdev);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index f6f3c94de97a..2cafb723a4d9 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -222,6 +222,7 @@ static void txgbe_mac_link_up(struct phylink_config *config,
wr32(wx, WX_MAC_WDG_TIMEOUT, wdg);
wx->speed = speed;
+ wx->last_rx_ptp_check = jiffies;
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
wx_ptp_start_cyclecounter(wx);
}
--
2.27.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH net-next 4/4] net: ngbe: Add support for 1PPS and TOD
2025-01-02 10:30 [PATCH net-next 0/4] Support PTP clock for Wangxun NICs Jiawen Wu
` (2 preceding siblings ...)
2025-01-02 10:30 ` [PATCH net-next 3/4] net: wangxun: Add watchdog task for PTP clock Jiawen Wu
@ 2025-01-02 10:30 ` Jiawen Wu
3 siblings, 0 replies; 20+ messages in thread
From: Jiawen Wu @ 2025-01-02 10:30 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, jacob.e.keller, netdev
Cc: mengyuanlou, Jiawen Wu
Implement support for generating a 1pps output signal on SDP0.
And support custom firmware to output TOD.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 19 ++
drivers/net/ethernet/wangxun/libwx/wx_hw.h | 1 +
drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 199 +++++++++++++++++-
drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 1 +
drivers/net/ethernet/wangxun/libwx/wx_type.h | 34 +++
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 12 +-
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 5 +
7 files changed, 268 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index 1bf9c38e4125..6ba69e41faa6 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -395,6 +395,25 @@ int wx_host_interface_command(struct wx *wx, u32 *buffer,
}
EXPORT_SYMBOL(wx_host_interface_command);
+int wx_set_pps(struct wx *wx, bool enable, u64 nsec, u64 cycles)
+{
+ struct wx_hic_set_pps pps_cmd;
+
+ pps_cmd.hdr.cmd = FW_PPS_SET_CMD;
+ pps_cmd.hdr.buf_len = FW_PPS_SET_LEN;
+ pps_cmd.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
+ pps_cmd.lan_id = wx->bus.func;
+ pps_cmd.enable = (u8)enable;
+ pps_cmd.nsec = nsec;
+ pps_cmd.cycles = cycles;
+ pps_cmd.hdr.checksum = FW_DEFAULT_CHECKSUM;
+
+ return wx_host_interface_command(wx, (u32 *)&pps_cmd,
+ sizeof(pps_cmd),
+ WX_HI_COMMAND_TIMEOUT,
+ false);
+}
+
/**
* wx_read_ee_hostif_data - Read EEPROM word using a host interface cmd
* assuming that the semaphore is already obtained.
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h
index 11fb33349482..b883342bb576 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h
@@ -18,6 +18,7 @@ void wx_control_hw(struct wx *wx, bool drv);
int wx_mng_present(struct wx *wx);
int wx_host_interface_command(struct wx *wx, u32 *buffer,
u32 length, u32 timeout, bool return_data);
+int wx_set_pps(struct wx *wx, bool enable, u64 nsec, u64 cycles);
int wx_read_ee_hostif(struct wx *wx, u16 offset, u16 *data);
int wx_read_ee_hostif_buffer(struct wx *wx,
u16 offset, u16 words, u16 *data);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
index 2047667ce0fe..57846694b59b 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
@@ -29,6 +29,82 @@
#define WX_NS_PER_SEC 1000000000ULL
#define WX_NS_PER_MSEC 1000000ULL
+static void wx_ptp_setup_sdp(struct wx *wx)
+{
+ struct cyclecounter *cc = &wx->hw_cc;
+ u32 tsauxc, rem, tssdp, tssdp1;
+ u32 trgttiml0, trgttimh0;
+ u32 trgttiml1, trgttimh1;
+ unsigned long flags;
+ u64 ns = 0;
+
+ if (WX_1588_PPS_WIDTH_EM >= WX_NS_PER_SEC) {
+ wx_err(wx, "PTP pps width cannot be longer than 1s!\n");
+ return;
+ }
+
+ /* disable the pin first */
+ wr32ptp(wx, WX_TSC_1588_AUX_CTL, 0);
+ WX_WRITE_FLUSH(wx);
+
+ if (!test_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags)) {
+ if (wx->pps_enabled) {
+ wx->pps_enabled = false;
+ wx_set_pps(wx, false, 0, 0);
+ }
+ return;
+ }
+
+ wx->pps_enabled = true;
+
+ tssdp = WX_TSC_1588_SDP_FUN_SEL_TT0;
+ tssdp |= WX_TSC_1588_SDP_OUT_LEVEL_H;
+ tssdp1 = WX_TSC_1588_SDP_FUN_SEL_TS0;
+ tsauxc = WX_TSC_1588_AUX_CTL_PLSG | WX_TSC_1588_AUX_CTL_EN_TT0 |
+ WX_TSC_1588_AUX_CTL_EN_TT1 | WX_TSC_1588_AUX_CTL_EN_TS0;
+
+ /* Read the current clock time, and save the cycle counter value */
+ spin_lock_irqsave(&wx->tmreg_lock, flags);
+ ns = timecounter_read(&wx->hw_tc);
+ wx->pps_edge_start = wx->hw_tc.cycle_last;
+ spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+ wx->pps_edge_end = wx->pps_edge_start;
+
+ /* Figure out how far past the next second we are */
+ div_u64_rem(ns, WX_NS_PER_SEC, &rem);
+
+ /* Figure out how many nanoseconds to add to round the clock edge up
+ * to the next full second
+ */
+ rem = (WX_NS_PER_SEC - rem);
+
+ /* Adjust the clock edge to align with the next full second. */
+ wx->pps_edge_start += div_u64(((u64)rem << cc->shift), cc->mult);
+ trgttiml0 = (u32)wx->pps_edge_start;
+ trgttimh0 = (u32)(wx->pps_edge_start >> 32);
+
+ wx_set_pps(wx, wx->pps_enabled, ns + rem, wx->pps_edge_start);
+
+ rem += WX_1588_PPS_WIDTH_EM * WX_NS_PER_MSEC;
+ wx->pps_edge_end += div_u64(((u64)rem << cc->shift), cc->mult);
+ trgttiml1 = (u32)wx->pps_edge_end;
+ trgttimh1 = (u32)(wx->pps_edge_end >> 32);
+
+ wr32ptp(wx, WX_TSC_1588_TRGT_L(0), trgttiml0);
+ wr32ptp(wx, WX_TSC_1588_TRGT_H(0), trgttimh0);
+ wr32ptp(wx, WX_TSC_1588_TRGT_L(1), trgttiml1);
+ wr32ptp(wx, WX_TSC_1588_TRGT_H(1), trgttimh1);
+ wr32ptp(wx, WX_TSC_1588_SDP(0), tssdp);
+ wr32ptp(wx, WX_TSC_1588_SDP(1), tssdp1);
+ wr32ptp(wx, WX_TSC_1588_AUX_CTL, tsauxc);
+ wr32ptp(wx, WX_TSC_1588_INT_EN, WX_TSC_1588_INT_EN_TT1);
+ WX_WRITE_FLUSH(wx);
+
+ rem = WX_NS_PER_SEC;
+ /* Adjust the clock edge to align with the next full second. */
+ wx->sec_to_cc = div_u64(((u64)rem << cc->shift), cc->mult);
+}
+
/**
* wx_ptp_adjfine
* @ptp: the ptp clock structure
@@ -78,6 +154,9 @@ static int wx_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
timecounter_adjtime(&wx->hw_tc, delta);
spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+ if (wx->ptp_setup_sdp)
+ wx->ptp_setup_sdp(wx);
+
return 0;
}
@@ -137,9 +216,111 @@ static int wx_ptp_settime64(struct ptp_clock_info *ptp,
timecounter_init(&wx->hw_tc, &wx->hw_cc, ns);
spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+ if (wx->ptp_setup_sdp)
+ wx->ptp_setup_sdp(wx);
+
return 0;
}
+/**
+ * wx_ptp_feature_enable
+ * @ptp: the ptp clock structure
+ * @rq: the requested feature to change
+ * @on: whether to enable or disable the feature
+ *
+ * enable (or disable) ancillary features of the phc subsystem.
+ */
+static int wx_ptp_feature_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ struct wx *wx = container_of(ptp, struct wx, ptp_caps);
+
+ /**
+ * When PPS is enabled, unmask the interrupt for the ClockOut
+ * feature, so that the interrupt handler can send the PPS
+ * event when the clock SDP triggers. Clear mask when PPS is
+ * disabled
+ */
+ if (rq->type != PTP_CLK_REQ_PPS || !wx->ptp_setup_sdp)
+ return -EOPNOTSUPP;
+
+ if (on)
+ set_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
+ else
+ clear_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
+
+ wx->ptp_setup_sdp(wx);
+
+ return 0;
+}
+
+/**
+ * wx_ptp_check_pps_event
+ * @wx: the private wx structure
+ *
+ * This function is called by the interrupt routine when checking for
+ * interrupts. It will check and handle a pps event.
+ */
+void wx_ptp_check_pps_event(struct wx *wx)
+{
+ struct cyclecounter *cc = &wx->hw_cc;
+ u32 tsauxc, rem, int_status;
+ u32 trgttiml0, trgttimh0;
+ u32 trgttiml1, trgttimh1;
+ unsigned long flags;
+ u64 ns = 0;
+
+ /* this check is necessary in case the interrupt was enabled via some
+ * alternative means (ex. debug_fs). Better to check here than
+ * everywhere that calls this function.
+ */
+ if (!wx->ptp_clock)
+ return;
+
+ int_status = rd32ptp(wx, WX_TSC_1588_INT_ST);
+ if (int_status & WX_TSC_1588_INT_ST_TT1) {
+ /* disable the pin first */
+ wr32ptp(wx, WX_TSC_1588_AUX_CTL, 0);
+ WX_WRITE_FLUSH(wx);
+
+ tsauxc = WX_TSC_1588_AUX_CTL_PLSG | WX_TSC_1588_AUX_CTL_EN_TT0 |
+ WX_TSC_1588_AUX_CTL_EN_TT1 | WX_TSC_1588_AUX_CTL_EN_TS0;
+
+ /* Read the current clock time, and save the cycle counter value */
+ spin_lock_irqsave(&wx->tmreg_lock, flags);
+ ns = timecounter_read(&wx->hw_tc);
+ wx->pps_edge_start = wx->hw_tc.cycle_last;
+ spin_unlock_irqrestore(&wx->tmreg_lock, flags);
+ wx->pps_edge_end = wx->pps_edge_start;
+
+ /* Figure out how far past the next second we are */
+ div_u64_rem(ns, WX_NS_PER_SEC, &rem);
+
+ /* Figure out how many nanoseconds to add to round the clock edge up
+ * to the next full second
+ */
+ rem = (WX_NS_PER_SEC - rem);
+
+ /* Adjust the clock edge to align with the next full second. */
+ wx->pps_edge_start += div_u64(((u64)rem << cc->shift), cc->mult);
+ trgttiml0 = (u32)wx->pps_edge_start;
+ trgttimh0 = (u32)(wx->pps_edge_start >> 32);
+
+ rem += WX_1588_PPS_WIDTH_EM * WX_NS_PER_MSEC;
+ wx->pps_edge_end += div_u64(((u64)rem << cc->shift), cc->mult);
+ trgttiml1 = (u32)wx->pps_edge_end;
+ trgttimh1 = (u32)(wx->pps_edge_end >> 32);
+
+ wr32ptp(wx, WX_TSC_1588_TRGT_L(0), trgttiml0);
+ wr32ptp(wx, WX_TSC_1588_TRGT_H(0), trgttimh0);
+ wr32ptp(wx, WX_TSC_1588_TRGT_L(1), trgttiml1);
+ wr32ptp(wx, WX_TSC_1588_TRGT_H(1), trgttimh1);
+ wr32ptp(wx, WX_TSC_1588_AUX_CTL, tsauxc);
+ WX_WRITE_FLUSH(wx);
+ }
+}
+EXPORT_SYMBOL(wx_ptp_check_pps_event);
+
/**
* wx_ptp_create_clock
* @wx: the private board structure
@@ -172,10 +353,14 @@ static long wx_ptp_create_clock(struct wx *wx)
wx->ptp_caps.adjtime = wx_ptp_adjtime;
wx->ptp_caps.gettimex64 = wx_ptp_gettimex64;
wx->ptp_caps.settime64 = wx_ptp_settime64;
- if (wx->mac.type == wx_mac_em)
+ if (wx->mac.type == wx_mac_em) {
wx->ptp_caps.max_adj = 500000000;
- else
+ wx->ptp_setup_sdp = wx_ptp_setup_sdp;
+ wx->ptp_caps.enable = wx_ptp_feature_enable;
+ } else {
wx->ptp_caps.max_adj = 250000000;
+ wx->ptp_setup_sdp = NULL;
+ }
wx->ptp_clock = ptp_clock_register(&wx->ptp_caps, &wx->pdev->dev);
if (IS_ERR(wx->ptp_clock)) {
@@ -573,6 +758,12 @@ void wx_ptp_reset(struct wx *wx)
spin_unlock_irqrestore(&wx->tmreg_lock, flags);
wx->last_overflow_check = jiffies;
+
+ /* Now that the shift has been calculated and the systime
+ * registers reset, (re-)enable the Clock out feature
+ */
+ if (wx->ptp_setup_sdp)
+ wx->ptp_setup_sdp(wx);
}
EXPORT_SYMBOL(wx_ptp_reset);
@@ -622,6 +813,10 @@ void wx_ptp_suspend(struct wx *wx)
if (!test_and_clear_bit(WX_STATE_PTP_RUNNING, wx->state))
return;
+ clear_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
+ if (wx->ptp_setup_sdp)
+ wx->ptp_setup_sdp(wx);
+
cancel_work_sync(&wx->ptp_tx_work);
wx_ptp_clear_tx_timestamp(wx);
}
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
index 4a7a9cda2a35..f98dc2721c83 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
@@ -4,6 +4,7 @@
#ifndef _WX_PTP_H_
#define _WX_PTP_H_
+void wx_ptp_check_pps_event(struct wx *wx);
void wx_ptp_start_cyclecounter(struct wx *wx);
void wx_ptp_reset(struct wx *wx);
void wx_ptp_init(struct wx *wx);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index 706ee8dd99c1..e9b80ef241fd 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -281,6 +281,23 @@
#define WX_TSC_1588_SYSTIML 0x11F0C
#define WX_TSC_1588_SYSTIMH 0x11F10
#define WX_TSC_1588_INC 0x11F14
+#define WX_TSC_1588_INT_ST 0x11F20
+#define WX_TSC_1588_INT_ST_TT1 BIT(5)
+#define WX_TSC_1588_INT_EN 0x11F24
+#define WX_TSC_1588_INT_EN_TT1 BIT(5)
+#define WX_TSC_1588_AUX_CTL 0x11F28
+#define WX_TSC_1588_AUX_CTL_EN_TS0 BIT(8)
+#define WX_TSC_1588_AUX_CTL_EN_TT1 BIT(2)
+#define WX_TSC_1588_AUX_CTL_PLSG BIT(1)
+#define WX_TSC_1588_AUX_CTL_EN_TT0 BIT(0)
+#define WX_TSC_1588_TRGT_L(i) (0x11F2C + ((i) * 8)) /* [0,1] */
+#define WX_TSC_1588_TRGT_H(i) (0x11F30 + ((i) * 8)) /* [0,1] */
+#define WX_TSC_1588_SDP(i) (0x11F5C + ((i) * 4)) /* [0,3] */
+#define WX_TSC_1588_SDP_OUT_LEVEL_H FIELD_PREP(BIT(4), 0)
+#define WX_TSC_1588_SDP_OUT_LEVEL_L FIELD_PREP(BIT(4), 1)
+#define WX_TSC_1588_SDP_FUN_SEL_MASK GENMASK(2, 0)
+#define WX_TSC_1588_SDP_FUN_SEL_TT0 FIELD_PREP(WX_TSC_1588_SDP_FUN_SEL_MASK, 1)
+#define WX_TSC_1588_SDP_FUN_SEL_TS0 FIELD_PREP(WX_TSC_1588_SDP_FUN_SEL_MASK, 5)
/************************************** MNG ********************************/
#define WX_MNG_SWFW_SYNC 0x1E008
@@ -410,6 +427,8 @@ enum WX_MSCA_CMD_value {
#define FW_CEM_CMD_RESERVED 0X0
#define FW_CEM_MAX_RETRIES 3
#define FW_CEM_RESP_STATUS_SUCCESS 0x1
+#define FW_PPS_SET_CMD 0xF6
+#define FW_PPS_SET_LEN 0x14
#define WX_SW_REGION_PTR 0x1C
@@ -730,6 +749,15 @@ struct wx_hic_reset {
u16 reset_type;
};
+struct wx_hic_set_pps {
+ struct wx_hic_hdr hdr;
+ u8 lan_id;
+ u8 enable;
+ u16 pad2;
+ u64 nsec;
+ u64 cycles;
+};
+
/* Bus parameters */
struct wx_bus_info {
u8 func;
@@ -1069,6 +1097,7 @@ enum wx_pf_flags {
WX_FLAG_FDIR_PERFECT,
WX_FLAG_RX_HWTSTAMP_ENABLED,
WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
+ WX_FLAG_PTP_PPS_ENABLED,
WX_PF_FLAGS_NBITS /* must be last */
};
@@ -1169,10 +1198,15 @@ struct wx {
void (*atr)(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype);
void (*configure_fdir)(struct wx *wx);
void (*do_reset)(struct net_device *netdev);
+ void (*ptp_setup_sdp)(struct wx *wx);
struct timer_list service_timer;
struct work_struct service_task;
+ bool pps_enabled;
+ u64 pps_edge_start;
+ u64 pps_edge_end;
+ u64 sec_to_cc;
u32 base_incval;
u32 tx_hwtstamp_timeouts;
u32 tx_hwtstamp_skipped;
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index 83bc0100fb3e..8581fd48d056 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -168,7 +168,7 @@ static irqreturn_t ngbe_intr(int __always_unused irq, void *data)
struct wx_q_vector *q_vector;
struct wx *wx = data;
struct pci_dev *pdev;
- u32 eicr;
+ u32 eicr, eicr_misc;
q_vector = wx->q_vector[0];
pdev = wx->pdev;
@@ -186,6 +186,10 @@ static irqreturn_t ngbe_intr(int __always_unused irq, void *data)
if (!(pdev->msi_enabled))
wr32(wx, WX_PX_INTA, 1);
+ eicr_misc = wx_misc_isb(wx, WX_ISB_MISC);
+ if (unlikely(eicr_misc & NGBE_PX_MISC_IC_TIMESYNC))
+ wx_ptp_check_pps_event(wx);
+
wx->isb_mem[WX_ISB_MISC] = 0;
/* would disable interrupts here but it is auto disabled */
napi_schedule_irqoff(&q_vector->napi);
@@ -199,6 +203,12 @@ static irqreturn_t ngbe_intr(int __always_unused irq, void *data)
static irqreturn_t ngbe_msix_other(int __always_unused irq, void *data)
{
struct wx *wx = data;
+ u32 eicr;
+
+ eicr = wx_misc_isb(wx, WX_ISB_MISC);
+
+ if (unlikely(eicr & NGBE_PX_MISC_IC_TIMESYNC))
+ wx_ptp_check_pps_event(wx);
/* re-enable the original interrupt state, no lsc, no queues */
if (netif_running(wx->netdev))
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index f48ed7fc1805..992adbb98c7d 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -70,15 +70,20 @@
/* Extended Interrupt Enable Set */
#define NGBE_PX_MISC_IEN_DEV_RST BIT(10)
+#define NGBE_PX_MISC_IEN_TIMESYNC BIT(11)
#define NGBE_PX_MISC_IEN_ETH_LK BIT(18)
#define NGBE_PX_MISC_IEN_INT_ERR BIT(20)
#define NGBE_PX_MISC_IEN_GPIO BIT(26)
#define NGBE_PX_MISC_IEN_MASK ( \
NGBE_PX_MISC_IEN_DEV_RST | \
+ NGBE_PX_MISC_IEN_TIMESYNC | \
NGBE_PX_MISC_IEN_ETH_LK | \
NGBE_PX_MISC_IEN_INT_ERR | \
NGBE_PX_MISC_IEN_GPIO)
+/* Extended Interrupt Cause Read */
+#define NGBE_PX_MISC_IC_TIMESYNC BIT(11) /* time sync */
+
#define NGBE_INTR_ALL 0x1FF
#define NGBE_INTR_MISC BIT(0)
--
2.27.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-02 10:30 ` [PATCH net-next 1/4] net: wangxun: Add support for PTP clock Jiawen Wu
@ 2025-01-02 14:13 ` Andrew Lunn
2025-01-06 7:42 ` Jiawen Wu
2025-01-02 16:16 ` kernel test robot
` (2 subsequent siblings)
3 siblings, 1 reply; 20+ messages in thread
From: Andrew Lunn @ 2025-01-02 14:13 UTC (permalink / raw)
To: Jiawen Wu
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, jacob.e.keller, netdev, mengyuanlou
> +static int wx_tx_map(struct wx_ring *tx_ring,
> + struct wx_tx_buffer *first,
> + const u8 hdr_len)
> {
> struct sk_buff *skb = first->skb;
> struct wx_tx_buffer *tx_buffer;
> @@ -1013,6 +1023,8 @@ static void wx_tx_map(struct wx_ring *tx_ring,
>
> netdev_tx_sent_queue(wx_txring_txq(tx_ring), first->bytecount);
>
> + /* set the timestamp */
> + first->time_stamp = jiffies;
> skb_tx_timestamp(skb);
>
> /* Force memory writes to complete before letting h/w know there
> @@ -1038,7 +1050,7 @@ static void wx_tx_map(struct wx_ring *tx_ring,
> if (netif_xmit_stopped(wx_txring_txq(tx_ring)) || !netdev_xmit_more())
> writel(i, tx_ring->tail);
>
> - return;
> + return 0;
> dma_error:
> dev_err(tx_ring->dev, "TX DMA map failed\n");
>
> @@ -1062,6 +1074,8 @@ static void wx_tx_map(struct wx_ring *tx_ring,
> first->skb = NULL;
>
> tx_ring->next_to_use = i;
> +
> + return -EPERM;
EPERM Operation not permitted (POSIX.1-2001).
This is normally about restricted access because of security
settings. So i don't think this is the correct error code here. What
is the reason the function is exiting with an error? Once we
understand that, maybe we can suggest a better error code.
> +static int wx_ptp_adjfine(struct ptp_clock_info *ptp, long ppb)
> +{
> + struct wx *wx = container_of(ptp, struct wx, ptp_caps);
> + u64 incval, mask;
> +
> + smp_mb(); /* Force any pending update before accessing. */
> + incval = READ_ONCE(wx->base_incval);
> + incval = adjust_by_scaled_ppm(incval, ppb);
> +
> + mask = (wx->mac.type == wx_mac_em) ? 0x7FFFFFF : 0xFFFFFF;
> + if (incval > mask)
> + dev_warn(&wx->pdev->dev,
> + "PTP ppb adjusted SYSTIME rate overflowed!\n");
There is no return here, you just keep going. What happens if there is
an overflow?
> +/**
> + * wx_ptp_tx_hwtstamp_work
> + * @work: pointer to the work struct
> + *
> + * This work item polls TSYNCTXCTL valid bit to determine when a Tx hardware
> + * timestamp has been taken for the current skb. It is necessary, because the
> + * descriptor's "done" bit does not correlate with the timestamp event.
> + */
Are you saying the "done" bit can be set, but the timestamp is not yet
in place? I've not read the whole patch, but do you start polling once
"done" is set, or as soon at the skbuff is queues for transmission?
> static void ngbe_mac_link_down(struct phylink_config *config,
> unsigned int mode, phy_interface_t interface)
> {
> + struct wx *wx = phylink_to_wx(config);
> +
> + wx->speed = SPEED_UNKNOWN;
> + if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
> + wx_ptp_start_cyclecounter(wx);
This is probably a naming issue, but it seems odd to call a _start_
function on link down.
Andrew
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-02 10:30 ` [PATCH net-next 1/4] net: wangxun: Add support for PTP clock Jiawen Wu
2025-01-02 14:13 ` Andrew Lunn
@ 2025-01-02 16:16 ` kernel test robot
2025-01-02 17:33 ` Vadim Fedorenko
2025-01-06 21:29 ` Keller, Jacob E
3 siblings, 0 replies; 20+ messages in thread
From: kernel test robot @ 2025-01-02 16:16 UTC (permalink / raw)
To: Jiawen Wu, andrew+netdev, davem, edumazet, kuba, pabeni,
richardcochran, linux, horms, jacob.e.keller, netdev
Cc: llvm, oe-kbuild-all, mengyuanlou, Jiawen Wu
Hi Jiawen,
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/Jiawen-Wu/net-wangxun-Add-support-for-PTP-clock/20250102-181338
base: net-next/main
patch link: https://lore.kernel.org/r/20250102103026.1982137-2-jiawenwu%40trustnetic.com
patch subject: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
config: x86_64-buildonly-randconfig-004-20250102 (https://download.01.org/0day-ci/archive/20250102/202501022323.HDFZ6FVp-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250102/202501022323.HDFZ6FVp-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/202501022323.HDFZ6FVp-lkp@intel.com/
All warnings (new ones prefixed by >>):
In file included from drivers/net/ethernet/wangxun/libwx/wx_ptp.c:4:
In file included from include/linux/ptp_classify.h:14:
In file included from include/linux/ip.h:16:
In file included from include/linux/skbuff.h:17:
In file included from include/linux/bvec.h:10:
In file included from include/linux/highmem.h:8:
In file included from include/linux/cacheflush.h:5:
In file included from arch/x86/include/asm/cacheflush.h:5:
In file included from include/linux/mm.h:2223:
include/linux/vmstat.h:518:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
518 | return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
| ~~~~~~~~~~~ ^ ~~~
>> drivers/net/ethernet/wangxun/libwx/wx_ptp.c:358:2: warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough]
358 | case HWTSTAMP_TX_ON:
| ^
drivers/net/ethernet/wangxun/libwx/wx_ptp.c:358:2: note: insert 'break;' to avoid fall-through
358 | case HWTSTAMP_TX_ON:
| ^
| break;
2 warnings generated.
vim +358 drivers/net/ethernet/wangxun/libwx/wx_ptp.c
315
316 /**
317 * wx_ptp_set_timestamp_mode - setup the hardware for the requested mode
318 * @wx: the private board structure
319 * @config: the hwtstamp configuration requested
320 *
321 * Returns 0 on success, negative on failure
322 *
323 * Outgoing time stamping can be enabled and disabled. Play nice and
324 * disable it when requested, although it shouldn't cause any overhead
325 * when no packet needs it. At most one packet in the queue may be
326 * marked for time stamping, otherwise it would be impossible to tell
327 * for sure to which packet the hardware time stamp belongs.
328 *
329 * Incoming time stamping has to be configured via the hardware
330 * filters. Not all combinations are supported, in particular event
331 * type has to be specified. Matching the kind of event packet is
332 * not supported, with the exception of "all V2 events regardless of
333 * level 2 or 4".
334 *
335 * Since hardware always timestamps Path delay packets when timestamping V2
336 * packets, regardless of the type specified in the register, only use V2
337 * Event mode. This more accurately tells the user what the hardware is going
338 * to do anyways.
339 *
340 * Note: this may modify the hwtstamp configuration towards a more general
341 * mode, if required to support the specifically requested mode.
342 */
343 static int wx_ptp_set_timestamp_mode(struct wx *wx,
344 struct hwtstamp_config *config)
345 {
346 u32 tsync_tx_ctl = WX_TSC_1588_CTL_ENABLED;
347 u32 tsync_rx_ctl = WX_PSR_1588_CTL_ENABLED;
348 DECLARE_BITMAP(flags, WX_PF_FLAGS_NBITS);
349 u32 tsync_rx_mtrl = PTP_EV_PORT << 16;
350 bool is_l2 = false;
351 u32 regval;
352
353 memcpy(flags, wx->flags, sizeof(wx->flags));
354
355 switch (config->tx_type) {
356 case HWTSTAMP_TX_OFF:
357 tsync_tx_ctl = 0;
> 358 case HWTSTAMP_TX_ON:
359 break;
360 default:
361 return -ERANGE;
362 }
363
364 switch (config->rx_filter) {
365 case HWTSTAMP_FILTER_NONE:
366 tsync_rx_ctl = 0;
367 tsync_rx_mtrl = 0;
368 clear_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
369 clear_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
370 break;
371 case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
372 tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_L4_V1;
373 tsync_rx_mtrl |= WX_PSR_1588_MSG_V1_SYNC;
374 set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
375 set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
376 break;
377 case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
378 tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_L4_V1;
379 tsync_rx_mtrl |= WX_PSR_1588_MSG_V1_DELAY_REQ;
380 set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
381 set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
382 break;
383 case HWTSTAMP_FILTER_PTP_V2_EVENT:
384 case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
385 case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
386 case HWTSTAMP_FILTER_PTP_V2_SYNC:
387 case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
388 case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
389 case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
390 case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
391 case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
392 tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_EVENT_V2;
393 is_l2 = true;
394 config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
395 set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
396 set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
397 break;
398 default:
399 /* register RXMTRL must be set in order to do V1 packets,
400 * therefore it is not possible to time stamp both V1 Sync and
401 * Delay_Req messages unless hardware supports timestamping all
402 * packets => return error
403 */
404 clear_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, wx->flags);
405 clear_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, wx->flags);
406 config->rx_filter = HWTSTAMP_FILTER_NONE;
407 return -ERANGE;
408 }
409
410 /* define ethertype filter for timestamping L2 packets */
411 if (is_l2)
412 wr32(wx, WX_PSR_ETYPE_SWC(WX_PSR_ETYPE_SWC_FILTER_1588),
413 (WX_PSR_ETYPE_SWC_FILTER_EN | /* enable filter */
414 WX_PSR_ETYPE_SWC_1588 | /* enable timestamping */
415 ETH_P_1588)); /* 1588 eth protocol type */
416 else
417 wr32(wx, WX_PSR_ETYPE_SWC(WX_PSR_ETYPE_SWC_FILTER_1588), 0);
418
419 /* enable/disable TX */
420 regval = rd32ptp(wx, WX_TSC_1588_CTL);
421 regval &= ~WX_TSC_1588_CTL_ENABLED;
422 regval |= tsync_tx_ctl;
423 wr32ptp(wx, WX_TSC_1588_CTL, regval);
424
425 /* enable/disable RX */
426 regval = rd32(wx, WX_PSR_1588_CTL);
427 regval &= ~(WX_PSR_1588_CTL_ENABLED | WX_PSR_1588_CTL_TYPE_MASK);
428 regval |= tsync_rx_ctl;
429 wr32(wx, WX_PSR_1588_CTL, regval);
430
431 /* define which PTP packets are time stamped */
432 wr32(wx, WX_PSR_1588_MSG, tsync_rx_mtrl);
433
434 WX_WRITE_FLUSH(wx);
435
436 /* configure adapter flags only when HW is actually configured */
437 memcpy(wx->flags, flags, sizeof(wx->flags));
438
439 /* clear TX/RX timestamp state, just to be sure */
440 wx_ptp_clear_tx_timestamp(wx);
441 rd32(wx, WX_PSR_1588_STMPH);
442
443 return 0;
444 }
445
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-02 10:30 ` [PATCH net-next 1/4] net: wangxun: Add support for PTP clock Jiawen Wu
2025-01-02 14:13 ` Andrew Lunn
2025-01-02 16:16 ` kernel test robot
@ 2025-01-02 17:33 ` Vadim Fedorenko
2025-01-06 21:29 ` Keller, Jacob E
3 siblings, 0 replies; 20+ messages in thread
From: Vadim Fedorenko @ 2025-01-02 17:33 UTC (permalink / raw)
To: Jiawen Wu, andrew+netdev, davem, edumazet, kuba, pabeni,
richardcochran, linux, horms, jacob.e.keller, netdev
Cc: mengyuanlou
On 02/01/2025 10:30, Jiawen Wu wrote:
> Implement support for PTP clock on Wangxun NICs.
>
> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
[...]
>
> +int wx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
> +{
> + struct wx *wx = netdev_priv(netdev);
> +
> + switch (cmd) {
> + case SIOCGHWTSTAMP:
> + return wx_ptp_get_ts_config(wx, ifr);
> + case SIOCSHWTSTAMP:
> + return wx_ptp_set_ts_config(wx, ifr);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +EXPORT_SYMBOL(wx_ioctl);
> +
> MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers.");
> MODULE_LICENSE("GPL");
[...]
> @@ -507,6 +513,7 @@ static const struct net_device_ops ngbe_netdev_ops = {
> .ndo_get_stats64 = wx_get_stats64,
> .ndo_vlan_rx_add_vid = wx_vlan_rx_add_vid,
> .ndo_vlan_rx_kill_vid = wx_vlan_rx_kill_vid,
> + .ndo_eth_ioctl = wx_ioctl,
> };
>
> /**
[...]
> @@ -479,6 +488,7 @@ static const struct net_device_ops txgbe_netdev_ops = {
> .ndo_get_stats64 = wx_get_stats64,
> .ndo_vlan_rx_add_vid = wx_vlan_rx_add_vid,
> .ndo_vlan_rx_kill_vid = wx_vlan_rx_kill_vid,
> + .ndo_eth_ioctl = wx_ioctl,
> };
>
ioctl interface is deprecated for this case. Could you please use
.ndo_hwtstamp_get and .ndo_hwtstamp_set for the new code?
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH net-next 2/4] net: wangxun: Implement get_ts_info
2025-01-02 10:30 ` [PATCH net-next 2/4] net: wangxun: Implement get_ts_info Jiawen Wu
@ 2025-01-02 17:44 ` Vadim Fedorenko
2025-01-06 2:35 ` Jiawen Wu
0 siblings, 1 reply; 20+ messages in thread
From: Vadim Fedorenko @ 2025-01-02 17:44 UTC (permalink / raw)
To: Jiawen Wu, andrew+netdev, davem, edumazet, kuba, pabeni,
richardcochran, linux, horms, jacob.e.keller, netdev
Cc: mengyuanlou
On 02/01/2025 10:30, Jiawen Wu wrote:
> Implement the function get_ts_info in ethtool_ops which is needed to get
> the HW capabilities for timestamping.
>
> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> ---
> .../net/ethernet/wangxun/libwx/wx_ethtool.c | 37 +++++++++++++++++++
> .../net/ethernet/wangxun/libwx/wx_ethtool.h | 2 +
> .../net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 1 +
> .../ethernet/wangxun/txgbe/txgbe_ethtool.c | 1 +
> 4 files changed, 41 insertions(+)
>
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
> index c4b3b00b0926..2a228faf0c26 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
> @@ -455,3 +455,40 @@ void wx_set_msglevel(struct net_device *netdev, u32 data)
> wx->msg_enable = data;
> }
> EXPORT_SYMBOL(wx_set_msglevel);
> +
> +int wx_get_ts_info(struct net_device *dev,
> + struct kernel_ethtool_ts_info *info)
> +{
> + struct wx *wx = netdev_priv(dev);
> +
> + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
> + BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
> + BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
> + BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
> + BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
> + BIT(HWTSTAMP_FILTER_PTP_V2_SYNC) |
> + BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
> + BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
> + BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) |
> + BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) |
> + BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
> + BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
> +
> + info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
> + SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE is now moved to core networking and there
is no need to report it from driver
> + SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE means "software-system-clock". What kind of
software clock is provided by the driver?
> + SOF_TIMESTAMPING_TX_HARDWARE |
> + SOF_TIMESTAMPING_RX_HARDWARE |
> + SOF_TIMESTAMPING_RAW_HARDWARE;
> +
> + if (wx->ptp_clock)
> + info->phc_index = ptp_clock_index(wx->ptp_clock);
> + else
> + info->phc_index = -1;
> +
> + info->tx_types = BIT(HWTSTAMP_TX_OFF) |
> + BIT(HWTSTAMP_TX_ON);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(wx_get_ts_info);
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
> index 600c3b597d1a..7c3630e3e187 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
> @@ -40,4 +40,6 @@ int wx_set_channels(struct net_device *dev,
> struct ethtool_channels *ch);
> u32 wx_get_msglevel(struct net_device *netdev);
> void wx_set_msglevel(struct net_device *netdev, u32 data);
> +int wx_get_ts_info(struct net_device *dev,
> + struct kernel_ethtool_ts_info *info);
> #endif /* _WX_ETHTOOL_H_ */
> diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
> index e868f7ef4920..9270cf8e5bc7 100644
> --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
> +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
> @@ -138,6 +138,7 @@ static const struct ethtool_ops ngbe_ethtool_ops = {
> .set_channels = ngbe_set_channels,
> .get_msglevel = wx_get_msglevel,
> .set_msglevel = wx_set_msglevel,
> + .get_ts_info = wx_get_ts_info,
> };
>
> void ngbe_set_ethtool_ops(struct net_device *netdev)
> diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
> index d98314b26c19..9f8df5b3aee0 100644
> --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
> +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
> @@ -529,6 +529,7 @@ static const struct ethtool_ops txgbe_ethtool_ops = {
> .set_rxnfc = txgbe_set_rxnfc,
> .get_msglevel = wx_get_msglevel,
> .set_msglevel = wx_set_msglevel,
> + .get_ts_info = wx_get_ts_info,
> };
>
> void txgbe_set_ethtool_ops(struct net_device *netdev)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH net-next 3/4] net: wangxun: Add watchdog task for PTP clock
2025-01-02 10:30 ` [PATCH net-next 3/4] net: wangxun: Add watchdog task for PTP clock Jiawen Wu
@ 2025-01-02 17:53 ` Vadim Fedorenko
2025-01-03 8:45 ` Richard Cochran
0 siblings, 1 reply; 20+ messages in thread
From: Vadim Fedorenko @ 2025-01-02 17:53 UTC (permalink / raw)
To: Jiawen Wu, andrew+netdev, davem, edumazet, kuba, pabeni,
richardcochran, linux, horms, jacob.e.keller, netdev
Cc: mengyuanlou
On 02/01/2025 10:30, Jiawen Wu wrote:
> Implement watchdog task to detect SYSTIME overflow and error cases of
> Rx/Tx timestamp.
Commit message doesn't look easy to understand, but according to the
comment in the code, watchdog is checking for timecounter overflows.
For PTP use case it's better to use .do_aux_work of struct ptp_clock_info.
It will simplify a lot of code and will setup a worker in a dedicated
queue which can be prioritized separately.
> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> ---
> drivers/net/ethernet/wangxun/libwx/wx_lib.c | 57 +++++++++++
> drivers/net/ethernet/wangxun/libwx/wx_lib.h | 2 +
> drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 99 +++++++++++++++++++
> drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 3 +
> drivers/net/ethernet/wangxun/libwx/wx_type.h | 6 ++
> drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 8 ++
> drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c | 1 +
> .../net/ethernet/wangxun/txgbe/txgbe_main.c | 7 ++
> .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 1 +
> 9 files changed, 184 insertions(+)
>
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> index be1dcc278612..c6db69406f15 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> @@ -2,6 +2,7 @@
> /* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */
>
> #include <linux/etherdevice.h>
> +#include <linux/workqueue.h>
> #include <net/ip6_checksum.h>
> #include <net/page_pool/helpers.h>
> #include <net/inet_ecn.h>
> @@ -2909,6 +2910,62 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count,
> }
> EXPORT_SYMBOL(wx_set_ring);
>
> +void wx_service_event_schedule(struct wx *wx)
> +{
> + if (netif_carrier_ok(wx->netdev) &&
> + !test_and_set_bit(WX_STATE_SERVICE_SCHED, wx->state))
> + queue_work(system_power_efficient_wq, &wx->service_task);
> +}
> +EXPORT_SYMBOL(wx_service_event_schedule);
> +
> +static void wx_service_event_complete(struct wx *wx)
> +{
> + if (WARN_ON(!test_bit(WX_STATE_SERVICE_SCHED, wx->state)))
> + return;
> +
> + /* flush memory to make sure state is correct before next watchdog */
> + smp_mb__before_atomic();
> + clear_bit(WX_STATE_SERVICE_SCHED, wx->state);
> +}
> +
> +static void wx_service_timer(struct timer_list *t)
> +{
> + struct wx *wx = from_timer(wx, t, service_timer);
> + unsigned long next_event_offset = HZ * 2;
> +
> + /* Reset the timer */
> + mod_timer(&wx->service_timer, next_event_offset + jiffies);
> +
> + wx_service_event_schedule(wx);
> +}
> +
> +/**
> + * wx_service_task - manages and runs subtasks
> + * @work: pointer to work_struct containing our data
> + **/
> +static void wx_service_task(struct work_struct *work)
> +{
> + struct wx *wx = container_of(work, struct wx, service_task);
> +
> + if (test_bit(WX_STATE_PTP_RUNNING, wx->state)) {
> + wx_ptp_overflow_check(wx);
> + if (unlikely(test_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
> + wx->flags)))
> + wx_ptp_rx_hang(wx);
> + wx_ptp_tx_hang(wx);
> + }
> +
> + wx_service_event_complete(wx);
> +}
> +
> +void wx_init_service(struct wx *wx)
> +{
> + timer_setup(&wx->service_timer, wx_service_timer, 0);
> + INIT_WORK(&wx->service_task, wx_service_task);
> + clear_bit(WX_STATE_SERVICE_SCHED, wx->state);
> +}
> +EXPORT_SYMBOL(wx_init_service);
> +
> int wx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
> {
> struct wx *wx = netdev_priv(netdev);
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
> index d67a33216811..77c4ea4baabf 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
> @@ -35,6 +35,8 @@ netdev_features_t wx_fix_features(struct net_device *netdev,
> netdev_features_t features);
> void wx_set_ring(struct wx *wx, u32 new_tx_count,
> u32 new_rx_count, struct wx_ring *temp_ring);
> +void wx_service_event_schedule(struct wx *wx);
> +void wx_init_service(struct wx *wx);
> int wx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
>
> #endif /* _NGBE_LIB_H_ */
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
> index d78f99cf4a10..2047667ce0fe 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
> @@ -571,6 +571,8 @@ void wx_ptp_reset(struct wx *wx)
> timecounter_init(&wx->hw_tc, &wx->hw_cc,
> ktime_to_ns(ktime_get_real()));
> spin_unlock_irqrestore(&wx->tmreg_lock, flags);
> +
> + wx->last_overflow_check = jiffies;
> }
> EXPORT_SYMBOL(wx_ptp_reset);
>
> @@ -717,3 +719,100 @@ int wx_ptp_set_ts_config(struct wx *wx, struct ifreq *ifr)
> return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
> -EFAULT : 0;
> }
> +
> +/**
> + * wx_ptp_overflow_check - watchdog task to detect SYSTIME overflow
> + * @wx: pointer to wx struct
> + *
> + * this watchdog task periodically reads the timecounter
> + * in order to prevent missing when the system time registers wrap
> + * around. This needs to be run approximately twice a minute for the fastest
> + * overflowing hardware. We run it for all hardware since it shouldn't have a
> + * large impact.
> + */
> +void wx_ptp_overflow_check(struct wx *wx)
> +{
> + bool timeout = time_is_before_jiffies(wx->last_overflow_check +
> + WX_OVERFLOW_PERIOD);
> + unsigned long flags;
> +
> + if (timeout) {
> + /* Update the timecounter */
> + spin_lock_irqsave(&wx->tmreg_lock, flags);
> + timecounter_read(&wx->hw_tc);
> + spin_unlock_irqrestore(&wx->tmreg_lock, flags);
> +
> + wx->last_overflow_check = jiffies;
> + }
> +}
> +
> +/**
> + * wx_ptp_rx_hang - detect error case when Rx timestamp registers latched
> + * @wx: pointer to wx struct
> + *
> + * this watchdog task is scheduled to detect error case where hardware has
> + * dropped an Rx packet that was timestamped when the ring is full. The
> + * particular error is rare but leaves the device in a state unable to
> + * timestamp any future packets.
> + */
> +void wx_ptp_rx_hang(struct wx *wx)
> +{
> + struct wx_ring *rx_ring;
> + unsigned long rx_event;
> + u32 tsyncrxctl;
> + int n;
> +
> + tsyncrxctl = rd32(wx, WX_PSR_1588_CTL);
> +
> + /* if we don't have a valid timestamp in the registers, just update the
> + * timeout counter and exit
> + */
> + if (!(tsyncrxctl & WX_PSR_1588_CTL_VALID)) {
> + wx->last_rx_ptp_check = jiffies;
> + return;
> + }
> +
> + /* determine the most recent watchdog or rx_timestamp event */
> + rx_event = wx->last_rx_ptp_check;
> + for (n = 0; n < wx->num_rx_queues; n++) {
> + rx_ring = wx->rx_ring[n];
> + if (time_after(rx_ring->last_rx_timestamp, rx_event))
> + rx_event = rx_ring->last_rx_timestamp;
> + }
> +
> + /* only need to read the high RXSTMP register to clear the lock */
> + if (time_is_before_jiffies(rx_event + 5 * HZ)) {
> + rd32(wx, WX_PSR_1588_STMPH);
> + wx->last_rx_ptp_check = jiffies;
> +
> + wx->rx_hwtstamp_cleared++;
> + dev_warn(&wx->pdev->dev, "clearing RX Timestamp hang");
> + }
> +}
> +
> +/**
> + * wx_ptp_tx_hang - detect error case where Tx timestamp never finishes
> + * @wx: private network wx structure
> + */
> +void wx_ptp_tx_hang(struct wx *wx)
> +{
> + bool timeout = time_is_before_jiffies(wx->ptp_tx_start +
> + WX_PTP_TX_TIMEOUT);
> +
> + if (!wx->ptp_tx_skb)
> + return;
> +
> + if (!test_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state))
> + return;
> +
> + /* If we haven't received a timestamp within the timeout, it is
> + * reasonable to assume that it will never occur, so we can unlock the
> + * timestamp bit when this occurs.
> + */
> + if (timeout) {
> + cancel_work_sync(&wx->ptp_tx_work);
> + wx_ptp_clear_tx_timestamp(wx);
> + wx->tx_hwtstamp_timeouts++;
> + dev_warn(&wx->pdev->dev, "clearing Tx timestamp hang\n");
> + }
> +}
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
> index 0fdc4f808636..4a7a9cda2a35 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h
> @@ -9,6 +9,9 @@ void wx_ptp_reset(struct wx *wx);
> void wx_ptp_init(struct wx *wx);
> void wx_ptp_suspend(struct wx *wx);
> void wx_ptp_stop(struct wx *wx);
> +void wx_ptp_overflow_check(struct wx *wx);
> +void wx_ptp_rx_hang(struct wx *wx);
> +void wx_ptp_tx_hang(struct wx *wx);
> void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb);
> int wx_ptp_get_ts_config(struct wx *wx, struct ifreq *ifr);
> int wx_ptp_set_ts_config(struct wx *wx, struct ifreq *ifr);
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
> index 4fa817f55c0a..706ee8dd99c1 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
> @@ -1057,6 +1057,7 @@ struct wx_hw_stats {
>
> enum wx_state {
> WX_STATE_RESETTING,
> + WX_STATE_SERVICE_SCHED,
> WX_STATE_PTP_RUNNING,
> WX_STATE_PTP_TX_IN_PROGRESS,
> WX_STATE_NBITS, /* must be last */
> @@ -1169,10 +1170,15 @@ struct wx {
> void (*configure_fdir)(struct wx *wx);
> void (*do_reset)(struct net_device *netdev);
>
> + struct timer_list service_timer;
> + struct work_struct service_task;
> +
> u32 base_incval;
> u32 tx_hwtstamp_timeouts;
> u32 tx_hwtstamp_skipped;
> u32 rx_hwtstamp_cleared;
> + unsigned long last_overflow_check;
> + unsigned long last_rx_ptp_check;
> unsigned long ptp_tx_start;
> spinlock_t tmreg_lock; /* spinlock for ptp */
> struct cyclecounter hw_cc;
> diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> index 655433bd5545..83bc0100fb3e 100644
> --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> @@ -304,6 +304,9 @@ static void ngbe_disable_device(struct wx *wx)
> if (wx->gpio_ctrl)
> ngbe_sfp_modules_txrx_powerctl(wx, false);
> wx_irq_disable(wx);
> +
> + del_timer_sync(&wx->service_timer);
> +
> /* disable transmits in the hardware now that interrupts are off */
> for (i = 0; i < wx->num_tx_queues; i++) {
> u8 reg_idx = wx->tx_ring[i]->reg_idx;
> @@ -342,6 +345,7 @@ void ngbe_up(struct wx *wx)
> ngbe_sfp_modules_txrx_powerctl(wx, true);
>
> phylink_start(wx->phylink);
> + mod_timer(&wx->service_timer, jiffies);
> }
>
> /**
> @@ -685,6 +689,8 @@ static int ngbe_probe(struct pci_dev *pdev,
> eth_hw_addr_set(netdev, wx->mac.perm_addr);
> wx_mac_set_default_filter(wx, wx->mac.perm_addr);
>
> + wx_init_service(wx);
> +
> err = wx_init_interrupt_scheme(wx);
> if (err)
> goto err_free_mac_table;
> @@ -731,6 +737,8 @@ static void ngbe_remove(struct pci_dev *pdev)
> struct wx *wx = pci_get_drvdata(pdev);
> struct net_device *netdev;
>
> + cancel_work_sync(&wx->service_task);
> +
> netdev = wx->netdev;
> unregister_netdev(netdev);
> phylink_destroy(wx->phylink);
> diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c
> index 7282ca53d834..623c615361a9 100644
> --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c
> +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c
> @@ -111,6 +111,7 @@ static void ngbe_mac_link_up(struct phylink_config *config,
> wr32(wx, WX_MAC_WDG_TIMEOUT, reg);
>
> wx->speed = speed;
> + wx->last_rx_ptp_check = jiffies;
> if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
> wx_ptp_start_cyclecounter(wx);
> }
> diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> index c0e800d0f66b..fccfd02b0e54 100644
> --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> @@ -100,6 +100,7 @@ static void txgbe_up_complete(struct wx *wx)
>
> /* enable transmits */
> netif_tx_start_all_queues(netdev);
> + mod_timer(&wx->service_timer, jiffies);
> }
>
> static void txgbe_reset(struct wx *wx)
> @@ -142,6 +143,8 @@ static void txgbe_disable_device(struct wx *wx)
> wx_irq_disable(wx);
> wx_napi_disable_all(wx);
>
> + del_timer_sync(&wx->service_timer);
> +
> if (wx->bus.func < 2)
> wr32m(wx, TXGBE_MIS_PRB_CTL, TXGBE_MIS_PRB_CTL_LAN_UP(wx->bus.func), 0);
> else
> @@ -631,6 +634,8 @@ static int txgbe_probe(struct pci_dev *pdev,
> eth_hw_addr_set(netdev, wx->mac.perm_addr);
> wx_mac_set_default_filter(wx, wx->mac.perm_addr);
>
> + wx_init_service(wx);
> +
> err = wx_init_interrupt_scheme(wx);
> if (err)
> goto err_free_mac_table;
> @@ -751,6 +756,8 @@ static void txgbe_remove(struct pci_dev *pdev)
> struct txgbe *txgbe = wx->priv;
> struct net_device *netdev;
>
> + cancel_work_sync(&wx->service_task);
> +
> netdev = wx->netdev;
> unregister_netdev(netdev);
>
> diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
> index f6f3c94de97a..2cafb723a4d9 100644
> --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
> +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
> @@ -222,6 +222,7 @@ static void txgbe_mac_link_up(struct phylink_config *config,
> wr32(wx, WX_MAC_WDG_TIMEOUT, wdg);
>
> wx->speed = speed;
> + wx->last_rx_ptp_check = jiffies;
> if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
> wx_ptp_start_cyclecounter(wx);
> }
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH net-next 3/4] net: wangxun: Add watchdog task for PTP clock
2025-01-02 17:53 ` Vadim Fedorenko
@ 2025-01-03 8:45 ` Richard Cochran
0 siblings, 0 replies; 20+ messages in thread
From: Richard Cochran @ 2025-01-03 8:45 UTC (permalink / raw)
To: Vadim Fedorenko
Cc: Jiawen Wu, andrew+netdev, davem, edumazet, kuba, pabeni, linux,
horms, jacob.e.keller, netdev, mengyuanlou
On Thu, Jan 02, 2025 at 05:53:17PM +0000, Vadim Fedorenko wrote:
> On 02/01/2025 10:30, Jiawen Wu wrote:
> > Implement watchdog task to detect SYSTIME overflow and error cases of
> > Rx/Tx timestamp.
>
> Commit message doesn't look easy to understand, but according to the
> comment in the code, watchdog is checking for timecounter overflows.
> For PTP use case it's better to use .do_aux_work of struct ptp_clock_info.
> It will simplify a lot of code and will setup a worker in a dedicated
> queue which can be prioritized separately.
+1
Thanks,
Richard
^ permalink raw reply [flat|nested] 20+ messages in thread
* RE: [PATCH net-next 2/4] net: wangxun: Implement get_ts_info
2025-01-02 17:44 ` Vadim Fedorenko
@ 2025-01-06 2:35 ` Jiawen Wu
0 siblings, 0 replies; 20+ messages in thread
From: Jiawen Wu @ 2025-01-06 2:35 UTC (permalink / raw)
To: 'Vadim Fedorenko', andrew+netdev, davem, edumazet, kuba,
pabeni, richardcochran, linux, horms, jacob.e.keller, netdev
Cc: mengyuanlou
> > +int wx_get_ts_info(struct net_device *dev,
> > + struct kernel_ethtool_ts_info *info)
> > +{
> > + struct wx *wx = netdev_priv(dev);
> > +
> > + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
> > + BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
> > + BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
> > + BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
> > + BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
> > + BIT(HWTSTAMP_FILTER_PTP_V2_SYNC) |
> > + BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
> > + BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
> > + BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) |
> > + BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) |
> > + BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
> > + BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
> > +
> > + info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
> > + SOF_TIMESTAMPING_RX_SOFTWARE |
>
> SOF_TIMESTAMPING_RX_SOFTWARE is now moved to core networking and there
> is no need to report it from driver
>
> > + SOF_TIMESTAMPING_SOFTWARE |
>
> SOF_TIMESTAMPING_SOFTWARE means "software-system-clock". What kind of
> software clock is provided by the driver?
I think I should remove it.
^ permalink raw reply [flat|nested] 20+ messages in thread
* RE: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-02 14:13 ` Andrew Lunn
@ 2025-01-06 7:42 ` Jiawen Wu
2025-01-06 14:26 ` Andrew Lunn
0 siblings, 1 reply; 20+ messages in thread
From: Jiawen Wu @ 2025-01-06 7:42 UTC (permalink / raw)
To: 'Andrew Lunn'
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, jacob.e.keller, netdev, mengyuanlou
On Thu, Jan 2, 2025 10:13 PM, Andrew Lunn wrote:
> > +static int wx_tx_map(struct wx_ring *tx_ring,
> > + struct wx_tx_buffer *first,
> > + const u8 hdr_len)
> > {
> > struct sk_buff *skb = first->skb;
> > struct wx_tx_buffer *tx_buffer;
> > @@ -1013,6 +1023,8 @@ static void wx_tx_map(struct wx_ring *tx_ring,
> >
> > netdev_tx_sent_queue(wx_txring_txq(tx_ring), first->bytecount);
> >
> > + /* set the timestamp */
> > + first->time_stamp = jiffies;
> > skb_tx_timestamp(skb);
> >
> > /* Force memory writes to complete before letting h/w know there
> > @@ -1038,7 +1050,7 @@ static void wx_tx_map(struct wx_ring *tx_ring,
> > if (netif_xmit_stopped(wx_txring_txq(tx_ring)) || !netdev_xmit_more())
> > writel(i, tx_ring->tail);
> >
> > - return;
> > + return 0;
> > dma_error:
> > dev_err(tx_ring->dev, "TX DMA map failed\n");
> >
> > @@ -1062,6 +1074,8 @@ static void wx_tx_map(struct wx_ring *tx_ring,
> > first->skb = NULL;
> >
> > tx_ring->next_to_use = i;
> > +
> > + return -EPERM;
>
> EPERM Operation not permitted (POSIX.1-2001).
>
> This is normally about restricted access because of security
> settings. So i don't think this is the correct error code here. What
> is the reason the function is exiting with an error? Once we
> understand that, maybe we can suggest a better error code.
I'll change it to -ENOMEM.
>
> > +static int wx_ptp_adjfine(struct ptp_clock_info *ptp, long ppb)
> > +{
> > + struct wx *wx = container_of(ptp, struct wx, ptp_caps);
> > + u64 incval, mask;
> > +
> > + smp_mb(); /* Force any pending update before accessing. */
> > + incval = READ_ONCE(wx->base_incval);
> > + incval = adjust_by_scaled_ppm(incval, ppb);
> > +
> > + mask = (wx->mac.type == wx_mac_em) ? 0x7FFFFFF : 0xFFFFFF;
> > + if (incval > mask)
> > + dev_warn(&wx->pdev->dev,
> > + "PTP ppb adjusted SYSTIME rate overflowed!\n");
>
> There is no return here, you just keep going. What happens if there is
> an overflow?
If there is an overflow, the calibration value of this second will be
inaccurate. But it does not affect the calibration value of the next
second. And this rarely happens.
>
> > +/**
> > + * wx_ptp_tx_hwtstamp_work
> > + * @work: pointer to the work struct
> > + *
> > + * This work item polls TSYNCTXCTL valid bit to determine when a Tx hardware
> > + * timestamp has been taken for the current skb. It is necessary, because the
> > + * descriptor's "done" bit does not correlate with the timestamp event.
> > + */
>
> Are you saying the "done" bit can be set, but the timestamp is not yet
> in place? I've not read the whole patch, but do you start polling once
> "done" is set, or as soon at the skbuff is queues for transmission?
The descriptor's "done" bit cannot be used as a basis for Tx hardware
timestamp. So we should poll the valid bit in the register.
>
> > static void ngbe_mac_link_down(struct phylink_config *config,
> > unsigned int mode, phy_interface_t interface)
> > {
> > + struct wx *wx = phylink_to_wx(config);
> > +
> > + wx->speed = SPEED_UNKNOWN;
> > + if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
> > + wx_ptp_start_cyclecounter(wx);
>
> This is probably a naming issue, but it seems odd to call a _start_
> function on link down.
I think this function could be named wx_ptp_reset_cyclecounter().
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-06 7:42 ` Jiawen Wu
@ 2025-01-06 14:26 ` Andrew Lunn
2025-01-07 2:24 ` Jiawen Wu
0 siblings, 1 reply; 20+ messages in thread
From: Andrew Lunn @ 2025-01-06 14:26 UTC (permalink / raw)
To: Jiawen Wu
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, jacob.e.keller, netdev, mengyuanlou
> > > + smp_mb(); /* Force any pending update before accessing. */
> > > + incval = READ_ONCE(wx->base_incval);
> > > + incval = adjust_by_scaled_ppm(incval, ppb);
> > > +
> > > + mask = (wx->mac.type == wx_mac_em) ? 0x7FFFFFF : 0xFFFFFF;
> > > + if (incval > mask)
> > > + dev_warn(&wx->pdev->dev,
> > > + "PTP ppb adjusted SYSTIME rate overflowed!\n");
> >
> > There is no return here, you just keep going. What happens if there is
> > an overflow?
>
> If there is an overflow, the calibration value of this second will be
> inaccurate. But it does not affect the calibration value of the next
> second. And this rarely happens.
If this is a onetime event you don't really care about, is a
dev_warn() justified? Do you want to be handling the user questions
about what it means, when all you are going to say is, ignore it, it
does not really matter?
> > > +/**
> > > + * wx_ptp_tx_hwtstamp_work
> > > + * @work: pointer to the work struct
> > > + *
> > > + * This work item polls TSYNCTXCTL valid bit to determine when a Tx hardware
> > > + * timestamp has been taken for the current skb. It is necessary, because the
> > > + * descriptor's "done" bit does not correlate with the timestamp event.
> > > + */
> >
> > Are you saying the "done" bit can be set, but the timestamp is not yet
> > in place? I've not read the whole patch, but do you start polling once
> > "done" is set, or as soon at the skbuff is queues for transmission?
>
> The descriptor's "done" bit cannot be used as a basis for Tx hardware
> timestamp. So we should poll the valid bit in the register.
You did not answer my question. When do you start polling?
Andrew
^ permalink raw reply [flat|nested] 20+ messages in thread
* RE: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-02 10:30 ` [PATCH net-next 1/4] net: wangxun: Add support for PTP clock Jiawen Wu
` (2 preceding siblings ...)
2025-01-02 17:33 ` Vadim Fedorenko
@ 2025-01-06 21:29 ` Keller, Jacob E
3 siblings, 0 replies; 20+ messages in thread
From: Keller, Jacob E @ 2025-01-06 21:29 UTC (permalink / raw)
To: Jiawen Wu, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
richardcochran@gmail.com, linux@armlinux.org.uk, horms@kernel.org,
netdev@vger.kernel.org
Cc: mengyuanlou@net-swift.com
> -----Original Message-----
> From: Jiawen Wu <jiawenwu@trustnetic.com>
> Sent: Thursday, January 2, 2025 2:30 AM
> To: andrew+netdev@lunn.ch; davem@davemloft.net; edumazet@google.com;
> kuba@kernel.org; pabeni@redhat.com; richardcochran@gmail.com;
> linux@armlinux.org.uk; horms@kernel.org; Keller, Jacob E
> <jacob.e.keller@intel.com>; netdev@vger.kernel.org
> Cc: mengyuanlou@net-swift.com; Jiawen Wu <jiawenwu@trustnetic.com>
> Subject: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
>
> Implement support for PTP clock on Wangxun NICs.
>
> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> ---
> drivers/net/ethernet/wangxun/libwx/Makefile | 2 +-
> .../net/ethernet/wangxun/libwx/wx_ethtool.c | 3 +
> drivers/net/ethernet/wangxun/libwx/wx_lib.c | 64 +-
> drivers/net/ethernet/wangxun/libwx/wx_lib.h | 1 +
> drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 719 ++++++++++++++++++
> drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 16 +
> drivers/net/ethernet/wangxun/libwx/wx_type.h | 67 ++
> drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 7 +
> drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c | 10 +
> .../net/ethernet/wangxun/txgbe/txgbe_main.c | 10 +
> .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 9 +
> 11 files changed, 902 insertions(+), 6 deletions(-)
> create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_ptp.c
> create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_ptp.h
>
> diff --git a/drivers/net/ethernet/wangxun/libwx/Makefile
> b/drivers/net/ethernet/wangxun/libwx/Makefile
> index 42ccd6e4052e..e9f0f1f2309b 100644
> --- a/drivers/net/ethernet/wangxun/libwx/Makefile
> +++ b/drivers/net/ethernet/wangxun/libwx/Makefile
> @@ -4,4 +4,4 @@
>
> obj-$(CONFIG_LIBWX) += libwx.o
>
> -libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o
> +libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
> b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
> index abe5921dde02..c4b3b00b0926 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
> @@ -41,6 +41,9 @@ static const struct wx_stats wx_gstrings_stats[] = {
> WX_STAT("rx_csum_offload_good_count", hw_csum_rx_good),
> WX_STAT("rx_csum_offload_errors", hw_csum_rx_error),
> WX_STAT("alloc_rx_buff_failed", alloc_rx_buff_failed),
> + WX_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
> + WX_STAT("tx_hwtstamp_skipped", tx_hwtstamp_skipped),
> + WX_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
> };
I know this is a little old, but didn't we recently introduce a generic ethtool interface for PTP statistics? Could you please make sure that interface is implemented? I think the current policy is that the more specific errors can still be here, but we should implement the basic standardization available.
Thanks,
Jake
^ permalink raw reply [flat|nested] 20+ messages in thread
* RE: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-06 14:26 ` Andrew Lunn
@ 2025-01-07 2:24 ` Jiawen Wu
2025-01-07 13:33 ` Andrew Lunn
0 siblings, 1 reply; 20+ messages in thread
From: Jiawen Wu @ 2025-01-07 2:24 UTC (permalink / raw)
To: 'Andrew Lunn'
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, jacob.e.keller, netdev, mengyuanlou
On Mon, Jan 6, 2025 10:27 PM, Andrew Lunn wrote:
> > > > + smp_mb(); /* Force any pending update before accessing. */
> > > > + incval = READ_ONCE(wx->base_incval);
> > > > + incval = adjust_by_scaled_ppm(incval, ppb);
> > > > +
> > > > + mask = (wx->mac.type == wx_mac_em) ? 0x7FFFFFF : 0xFFFFFF;
> > > > + if (incval > mask)
> > > > + dev_warn(&wx->pdev->dev,
> > > > + "PTP ppb adjusted SYSTIME rate overflowed!\n");
> > >
> > > There is no return here, you just keep going. What happens if there is
> > > an overflow?
> >
> > If there is an overflow, the calibration value of this second will be
> > inaccurate. But it does not affect the calibration value of the next
> > second. And this rarely happens.
>
> If this is a onetime event you don't really care about, is a
> dev_warn() justified? Do you want to be handling the user questions
> about what it means, when all you are going to say is, ignore it, it
> does not really matter?
I'll remove the dev_warn() to avoid user confusion.
>
> > > > +/**
> > > > + * wx_ptp_tx_hwtstamp_work
> > > > + * @work: pointer to the work struct
> > > > + *
> > > > + * This work item polls TSYNCTXCTL valid bit to determine when a Tx hardware
> > > > + * timestamp has been taken for the current skb. It is necessary, because the
> > > > + * descriptor's "done" bit does not correlate with the timestamp event.
> > > > + */
> > >
> > > Are you saying the "done" bit can be set, but the timestamp is not yet
> > > in place? I've not read the whole patch, but do you start polling once
> > > "done" is set, or as soon at the skbuff is queues for transmission?
> >
> > The descriptor's "done" bit cannot be used as a basis for Tx hardware
> > timestamp. So we should poll the valid bit in the register.
>
> You did not answer my question. When do you start polling?
As soon at the skbuff is queues for transmission.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-07 2:24 ` Jiawen Wu
@ 2025-01-07 13:33 ` Andrew Lunn
2025-01-08 0:34 ` Keller, Jacob E
0 siblings, 1 reply; 20+ messages in thread
From: Andrew Lunn @ 2025-01-07 13:33 UTC (permalink / raw)
To: Jiawen Wu
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, jacob.e.keller, netdev, mengyuanlou
> > > > > +/**
> > > > > + * wx_ptp_tx_hwtstamp_work
> > > > > + * @work: pointer to the work struct
> > > > > + *
> > > > > + * This work item polls TSYNCTXCTL valid bit to determine when a Tx hardware
> > > > > + * timestamp has been taken for the current skb. It is necessary, because the
> > > > > + * descriptor's "done" bit does not correlate with the timestamp event.
> > > > > + */
> > > >
> > > > Are you saying the "done" bit can be set, but the timestamp is not yet
> > > > in place? I've not read the whole patch, but do you start polling once
> > > > "done" is set, or as soon at the skbuff is queues for transmission?
> > >
> > > The descriptor's "done" bit cannot be used as a basis for Tx hardware
> > > timestamp. So we should poll the valid bit in the register.
> >
> > You did not answer my question. When do you start polling?
>
> As soon at the skbuff is queues for transmission.
I assume polling is not for free? Is it possible to start polling once
'done' is set? Maybe do some benchmarks and see if that saves you some
cycles?
Andrew
^ permalink raw reply [flat|nested] 20+ messages in thread
* RE: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-07 13:33 ` Andrew Lunn
@ 2025-01-08 0:34 ` Keller, Jacob E
2025-01-08 7:26 ` Jiawen Wu
0 siblings, 1 reply; 20+ messages in thread
From: Keller, Jacob E @ 2025-01-08 0:34 UTC (permalink / raw)
To: Andrew Lunn, Jiawen Wu
Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, richardcochran@gmail.com,
linux@armlinux.org.uk, horms@kernel.org, netdev@vger.kernel.org,
mengyuanlou@net-swift.com
> -----Original Message-----
> From: Andrew Lunn <andrew@lunn.ch>
> Sent: Tuesday, January 7, 2025 5:33 AM
> To: Jiawen Wu <jiawenwu@trustnetic.com>
> Cc: andrew+netdev@lunn.ch; davem@davemloft.net; edumazet@google.com;
> kuba@kernel.org; pabeni@redhat.com; richardcochran@gmail.com;
> linux@armlinux.org.uk; horms@kernel.org; Keller, Jacob E
> <jacob.e.keller@intel.com>; netdev@vger.kernel.org; mengyuanlou@net-
> swift.com
> Subject: Re: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
>
> > > > > > +/**
> > > > > > + * wx_ptp_tx_hwtstamp_work
> > > > > > + * @work: pointer to the work struct
> > > > > > + *
> > > > > > + * This work item polls TSYNCTXCTL valid bit to determine when a Tx
> hardware
> > > > > > + * timestamp has been taken for the current skb. It is necessary,
> because the
> > > > > > + * descriptor's "done" bit does not correlate with the timestamp event.
> > > > > > + */
> > > > >
> > > > > Are you saying the "done" bit can be set, but the timestamp is not yet
> > > > > in place? I've not read the whole patch, but do you start polling once
> > > > > "done" is set, or as soon at the skbuff is queues for transmission?
> > > >
> > > > The descriptor's "done" bit cannot be used as a basis for Tx hardware
> > > > timestamp. So we should poll the valid bit in the register.
> > >
> > > You did not answer my question. When do you start polling?
> >
> > As soon at the skbuff is queues for transmission.
>
> I assume polling is not for free? Is it possible to start polling once
> 'done' is set? Maybe do some benchmarks and see if that saves you some
> cycles?
>
> Andrew
>
Agreed, I would try to benchmark that. Timestamps need to be returned relatively quickly, which means the polling rate needs to be high. This costs a lot of CPU, and so any mechanism that lets you start later will help the CPU cost.
Thanks,
Jake
^ permalink raw reply [flat|nested] 20+ messages in thread
* RE: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-08 0:34 ` Keller, Jacob E
@ 2025-01-08 7:26 ` Jiawen Wu
2025-01-08 18:32 ` Andrew Lunn
0 siblings, 1 reply; 20+ messages in thread
From: Jiawen Wu @ 2025-01-08 7:26 UTC (permalink / raw)
To: 'Keller, Jacob E', 'Andrew Lunn'
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, netdev, mengyuanlou, 'linglingzhang'
> > > > > > > +/**
> > > > > > > + * wx_ptp_tx_hwtstamp_work
> > > > > > > + * @work: pointer to the work struct
> > > > > > > + *
> > > > > > > + * This work item polls TSYNCTXCTL valid bit to determine when a Tx
> > hardware
> > > > > > > + * timestamp has been taken for the current skb. It is necessary,
> > because the
> > > > > > > + * descriptor's "done" bit does not correlate with the timestamp event.
> > > > > > > + */
> > > > > >
> > > > > > Are you saying the "done" bit can be set, but the timestamp is not yet
> > > > > > in place? I've not read the whole patch, but do you start polling once
> > > > > > "done" is set, or as soon at the skbuff is queues for transmission?
> > > > >
> > > > > The descriptor's "done" bit cannot be used as a basis for Tx hardware
> > > > > timestamp. So we should poll the valid bit in the register.
> > > >
> > > > You did not answer my question. When do you start polling?
> > >
> > > As soon at the skbuff is queues for transmission.
> >
> > I assume polling is not for free? Is it possible to start polling once
> > 'done' is set? Maybe do some benchmarks and see if that saves you some
> > cycles?
> >
> > Andrew
> >
>
> Agreed, I would try to benchmark that. Timestamps need to be returned
> relatively quickly, which means the polling rate needs to be high. This costs a lot
> of CPU, and so any mechanism that lets you start later will help the CPU cost.
May not. We should notify the stack as soon as we get Tx hardware timestamp.
But descriptor's "done" bit may hasn't been set yet.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH net-next 1/4] net: wangxun: Add support for PTP clock
2025-01-08 7:26 ` Jiawen Wu
@ 2025-01-08 18:32 ` Andrew Lunn
0 siblings, 0 replies; 20+ messages in thread
From: Andrew Lunn @ 2025-01-08 18:32 UTC (permalink / raw)
To: Jiawen Wu
Cc: 'Keller, Jacob E', andrew+netdev, davem, edumazet, kuba,
pabeni, richardcochran, linux, horms, netdev, mengyuanlou,
'linglingzhang'
On Wed, Jan 08, 2025 at 03:26:20PM +0800, Jiawen Wu wrote:
> > > > > > > > +/**
> > > > > > > > + * wx_ptp_tx_hwtstamp_work
> > > > > > > > + * @work: pointer to the work struct
> > > > > > > > + *
> > > > > > > > + * This work item polls TSYNCTXCTL valid bit to determine when a Tx
> > > hardware
> > > > > > > > + * timestamp has been taken for the current skb. It is necessary,
> > > because the
> > > > > > > > + * descriptor's "done" bit does not correlate with the timestamp event.
> > > > > > > > + */
> > > > > > >
> > > > > > > Are you saying the "done" bit can be set, but the timestamp is not yet
> > > > > > > in place? I've not read the whole patch, but do you start polling once
> > > > > > > "done" is set, or as soon at the skbuff is queues for transmission?
> > > > > >
> > > > > > The descriptor's "done" bit cannot be used as a basis for Tx hardware
> > > > > > timestamp. So we should poll the valid bit in the register.
> > > > >
> > > > > You did not answer my question. When do you start polling?
> > > >
> > > > As soon at the skbuff is queues for transmission.
> > >
> > > I assume polling is not for free? Is it possible to start polling once
> > > 'done' is set? Maybe do some benchmarks and see if that saves you some
> > > cycles?
> > >
> > > Andrew
> > >
> >
> > Agreed, I would try to benchmark that. Timestamps need to be returned
> > relatively quickly, which means the polling rate needs to be high. This costs a lot
> > of CPU, and so any mechanism that lets you start later will help the CPU cost.
>
> May not. We should notify the stack as soon as we get Tx hardware timestamp.
> But descriptor's "done" bit may hasn't been set yet.
Lets see if i understand this correctly....
"done" means the full packet is on the wire, and so the descriptor
status bits have their final meaning, so the host can read them, and
then reuse the descriptor?
The TX timestamp is however added earlier to the descriptor? Maybe
after the Ethernet header is on the wire? So it could be ~1400 bytes
at line speed before 'done' is set? So 'done' is too late?
But then i have to wounder how many descriptors you have? If you only
have one descriptor, then 1400 bytes could be significant. If there
are 100 packets in the queue waiting to be sent, 1400 bytes is
insignificant. For real world traffic, does polling make a difference?
Andrew
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2025-01-08 18:32 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-02 10:30 [PATCH net-next 0/4] Support PTP clock for Wangxun NICs Jiawen Wu
2025-01-02 10:30 ` [PATCH net-next 1/4] net: wangxun: Add support for PTP clock Jiawen Wu
2025-01-02 14:13 ` Andrew Lunn
2025-01-06 7:42 ` Jiawen Wu
2025-01-06 14:26 ` Andrew Lunn
2025-01-07 2:24 ` Jiawen Wu
2025-01-07 13:33 ` Andrew Lunn
2025-01-08 0:34 ` Keller, Jacob E
2025-01-08 7:26 ` Jiawen Wu
2025-01-08 18:32 ` Andrew Lunn
2025-01-02 16:16 ` kernel test robot
2025-01-02 17:33 ` Vadim Fedorenko
2025-01-06 21:29 ` Keller, Jacob E
2025-01-02 10:30 ` [PATCH net-next 2/4] net: wangxun: Implement get_ts_info Jiawen Wu
2025-01-02 17:44 ` Vadim Fedorenko
2025-01-06 2:35 ` Jiawen Wu
2025-01-02 10:30 ` [PATCH net-next 3/4] net: wangxun: Add watchdog task for PTP clock Jiawen Wu
2025-01-02 17:53 ` Vadim Fedorenko
2025-01-03 8:45 ` Richard Cochran
2025-01-02 10:30 ` [PATCH net-next 4/4] net: ngbe: Add support for 1PPS and TOD Jiawen Wu
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).