diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 08:47:44 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 08:47:44 -0700 | 
| commit | bf61c8840efe60fd8f91446860b63338fb424158 (patch) | |
| tree | 7a71832407a4f0d6346db773343f4c3ae2257b19 /drivers/net/ethernet/intel/igb/igb_ptp.c | |
| parent | 5846115b30f3a881e542c8bfde59a699c1c13740 (diff) | |
| parent | 0c6a61657da78098472fd0eb71cc01f2387fa1bb (diff) | |
| download | olio-linux-3.10-bf61c8840efe60fd8f91446860b63338fb424158.tar.xz olio-linux-3.10-bf61c8840efe60fd8f91446860b63338fb424158.zip  | |
Merge branch 'next' into for-linus
Prepare first set of updates for 3.10 merge window.
Diffstat (limited to 'drivers/net/ethernet/intel/igb/igb_ptp.c')
| -rw-r--r-- | drivers/net/ethernet/intel/igb/igb_ptp.c | 167 | 
1 files changed, 120 insertions, 47 deletions
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index ee21445157a..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.   * @@ -441,18 +497,46 @@ void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter)  	adapter->ptp_tx_skb = NULL;  } -void igb_ptp_rx_hwtstamp(struct igb_q_vector *q_vector, -			 union e1000_adv_rx_desc *rx_desc, +/** + * igb_ptp_rx_pktstamp - retrieve Rx per packet timestamp + * @q_vector: Pointer to interrupt specific structure + * @va: Pointer to address containing Rx buffer + * @skb: Buffer containing timestamp and packet + * + * This function is meant to retrieve a timestamp from the first buffer of an + * incoming frame.  The value is stored in little endian format starting on + * byte 8. + */ +void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, +			 unsigned char *va, +			 struct sk_buff *skb) +{ +	__le64 *regval = (__le64 *)va; + +	/* +	 * The timestamp is recorded in little endian format. +	 * DWORD: 0        1        2        3 +	 * Field: Reserved Reserved SYSTIML  SYSTIMH +	 */ +	igb_ptp_systim_to_hwtstamp(q_vector->adapter, skb_hwtstamps(skb), +				   le64_to_cpu(regval[1])); +} + +/** + * igb_ptp_rx_rgtstamp - retrieve Rx timestamp stored in register + * @q_vector: Pointer to interrupt specific structure + * @skb: Buffer containing timestamp and packet + * + * This function is meant to retrieve a timestamp from the internal registers + * of the adapter and store it in the skb. + */ +void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector,  			 struct sk_buff *skb)  {  	struct igb_adapter *adapter = q_vector->adapter;  	struct e1000_hw *hw = &adapter->hw;  	u64 regval; -	if (!igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP | -				       E1000_RXDADV_STAT_TS)) -		return; -  	/*  	 * If this bit is set, then the RX registers contain the time stamp. No  	 * other packet will be time stamped until we read these registers, so @@ -464,18 +548,11 @@ void igb_ptp_rx_hwtstamp(struct igb_q_vector *q_vector,  	 * If nothing went wrong, then it should have a shared tx_flags that we  	 * can turn into a skb_shared_hwtstamps.  	 */ -	if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) { -		u32 *stamp = (u32 *)skb->data; -		regval = le32_to_cpu(*(stamp + 2)); -		regval |= (u64)le32_to_cpu(*(stamp + 3)) << 32; -		skb_pull(skb, IGB_TS_HDR_LEN); -	} else { -		if (!(rd32(E1000_TSYNCRXCTL) & E1000_TSYNCRXCTL_VALID)) -			return; +	if (!(rd32(E1000_TSYNCRXCTL) & E1000_TSYNCRXCTL_VALID)) +		return; -		regval = rd32(E1000_RXSTMPL); -		regval |= (u64)rd32(E1000_RXSTMPH) << 32; -	} +	regval = rd32(E1000_RXSTMPL); +	regval |= (u64)rd32(E1000_RXSTMPH) << 32;  	igb_ptp_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval);  } @@ -532,18 +609,6 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,  	case HWTSTAMP_FILTER_NONE:  		tsync_rx_ctl = 0;  		break; -	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: -	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: -	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: -	case HWTSTAMP_FILTER_ALL: -		/* -		 * register TSYNCRXCFG must be set, therefore it is not -		 * possible to time stamp both Sync and Delay_Req messages -		 * => fall back to time stamping all packets -		 */ -		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL; -		config.rx_filter = HWTSTAMP_FILTER_ALL; -		break;  	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:  		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L4_V1;  		tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE; @@ -554,31 +619,33 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,  		tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE;  		is_l4 = true;  		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: -		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L2_L4_V2; -		tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE; -		is_l2 = true; -		is_l4 = true; -		config.rx_filter = HWTSTAMP_FILTER_SOME; -		break; +	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 |= E1000_TSYNCRXCTL_TYPE_L2_L4_V2; -		tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE; -		is_l2 = true; -		is_l4 = true; -		config.rx_filter = HWTSTAMP_FILTER_SOME; -		break; -	case HWTSTAMP_FILTER_PTP_V2_EVENT: -	case HWTSTAMP_FILTER_PTP_V2_SYNC: -	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:  		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_EVENT_V2;  		config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;  		is_l2 = true;  		is_l4 = true;  		break; +	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: +	case HWTSTAMP_FILTER_ALL: +		/* 82576 cannot timestamp all packets, which it needs to do to +		 * support both V1 Sync and Delay_Req messages +		 */ +		if (hw->mac.type != e1000_82576) { +			tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL; +			config.rx_filter = HWTSTAMP_FILTER_ALL; +			break; +		} +		/* fall through */  	default: +		config.rx_filter = HWTSTAMP_FILTER_NONE;  		return -ERANGE;  	} @@ -596,6 +663,9 @@ int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,  	if ((hw->mac.type >= e1000_82580) && tsync_rx_ctl) {  		tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;  		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL; +		config.rx_filter = HWTSTAMP_FILTER_ALL; +		is_l2 = true; +		is_l4 = true;  		if ((hw->mac.type == e1000_i210) ||  		    (hw->mac.type == e1000_i211)) { @@ -629,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 */ @@ -638,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); @@ -787,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);  |