diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igb/igb_ptp.c')
| -rw-r--r-- | drivers/net/ethernet/intel/igb/igb_ptp.c | 192 | 
1 files changed, 178 insertions, 14 deletions
| diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 0011b15e678c..6580fcddb4be 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -69,6 +69,7 @@  #define IGB_NBITS_82580			40  static void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter); +static void igb_ptp_sdp_init(struct igb_adapter *adapter);  /* SYSTIM read access for the 82576 */  static u64 igb_ptp_read_82576(const struct cyclecounter *cc) @@ -507,6 +508,158 @@ static void igb_pin_perout(struct igb_adapter *igb, int chan, int pin, int freq)  	wr32(E1000_CTRL_EXT, ctrl_ext);  } +static int igb_ptp_feature_enable_82580(struct ptp_clock_info *ptp, +					struct ptp_clock_request *rq, int on) +{ +	struct igb_adapter *igb = +		container_of(ptp, struct igb_adapter, ptp_caps); +	u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh, systiml, +		systimh, level_mask, level, rem; +	struct e1000_hw *hw = &igb->hw; +	struct timespec64 ts, start; +	unsigned long flags; +	u64 systim, now; +	int pin = -1; +	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; + +		if (on) { +			pin = ptp_find_pin(igb->ptp_clock, PTP_PF_EXTTS, +					   rq->extts.index); +			if (pin < 0) +				return -EBUSY; +		} +		if (rq->extts.index == 1) { +			tsauxc_mask = TSAUXC_EN_TS1; +			tsim_mask = TSINTR_AUTT1; +		} else { +			tsauxc_mask = TSAUXC_EN_TS0; +			tsim_mask = TSINTR_AUTT0; +		} +		spin_lock_irqsave(&igb->tmreg_lock, flags); +		tsauxc = rd32(E1000_TSAUXC); +		tsim = rd32(E1000_TSIM); +		if (on) { +			igb_pin_extts(igb, rq->extts.index, pin); +			tsauxc |= tsauxc_mask; +			tsim |= tsim_mask; +		} else { +			tsauxc &= ~tsauxc_mask; +			tsim &= ~tsim_mask; +		} +		wr32(E1000_TSAUXC, tsauxc); +		wr32(E1000_TSIM, tsim); +		spin_unlock_irqrestore(&igb->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(igb->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 < 8LL) +			return -EINVAL; +		ts = ns_to_timespec64(ns); +		if (rq->perout.index == 1) { +			tsauxc_mask = TSAUXC_EN_TT1; +			tsim_mask = TSINTR_TT1; +			trgttiml = E1000_TRGTTIML1; +			trgttimh = E1000_TRGTTIMH1; +		} else { +			tsauxc_mask = TSAUXC_EN_TT0; +			tsim_mask = TSINTR_TT0; +			trgttiml = E1000_TRGTTIML0; +			trgttimh = E1000_TRGTTIMH0; +		} +		spin_lock_irqsave(&igb->tmreg_lock, flags); +		tsauxc = rd32(E1000_TSAUXC); +		tsim = rd32(E1000_TSIM); +		if (rq->perout.index == 1) { +			tsauxc &= ~(TSAUXC_EN_TT1 | TSAUXC_EN_CLK1 | TSAUXC_ST1); +			tsim &= ~TSINTR_TT1; +		} else { +			tsauxc &= ~(TSAUXC_EN_TT0 | TSAUXC_EN_CLK0 | TSAUXC_ST0); +			tsim &= ~TSINTR_TT0; +		} +		if (on) { +			int i = rq->perout.index; + +			/* read systim registers in sequence */ +			rd32(E1000_SYSTIMR); +			systiml = rd32(E1000_SYSTIML); +			systimh = rd32(E1000_SYSTIMH); +			systim = (((u64)(systimh & 0xFF)) << 32) | ((u64)systiml); +			now = timecounter_cyc2time(&igb->tc, systim); + +			if (pin < 2) { +				level_mask = (i == 1) ? 0x80000 : 0x40000; +				level = (rd32(E1000_CTRL) & level_mask) ? 1 : 0; +			} else { +				level_mask = (i == 1) ? 0x80 : 0x40; +				level = (rd32(E1000_CTRL_EXT) & level_mask) ? 1 : 0; +			} + +			div_u64_rem(now, ns, &rem); +			systim = systim + (ns - rem); + +			/* synchronize pin level with rising/falling edges */ +			div_u64_rem(now, ns << 1, &rem); +			if (rem < ns) { +				/* first half of period */ +				if (level == 0) { +					/* output is already low, skip this period */ +					systim += ns; +				} +			} else { +				/* second half of period */ +				if (level == 1) { +					/* output is already high, skip this period */ +					systim += ns; +				} +			} + +			start = ns_to_timespec64(systim + (ns - rem)); +			igb_pin_perout(igb, i, pin, 0); +			igb->perout[i].start.tv_sec = start.tv_sec; +			igb->perout[i].start.tv_nsec = start.tv_nsec; +			igb->perout[i].period.tv_sec = ts.tv_sec; +			igb->perout[i].period.tv_nsec = ts.tv_nsec; + +			wr32(trgttiml, (u32)systim); +			wr32(trgttimh, ((u32)(systim >> 32)) & 0xFF); +			tsauxc |= tsauxc_mask; +			tsim |= tsim_mask; +		} +		wr32(E1000_TSAUXC, tsauxc); +		wr32(E1000_TSIM, tsim); +		spin_unlock_irqrestore(&igb->tmreg_lock, flags); +		return 0; + +	case PTP_CLK_REQ_PPS: +		return -EOPNOTSUPP; +	} + +	return -EOPNOTSUPP; +} +  static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp,  				       struct ptp_clock_request *rq, int on)  { @@ -1015,10 +1168,6 @@ static int igb_ptp_set_timestamp_mode(struct igb_adapter *adapter,  	bool is_l2 = false;  	u32 regval; -	/* reserved for future extensions */ -	if (config->flags) -		return -EINVAL; -  	switch (config->tx_type) {  	case HWTSTAMP_TX_OFF:  		tsync_tx_ctl = 0; @@ -1192,7 +1341,6 @@ void igb_ptp_init(struct igb_adapter *adapter)  {  	struct e1000_hw *hw = &adapter->hw;  	struct net_device *netdev = adapter->netdev; -	int i;  	switch (hw->mac.type) {  	case e1000_82576: @@ -1215,16 +1363,21 @@ void igb_ptp_init(struct igb_adapter *adapter)  	case e1000_82580:  	case e1000_i354:  	case e1000_i350: +		igb_ptp_sdp_init(adapter);  		snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);  		adapter->ptp_caps.owner = THIS_MODULE;  		adapter->ptp_caps.max_adj = 62499999; -		adapter->ptp_caps.n_ext_ts = 0; +		adapter->ptp_caps.n_ext_ts = IGB_N_EXTTS; +		adapter->ptp_caps.n_per_out = IGB_N_PEROUT; +		adapter->ptp_caps.n_pins = IGB_N_SDP;  		adapter->ptp_caps.pps = 0; +		adapter->ptp_caps.pin_config = adapter->sdp_config;  		adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580;  		adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;  		adapter->ptp_caps.gettimex64 = igb_ptp_gettimex_82580;  		adapter->ptp_caps.settime64 = igb_ptp_settime_82576; -		adapter->ptp_caps.enable = igb_ptp_feature_enable; +		adapter->ptp_caps.enable = igb_ptp_feature_enable_82580; +		adapter->ptp_caps.verify = igb_ptp_verify_pin;  		adapter->cc.read = igb_ptp_read_82580;  		adapter->cc.mask = CYCLECOUNTER_MASK(IGB_NBITS_82580);  		adapter->cc.mult = 1; @@ -1233,13 +1386,7 @@ void igb_ptp_init(struct igb_adapter *adapter)  		break;  	case e1000_i210:  	case e1000_i211: -		for (i = 0; i < IGB_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; -		} +		igb_ptp_sdp_init(adapter);  		snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);  		adapter->ptp_caps.owner = THIS_MODULE;  		adapter->ptp_caps.max_adj = 62499999; @@ -1285,6 +1432,23 @@ void igb_ptp_init(struct igb_adapter *adapter)  }  /** + * igb_ptp_sdp_init - utility function which inits the SDP config structs + * @adapter: Board private structure. + **/ +void igb_ptp_sdp_init(struct igb_adapter *adapter) +{ +	int i; + +	for (i = 0; i < IGB_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; +	} +} + +/**   * igb_ptp_suspend - Disable PTP work items and prepare for suspend   * @adapter: Board private structure   * |