diff options
-rw-r--r-- | drivers/net/ethernet/sfc/nic.h | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/ptp.c | 47 |
2 files changed, 46 insertions, 2 deletions
diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index ac54b50f57a5..6549fc685a48 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h @@ -449,6 +449,7 @@ void efx_fini_sriov(void); struct ethtool_ts_info; int efx_ptp_probe(struct efx_nic *efx, struct efx_channel *channel); void efx_ptp_defer_probe_with_channel(struct efx_nic *efx); +struct efx_channel *efx_ptp_channel(struct efx_nic *efx); void efx_ptp_remove(struct efx_nic *efx); int efx_ptp_set_ts_config(struct efx_nic *efx, struct ifreq *ifr); int efx_ptp_get_ts_config(struct efx_nic *efx, struct ifreq *ifr); diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c index 7f877312c646..0d0160f15c35 100644 --- a/drivers/net/ethernet/sfc/ptp.c +++ b/drivers/net/ethernet/sfc/ptp.c @@ -483,14 +483,57 @@ 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); } +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_s27_to_ktime_correction(struct efx_nic *efx, + 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 = efx_ptp_s27_to_ktime_correction(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; - kt = ptp->nic_to_kernel_time(tx_queue->completed_timestamp_major, - tx_queue->completed_timestamp_minor, 0); + if (efx_ptp_use_mac_tx_timestamps(efx)) + kt = efx_ptp_mac_s27_to_ktime_correction(efx, + tx_queue->completed_timestamp_major, + tx_queue->completed_timestamp_minor, 0); + else + kt = ptp->nic_to_kernel_time( + tx_queue->completed_timestamp_major, + tx_queue->completed_timestamp_minor, 0); return kt; } |