diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igc/igc_ptp.c')
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ptp.c | 113 | 
1 files changed, 74 insertions, 39 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index b6c60b3d0e3a..cfb589f9da56 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -963,45 +963,62 @@ static void igc_ptm_log_error(struct igc_adapter *adapter, u32 ptm_stat)  	}  } +/* The PTM lock: adapter->ptm_lock must be held when calling igc_ptm_trigger() */ +static void igc_ptm_trigger(struct igc_hw *hw) +{ +	u32 ctrl; + +	/* To "manually" start the PTM cycle we need to set the +	 * trigger (TRIG) bit +	 */ +	ctrl = rd32(IGC_PTM_CTRL); +	ctrl |= IGC_PTM_CTRL_TRIG; +	wr32(IGC_PTM_CTRL, ctrl); +	/* Perform flush after write to CTRL register otherwise +	 * transaction may not start +	 */ +	wrfl(); +} + +/* The PTM lock: adapter->ptm_lock must be held when calling igc_ptm_reset() */ +static void igc_ptm_reset(struct igc_hw *hw) +{ +	u32 ctrl; + +	ctrl = rd32(IGC_PTM_CTRL); +	ctrl &= ~IGC_PTM_CTRL_TRIG; +	wr32(IGC_PTM_CTRL, ctrl); +	/* Write to clear all status */ +	wr32(IGC_PTM_STAT, IGC_PTM_STAT_ALL); +} +  static int igc_phc_get_syncdevicetime(ktime_t *device,  				      struct system_counterval_t *system,  				      void *ctx)  { -	u32 stat, t2_curr_h, t2_curr_l, ctrl;  	struct igc_adapter *adapter = ctx;  	struct igc_hw *hw = &adapter->hw; +	u32 stat, t2_curr_h, t2_curr_l;  	int err, count = 100;  	ktime_t t1, t2_curr; -	/* Get a snapshot of system clocks to use as historic value. */ -	ktime_get_snapshot(&adapter->snapshot); - +	/* Doing this in a loop because in the event of a +	 * badly timed (ha!) system clock adjustment, we may +	 * get PTM errors from the PCI root, but these errors +	 * are transitory. Repeating the process returns valid +	 * data eventually. +	 */  	do { -		/* Doing this in a loop because in the event of a -		 * badly timed (ha!) system clock adjustment, we may -		 * get PTM errors from the PCI root, but these errors -		 * are transitory. Repeating the process returns valid -		 * data eventually. -		 */ +		/* Get a snapshot of system clocks to use as historic value. */ +		ktime_get_snapshot(&adapter->snapshot); -		/* To "manually" start the PTM cycle we need to clear and -		 * then set again the TRIG bit. -		 */ -		ctrl = rd32(IGC_PTM_CTRL); -		ctrl &= ~IGC_PTM_CTRL_TRIG; -		wr32(IGC_PTM_CTRL, ctrl); -		ctrl |= IGC_PTM_CTRL_TRIG; -		wr32(IGC_PTM_CTRL, ctrl); - -		/* The cycle only starts "for real" when software notifies -		 * that it has read the registers, this is done by setting -		 * VALID bit. -		 */ -		wr32(IGC_PTM_STAT, IGC_PTM_STAT_VALID); +		igc_ptm_trigger(hw);  		err = readx_poll_timeout(rd32, IGC_PTM_STAT, stat,  					 stat, IGC_PTM_STAT_SLEEP,  					 IGC_PTM_STAT_TIMEOUT); +		igc_ptm_reset(hw); +  		if (err < 0) {  			netdev_err(adapter->netdev, "Timeout reading IGC_PTM_STAT register\n");  			return err; @@ -1010,15 +1027,7 @@ static int igc_phc_get_syncdevicetime(ktime_t *device,  		if ((stat & IGC_PTM_STAT_VALID) == IGC_PTM_STAT_VALID)  			break; -		if (stat & ~IGC_PTM_STAT_VALID) { -			/* An error occurred, log it. */ -			igc_ptm_log_error(adapter, stat); -			/* The STAT register is write-1-to-clear (W1C), -			 * so write the previous error status to clear it. -			 */ -			wr32(IGC_PTM_STAT, stat); -			continue; -		} +		igc_ptm_log_error(adapter, stat);  	} while (--count);  	if (!count) { @@ -1050,9 +1059,16 @@ static int igc_ptp_getcrosststamp(struct ptp_clock_info *ptp,  {  	struct igc_adapter *adapter = container_of(ptp, struct igc_adapter,  						   ptp_caps); +	int ret; + +	/* This blocks until any in progress PTM transactions complete */ +	mutex_lock(&adapter->ptm_lock); -	return get_device_system_crosststamp(igc_phc_get_syncdevicetime, -					     adapter, &adapter->snapshot, cts); +	ret = get_device_system_crosststamp(igc_phc_get_syncdevicetime, +					    adapter, &adapter->snapshot, cts); +	mutex_unlock(&adapter->ptm_lock); + +	return ret;  }  static int igc_ptp_getcyclesx64(struct ptp_clock_info *ptp, @@ -1154,6 +1170,7 @@ void igc_ptp_init(struct igc_adapter *adapter)  	spin_lock_init(&adapter->ptp_tx_lock);  	spin_lock_init(&adapter->free_timer_lock);  	spin_lock_init(&adapter->tmreg_lock); +	mutex_init(&adapter->ptm_lock);  	adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;  	adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF; @@ -1166,6 +1183,7 @@ void igc_ptp_init(struct igc_adapter *adapter)  	if (IS_ERR(adapter->ptp_clock)) {  		adapter->ptp_clock = NULL;  		netdev_err(netdev, "ptp_clock_register failed\n"); +		mutex_destroy(&adapter->ptm_lock);  	} else if (adapter->ptp_clock) {  		netdev_info(netdev, "PHC added\n");  		adapter->ptp_flags |= IGC_PTP_ENABLED; @@ -1195,10 +1213,12 @@ static void igc_ptm_stop(struct igc_adapter *adapter)  	struct igc_hw *hw = &adapter->hw;  	u32 ctrl; +	mutex_lock(&adapter->ptm_lock);  	ctrl = rd32(IGC_PTM_CTRL);  	ctrl &= ~IGC_PTM_CTRL_EN;  	wr32(IGC_PTM_CTRL, ctrl); +	mutex_unlock(&adapter->ptm_lock);  }  /** @@ -1229,13 +1249,18 @@ void igc_ptp_suspend(struct igc_adapter *adapter)   **/  void igc_ptp_stop(struct igc_adapter *adapter)  { +	if (!(adapter->ptp_flags & IGC_PTP_ENABLED)) +		return; +  	igc_ptp_suspend(adapter); +	adapter->ptp_flags &= ~IGC_PTP_ENABLED;  	if (adapter->ptp_clock) {  		ptp_clock_unregister(adapter->ptp_clock);  		netdev_info(adapter->netdev, "PHC removed\n");  		adapter->ptp_flags &= ~IGC_PTP_ENABLED;  	} +	mutex_destroy(&adapter->ptm_lock);  }  /** @@ -1247,10 +1272,13 @@ void igc_ptp_stop(struct igc_adapter *adapter)  void igc_ptp_reset(struct igc_adapter *adapter)  {  	struct igc_hw *hw = &adapter->hw; -	u32 cycle_ctrl, ctrl; +	u32 cycle_ctrl, ctrl, stat;  	unsigned long flags;  	u32 timadj; +	if (!(adapter->ptp_flags & IGC_PTP_ENABLED)) +		return; +  	/* reset the tstamp_config */  	igc_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config); @@ -1272,6 +1300,7 @@ void igc_ptp_reset(struct igc_adapter *adapter)  		if (!igc_is_crosststamp_supported(adapter))  			break; +		mutex_lock(&adapter->ptm_lock);  		wr32(IGC_PCIE_DIG_DELAY, IGC_PCIE_DIG_DELAY_DEFAULT);  		wr32(IGC_PCIE_PHY_DELAY, IGC_PCIE_PHY_DELAY_DEFAULT); @@ -1282,14 +1311,20 @@ void igc_ptp_reset(struct igc_adapter *adapter)  		ctrl = IGC_PTM_CTRL_EN |  			IGC_PTM_CTRL_START_NOW |  			IGC_PTM_CTRL_SHRT_CYC(IGC_PTM_SHORT_CYC_DEFAULT) | -			IGC_PTM_CTRL_PTM_TO(IGC_PTM_TIMEOUT_DEFAULT) | -			IGC_PTM_CTRL_TRIG; +			IGC_PTM_CTRL_PTM_TO(IGC_PTM_TIMEOUT_DEFAULT);  		wr32(IGC_PTM_CTRL, ctrl);  		/* Force the first cycle to run. */ -		wr32(IGC_PTM_STAT, IGC_PTM_STAT_VALID); +		igc_ptm_trigger(hw); + +		if (readx_poll_timeout_atomic(rd32, IGC_PTM_STAT, stat, +					      stat, IGC_PTM_STAT_SLEEP, +					      IGC_PTM_STAT_TIMEOUT)) +			netdev_err(adapter->netdev, "Timeout reading IGC_PTM_STAT register\n"); +		igc_ptm_reset(hw); +		mutex_unlock(&adapter->ptm_lock);  		break;  	default:  		/* No work to do. */  | 
