diff options
Diffstat (limited to 'drivers/net/ethernet/sfc/ptp.c')
| -rw-r--r-- | drivers/net/ethernet/sfc/ptp.c | 370 | 
1 files changed, 304 insertions, 66 deletions
| diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c index caa89bf7603e..f21661532ed3 100644 --- a/drivers/net/ethernet/sfc/ptp.c +++ b/drivers/net/ethernet/sfc/ptp.c @@ -149,18 +149,14 @@ enum ptp_packet_state {  /* Maximum parts-per-billion adjustment that is acceptable */  #define MAX_PPB			1000000 -/* Number of bits required to hold the above */ -#define	MAX_PPB_BITS		20 - -/* Number of extra bits allowed when calculating fractional ns. - * EXTRA_BITS + MC_CMD_PTP_IN_ADJUST_BITS + MAX_PPB_BITS should - * be less than 63. - */ -#define	PPB_EXTRA_BITS		2 -  /* Precalculate scale word to avoid long long division at runtime */ -#define	PPB_SCALE_WORD	((1LL << (PPB_EXTRA_BITS + MC_CMD_PTP_IN_ADJUST_BITS +\ -			MAX_PPB_BITS)) / 1000000000LL) +/* This is equivalent to 2^66 / 10^9. */ +#define PPB_SCALE_WORD  ((1LL << (57)) / 1953125LL) + +/* How much to shift down after scaling to convert to FP40 */ +#define PPB_SHIFT_FP40		26 +/* ... and FP44. */ +#define PPB_SHIFT_FP44		22  #define PTP_SYNC_ATTEMPTS	4 @@ -218,8 +214,8 @@ struct efx_ptp_timeset {   * @channel: The PTP channel (Siena only)   * @rx_ts_inline: Flag for whether RX timestamps are inline (else they are   *	separate events) - * @rxq: Receive queue (awaiting timestamps) - * @txq: Transmit queue + * @rxq: Receive SKB queue (awaiting timestamps) + * @txq: Transmit SKB queue   * @evt_list: List of MC receive events awaiting packets   * @evt_free_list: List of free events   * @evt_lock: Lock for manipulating evt_list and evt_free_list @@ -233,19 +229,36 @@ struct efx_ptp_timeset {   * @config: Current timestamp configuration   * @enabled: PTP operation enabled   * @mode: Mode in which PTP operating (PTP version) - * @time_format: Time format supported by this NIC   * @ns_to_nic_time: Function to convert from scalar nanoseconds to NIC time   * @nic_to_kernel_time: Function to convert from NIC to kernel time + * @nic_time.minor_max: Wrap point for NIC minor times + * @nic_time.sync_event_diff_min: Minimum acceptable difference between time + * in packet prefix and last MCDI time sync event i.e. how much earlier than + * the last sync event time a packet timestamp can be. + * @nic_time.sync_event_diff_max: Maximum acceptable difference between time + * in packet prefix and last MCDI time sync event i.e. how much later than + * the last sync event time a packet timestamp can be. + * @nic_time.sync_event_minor_shift: Shift required to make minor time from + * field in MCDI time sync event.   * @min_synchronisation_ns: Minimum acceptable corrected sync window - * @ts_corrections.tx: Required driver correction of transmit timestamps - * @ts_corrections.rx: Required driver correction of receive timestamps + * @capabilities: Capabilities flags from the NIC + * @ts_corrections.ptp_tx: Required driver correction of PTP packet transmit + *                         timestamps + * @ts_corrections.ptp_rx: Required driver correction of PTP packet receive + *                         timestamps   * @ts_corrections.pps_out: PPS output error (information only)   * @ts_corrections.pps_in: Required driver correction of PPS input timestamps + * @ts_corrections.general_tx: Required driver correction of general packet + *                             transmit timestamps + * @ts_corrections.general_rx: Required driver correction of general packet + *                             receive timestamps   * @evt_frags: Partly assembled PTP events   * @evt_frag_idx: Current fragment number   * @evt_code: Last event code   * @start: Address at which MC indicates ready for synchronisation   * @host_time_pps: Host time at last PPS + * @adjfreq_ppb_shift: Shift required to convert scaled parts-per-billion + * frequency adjustment into a fixed point fractional nanosecond format.   * @current_adjfreq: Current ppb adjustment.   * @phc_clock: Pointer to registered phc device (if primary function)   * @phc_clock_info: Registration structure for phc device @@ -264,6 +277,7 @@ struct efx_ptp_timeset {   * @oversize_sync_windows: Number of corrected sync windows that are too large   * @rx_no_timestamp: Number of packets received without a timestamp.   * @timeset: Last set of synchronisation statistics. + * @xmit_skb: Transmit SKB function.   */  struct efx_ptp_data {  	struct efx_nic *efx; @@ -284,22 +298,31 @@ struct efx_ptp_data {  	struct hwtstamp_config config;  	bool enabled;  	unsigned int mode; -	unsigned int time_format;  	void (*ns_to_nic_time)(s64 ns, u32 *nic_major, u32 *nic_minor);  	ktime_t (*nic_to_kernel_time)(u32 nic_major, u32 nic_minor,  				      s32 correction); +	struct { +		u32 minor_max; +		u32 sync_event_diff_min; +		u32 sync_event_diff_max; +		unsigned int sync_event_minor_shift; +	} nic_time;  	unsigned int min_synchronisation_ns; +	unsigned int capabilities;  	struct { -		s32 tx; -		s32 rx; +		s32 ptp_tx; +		s32 ptp_rx;  		s32 pps_out;  		s32 pps_in; +		s32 general_tx; +		s32 general_rx;  	} ts_corrections;  	efx_qword_t evt_frags[MAX_EVENT_FRAGS];  	int evt_frag_idx;  	int evt_code;  	struct efx_buffer start;  	struct pps_event_time host_time_pps; +	unsigned int adjfreq_ppb_shift;  	s64 current_adjfreq;  	struct ptp_clock *phc_clock;  	struct ptp_clock_info phc_clock_info; @@ -319,6 +342,7 @@ struct efx_ptp_data {  	unsigned int rx_no_timestamp;  	struct efx_ptp_timeset  	timeset[MC_CMD_PTP_OUT_SYNCHRONIZE_TIMESET_MAXNUM]; +	void (*xmit_skb)(struct efx_nic *efx, struct sk_buff *skb);  };  static int efx_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta); @@ -329,6 +353,24 @@ static int efx_phc_settime(struct ptp_clock_info *ptp,  static int efx_phc_enable(struct ptp_clock_info *ptp,  			  struct ptp_clock_request *request, int on); +bool efx_ptp_use_mac_tx_timestamps(struct efx_nic *efx) +{ +	struct efx_ef10_nic_data *nic_data = efx->nic_data; + +	return ((efx_nic_rev(efx) >= EFX_REV_HUNT_A0) && +		(nic_data->datapath_caps2 & +		 (1 << MC_CMD_GET_CAPABILITIES_V2_OUT_TX_MAC_TIMESTAMPING_LBN) +		)); +} + +/* PTP 'extra' channel is still a traffic channel, but we only create TX queues + * if PTP uses MAC TX timestamps, not if PTP uses the MC directly to transmit. + */ +static bool efx_ptp_want_txqs(struct efx_channel *channel) +{ +	return efx_ptp_use_mac_tx_timestamps(channel->efx); +} +  #define PTP_SW_STAT(ext_name, field_name)				\  	{ #ext_name, 0, offsetof(struct efx_ptp_data, field_name) }  #define PTP_MC_STAT(ext_name, mcdi_name)				\ @@ -471,6 +513,89 @@ static ktime_t efx_ptp_s27_to_ktime_correction(u32 nic_major, u32 nic_minor,  	return efx_ptp_s27_to_ktime(nic_major, nic_minor);  } +/* For Medford2 platforms the time is in seconds and quarter nanoseconds. */ +static void efx_ptp_ns_to_s_qns(s64 ns, u32 *nic_major, u32 *nic_minor) +{ +	struct timespec64 ts = ns_to_timespec64(ns); + +	*nic_major = (u32)ts.tv_sec; +	*nic_minor = ts.tv_nsec * 4; +} + +static ktime_t efx_ptp_s_qns_to_ktime_correction(u32 nic_major, u32 nic_minor, +						 s32 correction) +{ +	ktime_t kt; + +	nic_minor = DIV_ROUND_CLOSEST(nic_minor, 4); +	correction = DIV_ROUND_CLOSEST(correction, 4); + +	kt = ktime_set(nic_major, nic_minor); + +	if (correction >= 0) +		kt = ktime_add_ns(kt, (u64)correction); +	else +		kt = ktime_sub_ns(kt, (u64)-correction); +	return kt; +} + +struct efx_channel *efx_ptp_channel(struct efx_nic *efx) +{ +	return efx->ptp_data ? efx->ptp_data->channel : NULL; +} + +static u32 last_sync_timestamp_major(struct efx_nic *efx) +{ +	struct efx_channel *channel = efx_ptp_channel(efx); +	u32 major = 0; + +	if (channel) +		major = channel->sync_timestamp_major; +	return major; +} + +/* The 8000 series and later can provide the time from the MAC, which is only + * 48 bits long and provides meta-information in the top 2 bits. + */ +static ktime_t +efx_ptp_mac_nic_to_ktime_correction(struct efx_nic *efx, +				    struct efx_ptp_data *ptp, +				    u32 nic_major, u32 nic_minor, +				    s32 correction) +{ +	ktime_t kt = { 0 }; + +	if (!(nic_major & 0x80000000)) { +		WARN_ON_ONCE(nic_major >> 16); +		/* Use the top bits from the latest sync event. */ +		nic_major &= 0xffff; +		nic_major |= (last_sync_timestamp_major(efx) & 0xffff0000); + +		kt = ptp->nic_to_kernel_time(nic_major, nic_minor, +					     correction); +	} +	return kt; +} + +ktime_t efx_ptp_nic_to_kernel_time(struct efx_tx_queue *tx_queue) +{ +	struct efx_nic *efx = tx_queue->efx; +	struct efx_ptp_data *ptp = efx->ptp_data; +	ktime_t kt; + +	if (efx_ptp_use_mac_tx_timestamps(efx)) +		kt = efx_ptp_mac_nic_to_ktime_correction(efx, ptp, +				tx_queue->completed_timestamp_major, +				tx_queue->completed_timestamp_minor, +				ptp->ts_corrections.general_tx); +	else +		kt = ptp->nic_to_kernel_time( +				tx_queue->completed_timestamp_major, +				tx_queue->completed_timestamp_minor, +				ptp->ts_corrections.general_tx); +	return kt; +} +  /* Get PTP attributes and set up time conversions */  static int efx_ptp_get_attributes(struct efx_nic *efx)  { @@ -502,31 +627,71 @@ static int efx_ptp_get_attributes(struct efx_nic *efx)  		return rc;  	} -	if (fmt == MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_27FRACTION) { +	switch (fmt) { +	case MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_27FRACTION:  		ptp->ns_to_nic_time = efx_ptp_ns_to_s27;  		ptp->nic_to_kernel_time = efx_ptp_s27_to_ktime_correction; -	} else if (fmt == MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_NANOSECONDS) { +		ptp->nic_time.minor_max = 1 << 27; +		ptp->nic_time.sync_event_minor_shift = 19; +		break; +	case MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_NANOSECONDS:  		ptp->ns_to_nic_time = efx_ptp_ns_to_s_ns;  		ptp->nic_to_kernel_time = efx_ptp_s_ns_to_ktime_correction; -	} else { +		ptp->nic_time.minor_max = 1000000000; +		ptp->nic_time.sync_event_minor_shift = 22; +		break; +	case MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_QTR_NANOSECONDS: +		ptp->ns_to_nic_time = efx_ptp_ns_to_s_qns; +		ptp->nic_to_kernel_time = efx_ptp_s_qns_to_ktime_correction; +		ptp->nic_time.minor_max = 4000000000UL; +		ptp->nic_time.sync_event_minor_shift = 24; +		break; +	default:  		return -ERANGE;  	} -	ptp->time_format = fmt; - -	/* MC_CMD_PTP_OP_GET_ATTRIBUTES is an extended version of an older -	 * operation MC_CMD_PTP_OP_GET_TIME_FORMAT that also returns a value -	 * to use for the minimum acceptable corrected synchronization window. +	/* Precalculate acceptable difference between the minor time in the +	 * packet prefix and the last MCDI time sync event. We expect the +	 * packet prefix timestamp to be after of sync event by up to one +	 * sync event interval (0.25s) but we allow it to exceed this by a +	 * fuzz factor of (0.1s) +	 */ +	ptp->nic_time.sync_event_diff_min = ptp->nic_time.minor_max +		- (ptp->nic_time.minor_max / 10); +	ptp->nic_time.sync_event_diff_max = (ptp->nic_time.minor_max / 4) +		+ (ptp->nic_time.minor_max / 10); + +	/* MC_CMD_PTP_OP_GET_ATTRIBUTES has been extended twice from an older +	 * operation MC_CMD_PTP_OP_GET_TIME_FORMAT. The function now may return +	 * a value to use for the minimum acceptable corrected synchronization +	 * window and may return further capabilities.  	 * If we have the extra information store it. For older firmware that  	 * does not implement the extended command use the default value.  	 */ -	if (rc == 0 && out_len >= MC_CMD_PTP_OUT_GET_ATTRIBUTES_LEN) +	if (rc == 0 && +	    out_len >= MC_CMD_PTP_OUT_GET_ATTRIBUTES_CAPABILITIES_OFST)  		ptp->min_synchronisation_ns =  			MCDI_DWORD(outbuf,  				   PTP_OUT_GET_ATTRIBUTES_SYNC_WINDOW_MIN);  	else  		ptp->min_synchronisation_ns = DEFAULT_MIN_SYNCHRONISATION_NS; +	if (rc == 0 && +	    out_len >= MC_CMD_PTP_OUT_GET_ATTRIBUTES_LEN) +		ptp->capabilities = MCDI_DWORD(outbuf, +					PTP_OUT_GET_ATTRIBUTES_CAPABILITIES); +	else +		ptp->capabilities = 0; + +	/* Set up the shift for conversion between frequency +	 * adjustments in parts-per-billion and the fixed-point +	 * fractional ns format that the adapter uses. +	 */ +	if (ptp->capabilities & (1 << MC_CMD_PTP_OUT_GET_ATTRIBUTES_FP44_FREQ_ADJ_LBN)) +		ptp->adjfreq_ppb_shift = PPB_SHIFT_FP44; +	else +		ptp->adjfreq_ppb_shift = PPB_SHIFT_FP40; +  	return 0;  } @@ -534,8 +699,9 @@ static int efx_ptp_get_attributes(struct efx_nic *efx)  static int efx_ptp_get_timestamp_corrections(struct efx_nic *efx)  {  	MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_GET_TIMESTAMP_CORRECTIONS_LEN); -	MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_GET_TIMESTAMP_CORRECTIONS_LEN); +	MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_GET_TIMESTAMP_CORRECTIONS_V2_LEN);  	int rc; +	size_t out_len;  	/* Get the timestamp corrections from the NIC. If this operation is  	 * not supported (older NICs) then no correction is required. @@ -545,21 +711,37 @@ static int efx_ptp_get_timestamp_corrections(struct efx_nic *efx)  	MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);  	rc = efx_mcdi_rpc_quiet(efx, MC_CMD_PTP, inbuf, sizeof(inbuf), -				outbuf, sizeof(outbuf), NULL); +				outbuf, sizeof(outbuf), &out_len);  	if (rc == 0) { -		efx->ptp_data->ts_corrections.tx = MCDI_DWORD(outbuf, +		efx->ptp_data->ts_corrections.ptp_tx = MCDI_DWORD(outbuf,  			PTP_OUT_GET_TIMESTAMP_CORRECTIONS_TRANSMIT); -		efx->ptp_data->ts_corrections.rx = MCDI_DWORD(outbuf, +		efx->ptp_data->ts_corrections.ptp_rx = MCDI_DWORD(outbuf,  			PTP_OUT_GET_TIMESTAMP_CORRECTIONS_RECEIVE);  		efx->ptp_data->ts_corrections.pps_out = MCDI_DWORD(outbuf,  			PTP_OUT_GET_TIMESTAMP_CORRECTIONS_PPS_OUT);  		efx->ptp_data->ts_corrections.pps_in = MCDI_DWORD(outbuf,  			PTP_OUT_GET_TIMESTAMP_CORRECTIONS_PPS_IN); + +		if (out_len >= MC_CMD_PTP_OUT_GET_TIMESTAMP_CORRECTIONS_V2_LEN) { +			efx->ptp_data->ts_corrections.general_tx = MCDI_DWORD( +				outbuf, +				PTP_OUT_GET_TIMESTAMP_CORRECTIONS_V2_GENERAL_TX); +			efx->ptp_data->ts_corrections.general_rx = MCDI_DWORD( +				outbuf, +				PTP_OUT_GET_TIMESTAMP_CORRECTIONS_V2_GENERAL_RX); +		} else { +			efx->ptp_data->ts_corrections.general_tx = +				efx->ptp_data->ts_corrections.ptp_tx; +			efx->ptp_data->ts_corrections.general_rx = +				efx->ptp_data->ts_corrections.ptp_rx; +		}  	} else if (rc == -EINVAL) { -		efx->ptp_data->ts_corrections.tx = 0; -		efx->ptp_data->ts_corrections.rx = 0; +		efx->ptp_data->ts_corrections.ptp_tx = 0; +		efx->ptp_data->ts_corrections.ptp_rx = 0;  		efx->ptp_data->ts_corrections.pps_out = 0;  		efx->ptp_data->ts_corrections.pps_in = 0; +		efx->ptp_data->ts_corrections.general_tx = 0; +		efx->ptp_data->ts_corrections.general_rx = 0;  	} else {  		efx_mcdi_display_error(efx, MC_CMD_PTP, sizeof(inbuf), outbuf,  				       sizeof(outbuf), rc); @@ -873,8 +1055,24 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings)  	return rc;  } +/* Transmit a PTP packet via the dedicated hardware timestamped queue. */ +static void efx_ptp_xmit_skb_queue(struct efx_nic *efx, struct sk_buff *skb) +{ +	struct efx_ptp_data *ptp_data = efx->ptp_data; +	struct efx_tx_queue *tx_queue; +	u8 type = skb->ip_summed == CHECKSUM_PARTIAL ? EFX_TXQ_TYPE_OFFLOAD : 0; + +	tx_queue = &ptp_data->channel->tx_queue[type]; +	if (tx_queue && tx_queue->timestamping) { +		efx_enqueue_skb(tx_queue, skb); +	} else { +		WARN_ONCE(1, "PTP channel has no timestamped tx queue\n"); +		dev_kfree_skb_any(skb); +	} +} +  /* Transmit a PTP packet, via the MCDI interface, to the wire. */ -static int efx_ptp_xmit_skb(struct efx_nic *efx, struct sk_buff *skb) +static void efx_ptp_xmit_skb_mc(struct efx_nic *efx, struct sk_buff *skb)  {  	struct efx_ptp_data *ptp_data = efx->ptp_data;  	struct skb_shared_hwtstamps timestamps; @@ -910,16 +1108,16 @@ static int efx_ptp_xmit_skb(struct efx_nic *efx, struct sk_buff *skb)  	timestamps.hwtstamp = ptp_data->nic_to_kernel_time(  		MCDI_DWORD(txtime, PTP_OUT_TRANSMIT_MAJOR),  		MCDI_DWORD(txtime, PTP_OUT_TRANSMIT_MINOR), -		ptp_data->ts_corrections.tx); +		ptp_data->ts_corrections.ptp_tx);  	skb_tstamp_tx(skb, ×tamps);  	rc = 0;  fail: -	dev_kfree_skb(skb); +	dev_kfree_skb_any(skb); -	return rc; +	return;  }  static void efx_ptp_drop_time_expired_events(struct efx_nic *efx) @@ -1189,7 +1387,7 @@ static void efx_ptp_worker(struct work_struct *work)  	efx_ptp_process_events(efx, &tempq);  	while ((skb = skb_dequeue(&ptp_data->txq))) -		efx_ptp_xmit_skb(efx, skb); +		ptp_data->xmit_skb(efx, skb);  	while ((skb = __skb_dequeue(&tempq)))  		efx_ptp_process_rx(efx, skb); @@ -1239,6 +1437,14 @@ int efx_ptp_probe(struct efx_nic *efx, struct efx_channel *channel)  		goto fail2;  	} +	if (efx_ptp_use_mac_tx_timestamps(efx)) { +		ptp->xmit_skb = efx_ptp_xmit_skb_queue; +		/* Request sync events on this channel. */ +		channel->sync_events_state = SYNC_EVENTS_QUIESCENT; +	} else { +		ptp->xmit_skb = efx_ptp_xmit_skb_mc; +	} +  	INIT_WORK(&ptp->work, efx_ptp_worker);  	ptp->config.flags = 0;  	ptp->config.tx_type = HWTSTAMP_TX_OFF; @@ -1303,11 +1509,21 @@ fail1:  static int efx_ptp_probe_channel(struct efx_channel *channel)  {  	struct efx_nic *efx = channel->efx; +	int rc;  	channel->irq_moderation_us = 0;  	channel->rx_queue.core_index = 0; -	return efx_ptp_probe(efx, channel); +	rc = efx_ptp_probe(efx, channel); +	/* Failure to probe PTP is not fatal; this channel will just not be +	 * used for anything. +	 * In the case of EPERM, efx_ptp_probe will print its own message (in +	 * efx_ptp_get_attributes()), so we don't need to. +	 */ +	if (rc && rc != -EPERM) +		netif_warn(efx, drv, efx->net_dev, +			   "Failed to probe PTP, rc=%d\n", rc); +	return 0;  }  void efx_ptp_remove(struct efx_nic *efx) @@ -1332,6 +1548,7 @@ void efx_ptp_remove(struct efx_nic *efx)  	efx_nic_free_buffer(efx, &efx->ptp_data->start);  	kfree(efx->ptp_data); +	efx->ptp_data = NULL;  }  static void efx_ptp_remove_channel(struct efx_channel *channel) @@ -1548,6 +1765,17 @@ void efx_ptp_get_ts_info(struct efx_nic *efx, struct ethtool_ts_info *ts_info)  	ts_info->so_timestamping |= (SOF_TIMESTAMPING_TX_HARDWARE |  				     SOF_TIMESTAMPING_RX_HARDWARE |  				     SOF_TIMESTAMPING_RAW_HARDWARE); +	/* Check licensed features.  If we don't have the license for TX +	 * timestamps, the NIC will not support them. +	 */ +	if (efx_ptp_use_mac_tx_timestamps(efx)) { +		struct efx_ef10_nic_data *nic_data = efx->nic_data; + +		if (!(nic_data->licensed_features & +		      (1 << LICENSED_V3_FEATURES_TX_TIMESTAMPS_LBN))) +			ts_info->so_timestamping &= +				~SOF_TIMESTAMPING_TX_HARDWARE; +	}  	if (primary && primary->ptp_data && primary->ptp_data->phc_clock)  		ts_info->phc_index =  			ptp_clock_index(primary->ptp_data->phc_clock); @@ -1627,7 +1855,7 @@ static void ptp_event_rx(struct efx_nic *efx, struct efx_ptp_data *ptp)  		evt->hwtimestamp = efx->ptp_data->nic_to_kernel_time(  			EFX_QWORD_FIELD(ptp->evt_frags[0], MCDI_EVENT_DATA),  			EFX_QWORD_FIELD(ptp->evt_frags[1], MCDI_EVENT_DATA), -			ptp->ts_corrections.rx); +			ptp->ts_corrections.ptp_rx);  		evt->expiry = jiffies + msecs_to_jiffies(PKT_EVENT_LIFETIME_MS);  		list_add_tail(&evt->link, &ptp->evt_list); @@ -1662,9 +1890,11 @@ void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev)  	int code = EFX_QWORD_FIELD(*ev, MCDI_EVENT_CODE);  	if (!ptp) { -		if (net_ratelimit()) +		if (!efx->ptp_warned) {  			netif_warn(efx, drv, efx->net_dev,  				   "Received PTP event but PTP not set up\n"); +			efx->ptp_warned = true; +		}  		return;  	} @@ -1707,9 +1937,20 @@ void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev)  void efx_time_sync_event(struct efx_channel *channel, efx_qword_t *ev)  { +	struct efx_nic *efx = channel->efx; +	struct efx_ptp_data *ptp = efx->ptp_data; + +	/* When extracting the sync timestamp minor value, we should discard +	 * the least significant two bits. These are not required in order +	 * to reconstruct full-range timestamps and they are optionally used +	 * to report status depending on the options supplied when subscribing +	 * for sync events. +	 */  	channel->sync_timestamp_major = MCDI_EVENT_FIELD(*ev, PTP_TIME_MAJOR);  	channel->sync_timestamp_minor = -		MCDI_EVENT_FIELD(*ev, PTP_TIME_MINOR_26_19) << 19; +		(MCDI_EVENT_FIELD(*ev, PTP_TIME_MINOR_MS_8BITS) & 0xFC) +			<< ptp->nic_time.sync_event_minor_shift; +  	/* if sync events have been disabled then we want to silently ignore  	 * this event, so throw away result.  	 */ @@ -1717,15 +1958,6 @@ void efx_time_sync_event(struct efx_channel *channel, efx_qword_t *ev)  		       SYNC_EVENTS_VALID);  } -/* make some assumptions about the time representation rather than abstract it, - * since we currently only support one type of inline timestamping and only on - * EF10. - */ -#define MINOR_TICKS_PER_SECOND 0x8000000 -/* Fuzz factor for sync events to be out of order with RX events */ -#define FUZZ (MINOR_TICKS_PER_SECOND / 10) -#define EXPECTED_SYNC_EVENTS_PER_SECOND 4 -  static inline u32 efx_rx_buf_timestamp_minor(struct efx_nic *efx, const u8 *eh)  {  #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) @@ -1743,31 +1975,33 @@ void __efx_rx_skb_attach_timestamp(struct efx_channel *channel,  				   struct sk_buff *skb)  {  	struct efx_nic *efx = channel->efx; +	struct efx_ptp_data *ptp = efx->ptp_data;  	u32 pkt_timestamp_major, pkt_timestamp_minor;  	u32 diff, carry;  	struct skb_shared_hwtstamps *timestamps; -	pkt_timestamp_minor = (efx_rx_buf_timestamp_minor(efx, -							  skb_mac_header(skb)) + -			       (u32) efx->ptp_data->ts_corrections.rx) & -			      (MINOR_TICKS_PER_SECOND - 1); +	if (channel->sync_events_state != SYNC_EVENTS_VALID) +		return; + +	pkt_timestamp_minor = efx_rx_buf_timestamp_minor(efx, skb_mac_header(skb));  	/* get the difference between the packet and sync timestamps,  	 * modulo one second  	 */ -	diff = (pkt_timestamp_minor - channel->sync_timestamp_minor) & -		(MINOR_TICKS_PER_SECOND - 1); +	diff = pkt_timestamp_minor - channel->sync_timestamp_minor; +	if (pkt_timestamp_minor < channel->sync_timestamp_minor) +		diff += ptp->nic_time.minor_max; +  	/* do we roll over a second boundary and need to carry the one? */ -	carry = channel->sync_timestamp_minor + diff > MINOR_TICKS_PER_SECOND ? +	carry = (channel->sync_timestamp_minor >= ptp->nic_time.minor_max - diff) ?  		1 : 0; -	if (diff <= MINOR_TICKS_PER_SECOND / EXPECTED_SYNC_EVENTS_PER_SECOND + -		    FUZZ) { +	if (diff <= ptp->nic_time.sync_event_diff_max) {  		/* packet is ahead of the sync event by a quarter of a second or  		 * less (allowing for fuzz)  		 */  		pkt_timestamp_major = channel->sync_timestamp_major + carry; -	} else if (diff >= MINOR_TICKS_PER_SECOND - FUZZ) { +	} else if (diff >= ptp->nic_time.sync_event_diff_min) {  		/* packet is behind the sync event but within the fuzz factor.  		 * This means the RX packet and sync event crossed as they were  		 * placed on the event queue, which can sometimes happen. @@ -1789,7 +2023,9 @@ void __efx_rx_skb_attach_timestamp(struct efx_channel *channel,  	/* attach the timestamps to the skb */  	timestamps = skb_hwtstamps(skb);  	timestamps->hwtstamp = -		efx_ptp_s27_to_ktime(pkt_timestamp_major, pkt_timestamp_minor); +		ptp->nic_to_kernel_time(pkt_timestamp_major, +					pkt_timestamp_minor, +					ptp->ts_corrections.general_rx);  }  static int efx_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta) @@ -1807,9 +2043,10 @@ static int efx_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta)  	else if (delta < -MAX_PPB)  		delta = -MAX_PPB; -	/* Convert ppb to fixed point ns. */ -	adjustment_ns = (((s64)delta * PPB_SCALE_WORD) >> -			 (PPB_EXTRA_BITS + MAX_PPB_BITS)); +	/* Convert ppb to fixed point ns taking care to round correctly. */ +	adjustment_ns = ((s64)delta * PPB_SCALE_WORD + +			 (1 << (ptp_data->adjfreq_ppb_shift - 1))) >> +			ptp_data->adjfreq_ppb_shift;  	MCDI_SET_DWORD(inadj, PTP_IN_OP, MC_CMD_PTP_OP_ADJUST);  	MCDI_SET_DWORD(inadj, PTP_IN_PERIPH_ID, 0); @@ -1916,6 +2153,7 @@ static const struct efx_channel_type efx_ptp_channel_type = {  	.get_name		= efx_ptp_get_channel_name,  	/* no copy operation; there is no need to reallocate this channel */  	.receive_skb		= efx_ptp_rx, +	.want_txqs		= efx_ptp_want_txqs,  	.keep_eventq		= false,  }; |