diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igb/igb_ptp.c')
| -rw-r--r-- | drivers/net/ethernet/intel/igb/igb_ptp.c | 65 | 
1 files changed, 62 insertions, 3 deletions
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index ab3429729bd..0987822359f 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -20,6 +20,7 @@  #include <linux/module.h>  #include <linux/device.h>  #include <linux/pci.h> +#include <linux/ptp_classify.h>  #include "igb.h" @@ -70,6 +71,7 @@   */  #define IGB_SYSTIM_OVERFLOW_PERIOD	(HZ * 60 * 9) +#define IGB_PTP_TX_TIMEOUT		(HZ * 15)  #define INCPERIOD_82576			(1 << E1000_TIMINCA_16NS_SHIFT)  #define INCVALUE_82576_MASK		((1 << E1000_TIMINCA_16NS_SHIFT) - 1)  #define INCVALUE_82576			(16 << IGB_82576_TSYNC_SHIFT) @@ -396,6 +398,15 @@ void igb_ptp_tx_work(struct work_struct *work)  	if (!adapter->ptp_tx_skb)  		return; +	if (time_is_before_jiffies(adapter->ptp_tx_start + +				   IGB_PTP_TX_TIMEOUT)) { +		dev_kfree_skb_any(adapter->ptp_tx_skb); +		adapter->ptp_tx_skb = NULL; +		adapter->tx_hwtstamp_timeouts++; +		dev_warn(&adapter->pdev->dev, "clearing Tx timestamp hang"); +		return; +	} +  	tsynctxctl = rd32(E1000_TSYNCTXCTL);  	if (tsynctxctl & E1000_TSYNCTXCTL_VALID)  		igb_ptp_tx_hwtstamp(adapter); @@ -419,6 +430,51 @@ static void igb_ptp_overflow_check(struct work_struct *work)  }  /** + * igb_ptp_rx_hang - detect error case when Rx timestamp registers latched + * @adapter: private network adapter structure + * + * This watchdog task is scheduled to detect error case where hardware has + * dropped an Rx packet that was timestamped when the ring is full. The + * particular error is rare but leaves the device in a state unable to timestamp + * any future packets. + */ +void igb_ptp_rx_hang(struct igb_adapter *adapter) +{ +	struct e1000_hw *hw = &adapter->hw; +	struct igb_ring *rx_ring; +	u32 tsyncrxctl = rd32(E1000_TSYNCRXCTL); +	unsigned long rx_event; +	int n; + +	if (hw->mac.type != e1000_82576) +		return; + +	/* If we don't have a valid timestamp in the registers, just update the +	 * timeout counter and exit +	 */ +	if (!(tsyncrxctl & E1000_TSYNCRXCTL_VALID)) { +		adapter->last_rx_ptp_check = jiffies; +		return; +	} + +	/* Determine the most recent watchdog or rx_timestamp event */ +	rx_event = adapter->last_rx_ptp_check; +	for (n = 0; n < adapter->num_rx_queues; n++) { +		rx_ring = adapter->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(E1000_RXSTMPH); +		adapter->last_rx_ptp_check = jiffies; +		adapter->rx_hwtstamp_cleared++; +		dev_warn(&adapter->pdev->dev, "clearing Rx timestamp hang"); +	} +} + +/**   * igb_ptp_tx_hwtstamp - utility function which checks for TX time stamp   * @adapter: Board private structure.   * @@ -643,7 +699,6 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,  	else  		wr32(E1000_ETQF(3), 0); -#define PTP_PORT 319  	/* L4 Queue Filter[3]: filter by destination port and protocol */  	if (is_l4) {  		u32 ftqf = (IPPROTO_UDP /* UDP */ @@ -652,12 +707,12 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,  			| E1000_FTQF_MASK); /* mask all inputs */  		ftqf &= ~E1000_FTQF_MASK_PROTO_BP; /* enable protocol check */ -		wr32(E1000_IMIR(3), htons(PTP_PORT)); +		wr32(E1000_IMIR(3), htons(PTP_EV_PORT));  		wr32(E1000_IMIREXT(3),  		     (E1000_IMIREXT_SIZE_BP | E1000_IMIREXT_CTRL_BP));  		if (hw->mac.type == e1000_82576) {  			/* enable source port check */ -			wr32(E1000_SPQF(3), htons(PTP_PORT)); +			wr32(E1000_SPQF(3), htons(PTP_EV_PORT));  			ftqf &= ~E1000_FTQF_MASK_SOURCE_PORT_BP;  		}  		wr32(E1000_FTQF(3), ftqf); @@ -801,6 +856,10 @@ void igb_ptp_stop(struct igb_adapter *adapter)  	}  	cancel_work_sync(&adapter->ptp_tx_work); +	if (adapter->ptp_tx_skb) { +		dev_kfree_skb_any(adapter->ptp_tx_skb); +		adapter->ptp_tx_skb = NULL; +	}  	if (adapter->ptp_clock) {  		ptp_clock_unregister(adapter->ptp_clock);  |