diff mbox series

[net-next,v2,3/4] net: wangxun: Implement do_aux_work of ptp_clock_info

Message ID 20250106084506.2042912-4-jiawenwu@trustnetic.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series Support PTP clock for Wangxun NICs | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 10 this patch: 32
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers success CCed 9 of 9 maintainers
netdev/build_clang fail Errors and warnings before: 24 this patch: 43
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 10 this patch: 32
netdev/checkpatch warning WARNING: line length of 81 exceeds 80 columns WARNING: line length of 83 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc fail Errors and warnings before: 23 this patch: 28
netdev/source_inline success Was 0 now: 0

Commit Message

Jiawen Wu Jan. 6, 2025, 8:45 a.m. UTC
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(+)

Comments

Vadim Fedorenko Jan. 6, 2025, 10:36 a.m. UTC | #1
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);
>   }
Richard Cochran Jan. 6, 2025, 3:16 p.m. UTC | #2
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
Keller, Jacob E Jan. 6, 2025, 9:35 p.m. UTC | #3
> -----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
Jiawen Wu Jan. 7, 2025, 1:50 a.m. UTC | #4
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.
Jiawen Wu Jan. 7, 2025, 5:59 a.m. UTC | #5
> > > +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.
diff mbox series

Patch

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