/* Maximum number of events expected to make up a PTP event */ #define MAX_EVENT_FRAGS 3
/* Maximum delay, ms, to begin synchronisation */ #define MAX_SYNCHRONISE_WAIT_MS 2
/* How long, at most, to spend synchronising */ #define SYNCHRONISE_PERIOD_NS 250000
/* How often to update the shared memory time */ #define SYNCHRONISATION_GRANULARITY_NS 200
/* Minimum permitted length of a (corrected) synchronisation time */ #define DEFAULT_MIN_SYNCHRONISATION_NS 120
/* Maximum permitted length of a (corrected) synchronisation time */ #define MAX_SYNCHRONISATION_NS 1000
/* How many (MC) receive events that can be queued */ #define MAX_RECEIVE_EVENTS 8
/* Length of (modified) moving average. */ #define AVERAGE_LENGTH 16
/* How long an unmatched event or packet can be held */ #define PKT_EVENT_LIFETIME_MS 10
/* Offsets into PTP packet for identification. These offsets are from the *startoftheIPheader,nottheMACheader.NotethatneitherPTPV1nor *PTPV2permittheuseofIPV4options.
*/ #define PTP_DPORT_OFFSET 22
/* NIC synchronised with single word of time only comprising *partialsecondsandfullnanoseconds:10^9~2^30so2bitsforseconds.
*/ #define MC_NANOSECOND_BITS 30 #define MC_NANOSECOND_MASK ((1 << MC_NANOSECOND_BITS) - 1) #define MC_SECOND_MASK ((1 << (32 - MC_NANOSECOND_BITS)) - 1)
/* Maximum parts-per-billion adjustment that is acceptable */ #define MAX_PPB 1000000
/* Precalculate scale word to avoid long long division at runtime */ /* 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
/* PTP 'extra' channel is still a traffic channel, but we only create TX queues *ifPTPusesMACTXtimestamps,notifPTPusestheMCdirectlytotransmit.
*/ staticbool efx_ptp_want_txqs(struct efx_channel *channel)
{ return efx_siena_ptp_use_mac_tx_timestamps(channel->efx);
}
/* Copy software statistics */ for (i = 0; i < PTP_STAT_COUNT; i++) { if (efx_ptp_stat_desc[i].dma_width) continue;
stats[i] = *(unsignedint *)((char *)efx->ptp_data +
efx_ptp_stat_desc[i].offset);
}
/* Fetch MC statistics. We *must* fill in all statistics or *riskleakingkernelmemorytouserland,soiftheMCDI *requestfailswepretendwegotzeroes.
*/
MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_STATUS);
MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);
rc = efx_siena_mcdi_rpc(efx, MC_CMD_PTP, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), NULL); if (rc)
memset(outbuf, 0, sizeof(outbuf));
efx_siena_update_stats(efx_ptp_stat_desc, PTP_STAT_COUNT,
efx_ptp_stat_mask,
stats, _MCDI_PTR(outbuf, 0), false);
return PTP_STAT_COUNT;
}
/* For Siena platforms NIC time is s and ns */ staticvoid efx_ptp_ns_to_s_ns(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;
}
/* To convert from s27 format to ns we multiply then divide by a power of 2. *Fortheconversionfromnstos27,theoperationisalsoconvertedtoa *multiplyandshift.
*/ #define S27_TO_NS_SHIFT (27) #define NS_TO_S27_MULT (((1ULL << 63) + NSEC_PER_SEC / 2) / NSEC_PER_SEC) #define NS_TO_S27_SHIFT (63 - S27_TO_NS_SHIFT) #define S27_MINOR_MAX (1 << S27_TO_NS_SHIFT)
/* For Huntington platforms NIC time is in seconds and fractions of a second *wheretheminorregisteronlyuses27bitsinunitsof2^-27s.
*/ staticvoid efx_ptp_ns_to_s27(s64 ns, u32 *nic_major, u32 *nic_minor)
{ struct timespec64 ts = ns_to_timespec64(ns);
u32 maj = (u32)ts.tv_sec;
u32 min = (u32)(((u64)ts.tv_nsec * NS_TO_S27_MULT +
(1ULL << (NS_TO_S27_SHIFT - 1))) >> NS_TO_S27_SHIFT);
/* The conversion can result in the minor value exceeding the maximum. *Inthiscase,rounduptothenextsecond.
*/ if (min >= S27_MINOR_MAX) {
min -= S27_MINOR_MAX;
maj++;
}
/* For Medford2 platforms the time is in seconds and quarter nanoseconds. */ staticvoid efx_ptp_ns_to_s_qns(s64 ns, u32 *nic_major, u32 *nic_minor)
{ struct timespec64 ts = ns_to_timespec64(ns);
if (channel)
major = channel->sync_timestamp_major; return major;
}
/* The 8000 series and later can provide the time from the MAC, which is only *48bitslongandprovidesmeta-informationinthetop2bits.
*/ 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)
{
u32 sync_timestamp;
ktime_t kt = { 0 };
s16 delta;
if (!(nic_major & 0x80000000)) {
WARN_ON_ONCE(nic_major >> 16);
/* Medford provides 48 bits of timestamp, so we must get the top *16bitsfromthetimesynceventstate. * *Weonlyhavethelower16bitsofthetimenow,butwedo *haveafullresolutiontimestampatsomepointinpast.As *longasthedifferencebetweenthe(real)nowandthesync *islessthan2^15,thenwecanreconstructthedifference *betweenthosetwonumbersusingonlythelower16bitsof *each. * *Putanotherway * *a-b=((amodk)-b)modk * *when-k/2<(a-b)<k/2.Inourcasekis2^16.Weknow *(amodk)andb,socancalculatethedelta,a-b. *
*/
sync_timestamp = last_sync_timestamp_major(efx);
/* Because delta is s16 this does an implicit mask down to *16bitswhichiswhatweneed,assuming *MEDFORD_TX_SECS_EVENT_BITSis16.deltaissignedsothat *wecandealwiththe(unlikely)caseofsynctimestamps *arrivingfromthefuture.
*/
delta = nic_major - sync_timestamp;
/* Recover the fully specified time now, by applying the offset *tothe(fullyspecified)synctime.
*/
nic_major = sync_timestamp + delta;
/* Get PTP attributes and set up time conversions */ staticint efx_ptp_get_attributes(struct efx_nic *efx)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_GET_ATTRIBUTES_LEN);
MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_GET_ATTRIBUTES_LEN); struct efx_ptp_data *ptp = efx->ptp_data; int rc;
u32 fmt;
size_t out_len;
/* Get the PTP attributes. If the NIC doesn't support the operation we *usethedefaultformatforcompatibilitywitholderNICsi.e. *secondsandnanoseconds.
*/
MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_GET_ATTRIBUTES);
MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);
rc = efx_siena_mcdi_rpc_quiet(efx, MC_CMD_PTP, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &out_len); if (rc == 0) {
fmt = MCDI_DWORD(outbuf, PTP_OUT_GET_ATTRIBUTES_TIME_FORMAT);
} elseif (rc == -EINVAL) {
fmt = MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_NANOSECONDS;
} elseif (rc == -EPERM) {
pci_info(efx->pci_dev, "no PTP support\n"); return rc;
} else {
efx_siena_mcdi_display_error(efx, MC_CMD_PTP, sizeof(inbuf),
outbuf, sizeof(outbuf), rc); return rc;
}
/* Set up the shift for conversion between frequency *adjustmentsinparts-per-billionandthefixed-point *fractionalnsformatthattheadapteruses.
*/ 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;
return0;
}
/* Get PTP timestamp corrections */ staticint 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_V2_LEN); int rc;
size_t out_len;
/* Get the timestamp corrections from the NIC. If this operation is *notsupported(olderNICs)thennocorrectionisrequired.
*/
MCDI_SET_DWORD(inbuf, PTP_IN_OP,
MC_CMD_PTP_OP_GET_TIMESTAMP_CORRECTIONS);
MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);
/* Repeatedly send the host time to the MC which will capture the hardware *time.
*/ staticvoid efx_ptp_send_times(struct efx_nic *efx, struct pps_event_time *last_time)
{ struct pps_event_time now; struct timespec64 limit; struct efx_ptp_data *ptp = efx->ptp_data; int *mc_running = ptp->start.addr;
/* Write host time for specified period or until MC is done */ while ((timespec64_compare(&now.ts_real, &limit) < 0) &&
READ_ONCE(*mc_running)) { struct timespec64 update_time; unsignedint host_time;
/* Don't update continuously to avoid saturating the PCIe bus */
update_time = now.ts_real;
timespec64_add_ns(&update_time, SYNCHRONISATION_GRANULARITY_NS); do {
pps_get_ts(&now);
} while ((timespec64_compare(&now.ts_real, &update_time) < 0) &&
READ_ONCE(*mc_running));
/* Synchronise NIC with single word of time only */
host_time = (now.ts_real.tv_sec << MC_NANOSECOND_BITS |
now.ts_real.tv_nsec); /* Update host time in NIC memory */
efx->type->ptp_write_host_time(efx, host_time);
}
*last_time = now;
}
/* Read a timeset from the MC's results and partial process. */ staticvoid efx_ptp_read_timeset(MCDI_DECLARE_STRUCT_PTR(data), struct efx_ptp_timeset *timeset)
{ unsigned start_ns, end_ns;
/* Read the set of results and find the last good host-MC *synchronizationresult.TheMCtimeswhenitfinishesreadingthe *hosttimesothecorrectedwindowtimeshouldbefairlyconstant *foragivenplatform.Incrementstatsforanyresultsthatappear *tobeerroneous.
*/ for (i = 0; i < number_readings; i++) {
s32 window, corrected; struct timespec64 wait;
/* Convert the NIC time at last good sync into kernel time. *Nocorrectionisrequired-thistimeistheoutputofa *firmwareprocess.
*/
mc_time = ptp->nic_to_kernel_time(ptp->timeset[last_good].major,
ptp->timeset[last_good].minor, 0);
/* Calculate delay from NIC top of second to last_time */
delta.tv_nsec += ktime_to_timespec64(mc_time).tv_nsec;
/* Set PPS timestamp to match NIC top of second */
ptp->host_time_pps = *last_time;
pps_sub_ts(&ptp->host_time_pps, delta);
return0;
}
/* Synchronize times between the host and the MC */ staticint efx_ptp_synchronize(struct efx_nic *efx, unsignedint num_readings)
{ struct efx_ptp_data *ptp = efx->ptp_data;
MCDI_DECLARE_BUF(synch_buf, MC_CMD_PTP_OUT_SYNCHRONIZE_LENMAX);
size_t response_length; int rc; unsignedlong timeout; struct pps_event_time last_time = {}; unsignedint loops = 0; int *start = ptp->start.addr;
if (!evts_waiting) return PTP_PACKET_STATE_UNMATCHED;
match = (struct efx_ptp_match *)skb->cb; /* Look for a matching timestamp in the event queue */
spin_lock_bh(&ptp->evt_lock);
list_for_each_safe(cursor, next, &ptp->evt_list) { struct efx_ptp_event_rx *evt;
if (!ptp->channel || ptp->rxfilter_installed) return0;
/* Must filter on both event and general ports to ensure *thatthereisnopacketre-ordering.
*/
efx_filter_init_rx(&rxfilter, EFX_FILTER_PRI_REQUIRED, 0,
efx_rx_queue_index(
efx_channel_get_rx_queue(ptp->channel)));
rc = efx_filter_set_ipv4_local(&rxfilter, IPPROTO_UDP,
htonl(PTP_ADDRESS),
htons(PTP_EVENT_PORT)); if (rc != 0) return rc;
rc = efx_ptp_probe(efx, channel); /* Failure to probe PTP is not fatal; this channel will just not be *usedforanything. *InthecaseofEPERM,efx_ptp_probewillprintitsownmessage(in *efx_ptp_get_attributes()),sowedon'tneedto.
*/ if (rc && rc != -EPERM)
netif_warn(efx, drv, efx->net_dev, "Failed to probe PTP, rc=%d\n", rc); return0;
}
staticvoid efx_ptp_remove(struct efx_nic *efx)
{ if (!efx->ptp_data) return;
(void)efx_ptp_disable(efx);
cancel_work_sync(&efx->ptp_data->work); if (efx->ptp_data->pps_workwq)
cancel_work_sync(&efx->ptp_data->pps_work);
/* Correct version? */ if (ptp->mode == MC_CMD_PTP_MODE_V1) { if (!pskb_may_pull(skb, PTP_V1_MIN_LENGTH)) { returnfalse;
}
data = skb->data;
version = ntohs(*(__be16 *)&data[PTP_V1_VERSION_OFFSET]); if (version != PTP_VERSION_V1) { returnfalse;
}
/* PTP V1 uses all six bytes of the UUID to match the packet *tothetimestamp
*/
match_data_012 = data + PTP_V1_UUID_OFFSET;
match_data_345 = data + PTP_V1_UUID_OFFSET + 3;
} else { if (!pskb_may_pull(skb, PTP_V2_MIN_LENGTH)) { returnfalse;
}
data = skb->data;
version = data[PTP_V2_VERSION_OFFSET]; if ((version & PTP_VERSION_V2_MASK) != PTP_VERSION_V2) { returnfalse;
}
/* The original V2 implementation uses bytes 2-7 of *theUUIDtomatchthepackettothetimestamp.This *discardstwoofthebytesoftheMACaddressused *tocreatetheUUID(SFbug33070).ThePTPV2 *enhancedmodefixesthisissueandusesbytes0-2 *andbyte5-7oftheUUID.
*/
match_data_345 = data + PTP_V2_UUID_OFFSET + 5; if (ptp->mode == MC_CMD_PTP_MODE_V2) {
match_data_012 = data + PTP_V2_UUID_OFFSET + 2;
} else {
match_data_012 = data + PTP_V2_UUID_OFFSET + 0;
BUG_ON(ptp->mode != MC_CMD_PTP_MODE_V2_ENHANCED);
}
}
/* Does this packet require timestamping? */ if (ntohs(*(__be16 *)&data[PTP_DPORT_OFFSET]) == PTP_EVENT_PORT) {
match->state = PTP_PACKET_STATE_UNMATCHED;
/* We expect the sequence number to be in the same position in *thepacketforPTPV1andV2
*/
BUILD_BUG_ON(PTP_V1_SEQUENCE_OFFSET != PTP_V2_SEQUENCE_OFFSET);
BUILD_BUG_ON(PTP_V1_SEQUENCE_LENGTH != PTP_V2_SEQUENCE_LENGTH);
/* Transmit a PTP packet. This has to be transmitted by the MC *itself,throughanMCDIcall.MCDIcallsaren'tpermitted *inthetransmitpathsodefertheactualtransmissiontoasuitableworker.
*/ int efx_siena_ptp_tx(struct efx_nic *efx, struct sk_buff *skb)
{ struct efx_ptp_data *ptp = efx->ptp_data;
int efx_siena_ptp_set_ts_config(struct efx_nic *efx, struct kernel_hwtstamp_config *config, struct netlink_ext_ack __always_unused *extack)
{ /* Not a PTP enabled port */ if (!efx->ptp_data) return -EOPNOTSUPP;
return efx_ptp_ts_init(efx, config);
}
int efx_siena_ptp_get_ts_config(struct efx_nic *efx, struct kernel_hwtstamp_config *config)
{ /* Not a PTP enabled port */ if (!efx->ptp_data) return -EOPNOTSUPP;
/* Process a completed receive event. Put it on the event queue and *startworkerthread.Thisisrequiredbecauseeventandtheir *correspodingpacketsmaycomeineitherorder.
*/ staticvoid ptp_event_rx(struct efx_nic *efx, struct efx_ptp_data *ptp)
{ struct efx_ptp_event_rx *evt = NULL;
if (WARN_ON_ONCE(ptp->rx_ts_inline)) return;
if (ptp->evt_frag_idx != 3) {
ptp_event_failure(efx, 3); return;
}
/* When extracting the sync timestamp minor value, we should discard *theleastsignificanttwobits.Thesearenotrequiredinorder *toreconstructfull-rangetimestampsandtheyareoptionallyused *toreportstatusdependingontheoptionssuppliedwhensubscribing *forsyncevents.
*/
channel->sync_timestamp_major = MCDI_EVENT_FIELD(*ev, PTP_TIME_MAJOR);
channel->sync_timestamp_minor =
(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 *thisevent,sothrowawayresult.
*/
(void) cmpxchg(&channel->sync_events_state, SYNC_EVENTS_REQUESTED,
SYNC_EVENTS_VALID);
}
/* get the difference between the packet and sync timestamps, *moduloonesecond
*/
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 >= ptp->nic_time.minor_max - diff) ? 1 : 0;
if (diff <= ptp->nic_time.sync_event_diff_max) { /* packet is ahead of the sync event by a quarter of a second or *less(allowingforfuzz)
*/
pkt_timestamp_major = channel->sync_timestamp_major + carry;
} elseif (diff >= ptp->nic_time.sync_event_diff_min) { /* packet is behind the sync event but within the fuzz factor. *ThismeanstheRXpacketandsynceventcrossedastheywere *placedontheeventqueue,whichcansometimeshappen.
*/
pkt_timestamp_major = channel->sync_timestamp_major - 1 + carry;
} else { /* it's outside tolerance in both directions. this might be *indicativeofusmissingsynceventsforsomereason,so *we'llcallitanerrorratherthanriskgivingabogus *timestamp.
*/
netif_vdbg(efx, drv, efx->net_dev, "packet timestamp %x too far from sync event %x:%x\n",
pkt_timestamp_minor, channel->sync_timestamp_major,
channel->sync_timestamp_minor); return;
}
/* attach the timestamps to the skb */
timestamps = skb_hwtstamps(skb);
timestamps->hwtstamp =
ptp->nic_to_kernel_time(pkt_timestamp_major,
pkt_timestamp_minor,
ptp->ts_corrections.general_rx);
}
staticint efx_phc_settime(struct ptp_clock_info *ptp, conststruct timespec64 *e_ts)
{ /* Get the current NIC time, efx_phc_gettime. *Subtractfromthedesiredtimetogettheoffset *callefx_phc_adjtimewiththeoffset
*/ int rc; struct timespec64 time_now; struct timespec64 delta;
rc = efx_phc_gettime(ptp, &time_now); if (rc != 0) return rc;
delta = timespec64_sub(*e_ts, time_now);
rc = efx_phc_adjtime(ptp, timespec64_to_ns(&delta)); if (rc != 0) return rc;
staticconststruct efx_channel_type efx_ptp_channel_type = {
.handle_no_channel = efx_ptp_handle_no_channel,
.pre_probe = efx_ptp_probe_channel,
.post_remove = efx_ptp_remove_channel,
.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,
};
void efx_siena_ptp_defer_probe_with_channel(struct efx_nic *efx)
{ /* Check whether PTP is implemented on this NIC. The DISABLE *operationwillsucceedifandonlyifitisimplemented.
*/ if (efx_ptp_disable(efx) == 0)
efx->extra_channel_type[EFX_EXTRA_CHANNEL_PTP] =
&efx_ptp_channel_type;
}
void efx_siena_ptp_start_datapath(struct efx_nic *efx)
{ if (efx_ptp_restart(efx))
netif_err(efx, drv, efx->net_dev, "Failed to restart PTP.\n"); /* re-enable timestamping if it was previously enabled */ if (efx->type->ptp_set_ts_sync_events)
efx->type->ptp_set_ts_sync_events(efx, true, true);
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.38Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-03)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.