// SPDX-License-Identifier: GPL-2.0-only /**************************************************************************** * Driver for Solarflare network controllers and boards * Copyright 2011-2013 Solarflare Communications Inc.
*/
/* Theory of operation: * * PTP support is assisted by firmware running on the MC, which provides * the hardware timestamping capabilities. Both transmitted and received * PTP event packets are queued onto internal queues for subsequent processing; * this is because the MC operations are relatively long and would block * block NAPI/interrupt operation. * * Receive event processing: * The event contains the packet's UUID and sequence number, together * with the hardware timestamp. The PTP receive packet queue is searched * for this UUID/sequence number and, if found, put on a pending queue. * Packets not matching are delivered without timestamps (MCDI events will * always arrive after the actual packet). * It is important for the operation of the PTP protocol that the ordering * of packets between the event and general port is maintained. * * Work queue processing: * If work waiting, synchronise host/hardware time * * Transmit: send packet through MC, which returns the transmission time * that is converted to an appropriate timestamp. * * Receive: the packet's reception time is converted to an appropriate * timestamp.
*/ #include <linux/ip.h> #include <linux/udp.h> #include <linux/time.h> #include <linux/ktime.h> #include <linux/module.h> #include <linux/pps_kernel.h> #include <linux/ptp_clock_kernel.h> #include"net_driver.h" #include"efx.h" #include"mcdi.h" #include"mcdi_pcol.h" #include"io.h" #include"farch_regs.h" #include"tx.h" #include"nic.h"/* indirectly includes ptp.h */
/* 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 * start of the IP header, not the MAC header. Note that neither PTP V1 nor * PTP V2 permit the use of IPV4 options.
*/ #define PTP_DPORT_OFFSET 22
/* Although PTP V2 UUIDs are comprised a ClockIdentity (8) and PortNumber (2), * the MC only captures the last six bytes of the clock identity. These values * reflect those, not the ones used in the standard. The standard permits * mapping of V1 UUIDs to V2 UUIDs with these same values.
*/ #define PTP_V2_MC_UUID_LENGTH 6 #define PTP_V2_MC_UUID_OFFSET 50
/* Annoyingly the format of the version numbers are different between * versions 1 and 2 so it isn't possible to simply look for 1 or 2.
*/ #define PTP_VERSION_V1 1
/* NIC synchronised with single word of time only comprising * partial seconds and full nanoseconds: 10^9 ~ 2^30 so 2 bits for seconds.
*/ #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
#define PTP_SYNC_ATTEMPTS 4
/** * struct efx_ptp_match - Matching structure, stored in sk_buff's cb area. * @words: UUID and (partial) sequence number * @expiry: Time after which the packet should be delivered irrespective of * event arrival. * @state: The state of the packet - whether it is ready for processing or * whether that is of no interest.
*/ struct efx_ptp_match {
u32 words[DIV_ROUND_UP(PTP_V1_UUID_LENGTH, 4)]; unsignedlong expiry; enum ptp_packet_state state;
};
/** * struct efx_ptp_event_rx - A PTP receive event (from MC) * @link: list of events * @seq0: First part of (PTP) UUID * @seq1: Second part of (PTP) UUID and sequence number * @hwtimestamp: Event timestamp * @expiry: Time which the packet arrived
*/ struct efx_ptp_event_rx { struct list_head link;
u32 seq0;
u32 seq1;
ktime_t hwtimestamp; unsignedlong expiry;
};
/** * struct efx_ptp_timeset - Synchronisation between host and MC * @host_start: Host time immediately before hardware timestamp taken * @major: Hardware timestamp, major * @minor: Hardware timestamp, minor * @host_end: Host time immediately after hardware timestamp taken * @wait: Number of NIC clock ticks between hardware timestamp being read and * host end time being seen * @window: Difference of host_end and host_start * @valid: Whether this timeset is valid
*/ struct efx_ptp_timeset {
u32 host_start;
u32 major;
u32 minor;
u32 host_end;
u32 wait;
u32 window; /* Derived: end - start, allowing for wrap */
};
/** * struct efx_ptp_data - Precision Time Protocol (PTP) state * @efx: The NIC context * @channel: The PTP channel (Siena only) * @rx_ts_inline: Flag for whether RX timestamps are inline (else they are * separate events) * @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 * @rx_evts: Instantiated events (on evt_list and evt_free_list) * @workwq: Work queue for processing pending PTP operations * @work: Work task * @reset_required: A serious error has occurred and the PTP task needs to be * reset (disable, enable). * @rxfilter_event: Receive filter when operating * @rxfilter_general: Receive filter when operating * @rxfilter_installed: Receive filter installed * @config: Current timestamp configuration * @enabled: PTP operation enabled * @mode: Mode in which PTP operating (PTP version) * @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: contains time details * @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 * @capabilities: Capabilities flags from the NIC * @ts_corrections: contains corrections details * @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 * @pps_work: pps work task for handling pps events * @pps_workwq: pps work queue * @nic_ts_enabled: Flag indicating if NIC generated TS events are handled * @txbuf: Buffer for use when transmitting (PTP) packets to MC (avoids * allocations in main data path). * @good_syncs: Number of successful synchronisations. * @fast_syncs: Number of synchronisations requiring short delay * @bad_syncs: Number of failed synchronisations. * @sync_timeouts: Number of synchronisation timeouts * @no_time_syncs: Number of synchronisations with no good times. * @invalid_sync_windows: Number of sync windows with bad durations. * @undersize_sync_windows: Number of corrected sync windows that are too small * @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; struct efx_channel *channel; bool rx_ts_inline; struct sk_buff_head rxq; struct sk_buff_head txq; struct list_head evt_list; struct list_head evt_free_list;
spinlock_t evt_lock; struct efx_ptp_event_rx rx_evts[MAX_RECEIVE_EVENTS]; struct workqueue_struct *workwq; struct work_struct work; bool reset_required;
u32 rxfilter_event;
u32 rxfilter_general; bool rxfilter_installed; struct kernel_hwtstamp_config config; bool enabled; unsignedint mode; 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; unsignedint sync_event_minor_shift;
} nic_time; unsignedint min_synchronisation_ns; unsignedint capabilities; struct {
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; unsignedint adjfreq_ppb_shift;
s64 current_adjfreq; struct ptp_clock *phc_clock; struct ptp_clock_info phc_clock_info; struct work_struct pps_work; struct workqueue_struct *pps_workwq; bool nic_ts_enabled;
efx_dword_t txbuf[MCDI_TX_BUF_LEN(MC_CMD_PTP_IN_TRANSMIT_LENMAX)];
/* 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.
*/ 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 * risk leaking kernel memory to userland, so if the MCDI * request fails we pretend we got zeroes.
*/
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. * For the conversion from ns to s27, the operation is also converted to a * multiply and shift.
*/ #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 * where the minor register only uses 27 bits in units of 2^-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. * In this case, round up to the next second.
*/ 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 * 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)
{
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 * 16 bits from the timesync event state. * * We only have the lower 16 bits of the time now, but we do * have a full resolution timestamp at some point in past. As * long as the difference between the (real) now and the sync * is less than 2^15, then we can reconstruct the difference * between those two numbers using only the lower 16 bits of * each. * * Put another way * * a - b = ((a mod k) - b) mod k * * when -k/2 < (a-b) < k/2. In our case k is 2^16. We know * (a mod k) and b, so can calculate the delta, a - b. *
*/
sync_timestamp = last_sync_timestamp_major(efx);
/* Because delta is s16 this does an implicit mask down to * 16 bits which is what we need, assuming * MEDFORD_TX_SECS_EVENT_BITS is 16. delta is signed so that * we can deal with the (unlikely) case of sync timestamps * arriving from the future.
*/
delta = nic_major - sync_timestamp;
/* Recover the fully specified time now, by applying the offset * to the (fully specified) sync time.
*/
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 * use the default format for compatibility with older NICs i.e. * seconds and nanoseconds.
*/
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;
}
/* 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_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;
/* 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;
}
/* 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 * not supported (older NICs) then no correction is required.
*/
MCDI_SET_DWORD(inbuf, PTP_IN_OP,
MC_CMD_PTP_OP_GET_TIMESTAMP_CORRECTIONS);
MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);
/* Disable MCDI PTP support. * * Note that this function should never rely on the presence of ptp_data - * may be called before that exists.
*/ staticint efx_ptp_disable(struct efx_nic *efx)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_DISABLE_LEN);
MCDI_DECLARE_BUF_ERR(outbuf); int rc;
MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_DISABLE);
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), NULL);
rc = (rc == -EALREADY) ? 0 : rc; /* If we get ENOSYS, the NIC doesn't support PTP, and thus this function * should only have been called during probe.
*/ if (rc == -ENOSYS || rc == -EPERM)
pci_info(efx->pci_dev, "no PTP support\n"); elseif (rc)
efx_siena_mcdi_display_error(efx, MC_CMD_PTP,
MC_CMD_PTP_IN_DISABLE_LEN,
outbuf, sizeof(outbuf), rc); return rc;
}
/* 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;
/* Process times received from MC. * * Extract times from returned results, and establish the minimum value * seen. The minimum value represents the "best" possible time and events * too much greater than this are rejected - the machine is, perhaps, too * busy. A number of readings are taken so that, hopefully, at least one good * synchronisation will be seen in the results.
*/ staticint
efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
size_t response_length, conststruct pps_event_time *last_time)
{ unsigned number_readings =
MCDI_VAR_ARRAY_LEN(response_length,
PTP_OUT_SYNCHRONIZE_TIMESET); unsigned i; unsigned ngood = 0; unsigned last_good = 0; struct efx_ptp_data *ptp = efx->ptp_data;
u32 last_sec;
u32 start_sec; struct timespec64 delta;
ktime_t mc_time;
if (number_readings == 0) return -EAGAIN;
/* Read the set of results and find the last good host-MC * synchronization result. The MC times when it finishes reading the * host time so the corrected window time should be fairly constant * for a given platform. Increment stats for any results that appear * to be erroneous.
*/ for (i = 0; i < number_readings; i++) {
s32 window, corrected; struct timespec64 wait;
/* We expect the uncorrected synchronization window to be at * least as large as the interval between host start and end * times. If it is smaller than this then this is mostly likely * to be a consequence of the host's time being adjusted. * Check that the corrected sync window is in a reasonable * range. If it is out of range it is likely to be because an * interrupt or other delay occurred between reading the system * time and writing it to MC memory.
*/ if (window < SYNCHRONISATION_GRANULARITY_NS) {
++ptp->invalid_sync_windows;
} elseif (corrected >= MAX_SYNCHRONISATION_NS) {
++ptp->oversize_sync_windows;
} elseif (corrected < ptp->min_synchronisation_ns) {
++ptp->undersize_sync_windows;
} else {
ngood++;
last_good = i;
}
}
if (ngood == 0) {
netif_warn(efx, drv, efx->net_dev, "PTP no suitable synchronisations\n"); return -EAGAIN;
}
/* Calculate delay from last good sync (host time) to last_time. * It is possible that the seconds rolled over between taking * the start reading and the last value written by the host. The * timescales are such that a gap of more than one second is never * expected. delta is *not* normalised.
*/
start_sec = ptp->timeset[last_good].host_start >> MC_NANOSECOND_BITS;
last_sec = last_time->ts_real.tv_sec & MC_SECOND_MASK; if (start_sec != last_sec &&
((start_sec + 1) & MC_SECOND_MASK) != last_sec) {
netif_warn(efx, hw, efx->net_dev, "PTP bad synchronisation seconds\n"); return -EAGAIN;
}
delta.tv_sec = (last_sec - start_sec) & 1;
delta.tv_nsec =
last_time->ts_real.tv_nsec -
(ptp->timeset[last_good].host_start & MC_NANOSECOND_MASK);
/* Convert the NIC time at last good sync into kernel time. * No correction is required - this time is the output of a * firmware process.
*/
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);
return 0;
}
/* 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;
/* Process any queued receive events and corresponding packets * * q is returned with all the packets that are ready for delivery.
*/ staticvoid efx_ptp_process_events(struct efx_nic *efx, struct sk_buff_head *q)
{ struct efx_ptp_data *ptp = efx->ptp_data; struct sk_buff *skb;
while ((skb = skb_dequeue(&ptp->rxq))) { struct efx_ptp_match *match;
if (!ptp->channel || ptp->rxfilter_installed) return 0;
/* Must filter on both event and general ports to ensure * that there is no packet re-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;
/* Initialise PTP channel. * * Setting core_index to zero causes the queue to be initialised and doesn't * overlap with 'rxq0' because ptp.c doesn't use skb_record_rx_queue.
*/ staticint efx_ptp_probe_channel(struct efx_channel *channel)
{ struct efx_nic *efx = channel->efx; int rc;
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;
}
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);
/* Receive a PTP packet. Packets are queued until the arrival of * the receive timestamp from the MC - this will probably occur after the * packet arrival because of the processing in the MC.
*/ staticbool efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
{ struct efx_nic *efx = channel->efx; struct efx_ptp_data *ptp = efx->ptp_data; struct efx_ptp_match *match = (struct efx_ptp_match *)skb->cb;
u8 *match_data_012, *match_data_345; unsignedint version;
u8 *data;
/* 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 * to the timestamp
*/
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 * the UUID to match the packet to the timestamp. This * discards two of the bytes of the MAC address used * to create the UUID (SF bug 33070). The PTP V2 * enhanced mode fixes this issue and uses bytes 0-2 * and byte 5-7 of the UUID.
*/
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 * the packet for PTP V1 and V2
*/
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, through an MCDI call. MCDI calls aren't permitted * in the transmit path so defer the actual transmission to a suitable worker.
*/ 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 * start worker thread. This is required because event and their * correspoding packets may come in either order.
*/ 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 * 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_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.
*/
(void) cmpxchg(&channel->sync_events_state, SYNC_EVENTS_REQUESTED,
SYNC_EVENTS_VALID);
}
/* get the difference between the packet and sync timestamps, * modulo one second
*/
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 (allowing for fuzz)
*/
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. * This means the RX packet and sync event crossed as they were * placed on the event queue, which can sometimes happen.
*/
pkt_timestamp_major = channel->sync_timestamp_major - 1 + carry;
} else { /* it's outside tolerance in both directions. this might be * indicative of us missing sync events for some reason, so * we'll call it an error rather than risk giving a bogus * 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. * Subtract from the desired time to get the offset * call efx_phc_adjtime with the offset
*/ 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;
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.