diff options
Diffstat (limited to 'drivers/net/ethernet/sfc/ptp.c')
-rw-r--r-- | drivers/net/ethernet/sfc/ptp.c | 47 |
1 files changed, 45 insertions, 2 deletions
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; } |