* [PATCH net-next v2 0/4] Support PTP clock for Wangxun NICs
@ 2025-01-06 8:45 Jiawen Wu
2025-01-06 8:45 ` [PATCH net-next v2 1/4] net: wangxun: Add support for PTP clock Jiawen Wu
` (3 more replies)
0 siblings, 4 replies; 17+ messages in thread
From: Jiawen Wu @ 2025-01-06 8:45 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran,
linux, horms, jacob.e.keller, netdev, vadim.fedorenko
Cc: mengyuanlou, Jiawen Wu
Implement support for PTP clock on Wangxun NICs.
Changes in v2:
- Link to v1: https://lore.kernel.org/all/20250102103026.1982137-1-jiawenwu@trustnetic.com/
- Fix build warning
- Convert to .ndo_hwtstamp_get and .ndo_hwtstamp_set
- Remove needless timestamp flags
- Use .do_aux_work instead of driver service task
- Use the better error code
- Rename function wx_ptp_start_cyclecounter()
- Keep the register names consistent between comments and code
Jiawen Wu (4):
net: wangxun: Add support for PTP clock
net: wangxun: Implement get_ts_info
net: wangxun: Implement do_aux_work of ptp_clock_info
net: ngbe: Add support for 1PPS and TOD
drivers/net/ethernet/wangxun/libwx/Makefile | 2 +-
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 38 +
.../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 | 49 +-
drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 1019 +++++++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 20 +
drivers/net/ethernet/wangxun/libwx/wx_type.h | 103 ++
.../net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 1 +
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 20 +-
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 | 11 +
.../net/ethernet/wangxun/txgbe/txgbe_phy.c | 10 +
16 files changed, 1305 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] 17+ messages in thread* [PATCH net-next v2 1/4] net: wangxun: Add support for PTP clock 2025-01-06 8:45 [PATCH net-next v2 0/4] Support PTP clock for Wangxun NICs Jiawen Wu @ 2025-01-06 8:45 ` Jiawen Wu 2025-01-06 10:35 ` Vadim Fedorenko 2025-01-06 8:45 ` [PATCH net-next v2 2/4] net: wangxun: Implement get_ts_info Jiawen Wu ` (2 subsequent siblings) 3 siblings, 1 reply; 17+ messages in thread From: Jiawen Wu @ 2025-01-06 8:45 UTC (permalink / raw) To: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran, linux, horms, jacob.e.keller, netdev, vadim.fedorenko 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 | 49 +- drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 711 ++++++++++++++++++ drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 19 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 67 ++ drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 8 + drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c | 10 + .../net/ethernet/wangxun/txgbe/txgbe_main.c | 11 + .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 9 + 10 files changed, 883 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..2c266aee667e 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 -ENOMEM; } 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; } 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..0f683e576b29 --- /dev/null +++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c @@ -0,0 +1,711 @@ +// 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_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 TSC_1588_CTL 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_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; + wx->ptp_caps.do_aux_work = wx_ptp_do_aux_work; + 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_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 kernel_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; + break; + 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_reset_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 TSC_1588_INC + * 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 TSC_1588_INC value is + * necessary, such as during initialization or when the link speed changes. + */ +void wx_ptp_reset_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_reset_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_reset_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 WX_STATE_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); +} + +int wx_hwtstamp_get(struct net_device *dev, + struct kernel_hwtstamp_config *cfg) +{ + struct wx *wx = netdev_priv(dev); + + if (!netif_running(dev)) + return -EINVAL; + + *cfg = wx->tstamp_config; + + return 0; +} +EXPORT_SYMBOL(wx_hwtstamp_get); + +int wx_hwtstamp_set(struct net_device *dev, + struct kernel_hwtstamp_config *cfg, + struct netlink_ext_ack *extack) +{ + struct wx *wx = netdev_priv(dev); + int err; + + if (!netif_running(dev)) + return -EINVAL; + + err = wx_ptp_set_timestamp_mode(wx, cfg); + if (err) + return err; + + /* save these settings for future reference */ + memcpy(&wx->tstamp_config, cfg, sizeof(wx->tstamp_config)); + + return 0; +} +EXPORT_SYMBOL(wx_hwtstamp_set); 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..8742d2797363 --- /dev/null +++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h @@ -0,0 +1,19 @@ +/* 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_reset_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_hwtstamp_get(struct net_device *dev, + struct kernel_hwtstamp_config *cfg); +int wx_hwtstamp_set(struct net_device *dev, + struct kernel_hwtstamp_config *cfg, + struct netlink_ext_ack *extack); + +#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..31b11dba6bf5 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 kernel_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..c60a96cc3508 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,8 @@ 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_hwtstamp_set = wx_hwtstamp_set, + .ndo_hwtstamp_get = wx_hwtstamp_get, }; /** diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c index a5e9b779c44d..c7944e62838a 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_reset_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_reset_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..734450af9a43 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,8 @@ 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_hwtstamp_set = wx_hwtstamp_set, + .ndo_hwtstamp_get = wx_hwtstamp_get, }; /** diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c index 1ae68f94dd49..60e5f3288ad8 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_reset_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_reset_cyclecounter(wx); } static int txgbe_mac_prepare(struct phylink_config *config, unsigned int mode, -- 2.27.0 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH net-next v2 1/4] net: wangxun: Add support for PTP clock 2025-01-06 8:45 ` [PATCH net-next v2 1/4] net: wangxun: Add support for PTP clock Jiawen Wu @ 2025-01-06 10:35 ` Vadim Fedorenko 2025-01-07 2:01 ` Jiawen Wu 0 siblings, 1 reply; 17+ messages in thread From: Vadim Fedorenko @ 2025-01-06 10:35 UTC (permalink / raw) To: Jiawen Wu, andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran, linux, horms, jacob.e.keller, netdev Cc: mengyuanlou On 06/01/2025 08:45, Jiawen Wu wrote: > 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 | 49 +- > drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 711 ++++++++++++++++++ > drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 19 + > drivers/net/ethernet/wangxun/libwx/wx_type.h | 67 ++ > drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 8 + > drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c | 10 + > .../net/ethernet/wangxun/txgbe/txgbe_main.c | 11 + > .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 9 + > 10 files changed, 883 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..2c266aee667e 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 -ENOMEM; > } > > 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; > } > 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..0f683e576b29 > --- /dev/null > +++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c > @@ -0,0 +1,711 @@ > +// 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_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 TSC_1588_CTL 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_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; > + wx->ptp_caps.do_aux_work = wx_ptp_do_aux_work; wx_ptp_do_aux_work is not defined in this patch, it appears in patch 3 only. did you compile test your code patch by patch? > + 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_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 kernel_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; > + break; > + 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_reset_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 TSC_1588_INC > + * 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 TSC_1588_INC value is > + * necessary, such as during initialization or when the link speed changes. > + */ > +void wx_ptp_reset_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_reset_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_reset_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 WX_STATE_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); > +} > + > +int wx_hwtstamp_get(struct net_device *dev, > + struct kernel_hwtstamp_config *cfg) > +{ > + struct wx *wx = netdev_priv(dev); > + > + if (!netif_running(dev)) > + return -EINVAL; > + > + *cfg = wx->tstamp_config; > + > + return 0; > +} > +EXPORT_SYMBOL(wx_hwtstamp_get); > + > +int wx_hwtstamp_set(struct net_device *dev, > + struct kernel_hwtstamp_config *cfg, > + struct netlink_ext_ack *extack) > +{ > + struct wx *wx = netdev_priv(dev); > + int err; > + > + if (!netif_running(dev)) > + return -EINVAL; > + > + err = wx_ptp_set_timestamp_mode(wx, cfg); > + if (err) > + return err; > + > + /* save these settings for future reference */ > + memcpy(&wx->tstamp_config, cfg, sizeof(wx->tstamp_config)); > + > + return 0; > +} > +EXPORT_SYMBOL(wx_hwtstamp_set); > 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..8742d2797363 > --- /dev/null > +++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.h > @@ -0,0 +1,19 @@ > +/* 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_reset_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_hwtstamp_get(struct net_device *dev, > + struct kernel_hwtstamp_config *cfg); > +int wx_hwtstamp_set(struct net_device *dev, > + struct kernel_hwtstamp_config *cfg, > + struct netlink_ext_ack *extack); > + > +#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..31b11dba6bf5 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 kernel_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..c60a96cc3508 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,8 @@ 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_hwtstamp_set = wx_hwtstamp_set, > + .ndo_hwtstamp_get = wx_hwtstamp_get, > }; > > /** > diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c > index a5e9b779c44d..c7944e62838a 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_reset_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_reset_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..734450af9a43 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,8 @@ 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_hwtstamp_set = wx_hwtstamp_set, > + .ndo_hwtstamp_get = wx_hwtstamp_get, > }; > > /** > diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c > index 1ae68f94dd49..60e5f3288ad8 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_reset_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_reset_cyclecounter(wx); > } > > static int txgbe_mac_prepare(struct phylink_config *config, unsigned int mode, ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH net-next v2 1/4] net: wangxun: Add support for PTP clock 2025-01-06 10:35 ` Vadim Fedorenko @ 2025-01-07 2:01 ` Jiawen Wu 0 siblings, 0 replies; 17+ messages in thread From: Jiawen Wu @ 2025-01-07 2:01 UTC (permalink / raw) To: 'Vadim Fedorenko', andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran, linux, horms, jacob.e.keller, netdev Cc: mengyuanlou > > +/** > > + * 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; > > + wx->ptp_caps.do_aux_work = wx_ptp_do_aux_work; > > wx_ptp_do_aux_work is not defined in this patch, it appears in patch 3 > only. did you compile test your code patch by patch? I think I may have created some confusion between the coding and testing machines... Thanks for your careful review. ^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH net-next v2 2/4] net: wangxun: Implement get_ts_info 2025-01-06 8:45 [PATCH net-next v2 0/4] Support PTP clock for Wangxun NICs Jiawen Wu 2025-01-06 8:45 ` [PATCH net-next v2 1/4] net: wangxun: Add support for PTP clock Jiawen Wu @ 2025-01-06 8:45 ` Jiawen Wu 2025-01-06 10:28 ` Vadim Fedorenko 2025-01-06 8:45 ` [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info Jiawen Wu 2025-01-06 8:45 ` [PATCH net-next v2 4/4] net: ngbe: Add support for 1PPS and TOD Jiawen Wu 3 siblings, 1 reply; 17+ messages in thread From: Jiawen Wu @ 2025-01-06 8:45 UTC (permalink / raw) To: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran, linux, horms, jacob.e.keller, netdev, vadim.fedorenko 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 | 35 +++++++++++++++++++ .../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, 39 insertions(+) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c index c4b3b00b0926..27e6643509f6 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c @@ -455,3 +455,38 @@ 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_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] 17+ messages in thread
* Re: [PATCH net-next v2 2/4] net: wangxun: Implement get_ts_info 2025-01-06 8:45 ` [PATCH net-next v2 2/4] net: wangxun: Implement get_ts_info Jiawen Wu @ 2025-01-06 10:28 ` Vadim Fedorenko 0 siblings, 0 replies; 17+ messages in thread From: Vadim Fedorenko @ 2025-01-06 10:28 UTC (permalink / raw) To: Jiawen Wu, andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran, linux, horms, jacob.e.keller, netdev Cc: mengyuanlou On 06/01/2025 08:45, 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 | 35 +++++++++++++++++++ > .../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, 39 insertions(+) > > diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c > index c4b3b00b0926..27e6643509f6 100644 > --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c > +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c > @@ -455,3 +455,38 @@ 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_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) Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev> ^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info 2025-01-06 8:45 [PATCH net-next v2 0/4] Support PTP clock for Wangxun NICs Jiawen Wu 2025-01-06 8:45 ` [PATCH net-next v2 1/4] net: wangxun: Add support for PTP clock Jiawen Wu 2025-01-06 8:45 ` [PATCH net-next v2 2/4] net: wangxun: Implement get_ts_info Jiawen Wu @ 2025-01-06 8:45 ` Jiawen Wu 2025-01-06 10:36 ` Vadim Fedorenko 2025-01-06 15:16 ` Richard Cochran 2025-01-06 8:45 ` [PATCH net-next v2 4/4] net: ngbe: Add support for 1PPS and TOD Jiawen Wu 3 siblings, 2 replies; 17+ messages in thread From: Jiawen Wu @ 2025-01-06 8:45 UTC (permalink / raw) To: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran, linux, horms, jacob.e.keller, netdev, vadim.fedorenko 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_ptp.c | 211 ++++++++++++++++++ drivers/net/ethernet/wangxun/libwx/wx_type.h | 2 + drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c | 1 + .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 1 + 4 files changed, 215 insertions(+) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c index 0f683e576b29..0071ba929738 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c @@ -255,6 +255,215 @@ static void wx_ptp_tx_hwtstamp_work(struct work_struct *work) } } +/** + * 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. + */ +static 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. + */ +static 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 + */ +static 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"); + } +} + +static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp) +{ + struct wx *wx = container_of(ptp, struct wx, ptp_caps); + + 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); + + 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 @@ -573,6 +782,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); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 31b11dba6bf5..1f9ddddea191 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -1173,6 +1173,8 @@ struct wx { 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_mdio.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c index c7944e62838a..ea1d7e9a91f3 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_reset_cyclecounter(wx); } diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c index 60e5f3288ad8..7e17d727c2ba 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_reset_cyclecounter(wx); } -- 2.27.0 ^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info 2025-01-06 8:45 ` [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info Jiawen Wu @ 2025-01-06 10:36 ` Vadim Fedorenko 2025-01-06 15:16 ` Richard Cochran 1 sibling, 0 replies; 17+ messages in thread From: Vadim Fedorenko @ 2025-01-06 10:36 UTC (permalink / raw) To: Jiawen Wu, andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran, linux, horms, jacob.e.keller, netdev Cc: mengyuanlou On 06/01/2025 08:45, Jiawen Wu wrote: > 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_ptp.c | 211 ++++++++++++++++++ > drivers/net/ethernet/wangxun/libwx/wx_type.h | 2 + > drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c | 1 + > .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 1 + > 4 files changed, 215 insertions(+) > > diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c > index 0f683e576b29..0071ba929738 100644 > --- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c > +++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c > @@ -255,6 +255,215 @@ static void wx_ptp_tx_hwtstamp_work(struct work_struct *work) > } > } > > +/** > + * 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. > + */ > +static 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. > + */ > +static 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 > + */ > +static 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"); > + } > +} > + > +static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp) > +{ > + struct wx *wx = container_of(ptp, struct wx, ptp_caps); > + > + 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); > + > + return 0; This means do_aux_work will run only once. If it's not expected behavior, you have to return delay value to requeue work. > +} > + > +/** > + * 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 > @@ -573,6 +782,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); > > diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h > index 31b11dba6bf5..1f9ddddea191 100644 > --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h > +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h > @@ -1173,6 +1173,8 @@ struct wx { > 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_mdio.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c > index c7944e62838a..ea1d7e9a91f3 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_reset_cyclecounter(wx); > } > diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c > index 60e5f3288ad8..7e17d727c2ba 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_reset_cyclecounter(wx); > } ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info 2025-01-06 8:45 ` [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info Jiawen Wu 2025-01-06 10:36 ` Vadim Fedorenko @ 2025-01-06 15:16 ` Richard Cochran 2025-01-06 21:35 ` Keller, Jacob E 2025-01-07 1:50 ` Jiawen Wu 1 sibling, 2 replies; 17+ messages in thread From: Richard Cochran @ 2025-01-06 15:16 UTC (permalink / raw) To: Jiawen Wu Cc: andrew+netdev, davem, edumazet, kuba, pabeni, linux, horms, jacob.e.keller, netdev, vadim.fedorenko, mengyuanlou On Mon, Jan 06, 2025 at 04:45:05PM +0800, Jiawen Wu wrote: > +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; NAK. The logic that you added in patch #4 is a periodic output signal, so your driver will support PTP_CLK_REQ_PEROUT and not PTP_CLK_REQ_PPS. Please change the driver to use that instead. Thanks, Richard ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info 2025-01-06 15:16 ` Richard Cochran @ 2025-01-06 21:35 ` Keller, Jacob E 2025-01-07 5:59 ` Jiawen Wu 2025-01-07 1:50 ` Jiawen Wu 1 sibling, 1 reply; 17+ messages in thread From: Keller, Jacob E @ 2025-01-06 21:35 UTC (permalink / raw) To: Richard Cochran, Jiawen Wu Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, linux@armlinux.org.uk, horms@kernel.org, netdev@vger.kernel.org, vadim.fedorenko@linux.dev, mengyuanlou@net-swift.com > -----Original Message----- > From: Richard Cochran <richardcochran@gmail.com> > Sent: Monday, January 6, 2025 7:16 AM > To: Jiawen Wu <jiawenwu@trustnetic.com> > Cc: andrew+netdev@lunn.ch; davem@davemloft.net; edumazet@google.com; > kuba@kernel.org; pabeni@redhat.com; linux@armlinux.org.uk; > horms@kernel.org; Keller, Jacob E <jacob.e.keller@intel.com>; > netdev@vger.kernel.org; vadim.fedorenko@linux.dev; mengyuanlou@net- > swift.com > Subject: Re: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of > ptp_clock_info > > On Mon, Jan 06, 2025 at 04:45:05PM +0800, Jiawen Wu wrote: > > > +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; > > NAK. > > The logic that you added in patch #4 is a periodic output signal, so > your driver will support PTP_CLK_REQ_PEROUT and not PTP_CLK_REQ_PPS. > > Please change the driver to use that instead. > > Thanks, > Richard This is a common misconception because the industry lingo uses PPS to mean periodic output. I wonder if there's a place we can put an obvious warning about checking if you meant PEROUT... I've had this issue pop up with colleagues many times. Thanks, Jake ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info 2025-01-06 21:35 ` Keller, Jacob E @ 2025-01-07 5:59 ` Jiawen Wu 2025-01-08 0:33 ` Keller, Jacob E 0 siblings, 1 reply; 17+ messages in thread From: Jiawen Wu @ 2025-01-07 5:59 UTC (permalink / raw) To: 'Keller, Jacob E', 'Richard Cochran' Cc: andrew+netdev, davem, edumazet, kuba, pabeni, linux, horms, netdev, vadim.fedorenko, mengyuanlou > > > +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; > > > > NAK. > > > > The logic that you added in patch #4 is a periodic output signal, so > > your driver will support PTP_CLK_REQ_PEROUT and not PTP_CLK_REQ_PPS. > > > > Please change the driver to use that instead. > > > > Thanks, > > Richard > > This is a common misconception because the industry lingo uses PPS to mean > periodic output. I wonder if there's a place we can put an obvious warning > about checking if you meant PEROUT... I've had this issue pop up with > colleagues many times. Does a periodic output signal mean that a signal is output every second, whenever the start time is? But I want to implement that a signal is output when an integer number of seconds for the clock time. ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info 2025-01-07 5:59 ` Jiawen Wu @ 2025-01-08 0:33 ` Keller, Jacob E 2025-01-08 2:52 ` Jiawen Wu 0 siblings, 1 reply; 17+ messages in thread From: Keller, Jacob E @ 2025-01-08 0:33 UTC (permalink / raw) To: Jiawen Wu, 'Richard Cochran' Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, linux@armlinux.org.uk, horms@kernel.org, netdev@vger.kernel.org, vadim.fedorenko@linux.dev, mengyuanlou@net-swift.com > -----Original Message----- > From: Jiawen Wu <jiawenwu@trustnetic.com> > Sent: Monday, January 6, 2025 9:59 PM > To: Keller, Jacob E <jacob.e.keller@intel.com>; 'Richard Cochran' > <richardcochran@gmail.com> > Cc: andrew+netdev@lunn.ch; davem@davemloft.net; edumazet@google.com; > kuba@kernel.org; pabeni@redhat.com; linux@armlinux.org.uk; > horms@kernel.org; netdev@vger.kernel.org; vadim.fedorenko@linux.dev; > mengyuanlou@net-swift.com > Subject: RE: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of > ptp_clock_info > > > > > +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; > > > > > > NAK. > > > > > > The logic that you added in patch #4 is a periodic output signal, so > > > your driver will support PTP_CLK_REQ_PEROUT and not PTP_CLK_REQ_PPS. > > > > > > Please change the driver to use that instead. > > > > > > Thanks, > > > Richard > > > > This is a common misconception because the industry lingo uses PPS to mean > > periodic output. I wonder if there's a place we can put an obvious warning > > about checking if you meant PEROUT... I've had this issue pop up with > > colleagues many times. > > Does a periodic output signal mean that a signal is output every second, > whenever the start time is? But I want to implement that a signal is > output when an integer number of seconds for the clock time. > The periodic output can be configured in a bunch of ways, including periods that are not a full second, when the signal should start, as well as in "one shot" mode where it will only trigger once. You should check the possible flags in <uapi/linux/ptp_clock.h> for the various options. Thanks, Jake > ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info 2025-01-08 0:33 ` Keller, Jacob E @ 2025-01-08 2:52 ` Jiawen Wu 2025-01-08 6:28 ` Jiawen Wu 2025-01-08 18:09 ` Keller, Jacob E 0 siblings, 2 replies; 17+ messages in thread From: Jiawen Wu @ 2025-01-08 2:52 UTC (permalink / raw) To: 'Keller, Jacob E', 'Richard Cochran' Cc: andrew+netdev, davem, edumazet, kuba, pabeni, linux, horms, netdev, vadim.fedorenko, mengyuanlou, 'linglingzhang' > > > > > +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; > > > > > > > > NAK. > > > > > > > > The logic that you added in patch #4 is a periodic output signal, so > > > > your driver will support PTP_CLK_REQ_PEROUT and not PTP_CLK_REQ_PPS. > > > > > > > > Please change the driver to use that instead. > > > > > > > > Thanks, > > > > Richard > > > > > > This is a common misconception because the industry lingo uses PPS to mean > > > periodic output. I wonder if there's a place we can put an obvious warning > > > about checking if you meant PEROUT... I've had this issue pop up with > > > colleagues many times. > > > > Does a periodic output signal mean that a signal is output every second, > > whenever the start time is? But I want to implement that a signal is > > output when an integer number of seconds for the clock time. > > > > The periodic output can be configured in a bunch of ways, including periods that > are not a full second, when the signal should start, as well as in "one shot" mode > where it will only trigger once. You should check the possible flags in > <uapi/linux/ptp_clock.h> for the various options. Looks like I need to configure perout.phase {0, 0} to output signal at the closest next second. And configure perout.period {0, 120 * 1000000} to keep the signal 120ms. But where should I put these configuration? It used to be: echo 1 > /sys/class/ptp/ptp0/pps_enable ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info 2025-01-08 2:52 ` Jiawen Wu @ 2025-01-08 6:28 ` Jiawen Wu 2025-01-08 18:09 ` Keller, Jacob E 1 sibling, 0 replies; 17+ messages in thread From: Jiawen Wu @ 2025-01-08 6:28 UTC (permalink / raw) To: 'Keller, Jacob E', 'Richard Cochran' Cc: andrew+netdev, davem, edumazet, kuba, pabeni, linux, horms, netdev, vadim.fedorenko, mengyuanlou > > > > > > +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; > > > > > > > > > > NAK. > > > > > > > > > > The logic that you added in patch #4 is a periodic output signal, so > > > > > your driver will support PTP_CLK_REQ_PEROUT and not PTP_CLK_REQ_PPS. > > > > > > > > > > Please change the driver to use that instead. > > > > > > > > > > Thanks, > > > > > Richard > > > > > > > > This is a common misconception because the industry lingo uses PPS to mean > > > > periodic output. I wonder if there's a place we can put an obvious warning > > > > about checking if you meant PEROUT... I've had this issue pop up with > > > > colleagues many times. > > > > > > Does a periodic output signal mean that a signal is output every second, > > > whenever the start time is? But I want to implement that a signal is > > > output when an integer number of seconds for the clock time. > > > > > > > The periodic output can be configured in a bunch of ways, including periods that > > are not a full second, when the signal should start, as well as in "one shot" mode > > where it will only trigger once. You should check the possible flags in > > <uapi/linux/ptp_clock.h> for the various options. > > Looks like I need to configure perout.phase {0, 0} to output signal at the closest next > second. And configure perout.period {0, 120 * 1000000} to keep the signal 120ms. > > But where should I put these configuration? It used to be: > > echo 1 > /sys/class/ptp/ptp0/pps_enable I see. Thanks for your suggestion. ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info 2025-01-08 2:52 ` Jiawen Wu 2025-01-08 6:28 ` Jiawen Wu @ 2025-01-08 18:09 ` Keller, Jacob E 1 sibling, 0 replies; 17+ messages in thread From: Keller, Jacob E @ 2025-01-08 18:09 UTC (permalink / raw) To: Jiawen Wu, 'Richard Cochran' Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, linux@armlinux.org.uk, horms@kernel.org, netdev@vger.kernel.org, vadim.fedorenko@linux.dev, mengyuanlou@net-swift.com, 'linglingzhang' > -----Original Message----- > From: Jiawen Wu <jiawenwu@trustnetic.com> > Sent: Tuesday, January 7, 2025 6:52 PM > To: Keller, Jacob E <jacob.e.keller@intel.com>; 'Richard Cochran' > <richardcochran@gmail.com> > Cc: andrew+netdev@lunn.ch; davem@davemloft.net; edumazet@google.com; > kuba@kernel.org; pabeni@redhat.com; linux@armlinux.org.uk; > horms@kernel.org; netdev@vger.kernel.org; vadim.fedorenko@linux.dev; > mengyuanlou@net-swift.com; 'linglingzhang' <linglingzhang@net-swift.com> > Subject: RE: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of > ptp_clock_info > > > > > > > +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; > > > > > > > > > > NAK. > > > > > > > > > > The logic that you added in patch #4 is a periodic output signal, so > > > > > your driver will support PTP_CLK_REQ_PEROUT and not > PTP_CLK_REQ_PPS. > > > > > > > > > > Please change the driver to use that instead. > > > > > > > > > > Thanks, > > > > > Richard > > > > > > > > This is a common misconception because the industry lingo uses PPS to > mean > > > > periodic output. I wonder if there's a place we can put an obvious warning > > > > about checking if you meant PEROUT... I've had this issue pop up with > > > > colleagues many times. > > > > > > Does a periodic output signal mean that a signal is output every second, > > > whenever the start time is? But I want to implement that a signal is > > > output when an integer number of seconds for the clock time. > > > > > > > The periodic output can be configured in a bunch of ways, including periods that > > are not a full second, when the signal should start, as well as in "one shot" > mode > > where it will only trigger once. You should check the possible flags in > > <uapi/linux/ptp_clock.h> for the various options. > > Looks like I need to configure perout.phase {0, 0} to output signal at the closest > next > second. And configure perout.period {0, 120 * 1000000} to keep the signal 120ms. > > But where should I put these configuration? It used to be: > > echo 1 > /sys/class/ptp/ptp0/pps_enable You can use /sys/class/ptp/ptp0/period (assuming the driver is configured with a periodic output properly). Note that this may not be a fully featured interface. To access the full power of the periodic output support, you need to use the PTP_PEROUT_REQUEST2 ioctl. You can check the code in tools/testing/selftests/ptp/testptp.c or possibly see if there is an example of use in the LinuxPTP project. Thanks, Jake ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info 2025-01-06 15:16 ` Richard Cochran 2025-01-06 21:35 ` Keller, Jacob E @ 2025-01-07 1:50 ` Jiawen Wu 1 sibling, 0 replies; 17+ messages in thread From: Jiawen Wu @ 2025-01-07 1:50 UTC (permalink / raw) To: 'Richard Cochran' Cc: andrew+netdev, davem, edumazet, kuba, pabeni, linux, horms, jacob.e.keller, netdev, vadim.fedorenko, mengyuanlou On Mon, Jan 6, 2025 11:16 PM, Richard Cochran wrote: > On Mon, Jan 06, 2025 at 04:45:05PM +0800, Jiawen Wu wrote: > > > +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; > > NAK. > > The logic that you added in patch #4 is a periodic output signal, so > your driver will support PTP_CLK_REQ_PEROUT and not PTP_CLK_REQ_PPS. > > Please change the driver to use that instead. Oh sorry, I messed up the patches. These belong to patch 4/4. ^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH net-next v2 4/4] net: ngbe: Add support for 1PPS and TOD 2025-01-06 8:45 [PATCH net-next v2 0/4] Support PTP clock for Wangxun NICs Jiawen Wu ` (2 preceding siblings ...) 2025-01-06 8:45 ` [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info Jiawen Wu @ 2025-01-06 8:45 ` Jiawen Wu 3 siblings, 0 replies; 17+ messages in thread From: Jiawen Wu @ 2025-01-06 8:45 UTC (permalink / raw) To: andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran, linux, horms, jacob.e.keller, netdev, vadim.fedorenko 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 | 103 +++++++++++++++++- 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, 171 insertions(+), 4 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 0071ba929738..42024028b66c 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,6 +216,9 @@ 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; } @@ -491,16 +573,21 @@ static long wx_ptp_create_clock(struct wx *wx) 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; wx->ptp_caps.do_aux_work = wx_ptp_do_aux_work; - if (wx->mac.type == wx_mac_em) + if (wx->mac.type == wx_mac_em) { wx->ptp_caps.max_adj = 500000000; - else + wx->ptp_caps.pps = 1; + 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_caps.pps = 0; + wx->ptp_setup_sdp = NULL; + } wx->ptp_clock = ptp_clock_register(&wx->ptp_caps, &wx->pdev->dev); if (IS_ERR(wx->ptp_clock)) { @@ -784,6 +871,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); @@ -833,6 +926,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 8742d2797363..50db90a6e3ee 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_reset_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 1f9ddddea191..2c9c22d9491b 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; @@ -1068,6 +1096,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 */ }; @@ -1168,7 +1197,12 @@ 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); + 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 c60a96cc3508..a6159214ec0a 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] 17+ messages in thread
end of thread, other threads:[~2025-01-08 18:10 UTC | newest] Thread overview: 17+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-01-06 8:45 [PATCH net-next v2 0/4] Support PTP clock for Wangxun NICs Jiawen Wu 2025-01-06 8:45 ` [PATCH net-next v2 1/4] net: wangxun: Add support for PTP clock Jiawen Wu 2025-01-06 10:35 ` Vadim Fedorenko 2025-01-07 2:01 ` Jiawen Wu 2025-01-06 8:45 ` [PATCH net-next v2 2/4] net: wangxun: Implement get_ts_info Jiawen Wu 2025-01-06 10:28 ` Vadim Fedorenko 2025-01-06 8:45 ` [PATCH net-next v2 3/4] net: wangxun: Implement do_aux_work of ptp_clock_info Jiawen Wu 2025-01-06 10:36 ` Vadim Fedorenko 2025-01-06 15:16 ` Richard Cochran 2025-01-06 21:35 ` Keller, Jacob E 2025-01-07 5:59 ` Jiawen Wu 2025-01-08 0:33 ` Keller, Jacob E 2025-01-08 2:52 ` Jiawen Wu 2025-01-08 6:28 ` Jiawen Wu 2025-01-08 18:09 ` Keller, Jacob E 2025-01-07 1:50 ` Jiawen Wu 2025-01-06 8:45 ` [PATCH net-next v2 4/4] net: ngbe: Add support for 1PPS and TOD Jiawen Wu
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.