diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igc/igc_ptp.c')
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ptp.c | 320 | 
1 files changed, 307 insertions, 13 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index 545f4d0e67cf..69617d2c1be2 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -120,12 +120,289 @@ static int igc_ptp_settime_i225(struct ptp_clock_info *ptp,  	return 0;  } +static void igc_pin_direction(int pin, int input, u32 *ctrl, u32 *ctrl_ext) +{ +	u32 *ptr = pin < 2 ? ctrl : ctrl_ext; +	static const u32 mask[IGC_N_SDP] = { +		IGC_CTRL_SDP0_DIR, +		IGC_CTRL_SDP1_DIR, +		IGC_CTRL_EXT_SDP2_DIR, +		IGC_CTRL_EXT_SDP3_DIR, +	}; + +	if (input) +		*ptr &= ~mask[pin]; +	else +		*ptr |= mask[pin]; +} + +static void igc_pin_perout(struct igc_adapter *igc, int chan, int pin, int freq) +{ +	static const u32 igc_aux0_sel_sdp[IGC_N_SDP] = { +		IGC_AUX0_SEL_SDP0, IGC_AUX0_SEL_SDP1, IGC_AUX0_SEL_SDP2, IGC_AUX0_SEL_SDP3, +	}; +	static const u32 igc_aux1_sel_sdp[IGC_N_SDP] = { +		IGC_AUX1_SEL_SDP0, IGC_AUX1_SEL_SDP1, IGC_AUX1_SEL_SDP2, IGC_AUX1_SEL_SDP3, +	}; +	static const u32 igc_ts_sdp_en[IGC_N_SDP] = { +		IGC_TS_SDP0_EN, IGC_TS_SDP1_EN, IGC_TS_SDP2_EN, IGC_TS_SDP3_EN, +	}; +	static const u32 igc_ts_sdp_sel_tt0[IGC_N_SDP] = { +		IGC_TS_SDP0_SEL_TT0, IGC_TS_SDP1_SEL_TT0, +		IGC_TS_SDP2_SEL_TT0, IGC_TS_SDP3_SEL_TT0, +	}; +	static const u32 igc_ts_sdp_sel_tt1[IGC_N_SDP] = { +		IGC_TS_SDP0_SEL_TT1, IGC_TS_SDP1_SEL_TT1, +		IGC_TS_SDP2_SEL_TT1, IGC_TS_SDP3_SEL_TT1, +	}; +	static const u32 igc_ts_sdp_sel_fc0[IGC_N_SDP] = { +		IGC_TS_SDP0_SEL_FC0, IGC_TS_SDP1_SEL_FC0, +		IGC_TS_SDP2_SEL_FC0, IGC_TS_SDP3_SEL_FC0, +	}; +	static const u32 igc_ts_sdp_sel_fc1[IGC_N_SDP] = { +		IGC_TS_SDP0_SEL_FC1, IGC_TS_SDP1_SEL_FC1, +		IGC_TS_SDP2_SEL_FC1, IGC_TS_SDP3_SEL_FC1, +	}; +	static const u32 igc_ts_sdp_sel_clr[IGC_N_SDP] = { +		IGC_TS_SDP0_SEL_FC1, IGC_TS_SDP1_SEL_FC1, +		IGC_TS_SDP2_SEL_FC1, IGC_TS_SDP3_SEL_FC1, +	}; +	struct igc_hw *hw = &igc->hw; +	u32 ctrl, ctrl_ext, tssdp = 0; + +	ctrl = rd32(IGC_CTRL); +	ctrl_ext = rd32(IGC_CTRL_EXT); +	tssdp = rd32(IGC_TSSDP); + +	igc_pin_direction(pin, 0, &ctrl, &ctrl_ext); + +	/* Make sure this pin is not enabled as an input. */ +	if ((tssdp & IGC_AUX0_SEL_SDP3) == igc_aux0_sel_sdp[pin]) +		tssdp &= ~IGC_AUX0_TS_SDP_EN; + +	if ((tssdp & IGC_AUX1_SEL_SDP3) == igc_aux1_sel_sdp[pin]) +		tssdp &= ~IGC_AUX1_TS_SDP_EN; + +	tssdp &= ~igc_ts_sdp_sel_clr[pin]; +	if (freq) { +		if (chan == 1) +			tssdp |= igc_ts_sdp_sel_fc1[pin]; +		else +			tssdp |= igc_ts_sdp_sel_fc0[pin]; +	} else { +		if (chan == 1) +			tssdp |= igc_ts_sdp_sel_tt1[pin]; +		else +			tssdp |= igc_ts_sdp_sel_tt0[pin]; +	} +	tssdp |= igc_ts_sdp_en[pin]; + +	wr32(IGC_TSSDP, tssdp); +	wr32(IGC_CTRL, ctrl); +	wr32(IGC_CTRL_EXT, ctrl_ext); +} + +static void igc_pin_extts(struct igc_adapter *igc, int chan, int pin) +{ +	static const u32 igc_aux0_sel_sdp[IGC_N_SDP] = { +		IGC_AUX0_SEL_SDP0, IGC_AUX0_SEL_SDP1, IGC_AUX0_SEL_SDP2, IGC_AUX0_SEL_SDP3, +	}; +	static const u32 igc_aux1_sel_sdp[IGC_N_SDP] = { +		IGC_AUX1_SEL_SDP0, IGC_AUX1_SEL_SDP1, IGC_AUX1_SEL_SDP2, IGC_AUX1_SEL_SDP3, +	}; +	static const u32 igc_ts_sdp_en[IGC_N_SDP] = { +		IGC_TS_SDP0_EN, IGC_TS_SDP1_EN, IGC_TS_SDP2_EN, IGC_TS_SDP3_EN, +	}; +	struct igc_hw *hw = &igc->hw; +	u32 ctrl, ctrl_ext, tssdp = 0; + +	ctrl = rd32(IGC_CTRL); +	ctrl_ext = rd32(IGC_CTRL_EXT); +	tssdp = rd32(IGC_TSSDP); + +	igc_pin_direction(pin, 1, &ctrl, &ctrl_ext); + +	/* Make sure this pin is not enabled as an output. */ +	tssdp &= ~igc_ts_sdp_en[pin]; + +	if (chan == 1) { +		tssdp &= ~IGC_AUX1_SEL_SDP3; +		tssdp |= igc_aux1_sel_sdp[pin] | IGC_AUX1_TS_SDP_EN; +	} else { +		tssdp &= ~IGC_AUX0_SEL_SDP3; +		tssdp |= igc_aux0_sel_sdp[pin] | IGC_AUX0_TS_SDP_EN; +	} + +	wr32(IGC_TSSDP, tssdp); +	wr32(IGC_CTRL, ctrl); +	wr32(IGC_CTRL_EXT, ctrl_ext); +} +  static int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp,  				       struct ptp_clock_request *rq, int on)  { +	struct igc_adapter *igc = +		container_of(ptp, struct igc_adapter, ptp_caps); +	struct igc_hw *hw = &igc->hw; +	unsigned long flags; +	struct timespec64 ts; +	int use_freq = 0, pin = -1; +	u32 tsim, tsauxc, tsauxc_mask, tsim_mask, trgttiml, trgttimh, freqout; +	s64 ns; + +	switch (rq->type) { +	case PTP_CLK_REQ_EXTTS: +		/* Reject requests with unsupported flags */ +		if (rq->extts.flags & ~(PTP_ENABLE_FEATURE | +					PTP_RISING_EDGE | +					PTP_FALLING_EDGE | +					PTP_STRICT_FLAGS)) +			return -EOPNOTSUPP; + +		/* Reject requests failing to enable both edges. */ +		if ((rq->extts.flags & PTP_STRICT_FLAGS) && +		    (rq->extts.flags & PTP_ENABLE_FEATURE) && +		    (rq->extts.flags & PTP_EXTTS_EDGES) != PTP_EXTTS_EDGES) +			return -EOPNOTSUPP; + +		if (on) { +			pin = ptp_find_pin(igc->ptp_clock, PTP_PF_EXTTS, +					   rq->extts.index); +			if (pin < 0) +				return -EBUSY; +		} +		if (rq->extts.index == 1) { +			tsauxc_mask = IGC_TSAUXC_EN_TS1; +			tsim_mask = IGC_TSICR_AUTT1; +		} else { +			tsauxc_mask = IGC_TSAUXC_EN_TS0; +			tsim_mask = IGC_TSICR_AUTT0; +		} +		spin_lock_irqsave(&igc->tmreg_lock, flags); +		tsauxc = rd32(IGC_TSAUXC); +		tsim = rd32(IGC_TSIM); +		if (on) { +			igc_pin_extts(igc, rq->extts.index, pin); +			tsauxc |= tsauxc_mask; +			tsim |= tsim_mask; +		} else { +			tsauxc &= ~tsauxc_mask; +			tsim &= ~tsim_mask; +		} +		wr32(IGC_TSAUXC, tsauxc); +		wr32(IGC_TSIM, tsim); +		spin_unlock_irqrestore(&igc->tmreg_lock, flags); +		return 0; + +	case PTP_CLK_REQ_PEROUT: +		/* Reject requests with unsupported flags */ +		if (rq->perout.flags) +			return -EOPNOTSUPP; + +		if (on) { +			pin = ptp_find_pin(igc->ptp_clock, PTP_PF_PEROUT, +					   rq->perout.index); +			if (pin < 0) +				return -EBUSY; +		} +		ts.tv_sec = rq->perout.period.sec; +		ts.tv_nsec = rq->perout.period.nsec; +		ns = timespec64_to_ns(&ts); +		ns = ns >> 1; +		if (on && (ns <= 70000000LL || ns == 125000000LL || +			   ns == 250000000LL || ns == 500000000LL)) { +			if (ns < 8LL) +				return -EINVAL; +			use_freq = 1; +		} +		ts = ns_to_timespec64(ns); +		if (rq->perout.index == 1) { +			if (use_freq) { +				tsauxc_mask = IGC_TSAUXC_EN_CLK1; +				tsim_mask = 0; +			} else { +				tsauxc_mask = IGC_TSAUXC_EN_TT1; +				tsim_mask = IGC_TSICR_TT1; +			} +			trgttiml = IGC_TRGTTIML1; +			trgttimh = IGC_TRGTTIMH1; +			freqout = IGC_FREQOUT1; +		} else { +			if (use_freq) { +				tsauxc_mask = IGC_TSAUXC_EN_CLK0; +				tsim_mask = 0; +			} else { +				tsauxc_mask = IGC_TSAUXC_EN_TT0; +				tsim_mask = IGC_TSICR_TT0; +			} +			trgttiml = IGC_TRGTTIML0; +			trgttimh = IGC_TRGTTIMH0; +			freqout = IGC_FREQOUT0; +		} +		spin_lock_irqsave(&igc->tmreg_lock, flags); +		tsauxc = rd32(IGC_TSAUXC); +		tsim = rd32(IGC_TSIM); +		if (rq->perout.index == 1) { +			tsauxc &= ~(IGC_TSAUXC_EN_TT1 | IGC_TSAUXC_EN_CLK1); +			tsim &= ~IGC_TSICR_TT1; +		} else { +			tsauxc &= ~(IGC_TSAUXC_EN_TT0 | IGC_TSAUXC_EN_CLK0); +			tsim &= ~IGC_TSICR_TT0; +		} +		if (on) { +			int i = rq->perout.index; + +			igc_pin_perout(igc, i, pin, use_freq); +			igc->perout[i].start.tv_sec = rq->perout.start.sec; +			igc->perout[i].start.tv_nsec = rq->perout.start.nsec; +			igc->perout[i].period.tv_sec = ts.tv_sec; +			igc->perout[i].period.tv_nsec = ts.tv_nsec; +			wr32(trgttimh, rq->perout.start.sec); +			/* For now, always select timer 0 as source. */ +			wr32(trgttiml, rq->perout.start.nsec | IGC_TT_IO_TIMER_SEL_SYSTIM0); +			if (use_freq) +				wr32(freqout, ns); +			tsauxc |= tsauxc_mask; +			tsim |= tsim_mask; +		} +		wr32(IGC_TSAUXC, tsauxc); +		wr32(IGC_TSIM, tsim); +		spin_unlock_irqrestore(&igc->tmreg_lock, flags); +		return 0; + +	case PTP_CLK_REQ_PPS: +		spin_lock_irqsave(&igc->tmreg_lock, flags); +		tsim = rd32(IGC_TSIM); +		if (on) +			tsim |= IGC_TSICR_SYS_WRAP; +		else +			tsim &= ~IGC_TSICR_SYS_WRAP; +		igc->pps_sys_wrap_on = on; +		wr32(IGC_TSIM, tsim); +		spin_unlock_irqrestore(&igc->tmreg_lock, flags); +		return 0; + +	default: +		break; +	} +  	return -EOPNOTSUPP;  } +static int igc_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin, +			      enum ptp_pin_function func, unsigned int chan) +{ +	switch (func) { +	case PTP_PF_NONE: +	case PTP_PF_EXTTS: +	case PTP_PF_PEROUT: +		break; +	case PTP_PF_PHYSYNC: +		return -1; +	} +	return 0; +} +  /**   * igc_ptp_systim_to_hwtstamp - convert system time value to HW timestamp   * @adapter: board private structure @@ -153,20 +430,20 @@ static void igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter,  /**   * igc_ptp_rx_pktstamp - Retrieve timestamp from Rx packet buffer - * @q_vector: Pointer to interrupt specific structure - * @va: Pointer to address containing Rx buffer - * @skb: Buffer containing timestamp and packet + * @adapter: Pointer to adapter the packet buffer belongs to + * @buf: Pointer to packet buffer   *   * This function retrieves the timestamp saved in the beginning of packet   * buffer. While two timestamps are available, one in timer0 reference and the   * other in timer1 reference, this function considers only the timestamp in   * timer0 reference. + * + * Returns timestamp value.   */ -void igc_ptp_rx_pktstamp(struct igc_q_vector *q_vector, __le32 *va, -			 struct sk_buff *skb) +ktime_t igc_ptp_rx_pktstamp(struct igc_adapter *adapter, __le32 *buf)  { -	struct igc_adapter *adapter = q_vector->adapter; -	u64 regval; +	ktime_t timestamp; +	u32 secs, nsecs;  	int adjust;  	/* Timestamps are saved in little endian at the beginning of the packet @@ -178,9 +455,10 @@ void igc_ptp_rx_pktstamp(struct igc_q_vector *q_vector, __le32 *va,  	 * SYSTIML holds the nanoseconds part while SYSTIMH holds the seconds  	 * part of the timestamp.  	 */ -	regval = le32_to_cpu(va[2]); -	regval |= (u64)le32_to_cpu(va[3]) << 32; -	igc_ptp_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval); +	nsecs = le32_to_cpu(buf[2]); +	secs = le32_to_cpu(buf[3]); + +	timestamp = ktime_set(secs, nsecs);  	/* Adjust timestamp for the RX latency based on link speed */  	switch (adapter->link_speed) { @@ -201,8 +479,8 @@ void igc_ptp_rx_pktstamp(struct igc_q_vector *q_vector, __le32 *va,  		netdev_warn_once(adapter->netdev, "Imprecise timestamp\n");  		break;  	} -	skb_hwtstamps(skb)->hwtstamp = -		ktime_sub_ns(skb_hwtstamps(skb)->hwtstamp, adjust); + +	return ktime_sub_ns(timestamp, adjust);  }  static void igc_ptp_disable_rx_timestamp(struct igc_adapter *adapter) @@ -485,9 +763,17 @@ void igc_ptp_init(struct igc_adapter *adapter)  {  	struct net_device *netdev = adapter->netdev;  	struct igc_hw *hw = &adapter->hw; +	int i;  	switch (hw->mac.type) {  	case igc_i225: +		for (i = 0; i < IGC_N_SDP; i++) { +			struct ptp_pin_desc *ppd = &adapter->sdp_config[i]; + +			snprintf(ppd->name, sizeof(ppd->name), "SDP%d", i); +			ppd->index = i; +			ppd->func = PTP_PF_NONE; +		}  		snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);  		adapter->ptp_caps.owner = THIS_MODULE;  		adapter->ptp_caps.max_adj = 62499999; @@ -496,6 +782,12 @@ void igc_ptp_init(struct igc_adapter *adapter)  		adapter->ptp_caps.gettimex64 = igc_ptp_gettimex64_i225;  		adapter->ptp_caps.settime64 = igc_ptp_settime_i225;  		adapter->ptp_caps.enable = igc_ptp_feature_enable_i225; +		adapter->ptp_caps.pps = 1; +		adapter->ptp_caps.pin_config = adapter->sdp_config; +		adapter->ptp_caps.n_ext_ts = IGC_N_EXTTS; +		adapter->ptp_caps.n_per_out = IGC_N_PEROUT; +		adapter->ptp_caps.n_pins = IGC_N_SDP; +		adapter->ptp_caps.verify = igc_ptp_verify_pin;  		break;  	default:  		adapter->ptp_clock = NULL; @@ -597,7 +889,9 @@ void igc_ptp_reset(struct igc_adapter *adapter)  	case igc_i225:  		wr32(IGC_TSAUXC, 0x0);  		wr32(IGC_TSSDP, 0x0); -		wr32(IGC_TSIM, IGC_TSICR_INTERRUPTS); +		wr32(IGC_TSIM, +		     IGC_TSICR_INTERRUPTS | +		     (adapter->pps_sys_wrap_on ? IGC_TSICR_SYS_WRAP : 0));  		wr32(IGC_IMS, IGC_IMS_TS);  		break;  	default:  |