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 |
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); > }
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
> -----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
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.
> > > +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 --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); }
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(+)