diff options
Diffstat (limited to 'drivers/net/ethernet/intel/e1000e/ptp.c')
| -rw-r--r-- | drivers/net/ethernet/intel/e1000e/ptp.c | 85 | 
1 files changed, 85 insertions, 0 deletions
| diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c index 25a0ad5102d6..e2ff3ef75d5d 100644 --- a/drivers/net/ethernet/intel/e1000e/ptp.c +++ b/drivers/net/ethernet/intel/e1000e/ptp.c @@ -26,6 +26,12 @@  #include "e1000.h" +#ifdef CONFIG_E1000E_HWTS +#include <linux/clocksource.h> +#include <linux/ktime.h> +#include <asm/tsc.h> +#endif +  /**   * e1000e_phc_adjfreq - adjust the frequency of the hardware clock   * @ptp: ptp clock structure @@ -98,6 +104,78 @@ static int e1000e_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)  	return 0;  } +#ifdef CONFIG_E1000E_HWTS +#define MAX_HW_WAIT_COUNT (3) + +/** + * e1000e_phc_get_syncdevicetime - Callback given to timekeeping code reads system/device registers + * @device: current device time + * @system: system counter value read synchronously with device time + * @ctx: context provided by timekeeping code + * + * Read device and system (ART) clock simultaneously and return the corrected + * clock values in ns. + **/ +static int e1000e_phc_get_syncdevicetime(ktime_t *device, +					 struct system_counterval_t *system, +					 void *ctx) +{ +	struct e1000_adapter *adapter = (struct e1000_adapter *)ctx; +	struct e1000_hw *hw = &adapter->hw; +	unsigned long flags; +	int i; +	u32 tsync_ctrl; +	cycle_t dev_cycles; +	cycle_t sys_cycles; + +	tsync_ctrl = er32(TSYNCTXCTL); +	tsync_ctrl |= E1000_TSYNCTXCTL_START_SYNC | +		E1000_TSYNCTXCTL_MAX_ALLOWED_DLY_MASK; +	ew32(TSYNCTXCTL, tsync_ctrl); +	for (i = 0; i < MAX_HW_WAIT_COUNT; ++i) { +		udelay(1); +		tsync_ctrl = er32(TSYNCTXCTL); +		if (tsync_ctrl & E1000_TSYNCTXCTL_SYNC_COMP) +			break; +	} + +	if (i == MAX_HW_WAIT_COUNT) +		return -ETIMEDOUT; + +	dev_cycles = er32(SYSSTMPH); +	dev_cycles <<= 32; +	dev_cycles |= er32(SYSSTMPL); +	spin_lock_irqsave(&adapter->systim_lock, flags); +	*device = ns_to_ktime(timecounter_cyc2time(&adapter->tc, dev_cycles)); +	spin_unlock_irqrestore(&adapter->systim_lock, flags); + +	sys_cycles = er32(PLTSTMPH); +	sys_cycles <<= 32; +	sys_cycles |= er32(PLTSTMPL); +	*system = convert_art_to_tsc(sys_cycles); + +	return 0; +} + +/** + * e1000e_phc_getsynctime - Reads the current system/device cross timestamp + * @ptp: ptp clock structure + * @cts: structure containing timestamp + * + * Read device and system (ART) clock simultaneously and return the scaled + * clock values in ns. + **/ +static int e1000e_phc_getcrosststamp(struct ptp_clock_info *ptp, +				     struct system_device_crosststamp *xtstamp) +{ +	struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter, +						     ptp_clock_info); + +	return get_device_system_crosststamp(e1000e_phc_get_syncdevicetime, +						adapter, NULL, xtstamp); +} +#endif/*CONFIG_E1000E_HWTS*/ +  /**   * e1000e_phc_gettime - Reads the current time from the hardware clock   * @ptp: ptp clock structure @@ -236,6 +314,13 @@ void e1000e_ptp_init(struct e1000_adapter *adapter)  		break;  	} +#ifdef CONFIG_E1000E_HWTS +	/* CPU must have ART and GBe must be from Sunrise Point or greater */ +	if (hw->mac.type >= e1000_pch_spt && boot_cpu_has(X86_FEATURE_ART)) +		adapter->ptp_clock_info.getcrosststamp = +			e1000e_phc_getcrosststamp; +#endif/*CONFIG_E1000E_HWTS*/ +  	INIT_DELAYED_WORK(&adapter->systim_overflow_work,  			  e1000e_systim_overflow_work); | 
