18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/**************************************************************************** 38c2ecf20Sopenharmony_ci * Driver for Solarflare network controllers and boards 48c2ecf20Sopenharmony_ci * Copyright 2011-2013 Solarflare Communications Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci/* Theory of operation: 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * PTP support is assisted by firmware running on the MC, which provides 108c2ecf20Sopenharmony_ci * the hardware timestamping capabilities. Both transmitted and received 118c2ecf20Sopenharmony_ci * PTP event packets are queued onto internal queues for subsequent processing; 128c2ecf20Sopenharmony_ci * this is because the MC operations are relatively long and would block 138c2ecf20Sopenharmony_ci * block NAPI/interrupt operation. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Receive event processing: 168c2ecf20Sopenharmony_ci * The event contains the packet's UUID and sequence number, together 178c2ecf20Sopenharmony_ci * with the hardware timestamp. The PTP receive packet queue is searched 188c2ecf20Sopenharmony_ci * for this UUID/sequence number and, if found, put on a pending queue. 198c2ecf20Sopenharmony_ci * Packets not matching are delivered without timestamps (MCDI events will 208c2ecf20Sopenharmony_ci * always arrive after the actual packet). 218c2ecf20Sopenharmony_ci * It is important for the operation of the PTP protocol that the ordering 228c2ecf20Sopenharmony_ci * of packets between the event and general port is maintained. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Work queue processing: 258c2ecf20Sopenharmony_ci * If work waiting, synchronise host/hardware time 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Transmit: send packet through MC, which returns the transmission time 288c2ecf20Sopenharmony_ci * that is converted to an appropriate timestamp. 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Receive: the packet's reception time is converted to an appropriate 318c2ecf20Sopenharmony_ci * timestamp. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci#include <linux/ip.h> 348c2ecf20Sopenharmony_ci#include <linux/udp.h> 358c2ecf20Sopenharmony_ci#include <linux/time.h> 368c2ecf20Sopenharmony_ci#include <linux/ktime.h> 378c2ecf20Sopenharmony_ci#include <linux/module.h> 388c2ecf20Sopenharmony_ci#include <linux/pps_kernel.h> 398c2ecf20Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 408c2ecf20Sopenharmony_ci#include "net_driver.h" 418c2ecf20Sopenharmony_ci#include "efx.h" 428c2ecf20Sopenharmony_ci#include "mcdi.h" 438c2ecf20Sopenharmony_ci#include "mcdi_pcol.h" 448c2ecf20Sopenharmony_ci#include "io.h" 458c2ecf20Sopenharmony_ci#include "farch_regs.h" 468c2ecf20Sopenharmony_ci#include "tx.h" 478c2ecf20Sopenharmony_ci#include "nic.h" /* indirectly includes ptp.h */ 488c2ecf20Sopenharmony_ci#include "efx_channels.h" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Maximum number of events expected to make up a PTP event */ 518c2ecf20Sopenharmony_ci#define MAX_EVENT_FRAGS 3 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Maximum delay, ms, to begin synchronisation */ 548c2ecf20Sopenharmony_ci#define MAX_SYNCHRONISE_WAIT_MS 2 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* How long, at most, to spend synchronising */ 578c2ecf20Sopenharmony_ci#define SYNCHRONISE_PERIOD_NS 250000 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* How often to update the shared memory time */ 608c2ecf20Sopenharmony_ci#define SYNCHRONISATION_GRANULARITY_NS 200 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* Minimum permitted length of a (corrected) synchronisation time */ 638c2ecf20Sopenharmony_ci#define DEFAULT_MIN_SYNCHRONISATION_NS 120 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Maximum permitted length of a (corrected) synchronisation time */ 668c2ecf20Sopenharmony_ci#define MAX_SYNCHRONISATION_NS 1000 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* How many (MC) receive events that can be queued */ 698c2ecf20Sopenharmony_ci#define MAX_RECEIVE_EVENTS 8 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* Length of (modified) moving average. */ 728c2ecf20Sopenharmony_ci#define AVERAGE_LENGTH 16 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* How long an unmatched event or packet can be held */ 758c2ecf20Sopenharmony_ci#define PKT_EVENT_LIFETIME_MS 10 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Offsets into PTP packet for identification. These offsets are from the 788c2ecf20Sopenharmony_ci * start of the IP header, not the MAC header. Note that neither PTP V1 nor 798c2ecf20Sopenharmony_ci * PTP V2 permit the use of IPV4 options. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci#define PTP_DPORT_OFFSET 22 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define PTP_V1_VERSION_LENGTH 2 848c2ecf20Sopenharmony_ci#define PTP_V1_VERSION_OFFSET 28 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define PTP_V1_UUID_LENGTH 6 878c2ecf20Sopenharmony_ci#define PTP_V1_UUID_OFFSET 50 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define PTP_V1_SEQUENCE_LENGTH 2 908c2ecf20Sopenharmony_ci#define PTP_V1_SEQUENCE_OFFSET 58 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* The minimum length of a PTP V1 packet for offsets, etc. to be valid: 938c2ecf20Sopenharmony_ci * includes IP header. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci#define PTP_V1_MIN_LENGTH 64 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define PTP_V2_VERSION_LENGTH 1 988c2ecf20Sopenharmony_ci#define PTP_V2_VERSION_OFFSET 29 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define PTP_V2_UUID_LENGTH 8 1018c2ecf20Sopenharmony_ci#define PTP_V2_UUID_OFFSET 48 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Although PTP V2 UUIDs are comprised a ClockIdentity (8) and PortNumber (2), 1048c2ecf20Sopenharmony_ci * the MC only captures the last six bytes of the clock identity. These values 1058c2ecf20Sopenharmony_ci * reflect those, not the ones used in the standard. The standard permits 1068c2ecf20Sopenharmony_ci * mapping of V1 UUIDs to V2 UUIDs with these same values. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ci#define PTP_V2_MC_UUID_LENGTH 6 1098c2ecf20Sopenharmony_ci#define PTP_V2_MC_UUID_OFFSET 50 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define PTP_V2_SEQUENCE_LENGTH 2 1128c2ecf20Sopenharmony_ci#define PTP_V2_SEQUENCE_OFFSET 58 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* The minimum length of a PTP V2 packet for offsets, etc. to be valid: 1158c2ecf20Sopenharmony_ci * includes IP header. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci#define PTP_V2_MIN_LENGTH 63 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define PTP_MIN_LENGTH 63 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define PTP_ADDRESS 0xe0000181 /* 224.0.1.129 */ 1228c2ecf20Sopenharmony_ci#define PTP_EVENT_PORT 319 1238c2ecf20Sopenharmony_ci#define PTP_GENERAL_PORT 320 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* Annoyingly the format of the version numbers are different between 1268c2ecf20Sopenharmony_ci * versions 1 and 2 so it isn't possible to simply look for 1 or 2. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci#define PTP_VERSION_V1 1 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#define PTP_VERSION_V2 2 1318c2ecf20Sopenharmony_ci#define PTP_VERSION_V2_MASK 0x0f 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cienum ptp_packet_state { 1348c2ecf20Sopenharmony_ci PTP_PACKET_STATE_UNMATCHED = 0, 1358c2ecf20Sopenharmony_ci PTP_PACKET_STATE_MATCHED, 1368c2ecf20Sopenharmony_ci PTP_PACKET_STATE_TIMED_OUT, 1378c2ecf20Sopenharmony_ci PTP_PACKET_STATE_MATCH_UNWANTED 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* NIC synchronised with single word of time only comprising 1418c2ecf20Sopenharmony_ci * partial seconds and full nanoseconds: 10^9 ~ 2^30 so 2 bits for seconds. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci#define MC_NANOSECOND_BITS 30 1448c2ecf20Sopenharmony_ci#define MC_NANOSECOND_MASK ((1 << MC_NANOSECOND_BITS) - 1) 1458c2ecf20Sopenharmony_ci#define MC_SECOND_MASK ((1 << (32 - MC_NANOSECOND_BITS)) - 1) 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* Maximum parts-per-billion adjustment that is acceptable */ 1488c2ecf20Sopenharmony_ci#define MAX_PPB 1000000 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/* Precalculate scale word to avoid long long division at runtime */ 1518c2ecf20Sopenharmony_ci/* This is equivalent to 2^66 / 10^9. */ 1528c2ecf20Sopenharmony_ci#define PPB_SCALE_WORD ((1LL << (57)) / 1953125LL) 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* How much to shift down after scaling to convert to FP40 */ 1558c2ecf20Sopenharmony_ci#define PPB_SHIFT_FP40 26 1568c2ecf20Sopenharmony_ci/* ... and FP44. */ 1578c2ecf20Sopenharmony_ci#define PPB_SHIFT_FP44 22 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci#define PTP_SYNC_ATTEMPTS 4 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/** 1628c2ecf20Sopenharmony_ci * struct efx_ptp_match - Matching structure, stored in sk_buff's cb area. 1638c2ecf20Sopenharmony_ci * @words: UUID and (partial) sequence number 1648c2ecf20Sopenharmony_ci * @expiry: Time after which the packet should be delivered irrespective of 1658c2ecf20Sopenharmony_ci * event arrival. 1668c2ecf20Sopenharmony_ci * @state: The state of the packet - whether it is ready for processing or 1678c2ecf20Sopenharmony_ci * whether that is of no interest. 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_cistruct efx_ptp_match { 1708c2ecf20Sopenharmony_ci u32 words[DIV_ROUND_UP(PTP_V1_UUID_LENGTH, 4)]; 1718c2ecf20Sopenharmony_ci unsigned long expiry; 1728c2ecf20Sopenharmony_ci enum ptp_packet_state state; 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * struct efx_ptp_event_rx - A PTP receive event (from MC) 1778c2ecf20Sopenharmony_ci * @link: list of events 1788c2ecf20Sopenharmony_ci * @seq0: First part of (PTP) UUID 1798c2ecf20Sopenharmony_ci * @seq1: Second part of (PTP) UUID and sequence number 1808c2ecf20Sopenharmony_ci * @hwtimestamp: Event timestamp 1818c2ecf20Sopenharmony_ci * @expiry: Time which the packet arrived 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_cistruct efx_ptp_event_rx { 1848c2ecf20Sopenharmony_ci struct list_head link; 1858c2ecf20Sopenharmony_ci u32 seq0; 1868c2ecf20Sopenharmony_ci u32 seq1; 1878c2ecf20Sopenharmony_ci ktime_t hwtimestamp; 1888c2ecf20Sopenharmony_ci unsigned long expiry; 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/** 1928c2ecf20Sopenharmony_ci * struct efx_ptp_timeset - Synchronisation between host and MC 1938c2ecf20Sopenharmony_ci * @host_start: Host time immediately before hardware timestamp taken 1948c2ecf20Sopenharmony_ci * @major: Hardware timestamp, major 1958c2ecf20Sopenharmony_ci * @minor: Hardware timestamp, minor 1968c2ecf20Sopenharmony_ci * @host_end: Host time immediately after hardware timestamp taken 1978c2ecf20Sopenharmony_ci * @wait: Number of NIC clock ticks between hardware timestamp being read and 1988c2ecf20Sopenharmony_ci * host end time being seen 1998c2ecf20Sopenharmony_ci * @window: Difference of host_end and host_start 2008c2ecf20Sopenharmony_ci * @valid: Whether this timeset is valid 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_cistruct efx_ptp_timeset { 2038c2ecf20Sopenharmony_ci u32 host_start; 2048c2ecf20Sopenharmony_ci u32 major; 2058c2ecf20Sopenharmony_ci u32 minor; 2068c2ecf20Sopenharmony_ci u32 host_end; 2078c2ecf20Sopenharmony_ci u32 wait; 2088c2ecf20Sopenharmony_ci u32 window; /* Derived: end - start, allowing for wrap */ 2098c2ecf20Sopenharmony_ci}; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/** 2128c2ecf20Sopenharmony_ci * struct efx_ptp_data - Precision Time Protocol (PTP) state 2138c2ecf20Sopenharmony_ci * @efx: The NIC context 2148c2ecf20Sopenharmony_ci * @channel: The PTP channel (Siena only) 2158c2ecf20Sopenharmony_ci * @rx_ts_inline: Flag for whether RX timestamps are inline (else they are 2168c2ecf20Sopenharmony_ci * separate events) 2178c2ecf20Sopenharmony_ci * @rxq: Receive SKB queue (awaiting timestamps) 2188c2ecf20Sopenharmony_ci * @txq: Transmit SKB queue 2198c2ecf20Sopenharmony_ci * @evt_list: List of MC receive events awaiting packets 2208c2ecf20Sopenharmony_ci * @evt_free_list: List of free events 2218c2ecf20Sopenharmony_ci * @evt_lock: Lock for manipulating evt_list and evt_free_list 2228c2ecf20Sopenharmony_ci * @rx_evts: Instantiated events (on evt_list and evt_free_list) 2238c2ecf20Sopenharmony_ci * @workwq: Work queue for processing pending PTP operations 2248c2ecf20Sopenharmony_ci * @work: Work task 2258c2ecf20Sopenharmony_ci * @reset_required: A serious error has occurred and the PTP task needs to be 2268c2ecf20Sopenharmony_ci * reset (disable, enable). 2278c2ecf20Sopenharmony_ci * @rxfilter_event: Receive filter when operating 2288c2ecf20Sopenharmony_ci * @rxfilter_general: Receive filter when operating 2298c2ecf20Sopenharmony_ci * @rxfilter_installed: Receive filter installed 2308c2ecf20Sopenharmony_ci * @config: Current timestamp configuration 2318c2ecf20Sopenharmony_ci * @enabled: PTP operation enabled 2328c2ecf20Sopenharmony_ci * @mode: Mode in which PTP operating (PTP version) 2338c2ecf20Sopenharmony_ci * @ns_to_nic_time: Function to convert from scalar nanoseconds to NIC time 2348c2ecf20Sopenharmony_ci * @nic_to_kernel_time: Function to convert from NIC to kernel time 2358c2ecf20Sopenharmony_ci * @nic_time: contains time details 2368c2ecf20Sopenharmony_ci * @nic_time.minor_max: Wrap point for NIC minor times 2378c2ecf20Sopenharmony_ci * @nic_time.sync_event_diff_min: Minimum acceptable difference between time 2388c2ecf20Sopenharmony_ci * in packet prefix and last MCDI time sync event i.e. how much earlier than 2398c2ecf20Sopenharmony_ci * the last sync event time a packet timestamp can be. 2408c2ecf20Sopenharmony_ci * @nic_time.sync_event_diff_max: Maximum acceptable difference between time 2418c2ecf20Sopenharmony_ci * in packet prefix and last MCDI time sync event i.e. how much later than 2428c2ecf20Sopenharmony_ci * the last sync event time a packet timestamp can be. 2438c2ecf20Sopenharmony_ci * @nic_time.sync_event_minor_shift: Shift required to make minor time from 2448c2ecf20Sopenharmony_ci * field in MCDI time sync event. 2458c2ecf20Sopenharmony_ci * @min_synchronisation_ns: Minimum acceptable corrected sync window 2468c2ecf20Sopenharmony_ci * @capabilities: Capabilities flags from the NIC 2478c2ecf20Sopenharmony_ci * @ts_corrections: contains corrections details 2488c2ecf20Sopenharmony_ci * @ts_corrections.ptp_tx: Required driver correction of PTP packet transmit 2498c2ecf20Sopenharmony_ci * timestamps 2508c2ecf20Sopenharmony_ci * @ts_corrections.ptp_rx: Required driver correction of PTP packet receive 2518c2ecf20Sopenharmony_ci * timestamps 2528c2ecf20Sopenharmony_ci * @ts_corrections.pps_out: PPS output error (information only) 2538c2ecf20Sopenharmony_ci * @ts_corrections.pps_in: Required driver correction of PPS input timestamps 2548c2ecf20Sopenharmony_ci * @ts_corrections.general_tx: Required driver correction of general packet 2558c2ecf20Sopenharmony_ci * transmit timestamps 2568c2ecf20Sopenharmony_ci * @ts_corrections.general_rx: Required driver correction of general packet 2578c2ecf20Sopenharmony_ci * receive timestamps 2588c2ecf20Sopenharmony_ci * @evt_frags: Partly assembled PTP events 2598c2ecf20Sopenharmony_ci * @evt_frag_idx: Current fragment number 2608c2ecf20Sopenharmony_ci * @evt_code: Last event code 2618c2ecf20Sopenharmony_ci * @start: Address at which MC indicates ready for synchronisation 2628c2ecf20Sopenharmony_ci * @host_time_pps: Host time at last PPS 2638c2ecf20Sopenharmony_ci * @adjfreq_ppb_shift: Shift required to convert scaled parts-per-billion 2648c2ecf20Sopenharmony_ci * frequency adjustment into a fixed point fractional nanosecond format. 2658c2ecf20Sopenharmony_ci * @current_adjfreq: Current ppb adjustment. 2668c2ecf20Sopenharmony_ci * @phc_clock: Pointer to registered phc device (if primary function) 2678c2ecf20Sopenharmony_ci * @phc_clock_info: Registration structure for phc device 2688c2ecf20Sopenharmony_ci * @pps_work: pps work task for handling pps events 2698c2ecf20Sopenharmony_ci * @pps_workwq: pps work queue 2708c2ecf20Sopenharmony_ci * @nic_ts_enabled: Flag indicating if NIC generated TS events are handled 2718c2ecf20Sopenharmony_ci * @txbuf: Buffer for use when transmitting (PTP) packets to MC (avoids 2728c2ecf20Sopenharmony_ci * allocations in main data path). 2738c2ecf20Sopenharmony_ci * @good_syncs: Number of successful synchronisations. 2748c2ecf20Sopenharmony_ci * @fast_syncs: Number of synchronisations requiring short delay 2758c2ecf20Sopenharmony_ci * @bad_syncs: Number of failed synchronisations. 2768c2ecf20Sopenharmony_ci * @sync_timeouts: Number of synchronisation timeouts 2778c2ecf20Sopenharmony_ci * @no_time_syncs: Number of synchronisations with no good times. 2788c2ecf20Sopenharmony_ci * @invalid_sync_windows: Number of sync windows with bad durations. 2798c2ecf20Sopenharmony_ci * @undersize_sync_windows: Number of corrected sync windows that are too small 2808c2ecf20Sopenharmony_ci * @oversize_sync_windows: Number of corrected sync windows that are too large 2818c2ecf20Sopenharmony_ci * @rx_no_timestamp: Number of packets received without a timestamp. 2828c2ecf20Sopenharmony_ci * @timeset: Last set of synchronisation statistics. 2838c2ecf20Sopenharmony_ci * @xmit_skb: Transmit SKB function. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_cistruct efx_ptp_data { 2868c2ecf20Sopenharmony_ci struct efx_nic *efx; 2878c2ecf20Sopenharmony_ci struct efx_channel *channel; 2888c2ecf20Sopenharmony_ci bool rx_ts_inline; 2898c2ecf20Sopenharmony_ci struct sk_buff_head rxq; 2908c2ecf20Sopenharmony_ci struct sk_buff_head txq; 2918c2ecf20Sopenharmony_ci struct list_head evt_list; 2928c2ecf20Sopenharmony_ci struct list_head evt_free_list; 2938c2ecf20Sopenharmony_ci spinlock_t evt_lock; 2948c2ecf20Sopenharmony_ci struct efx_ptp_event_rx rx_evts[MAX_RECEIVE_EVENTS]; 2958c2ecf20Sopenharmony_ci struct workqueue_struct *workwq; 2968c2ecf20Sopenharmony_ci struct work_struct work; 2978c2ecf20Sopenharmony_ci bool reset_required; 2988c2ecf20Sopenharmony_ci u32 rxfilter_event; 2998c2ecf20Sopenharmony_ci u32 rxfilter_general; 3008c2ecf20Sopenharmony_ci bool rxfilter_installed; 3018c2ecf20Sopenharmony_ci struct hwtstamp_config config; 3028c2ecf20Sopenharmony_ci bool enabled; 3038c2ecf20Sopenharmony_ci unsigned int mode; 3048c2ecf20Sopenharmony_ci void (*ns_to_nic_time)(s64 ns, u32 *nic_major, u32 *nic_minor); 3058c2ecf20Sopenharmony_ci ktime_t (*nic_to_kernel_time)(u32 nic_major, u32 nic_minor, 3068c2ecf20Sopenharmony_ci s32 correction); 3078c2ecf20Sopenharmony_ci struct { 3088c2ecf20Sopenharmony_ci u32 minor_max; 3098c2ecf20Sopenharmony_ci u32 sync_event_diff_min; 3108c2ecf20Sopenharmony_ci u32 sync_event_diff_max; 3118c2ecf20Sopenharmony_ci unsigned int sync_event_minor_shift; 3128c2ecf20Sopenharmony_ci } nic_time; 3138c2ecf20Sopenharmony_ci unsigned int min_synchronisation_ns; 3148c2ecf20Sopenharmony_ci unsigned int capabilities; 3158c2ecf20Sopenharmony_ci struct { 3168c2ecf20Sopenharmony_ci s32 ptp_tx; 3178c2ecf20Sopenharmony_ci s32 ptp_rx; 3188c2ecf20Sopenharmony_ci s32 pps_out; 3198c2ecf20Sopenharmony_ci s32 pps_in; 3208c2ecf20Sopenharmony_ci s32 general_tx; 3218c2ecf20Sopenharmony_ci s32 general_rx; 3228c2ecf20Sopenharmony_ci } ts_corrections; 3238c2ecf20Sopenharmony_ci efx_qword_t evt_frags[MAX_EVENT_FRAGS]; 3248c2ecf20Sopenharmony_ci int evt_frag_idx; 3258c2ecf20Sopenharmony_ci int evt_code; 3268c2ecf20Sopenharmony_ci struct efx_buffer start; 3278c2ecf20Sopenharmony_ci struct pps_event_time host_time_pps; 3288c2ecf20Sopenharmony_ci unsigned int adjfreq_ppb_shift; 3298c2ecf20Sopenharmony_ci s64 current_adjfreq; 3308c2ecf20Sopenharmony_ci struct ptp_clock *phc_clock; 3318c2ecf20Sopenharmony_ci struct ptp_clock_info phc_clock_info; 3328c2ecf20Sopenharmony_ci struct work_struct pps_work; 3338c2ecf20Sopenharmony_ci struct workqueue_struct *pps_workwq; 3348c2ecf20Sopenharmony_ci bool nic_ts_enabled; 3358c2ecf20Sopenharmony_ci efx_dword_t txbuf[MCDI_TX_BUF_LEN(MC_CMD_PTP_IN_TRANSMIT_LENMAX)]; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci unsigned int good_syncs; 3388c2ecf20Sopenharmony_ci unsigned int fast_syncs; 3398c2ecf20Sopenharmony_ci unsigned int bad_syncs; 3408c2ecf20Sopenharmony_ci unsigned int sync_timeouts; 3418c2ecf20Sopenharmony_ci unsigned int no_time_syncs; 3428c2ecf20Sopenharmony_ci unsigned int invalid_sync_windows; 3438c2ecf20Sopenharmony_ci unsigned int undersize_sync_windows; 3448c2ecf20Sopenharmony_ci unsigned int oversize_sync_windows; 3458c2ecf20Sopenharmony_ci unsigned int rx_no_timestamp; 3468c2ecf20Sopenharmony_ci struct efx_ptp_timeset 3478c2ecf20Sopenharmony_ci timeset[MC_CMD_PTP_OUT_SYNCHRONIZE_TIMESET_MAXNUM]; 3488c2ecf20Sopenharmony_ci void (*xmit_skb)(struct efx_nic *efx, struct sk_buff *skb); 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int efx_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta); 3528c2ecf20Sopenharmony_cistatic int efx_phc_adjtime(struct ptp_clock_info *ptp, s64 delta); 3538c2ecf20Sopenharmony_cistatic int efx_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts); 3548c2ecf20Sopenharmony_cistatic int efx_phc_settime(struct ptp_clock_info *ptp, 3558c2ecf20Sopenharmony_ci const struct timespec64 *e_ts); 3568c2ecf20Sopenharmony_cistatic int efx_phc_enable(struct ptp_clock_info *ptp, 3578c2ecf20Sopenharmony_ci struct ptp_clock_request *request, int on); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cibool efx_ptp_use_mac_tx_timestamps(struct efx_nic *efx) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci return efx_has_cap(efx, TX_MAC_TIMESTAMPING); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* PTP 'extra' channel is still a traffic channel, but we only create TX queues 3658c2ecf20Sopenharmony_ci * if PTP uses MAC TX timestamps, not if PTP uses the MC directly to transmit. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_cistatic bool efx_ptp_want_txqs(struct efx_channel *channel) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci return efx_ptp_use_mac_tx_timestamps(channel->efx); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci#define PTP_SW_STAT(ext_name, field_name) \ 3738c2ecf20Sopenharmony_ci { #ext_name, 0, offsetof(struct efx_ptp_data, field_name) } 3748c2ecf20Sopenharmony_ci#define PTP_MC_STAT(ext_name, mcdi_name) \ 3758c2ecf20Sopenharmony_ci { #ext_name, 32, MC_CMD_PTP_OUT_STATUS_STATS_ ## mcdi_name ## _OFST } 3768c2ecf20Sopenharmony_cistatic const struct efx_hw_stat_desc efx_ptp_stat_desc[] = { 3778c2ecf20Sopenharmony_ci PTP_SW_STAT(ptp_good_syncs, good_syncs), 3788c2ecf20Sopenharmony_ci PTP_SW_STAT(ptp_fast_syncs, fast_syncs), 3798c2ecf20Sopenharmony_ci PTP_SW_STAT(ptp_bad_syncs, bad_syncs), 3808c2ecf20Sopenharmony_ci PTP_SW_STAT(ptp_sync_timeouts, sync_timeouts), 3818c2ecf20Sopenharmony_ci PTP_SW_STAT(ptp_no_time_syncs, no_time_syncs), 3828c2ecf20Sopenharmony_ci PTP_SW_STAT(ptp_invalid_sync_windows, invalid_sync_windows), 3838c2ecf20Sopenharmony_ci PTP_SW_STAT(ptp_undersize_sync_windows, undersize_sync_windows), 3848c2ecf20Sopenharmony_ci PTP_SW_STAT(ptp_oversize_sync_windows, oversize_sync_windows), 3858c2ecf20Sopenharmony_ci PTP_SW_STAT(ptp_rx_no_timestamp, rx_no_timestamp), 3868c2ecf20Sopenharmony_ci PTP_MC_STAT(ptp_tx_timestamp_packets, TX), 3878c2ecf20Sopenharmony_ci PTP_MC_STAT(ptp_rx_timestamp_packets, RX), 3888c2ecf20Sopenharmony_ci PTP_MC_STAT(ptp_timestamp_packets, TS), 3898c2ecf20Sopenharmony_ci PTP_MC_STAT(ptp_filter_matches, FM), 3908c2ecf20Sopenharmony_ci PTP_MC_STAT(ptp_non_filter_matches, NFM), 3918c2ecf20Sopenharmony_ci}; 3928c2ecf20Sopenharmony_ci#define PTP_STAT_COUNT ARRAY_SIZE(efx_ptp_stat_desc) 3938c2ecf20Sopenharmony_cistatic const unsigned long efx_ptp_stat_mask[] = { 3948c2ecf20Sopenharmony_ci [0 ... BITS_TO_LONGS(PTP_STAT_COUNT) - 1] = ~0UL, 3958c2ecf20Sopenharmony_ci}; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cisize_t efx_ptp_describe_stats(struct efx_nic *efx, u8 *strings) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci if (!efx->ptp_data) 4008c2ecf20Sopenharmony_ci return 0; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return efx_nic_describe_stats(efx_ptp_stat_desc, PTP_STAT_COUNT, 4038c2ecf20Sopenharmony_ci efx_ptp_stat_mask, strings); 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cisize_t efx_ptp_update_stats(struct efx_nic *efx, u64 *stats) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_STATUS_LEN); 4098c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_STATUS_LEN); 4108c2ecf20Sopenharmony_ci size_t i; 4118c2ecf20Sopenharmony_ci int rc; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (!efx->ptp_data) 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* Copy software statistics */ 4178c2ecf20Sopenharmony_ci for (i = 0; i < PTP_STAT_COUNT; i++) { 4188c2ecf20Sopenharmony_ci if (efx_ptp_stat_desc[i].dma_width) 4198c2ecf20Sopenharmony_ci continue; 4208c2ecf20Sopenharmony_ci stats[i] = *(unsigned int *)((char *)efx->ptp_data + 4218c2ecf20Sopenharmony_ci efx_ptp_stat_desc[i].offset); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* Fetch MC statistics. We *must* fill in all statistics or 4258c2ecf20Sopenharmony_ci * risk leaking kernel memory to userland, so if the MCDI 4268c2ecf20Sopenharmony_ci * request fails we pretend we got zeroes. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_STATUS); 4298c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0); 4308c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc(efx, MC_CMD_PTP, inbuf, sizeof(inbuf), 4318c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), NULL); 4328c2ecf20Sopenharmony_ci if (rc) 4338c2ecf20Sopenharmony_ci memset(outbuf, 0, sizeof(outbuf)); 4348c2ecf20Sopenharmony_ci efx_nic_update_stats(efx_ptp_stat_desc, PTP_STAT_COUNT, 4358c2ecf20Sopenharmony_ci efx_ptp_stat_mask, 4368c2ecf20Sopenharmony_ci stats, _MCDI_PTR(outbuf, 0), false); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return PTP_STAT_COUNT; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* For Siena platforms NIC time is s and ns */ 4428c2ecf20Sopenharmony_cistatic void efx_ptp_ns_to_s_ns(s64 ns, u32 *nic_major, u32 *nic_minor) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct timespec64 ts = ns_to_timespec64(ns); 4458c2ecf20Sopenharmony_ci *nic_major = (u32)ts.tv_sec; 4468c2ecf20Sopenharmony_ci *nic_minor = ts.tv_nsec; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic ktime_t efx_ptp_s_ns_to_ktime_correction(u32 nic_major, u32 nic_minor, 4508c2ecf20Sopenharmony_ci s32 correction) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci ktime_t kt = ktime_set(nic_major, nic_minor); 4538c2ecf20Sopenharmony_ci if (correction >= 0) 4548c2ecf20Sopenharmony_ci kt = ktime_add_ns(kt, (u64)correction); 4558c2ecf20Sopenharmony_ci else 4568c2ecf20Sopenharmony_ci kt = ktime_sub_ns(kt, (u64)-correction); 4578c2ecf20Sopenharmony_ci return kt; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci/* To convert from s27 format to ns we multiply then divide by a power of 2. 4618c2ecf20Sopenharmony_ci * For the conversion from ns to s27, the operation is also converted to a 4628c2ecf20Sopenharmony_ci * multiply and shift. 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_ci#define S27_TO_NS_SHIFT (27) 4658c2ecf20Sopenharmony_ci#define NS_TO_S27_MULT (((1ULL << 63) + NSEC_PER_SEC / 2) / NSEC_PER_SEC) 4668c2ecf20Sopenharmony_ci#define NS_TO_S27_SHIFT (63 - S27_TO_NS_SHIFT) 4678c2ecf20Sopenharmony_ci#define S27_MINOR_MAX (1 << S27_TO_NS_SHIFT) 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci/* For Huntington platforms NIC time is in seconds and fractions of a second 4708c2ecf20Sopenharmony_ci * where the minor register only uses 27 bits in units of 2^-27s. 4718c2ecf20Sopenharmony_ci */ 4728c2ecf20Sopenharmony_cistatic void efx_ptp_ns_to_s27(s64 ns, u32 *nic_major, u32 *nic_minor) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct timespec64 ts = ns_to_timespec64(ns); 4758c2ecf20Sopenharmony_ci u32 maj = (u32)ts.tv_sec; 4768c2ecf20Sopenharmony_ci u32 min = (u32)(((u64)ts.tv_nsec * NS_TO_S27_MULT + 4778c2ecf20Sopenharmony_ci (1ULL << (NS_TO_S27_SHIFT - 1))) >> NS_TO_S27_SHIFT); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* The conversion can result in the minor value exceeding the maximum. 4808c2ecf20Sopenharmony_ci * In this case, round up to the next second. 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_ci if (min >= S27_MINOR_MAX) { 4838c2ecf20Sopenharmony_ci min -= S27_MINOR_MAX; 4848c2ecf20Sopenharmony_ci maj++; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci *nic_major = maj; 4888c2ecf20Sopenharmony_ci *nic_minor = min; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic inline ktime_t efx_ptp_s27_to_ktime(u32 nic_major, u32 nic_minor) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci u32 ns = (u32)(((u64)nic_minor * NSEC_PER_SEC + 4948c2ecf20Sopenharmony_ci (1ULL << (S27_TO_NS_SHIFT - 1))) >> S27_TO_NS_SHIFT); 4958c2ecf20Sopenharmony_ci return ktime_set(nic_major, ns); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic ktime_t efx_ptp_s27_to_ktime_correction(u32 nic_major, u32 nic_minor, 4998c2ecf20Sopenharmony_ci s32 correction) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci /* Apply the correction and deal with carry */ 5028c2ecf20Sopenharmony_ci nic_minor += correction; 5038c2ecf20Sopenharmony_ci if ((s32)nic_minor < 0) { 5048c2ecf20Sopenharmony_ci nic_minor += S27_MINOR_MAX; 5058c2ecf20Sopenharmony_ci nic_major--; 5068c2ecf20Sopenharmony_ci } else if (nic_minor >= S27_MINOR_MAX) { 5078c2ecf20Sopenharmony_ci nic_minor -= S27_MINOR_MAX; 5088c2ecf20Sopenharmony_ci nic_major++; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return efx_ptp_s27_to_ktime(nic_major, nic_minor); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci/* For Medford2 platforms the time is in seconds and quarter nanoseconds. */ 5158c2ecf20Sopenharmony_cistatic void efx_ptp_ns_to_s_qns(s64 ns, u32 *nic_major, u32 *nic_minor) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct timespec64 ts = ns_to_timespec64(ns); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci *nic_major = (u32)ts.tv_sec; 5208c2ecf20Sopenharmony_ci *nic_minor = ts.tv_nsec * 4; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic ktime_t efx_ptp_s_qns_to_ktime_correction(u32 nic_major, u32 nic_minor, 5248c2ecf20Sopenharmony_ci s32 correction) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci ktime_t kt; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci nic_minor = DIV_ROUND_CLOSEST(nic_minor, 4); 5298c2ecf20Sopenharmony_ci correction = DIV_ROUND_CLOSEST(correction, 4); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci kt = ktime_set(nic_major, nic_minor); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (correction >= 0) 5348c2ecf20Sopenharmony_ci kt = ktime_add_ns(kt, (u64)correction); 5358c2ecf20Sopenharmony_ci else 5368c2ecf20Sopenharmony_ci kt = ktime_sub_ns(kt, (u64)-correction); 5378c2ecf20Sopenharmony_ci return kt; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistruct efx_channel *efx_ptp_channel(struct efx_nic *efx) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci return efx->ptp_data ? efx->ptp_data->channel : NULL; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_civoid efx_ptp_update_channel(struct efx_nic *efx, struct efx_channel *channel) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci if (efx->ptp_data) 5488c2ecf20Sopenharmony_ci efx->ptp_data->channel = channel; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic u32 last_sync_timestamp_major(struct efx_nic *efx) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct efx_channel *channel = efx_ptp_channel(efx); 5548c2ecf20Sopenharmony_ci u32 major = 0; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (channel) 5578c2ecf20Sopenharmony_ci major = channel->sync_timestamp_major; 5588c2ecf20Sopenharmony_ci return major; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/* The 8000 series and later can provide the time from the MAC, which is only 5628c2ecf20Sopenharmony_ci * 48 bits long and provides meta-information in the top 2 bits. 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_cistatic ktime_t 5658c2ecf20Sopenharmony_ciefx_ptp_mac_nic_to_ktime_correction(struct efx_nic *efx, 5668c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp, 5678c2ecf20Sopenharmony_ci u32 nic_major, u32 nic_minor, 5688c2ecf20Sopenharmony_ci s32 correction) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci u32 sync_timestamp; 5718c2ecf20Sopenharmony_ci ktime_t kt = { 0 }; 5728c2ecf20Sopenharmony_ci s16 delta; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (!(nic_major & 0x80000000)) { 5758c2ecf20Sopenharmony_ci WARN_ON_ONCE(nic_major >> 16); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* Medford provides 48 bits of timestamp, so we must get the top 5788c2ecf20Sopenharmony_ci * 16 bits from the timesync event state. 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci * We only have the lower 16 bits of the time now, but we do 5818c2ecf20Sopenharmony_ci * have a full resolution timestamp at some point in past. As 5828c2ecf20Sopenharmony_ci * long as the difference between the (real) now and the sync 5838c2ecf20Sopenharmony_ci * is less than 2^15, then we can reconstruct the difference 5848c2ecf20Sopenharmony_ci * between those two numbers using only the lower 16 bits of 5858c2ecf20Sopenharmony_ci * each. 5868c2ecf20Sopenharmony_ci * 5878c2ecf20Sopenharmony_ci * Put another way 5888c2ecf20Sopenharmony_ci * 5898c2ecf20Sopenharmony_ci * a - b = ((a mod k) - b) mod k 5908c2ecf20Sopenharmony_ci * 5918c2ecf20Sopenharmony_ci * when -k/2 < (a-b) < k/2. In our case k is 2^16. We know 5928c2ecf20Sopenharmony_ci * (a mod k) and b, so can calculate the delta, a - b. 5938c2ecf20Sopenharmony_ci * 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_ci sync_timestamp = last_sync_timestamp_major(efx); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* Because delta is s16 this does an implicit mask down to 5988c2ecf20Sopenharmony_ci * 16 bits which is what we need, assuming 5998c2ecf20Sopenharmony_ci * MEDFORD_TX_SECS_EVENT_BITS is 16. delta is signed so that 6008c2ecf20Sopenharmony_ci * we can deal with the (unlikely) case of sync timestamps 6018c2ecf20Sopenharmony_ci * arriving from the future. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_ci delta = nic_major - sync_timestamp; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* Recover the fully specified time now, by applying the offset 6068c2ecf20Sopenharmony_ci * to the (fully specified) sync time. 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci nic_major = sync_timestamp + delta; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci kt = ptp->nic_to_kernel_time(nic_major, nic_minor, 6118c2ecf20Sopenharmony_ci correction); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci return kt; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ciktime_t efx_ptp_nic_to_kernel_time(struct efx_tx_queue *tx_queue) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct efx_nic *efx = tx_queue->efx; 6198c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 6208c2ecf20Sopenharmony_ci ktime_t kt; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (efx_ptp_use_mac_tx_timestamps(efx)) 6238c2ecf20Sopenharmony_ci kt = efx_ptp_mac_nic_to_ktime_correction(efx, ptp, 6248c2ecf20Sopenharmony_ci tx_queue->completed_timestamp_major, 6258c2ecf20Sopenharmony_ci tx_queue->completed_timestamp_minor, 6268c2ecf20Sopenharmony_ci ptp->ts_corrections.general_tx); 6278c2ecf20Sopenharmony_ci else 6288c2ecf20Sopenharmony_ci kt = ptp->nic_to_kernel_time( 6298c2ecf20Sopenharmony_ci tx_queue->completed_timestamp_major, 6308c2ecf20Sopenharmony_ci tx_queue->completed_timestamp_minor, 6318c2ecf20Sopenharmony_ci ptp->ts_corrections.general_tx); 6328c2ecf20Sopenharmony_ci return kt; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci/* Get PTP attributes and set up time conversions */ 6368c2ecf20Sopenharmony_cistatic int efx_ptp_get_attributes(struct efx_nic *efx) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_GET_ATTRIBUTES_LEN); 6398c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_GET_ATTRIBUTES_LEN); 6408c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 6418c2ecf20Sopenharmony_ci int rc; 6428c2ecf20Sopenharmony_ci u32 fmt; 6438c2ecf20Sopenharmony_ci size_t out_len; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* Get the PTP attributes. If the NIC doesn't support the operation we 6468c2ecf20Sopenharmony_ci * use the default format for compatibility with older NICs i.e. 6478c2ecf20Sopenharmony_ci * seconds and nanoseconds. 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_GET_ATTRIBUTES); 6508c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0); 6518c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc_quiet(efx, MC_CMD_PTP, inbuf, sizeof(inbuf), 6528c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), &out_len); 6538c2ecf20Sopenharmony_ci if (rc == 0) { 6548c2ecf20Sopenharmony_ci fmt = MCDI_DWORD(outbuf, PTP_OUT_GET_ATTRIBUTES_TIME_FORMAT); 6558c2ecf20Sopenharmony_ci } else if (rc == -EINVAL) { 6568c2ecf20Sopenharmony_ci fmt = MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_NANOSECONDS; 6578c2ecf20Sopenharmony_ci } else if (rc == -EPERM) { 6588c2ecf20Sopenharmony_ci pci_info(efx->pci_dev, "no PTP support\n"); 6598c2ecf20Sopenharmony_ci return rc; 6608c2ecf20Sopenharmony_ci } else { 6618c2ecf20Sopenharmony_ci efx_mcdi_display_error(efx, MC_CMD_PTP, sizeof(inbuf), 6628c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), rc); 6638c2ecf20Sopenharmony_ci return rc; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci switch (fmt) { 6678c2ecf20Sopenharmony_ci case MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_27FRACTION: 6688c2ecf20Sopenharmony_ci ptp->ns_to_nic_time = efx_ptp_ns_to_s27; 6698c2ecf20Sopenharmony_ci ptp->nic_to_kernel_time = efx_ptp_s27_to_ktime_correction; 6708c2ecf20Sopenharmony_ci ptp->nic_time.minor_max = 1 << 27; 6718c2ecf20Sopenharmony_ci ptp->nic_time.sync_event_minor_shift = 19; 6728c2ecf20Sopenharmony_ci break; 6738c2ecf20Sopenharmony_ci case MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_NANOSECONDS: 6748c2ecf20Sopenharmony_ci ptp->ns_to_nic_time = efx_ptp_ns_to_s_ns; 6758c2ecf20Sopenharmony_ci ptp->nic_to_kernel_time = efx_ptp_s_ns_to_ktime_correction; 6768c2ecf20Sopenharmony_ci ptp->nic_time.minor_max = 1000000000; 6778c2ecf20Sopenharmony_ci ptp->nic_time.sync_event_minor_shift = 22; 6788c2ecf20Sopenharmony_ci break; 6798c2ecf20Sopenharmony_ci case MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_QTR_NANOSECONDS: 6808c2ecf20Sopenharmony_ci ptp->ns_to_nic_time = efx_ptp_ns_to_s_qns; 6818c2ecf20Sopenharmony_ci ptp->nic_to_kernel_time = efx_ptp_s_qns_to_ktime_correction; 6828c2ecf20Sopenharmony_ci ptp->nic_time.minor_max = 4000000000UL; 6838c2ecf20Sopenharmony_ci ptp->nic_time.sync_event_minor_shift = 24; 6848c2ecf20Sopenharmony_ci break; 6858c2ecf20Sopenharmony_ci default: 6868c2ecf20Sopenharmony_ci return -ERANGE; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* Precalculate acceptable difference between the minor time in the 6908c2ecf20Sopenharmony_ci * packet prefix and the last MCDI time sync event. We expect the 6918c2ecf20Sopenharmony_ci * packet prefix timestamp to be after of sync event by up to one 6928c2ecf20Sopenharmony_ci * sync event interval (0.25s) but we allow it to exceed this by a 6938c2ecf20Sopenharmony_ci * fuzz factor of (0.1s) 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ci ptp->nic_time.sync_event_diff_min = ptp->nic_time.minor_max 6968c2ecf20Sopenharmony_ci - (ptp->nic_time.minor_max / 10); 6978c2ecf20Sopenharmony_ci ptp->nic_time.sync_event_diff_max = (ptp->nic_time.minor_max / 4) 6988c2ecf20Sopenharmony_ci + (ptp->nic_time.minor_max / 10); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* MC_CMD_PTP_OP_GET_ATTRIBUTES has been extended twice from an older 7018c2ecf20Sopenharmony_ci * operation MC_CMD_PTP_OP_GET_TIME_FORMAT. The function now may return 7028c2ecf20Sopenharmony_ci * a value to use for the minimum acceptable corrected synchronization 7038c2ecf20Sopenharmony_ci * window and may return further capabilities. 7048c2ecf20Sopenharmony_ci * If we have the extra information store it. For older firmware that 7058c2ecf20Sopenharmony_ci * does not implement the extended command use the default value. 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci if (rc == 0 && 7088c2ecf20Sopenharmony_ci out_len >= MC_CMD_PTP_OUT_GET_ATTRIBUTES_CAPABILITIES_OFST) 7098c2ecf20Sopenharmony_ci ptp->min_synchronisation_ns = 7108c2ecf20Sopenharmony_ci MCDI_DWORD(outbuf, 7118c2ecf20Sopenharmony_ci PTP_OUT_GET_ATTRIBUTES_SYNC_WINDOW_MIN); 7128c2ecf20Sopenharmony_ci else 7138c2ecf20Sopenharmony_ci ptp->min_synchronisation_ns = DEFAULT_MIN_SYNCHRONISATION_NS; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (rc == 0 && 7168c2ecf20Sopenharmony_ci out_len >= MC_CMD_PTP_OUT_GET_ATTRIBUTES_LEN) 7178c2ecf20Sopenharmony_ci ptp->capabilities = MCDI_DWORD(outbuf, 7188c2ecf20Sopenharmony_ci PTP_OUT_GET_ATTRIBUTES_CAPABILITIES); 7198c2ecf20Sopenharmony_ci else 7208c2ecf20Sopenharmony_ci ptp->capabilities = 0; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* Set up the shift for conversion between frequency 7238c2ecf20Sopenharmony_ci * adjustments in parts-per-billion and the fixed-point 7248c2ecf20Sopenharmony_ci * fractional ns format that the adapter uses. 7258c2ecf20Sopenharmony_ci */ 7268c2ecf20Sopenharmony_ci if (ptp->capabilities & (1 << MC_CMD_PTP_OUT_GET_ATTRIBUTES_FP44_FREQ_ADJ_LBN)) 7278c2ecf20Sopenharmony_ci ptp->adjfreq_ppb_shift = PPB_SHIFT_FP44; 7288c2ecf20Sopenharmony_ci else 7298c2ecf20Sopenharmony_ci ptp->adjfreq_ppb_shift = PPB_SHIFT_FP40; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci return 0; 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci/* Get PTP timestamp corrections */ 7358c2ecf20Sopenharmony_cistatic int efx_ptp_get_timestamp_corrections(struct efx_nic *efx) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_GET_TIMESTAMP_CORRECTIONS_LEN); 7388c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_GET_TIMESTAMP_CORRECTIONS_V2_LEN); 7398c2ecf20Sopenharmony_ci int rc; 7408c2ecf20Sopenharmony_ci size_t out_len; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* Get the timestamp corrections from the NIC. If this operation is 7438c2ecf20Sopenharmony_ci * not supported (older NICs) then no correction is required. 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_OP, 7468c2ecf20Sopenharmony_ci MC_CMD_PTP_OP_GET_TIMESTAMP_CORRECTIONS); 7478c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc_quiet(efx, MC_CMD_PTP, inbuf, sizeof(inbuf), 7508c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), &out_len); 7518c2ecf20Sopenharmony_ci if (rc == 0) { 7528c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.ptp_tx = MCDI_DWORD(outbuf, 7538c2ecf20Sopenharmony_ci PTP_OUT_GET_TIMESTAMP_CORRECTIONS_TRANSMIT); 7548c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.ptp_rx = MCDI_DWORD(outbuf, 7558c2ecf20Sopenharmony_ci PTP_OUT_GET_TIMESTAMP_CORRECTIONS_RECEIVE); 7568c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.pps_out = MCDI_DWORD(outbuf, 7578c2ecf20Sopenharmony_ci PTP_OUT_GET_TIMESTAMP_CORRECTIONS_PPS_OUT); 7588c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.pps_in = MCDI_DWORD(outbuf, 7598c2ecf20Sopenharmony_ci PTP_OUT_GET_TIMESTAMP_CORRECTIONS_PPS_IN); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci if (out_len >= MC_CMD_PTP_OUT_GET_TIMESTAMP_CORRECTIONS_V2_LEN) { 7628c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.general_tx = MCDI_DWORD( 7638c2ecf20Sopenharmony_ci outbuf, 7648c2ecf20Sopenharmony_ci PTP_OUT_GET_TIMESTAMP_CORRECTIONS_V2_GENERAL_TX); 7658c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.general_rx = MCDI_DWORD( 7668c2ecf20Sopenharmony_ci outbuf, 7678c2ecf20Sopenharmony_ci PTP_OUT_GET_TIMESTAMP_CORRECTIONS_V2_GENERAL_RX); 7688c2ecf20Sopenharmony_ci } else { 7698c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.general_tx = 7708c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.ptp_tx; 7718c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.general_rx = 7728c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.ptp_rx; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci } else if (rc == -EINVAL) { 7758c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.ptp_tx = 0; 7768c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.ptp_rx = 0; 7778c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.pps_out = 0; 7788c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.pps_in = 0; 7798c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.general_tx = 0; 7808c2ecf20Sopenharmony_ci efx->ptp_data->ts_corrections.general_rx = 0; 7818c2ecf20Sopenharmony_ci } else { 7828c2ecf20Sopenharmony_ci efx_mcdi_display_error(efx, MC_CMD_PTP, sizeof(inbuf), outbuf, 7838c2ecf20Sopenharmony_ci sizeof(outbuf), rc); 7848c2ecf20Sopenharmony_ci return rc; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci return 0; 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci/* Enable MCDI PTP support. */ 7918c2ecf20Sopenharmony_cistatic int efx_ptp_enable(struct efx_nic *efx) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_ENABLE_LEN); 7948c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF_ERR(outbuf); 7958c2ecf20Sopenharmony_ci int rc; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_ENABLE); 7988c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0); 7998c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_ENABLE_QUEUE, 8008c2ecf20Sopenharmony_ci efx->ptp_data->channel ? 8018c2ecf20Sopenharmony_ci efx->ptp_data->channel->channel : 0); 8028c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_ENABLE_MODE, efx->ptp_data->mode); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc_quiet(efx, MC_CMD_PTP, inbuf, sizeof(inbuf), 8058c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), NULL); 8068c2ecf20Sopenharmony_ci rc = (rc == -EALREADY) ? 0 : rc; 8078c2ecf20Sopenharmony_ci if (rc) 8088c2ecf20Sopenharmony_ci efx_mcdi_display_error(efx, MC_CMD_PTP, 8098c2ecf20Sopenharmony_ci MC_CMD_PTP_IN_ENABLE_LEN, 8108c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), rc); 8118c2ecf20Sopenharmony_ci return rc; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci/* Disable MCDI PTP support. 8158c2ecf20Sopenharmony_ci * 8168c2ecf20Sopenharmony_ci * Note that this function should never rely on the presence of ptp_data - 8178c2ecf20Sopenharmony_ci * may be called before that exists. 8188c2ecf20Sopenharmony_ci */ 8198c2ecf20Sopenharmony_cistatic int efx_ptp_disable(struct efx_nic *efx) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_DISABLE_LEN); 8228c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF_ERR(outbuf); 8238c2ecf20Sopenharmony_ci int rc; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_DISABLE); 8268c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0); 8278c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc_quiet(efx, MC_CMD_PTP, inbuf, sizeof(inbuf), 8288c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), NULL); 8298c2ecf20Sopenharmony_ci rc = (rc == -EALREADY) ? 0 : rc; 8308c2ecf20Sopenharmony_ci /* If we get ENOSYS, the NIC doesn't support PTP, and thus this function 8318c2ecf20Sopenharmony_ci * should only have been called during probe. 8328c2ecf20Sopenharmony_ci */ 8338c2ecf20Sopenharmony_ci if (rc == -ENOSYS || rc == -EPERM) 8348c2ecf20Sopenharmony_ci pci_info(efx->pci_dev, "no PTP support\n"); 8358c2ecf20Sopenharmony_ci else if (rc) 8368c2ecf20Sopenharmony_ci efx_mcdi_display_error(efx, MC_CMD_PTP, 8378c2ecf20Sopenharmony_ci MC_CMD_PTP_IN_DISABLE_LEN, 8388c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), rc); 8398c2ecf20Sopenharmony_ci return rc; 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_cistatic void efx_ptp_deliver_rx_queue(struct sk_buff_head *q) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci struct sk_buff *skb; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(q))) { 8478c2ecf20Sopenharmony_ci local_bh_disable(); 8488c2ecf20Sopenharmony_ci netif_receive_skb(skb); 8498c2ecf20Sopenharmony_ci local_bh_enable(); 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic void efx_ptp_handle_no_channel(struct efx_nic *efx) 8548c2ecf20Sopenharmony_ci{ 8558c2ecf20Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 8568c2ecf20Sopenharmony_ci "ERROR: PTP requires MSI-X and 1 additional interrupt" 8578c2ecf20Sopenharmony_ci "vector. PTP disabled\n"); 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci/* Repeatedly send the host time to the MC which will capture the hardware 8618c2ecf20Sopenharmony_ci * time. 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_cistatic void efx_ptp_send_times(struct efx_nic *efx, 8648c2ecf20Sopenharmony_ci struct pps_event_time *last_time) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct pps_event_time now; 8678c2ecf20Sopenharmony_ci struct timespec64 limit; 8688c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 8698c2ecf20Sopenharmony_ci int *mc_running = ptp->start.addr; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci pps_get_ts(&now); 8728c2ecf20Sopenharmony_ci limit = now.ts_real; 8738c2ecf20Sopenharmony_ci timespec64_add_ns(&limit, SYNCHRONISE_PERIOD_NS); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci /* Write host time for specified period or until MC is done */ 8768c2ecf20Sopenharmony_ci while ((timespec64_compare(&now.ts_real, &limit) < 0) && 8778c2ecf20Sopenharmony_ci READ_ONCE(*mc_running)) { 8788c2ecf20Sopenharmony_ci struct timespec64 update_time; 8798c2ecf20Sopenharmony_ci unsigned int host_time; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* Don't update continuously to avoid saturating the PCIe bus */ 8828c2ecf20Sopenharmony_ci update_time = now.ts_real; 8838c2ecf20Sopenharmony_ci timespec64_add_ns(&update_time, SYNCHRONISATION_GRANULARITY_NS); 8848c2ecf20Sopenharmony_ci do { 8858c2ecf20Sopenharmony_ci pps_get_ts(&now); 8868c2ecf20Sopenharmony_ci } while ((timespec64_compare(&now.ts_real, &update_time) < 0) && 8878c2ecf20Sopenharmony_ci READ_ONCE(*mc_running)); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci /* Synchronise NIC with single word of time only */ 8908c2ecf20Sopenharmony_ci host_time = (now.ts_real.tv_sec << MC_NANOSECOND_BITS | 8918c2ecf20Sopenharmony_ci now.ts_real.tv_nsec); 8928c2ecf20Sopenharmony_ci /* Update host time in NIC memory */ 8938c2ecf20Sopenharmony_ci efx->type->ptp_write_host_time(efx, host_time); 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci *last_time = now; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci/* Read a timeset from the MC's results and partial process. */ 8998c2ecf20Sopenharmony_cistatic void efx_ptp_read_timeset(MCDI_DECLARE_STRUCT_PTR(data), 9008c2ecf20Sopenharmony_ci struct efx_ptp_timeset *timeset) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci unsigned start_ns, end_ns; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci timeset->host_start = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_HOSTSTART); 9058c2ecf20Sopenharmony_ci timeset->major = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_MAJOR); 9068c2ecf20Sopenharmony_ci timeset->minor = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_MINOR); 9078c2ecf20Sopenharmony_ci timeset->host_end = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_HOSTEND), 9088c2ecf20Sopenharmony_ci timeset->wait = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_WAITNS); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* Ignore seconds */ 9118c2ecf20Sopenharmony_ci start_ns = timeset->host_start & MC_NANOSECOND_MASK; 9128c2ecf20Sopenharmony_ci end_ns = timeset->host_end & MC_NANOSECOND_MASK; 9138c2ecf20Sopenharmony_ci /* Allow for rollover */ 9148c2ecf20Sopenharmony_ci if (end_ns < start_ns) 9158c2ecf20Sopenharmony_ci end_ns += NSEC_PER_SEC; 9168c2ecf20Sopenharmony_ci /* Determine duration of operation */ 9178c2ecf20Sopenharmony_ci timeset->window = end_ns - start_ns; 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/* Process times received from MC. 9218c2ecf20Sopenharmony_ci * 9228c2ecf20Sopenharmony_ci * Extract times from returned results, and establish the minimum value 9238c2ecf20Sopenharmony_ci * seen. The minimum value represents the "best" possible time and events 9248c2ecf20Sopenharmony_ci * too much greater than this are rejected - the machine is, perhaps, too 9258c2ecf20Sopenharmony_ci * busy. A number of readings are taken so that, hopefully, at least one good 9268c2ecf20Sopenharmony_ci * synchronisation will be seen in the results. 9278c2ecf20Sopenharmony_ci */ 9288c2ecf20Sopenharmony_cistatic int 9298c2ecf20Sopenharmony_ciefx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf), 9308c2ecf20Sopenharmony_ci size_t response_length, 9318c2ecf20Sopenharmony_ci const struct pps_event_time *last_time) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci unsigned number_readings = 9348c2ecf20Sopenharmony_ci MCDI_VAR_ARRAY_LEN(response_length, 9358c2ecf20Sopenharmony_ci PTP_OUT_SYNCHRONIZE_TIMESET); 9368c2ecf20Sopenharmony_ci unsigned i; 9378c2ecf20Sopenharmony_ci unsigned ngood = 0; 9388c2ecf20Sopenharmony_ci unsigned last_good = 0; 9398c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 9408c2ecf20Sopenharmony_ci u32 last_sec; 9418c2ecf20Sopenharmony_ci u32 start_sec; 9428c2ecf20Sopenharmony_ci struct timespec64 delta; 9438c2ecf20Sopenharmony_ci ktime_t mc_time; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci if (number_readings == 0) 9468c2ecf20Sopenharmony_ci return -EAGAIN; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci /* Read the set of results and find the last good host-MC 9498c2ecf20Sopenharmony_ci * synchronization result. The MC times when it finishes reading the 9508c2ecf20Sopenharmony_ci * host time so the corrected window time should be fairly constant 9518c2ecf20Sopenharmony_ci * for a given platform. Increment stats for any results that appear 9528c2ecf20Sopenharmony_ci * to be erroneous. 9538c2ecf20Sopenharmony_ci */ 9548c2ecf20Sopenharmony_ci for (i = 0; i < number_readings; i++) { 9558c2ecf20Sopenharmony_ci s32 window, corrected; 9568c2ecf20Sopenharmony_ci struct timespec64 wait; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci efx_ptp_read_timeset( 9598c2ecf20Sopenharmony_ci MCDI_ARRAY_STRUCT_PTR(synch_buf, 9608c2ecf20Sopenharmony_ci PTP_OUT_SYNCHRONIZE_TIMESET, i), 9618c2ecf20Sopenharmony_ci &ptp->timeset[i]); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci wait = ktime_to_timespec64( 9648c2ecf20Sopenharmony_ci ptp->nic_to_kernel_time(0, ptp->timeset[i].wait, 0)); 9658c2ecf20Sopenharmony_ci window = ptp->timeset[i].window; 9668c2ecf20Sopenharmony_ci corrected = window - wait.tv_nsec; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* We expect the uncorrected synchronization window to be at 9698c2ecf20Sopenharmony_ci * least as large as the interval between host start and end 9708c2ecf20Sopenharmony_ci * times. If it is smaller than this then this is mostly likely 9718c2ecf20Sopenharmony_ci * to be a consequence of the host's time being adjusted. 9728c2ecf20Sopenharmony_ci * Check that the corrected sync window is in a reasonable 9738c2ecf20Sopenharmony_ci * range. If it is out of range it is likely to be because an 9748c2ecf20Sopenharmony_ci * interrupt or other delay occurred between reading the system 9758c2ecf20Sopenharmony_ci * time and writing it to MC memory. 9768c2ecf20Sopenharmony_ci */ 9778c2ecf20Sopenharmony_ci if (window < SYNCHRONISATION_GRANULARITY_NS) { 9788c2ecf20Sopenharmony_ci ++ptp->invalid_sync_windows; 9798c2ecf20Sopenharmony_ci } else if (corrected >= MAX_SYNCHRONISATION_NS) { 9808c2ecf20Sopenharmony_ci ++ptp->oversize_sync_windows; 9818c2ecf20Sopenharmony_ci } else if (corrected < ptp->min_synchronisation_ns) { 9828c2ecf20Sopenharmony_ci ++ptp->undersize_sync_windows; 9838c2ecf20Sopenharmony_ci } else { 9848c2ecf20Sopenharmony_ci ngood++; 9858c2ecf20Sopenharmony_ci last_good = i; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (ngood == 0) { 9908c2ecf20Sopenharmony_ci netif_warn(efx, drv, efx->net_dev, 9918c2ecf20Sopenharmony_ci "PTP no suitable synchronisations\n"); 9928c2ecf20Sopenharmony_ci return -EAGAIN; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci /* Calculate delay from last good sync (host time) to last_time. 9968c2ecf20Sopenharmony_ci * It is possible that the seconds rolled over between taking 9978c2ecf20Sopenharmony_ci * the start reading and the last value written by the host. The 9988c2ecf20Sopenharmony_ci * timescales are such that a gap of more than one second is never 9998c2ecf20Sopenharmony_ci * expected. delta is *not* normalised. 10008c2ecf20Sopenharmony_ci */ 10018c2ecf20Sopenharmony_ci start_sec = ptp->timeset[last_good].host_start >> MC_NANOSECOND_BITS; 10028c2ecf20Sopenharmony_ci last_sec = last_time->ts_real.tv_sec & MC_SECOND_MASK; 10038c2ecf20Sopenharmony_ci if (start_sec != last_sec && 10048c2ecf20Sopenharmony_ci ((start_sec + 1) & MC_SECOND_MASK) != last_sec) { 10058c2ecf20Sopenharmony_ci netif_warn(efx, hw, efx->net_dev, 10068c2ecf20Sopenharmony_ci "PTP bad synchronisation seconds\n"); 10078c2ecf20Sopenharmony_ci return -EAGAIN; 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci delta.tv_sec = (last_sec - start_sec) & 1; 10108c2ecf20Sopenharmony_ci delta.tv_nsec = 10118c2ecf20Sopenharmony_ci last_time->ts_real.tv_nsec - 10128c2ecf20Sopenharmony_ci (ptp->timeset[last_good].host_start & MC_NANOSECOND_MASK); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci /* Convert the NIC time at last good sync into kernel time. 10158c2ecf20Sopenharmony_ci * No correction is required - this time is the output of a 10168c2ecf20Sopenharmony_ci * firmware process. 10178c2ecf20Sopenharmony_ci */ 10188c2ecf20Sopenharmony_ci mc_time = ptp->nic_to_kernel_time(ptp->timeset[last_good].major, 10198c2ecf20Sopenharmony_ci ptp->timeset[last_good].minor, 0); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci /* Calculate delay from NIC top of second to last_time */ 10228c2ecf20Sopenharmony_ci delta.tv_nsec += ktime_to_timespec64(mc_time).tv_nsec; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* Set PPS timestamp to match NIC top of second */ 10258c2ecf20Sopenharmony_ci ptp->host_time_pps = *last_time; 10268c2ecf20Sopenharmony_ci pps_sub_ts(&ptp->host_time_pps, delta); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci return 0; 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci/* Synchronize times between the host and the MC */ 10328c2ecf20Sopenharmony_cistatic int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 10358c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(synch_buf, MC_CMD_PTP_OUT_SYNCHRONIZE_LENMAX); 10368c2ecf20Sopenharmony_ci size_t response_length; 10378c2ecf20Sopenharmony_ci int rc; 10388c2ecf20Sopenharmony_ci unsigned long timeout; 10398c2ecf20Sopenharmony_ci struct pps_event_time last_time = {}; 10408c2ecf20Sopenharmony_ci unsigned int loops = 0; 10418c2ecf20Sopenharmony_ci int *start = ptp->start.addr; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci MCDI_SET_DWORD(synch_buf, PTP_IN_OP, MC_CMD_PTP_OP_SYNCHRONIZE); 10448c2ecf20Sopenharmony_ci MCDI_SET_DWORD(synch_buf, PTP_IN_PERIPH_ID, 0); 10458c2ecf20Sopenharmony_ci MCDI_SET_DWORD(synch_buf, PTP_IN_SYNCHRONIZE_NUMTIMESETS, 10468c2ecf20Sopenharmony_ci num_readings); 10478c2ecf20Sopenharmony_ci MCDI_SET_QWORD(synch_buf, PTP_IN_SYNCHRONIZE_START_ADDR, 10488c2ecf20Sopenharmony_ci ptp->start.dma_addr); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci /* Clear flag that signals MC ready */ 10518c2ecf20Sopenharmony_ci WRITE_ONCE(*start, 0); 10528c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc_start(efx, MC_CMD_PTP, synch_buf, 10538c2ecf20Sopenharmony_ci MC_CMD_PTP_IN_SYNCHRONIZE_LEN); 10548c2ecf20Sopenharmony_ci EFX_WARN_ON_ONCE_PARANOID(rc); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci /* Wait for start from MCDI (or timeout) */ 10578c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(MAX_SYNCHRONISE_WAIT_MS); 10588c2ecf20Sopenharmony_ci while (!READ_ONCE(*start) && (time_before(jiffies, timeout))) { 10598c2ecf20Sopenharmony_ci udelay(20); /* Usually start MCDI execution quickly */ 10608c2ecf20Sopenharmony_ci loops++; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if (loops <= 1) 10648c2ecf20Sopenharmony_ci ++ptp->fast_syncs; 10658c2ecf20Sopenharmony_ci if (!time_before(jiffies, timeout)) 10668c2ecf20Sopenharmony_ci ++ptp->sync_timeouts; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if (READ_ONCE(*start)) 10698c2ecf20Sopenharmony_ci efx_ptp_send_times(efx, &last_time); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* Collect results */ 10728c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc_finish(efx, MC_CMD_PTP, 10738c2ecf20Sopenharmony_ci MC_CMD_PTP_IN_SYNCHRONIZE_LEN, 10748c2ecf20Sopenharmony_ci synch_buf, sizeof(synch_buf), 10758c2ecf20Sopenharmony_ci &response_length); 10768c2ecf20Sopenharmony_ci if (rc == 0) { 10778c2ecf20Sopenharmony_ci rc = efx_ptp_process_times(efx, synch_buf, response_length, 10788c2ecf20Sopenharmony_ci &last_time); 10798c2ecf20Sopenharmony_ci if (rc == 0) 10808c2ecf20Sopenharmony_ci ++ptp->good_syncs; 10818c2ecf20Sopenharmony_ci else 10828c2ecf20Sopenharmony_ci ++ptp->no_time_syncs; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci /* Increment the bad syncs counter if the synchronize fails, whatever 10868c2ecf20Sopenharmony_ci * the reason. 10878c2ecf20Sopenharmony_ci */ 10888c2ecf20Sopenharmony_ci if (rc != 0) 10898c2ecf20Sopenharmony_ci ++ptp->bad_syncs; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci return rc; 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci/* Transmit a PTP packet via the dedicated hardware timestamped queue. */ 10958c2ecf20Sopenharmony_cistatic void efx_ptp_xmit_skb_queue(struct efx_nic *efx, struct sk_buff *skb) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp_data = efx->ptp_data; 10988c2ecf20Sopenharmony_ci u8 type = efx_tx_csum_type_skb(skb); 10998c2ecf20Sopenharmony_ci struct efx_tx_queue *tx_queue; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci tx_queue = efx_channel_get_tx_queue(ptp_data->channel, type); 11028c2ecf20Sopenharmony_ci if (tx_queue && tx_queue->timestamping) { 11038c2ecf20Sopenharmony_ci /* This code invokes normal driver TX code which is always 11048c2ecf20Sopenharmony_ci * protected from softirqs when called from generic TX code, 11058c2ecf20Sopenharmony_ci * which in turn disables preemption. Look at __dev_queue_xmit 11068c2ecf20Sopenharmony_ci * which uses rcu_read_lock_bh disabling preemption for RCU 11078c2ecf20Sopenharmony_ci * plus disabling softirqs. We do not need RCU reader 11088c2ecf20Sopenharmony_ci * protection here. 11098c2ecf20Sopenharmony_ci * 11108c2ecf20Sopenharmony_ci * Although it is theoretically safe for current PTP TX/RX code 11118c2ecf20Sopenharmony_ci * running without disabling softirqs, there are three good 11128c2ecf20Sopenharmony_ci * reasond for doing so: 11138c2ecf20Sopenharmony_ci * 11148c2ecf20Sopenharmony_ci * 1) The code invoked is mainly implemented for non-PTP 11158c2ecf20Sopenharmony_ci * packets and it is always executed with softirqs 11168c2ecf20Sopenharmony_ci * disabled. 11178c2ecf20Sopenharmony_ci * 2) This being a single PTP packet, better to not 11188c2ecf20Sopenharmony_ci * interrupt its processing by softirqs which can lead 11198c2ecf20Sopenharmony_ci * to high latencies. 11208c2ecf20Sopenharmony_ci * 3) netdev_xmit_more checks preemption is disabled and 11218c2ecf20Sopenharmony_ci * triggers a BUG_ON if not. 11228c2ecf20Sopenharmony_ci */ 11238c2ecf20Sopenharmony_ci local_bh_disable(); 11248c2ecf20Sopenharmony_ci efx_enqueue_skb(tx_queue, skb); 11258c2ecf20Sopenharmony_ci local_bh_enable(); 11268c2ecf20Sopenharmony_ci } else { 11278c2ecf20Sopenharmony_ci WARN_ONCE(1, "PTP channel has no timestamped tx queue\n"); 11288c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci/* Transmit a PTP packet, via the MCDI interface, to the wire. */ 11338c2ecf20Sopenharmony_cistatic void efx_ptp_xmit_skb_mc(struct efx_nic *efx, struct sk_buff *skb) 11348c2ecf20Sopenharmony_ci{ 11358c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp_data = efx->ptp_data; 11368c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps timestamps; 11378c2ecf20Sopenharmony_ci int rc = -EIO; 11388c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(txtime, MC_CMD_PTP_OUT_TRANSMIT_LEN); 11398c2ecf20Sopenharmony_ci size_t len; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci MCDI_SET_DWORD(ptp_data->txbuf, PTP_IN_OP, MC_CMD_PTP_OP_TRANSMIT); 11428c2ecf20Sopenharmony_ci MCDI_SET_DWORD(ptp_data->txbuf, PTP_IN_PERIPH_ID, 0); 11438c2ecf20Sopenharmony_ci MCDI_SET_DWORD(ptp_data->txbuf, PTP_IN_TRANSMIT_LENGTH, skb->len); 11448c2ecf20Sopenharmony_ci if (skb_shinfo(skb)->nr_frags != 0) { 11458c2ecf20Sopenharmony_ci rc = skb_linearize(skb); 11468c2ecf20Sopenharmony_ci if (rc != 0) 11478c2ecf20Sopenharmony_ci goto fail; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 11518c2ecf20Sopenharmony_ci rc = skb_checksum_help(skb); 11528c2ecf20Sopenharmony_ci if (rc != 0) 11538c2ecf20Sopenharmony_ci goto fail; 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, 11568c2ecf20Sopenharmony_ci MCDI_PTR(ptp_data->txbuf, 11578c2ecf20Sopenharmony_ci PTP_IN_TRANSMIT_PACKET), 11588c2ecf20Sopenharmony_ci skb->len); 11598c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc(efx, MC_CMD_PTP, 11608c2ecf20Sopenharmony_ci ptp_data->txbuf, MC_CMD_PTP_IN_TRANSMIT_LEN(skb->len), 11618c2ecf20Sopenharmony_ci txtime, sizeof(txtime), &len); 11628c2ecf20Sopenharmony_ci if (rc != 0) 11638c2ecf20Sopenharmony_ci goto fail; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci memset(×tamps, 0, sizeof(timestamps)); 11668c2ecf20Sopenharmony_ci timestamps.hwtstamp = ptp_data->nic_to_kernel_time( 11678c2ecf20Sopenharmony_ci MCDI_DWORD(txtime, PTP_OUT_TRANSMIT_MAJOR), 11688c2ecf20Sopenharmony_ci MCDI_DWORD(txtime, PTP_OUT_TRANSMIT_MINOR), 11698c2ecf20Sopenharmony_ci ptp_data->ts_corrections.ptp_tx); 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci skb_tstamp_tx(skb, ×tamps); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci rc = 0; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cifail: 11768c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci return; 11798c2ecf20Sopenharmony_ci} 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_cistatic void efx_ptp_drop_time_expired_events(struct efx_nic *efx) 11828c2ecf20Sopenharmony_ci{ 11838c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 11848c2ecf20Sopenharmony_ci struct list_head *cursor; 11858c2ecf20Sopenharmony_ci struct list_head *next; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci if (ptp->rx_ts_inline) 11888c2ecf20Sopenharmony_ci return; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* Drop time-expired events */ 11918c2ecf20Sopenharmony_ci spin_lock_bh(&ptp->evt_lock); 11928c2ecf20Sopenharmony_ci list_for_each_safe(cursor, next, &ptp->evt_list) { 11938c2ecf20Sopenharmony_ci struct efx_ptp_event_rx *evt; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci evt = list_entry(cursor, struct efx_ptp_event_rx, 11968c2ecf20Sopenharmony_ci link); 11978c2ecf20Sopenharmony_ci if (time_after(jiffies, evt->expiry)) { 11988c2ecf20Sopenharmony_ci list_move(&evt->link, &ptp->evt_free_list); 11998c2ecf20Sopenharmony_ci netif_warn(efx, hw, efx->net_dev, 12008c2ecf20Sopenharmony_ci "PTP rx event dropped\n"); 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci spin_unlock_bh(&ptp->evt_lock); 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cistatic enum ptp_packet_state efx_ptp_match_rx(struct efx_nic *efx, 12078c2ecf20Sopenharmony_ci struct sk_buff *skb) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 12108c2ecf20Sopenharmony_ci bool evts_waiting; 12118c2ecf20Sopenharmony_ci struct list_head *cursor; 12128c2ecf20Sopenharmony_ci struct list_head *next; 12138c2ecf20Sopenharmony_ci struct efx_ptp_match *match; 12148c2ecf20Sopenharmony_ci enum ptp_packet_state rc = PTP_PACKET_STATE_UNMATCHED; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci WARN_ON_ONCE(ptp->rx_ts_inline); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci spin_lock_bh(&ptp->evt_lock); 12198c2ecf20Sopenharmony_ci evts_waiting = !list_empty(&ptp->evt_list); 12208c2ecf20Sopenharmony_ci spin_unlock_bh(&ptp->evt_lock); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci if (!evts_waiting) 12238c2ecf20Sopenharmony_ci return PTP_PACKET_STATE_UNMATCHED; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci match = (struct efx_ptp_match *)skb->cb; 12268c2ecf20Sopenharmony_ci /* Look for a matching timestamp in the event queue */ 12278c2ecf20Sopenharmony_ci spin_lock_bh(&ptp->evt_lock); 12288c2ecf20Sopenharmony_ci list_for_each_safe(cursor, next, &ptp->evt_list) { 12298c2ecf20Sopenharmony_ci struct efx_ptp_event_rx *evt; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci evt = list_entry(cursor, struct efx_ptp_event_rx, link); 12328c2ecf20Sopenharmony_ci if ((evt->seq0 == match->words[0]) && 12338c2ecf20Sopenharmony_ci (evt->seq1 == match->words[1])) { 12348c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *timestamps; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci /* Match - add in hardware timestamp */ 12378c2ecf20Sopenharmony_ci timestamps = skb_hwtstamps(skb); 12388c2ecf20Sopenharmony_ci timestamps->hwtstamp = evt->hwtimestamp; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci match->state = PTP_PACKET_STATE_MATCHED; 12418c2ecf20Sopenharmony_ci rc = PTP_PACKET_STATE_MATCHED; 12428c2ecf20Sopenharmony_ci list_move(&evt->link, &ptp->evt_free_list); 12438c2ecf20Sopenharmony_ci break; 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci spin_unlock_bh(&ptp->evt_lock); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci return rc; 12498c2ecf20Sopenharmony_ci} 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci/* Process any queued receive events and corresponding packets 12528c2ecf20Sopenharmony_ci * 12538c2ecf20Sopenharmony_ci * q is returned with all the packets that are ready for delivery. 12548c2ecf20Sopenharmony_ci */ 12558c2ecf20Sopenharmony_cistatic void efx_ptp_process_events(struct efx_nic *efx, struct sk_buff_head *q) 12568c2ecf20Sopenharmony_ci{ 12578c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 12588c2ecf20Sopenharmony_ci struct sk_buff *skb; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&ptp->rxq))) { 12618c2ecf20Sopenharmony_ci struct efx_ptp_match *match; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci match = (struct efx_ptp_match *)skb->cb; 12648c2ecf20Sopenharmony_ci if (match->state == PTP_PACKET_STATE_MATCH_UNWANTED) { 12658c2ecf20Sopenharmony_ci __skb_queue_tail(q, skb); 12668c2ecf20Sopenharmony_ci } else if (efx_ptp_match_rx(efx, skb) == 12678c2ecf20Sopenharmony_ci PTP_PACKET_STATE_MATCHED) { 12688c2ecf20Sopenharmony_ci __skb_queue_tail(q, skb); 12698c2ecf20Sopenharmony_ci } else if (time_after(jiffies, match->expiry)) { 12708c2ecf20Sopenharmony_ci match->state = PTP_PACKET_STATE_TIMED_OUT; 12718c2ecf20Sopenharmony_ci ++ptp->rx_no_timestamp; 12728c2ecf20Sopenharmony_ci __skb_queue_tail(q, skb); 12738c2ecf20Sopenharmony_ci } else { 12748c2ecf20Sopenharmony_ci /* Replace unprocessed entry and stop */ 12758c2ecf20Sopenharmony_ci skb_queue_head(&ptp->rxq, skb); 12768c2ecf20Sopenharmony_ci break; 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci/* Complete processing of a received packet */ 12828c2ecf20Sopenharmony_cistatic inline void efx_ptp_process_rx(struct efx_nic *efx, struct sk_buff *skb) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci local_bh_disable(); 12858c2ecf20Sopenharmony_ci netif_receive_skb(skb); 12868c2ecf20Sopenharmony_ci local_bh_enable(); 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_cistatic void efx_ptp_remove_multicast_filters(struct efx_nic *efx) 12908c2ecf20Sopenharmony_ci{ 12918c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci if (ptp->rxfilter_installed) { 12948c2ecf20Sopenharmony_ci efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED, 12958c2ecf20Sopenharmony_ci ptp->rxfilter_general); 12968c2ecf20Sopenharmony_ci efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED, 12978c2ecf20Sopenharmony_ci ptp->rxfilter_event); 12988c2ecf20Sopenharmony_ci ptp->rxfilter_installed = false; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_cistatic int efx_ptp_insert_multicast_filters(struct efx_nic *efx) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 13058c2ecf20Sopenharmony_ci struct efx_filter_spec rxfilter; 13068c2ecf20Sopenharmony_ci int rc; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci if (!ptp->channel || ptp->rxfilter_installed) 13098c2ecf20Sopenharmony_ci return 0; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci /* Must filter on both event and general ports to ensure 13128c2ecf20Sopenharmony_ci * that there is no packet re-ordering. 13138c2ecf20Sopenharmony_ci */ 13148c2ecf20Sopenharmony_ci efx_filter_init_rx(&rxfilter, EFX_FILTER_PRI_REQUIRED, 0, 13158c2ecf20Sopenharmony_ci efx_rx_queue_index( 13168c2ecf20Sopenharmony_ci efx_channel_get_rx_queue(ptp->channel))); 13178c2ecf20Sopenharmony_ci rc = efx_filter_set_ipv4_local(&rxfilter, IPPROTO_UDP, 13188c2ecf20Sopenharmony_ci htonl(PTP_ADDRESS), 13198c2ecf20Sopenharmony_ci htons(PTP_EVENT_PORT)); 13208c2ecf20Sopenharmony_ci if (rc != 0) 13218c2ecf20Sopenharmony_ci return rc; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci rc = efx_filter_insert_filter(efx, &rxfilter, true); 13248c2ecf20Sopenharmony_ci if (rc < 0) 13258c2ecf20Sopenharmony_ci return rc; 13268c2ecf20Sopenharmony_ci ptp->rxfilter_event = rc; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci efx_filter_init_rx(&rxfilter, EFX_FILTER_PRI_REQUIRED, 0, 13298c2ecf20Sopenharmony_ci efx_rx_queue_index( 13308c2ecf20Sopenharmony_ci efx_channel_get_rx_queue(ptp->channel))); 13318c2ecf20Sopenharmony_ci rc = efx_filter_set_ipv4_local(&rxfilter, IPPROTO_UDP, 13328c2ecf20Sopenharmony_ci htonl(PTP_ADDRESS), 13338c2ecf20Sopenharmony_ci htons(PTP_GENERAL_PORT)); 13348c2ecf20Sopenharmony_ci if (rc != 0) 13358c2ecf20Sopenharmony_ci goto fail; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci rc = efx_filter_insert_filter(efx, &rxfilter, true); 13388c2ecf20Sopenharmony_ci if (rc < 0) 13398c2ecf20Sopenharmony_ci goto fail; 13408c2ecf20Sopenharmony_ci ptp->rxfilter_general = rc; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci ptp->rxfilter_installed = true; 13438c2ecf20Sopenharmony_ci return 0; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cifail: 13468c2ecf20Sopenharmony_ci efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED, 13478c2ecf20Sopenharmony_ci ptp->rxfilter_event); 13488c2ecf20Sopenharmony_ci return rc; 13498c2ecf20Sopenharmony_ci} 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_cistatic int efx_ptp_start(struct efx_nic *efx) 13528c2ecf20Sopenharmony_ci{ 13538c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 13548c2ecf20Sopenharmony_ci int rc; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci ptp->reset_required = false; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci rc = efx_ptp_insert_multicast_filters(efx); 13598c2ecf20Sopenharmony_ci if (rc) 13608c2ecf20Sopenharmony_ci return rc; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci rc = efx_ptp_enable(efx); 13638c2ecf20Sopenharmony_ci if (rc != 0) 13648c2ecf20Sopenharmony_ci goto fail; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci ptp->evt_frag_idx = 0; 13678c2ecf20Sopenharmony_ci ptp->current_adjfreq = 0; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci return 0; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_cifail: 13728c2ecf20Sopenharmony_ci efx_ptp_remove_multicast_filters(efx); 13738c2ecf20Sopenharmony_ci return rc; 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_cistatic int efx_ptp_stop(struct efx_nic *efx) 13778c2ecf20Sopenharmony_ci{ 13788c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 13798c2ecf20Sopenharmony_ci struct list_head *cursor; 13808c2ecf20Sopenharmony_ci struct list_head *next; 13818c2ecf20Sopenharmony_ci int rc; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci if (ptp == NULL) 13848c2ecf20Sopenharmony_ci return 0; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci rc = efx_ptp_disable(efx); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci efx_ptp_remove_multicast_filters(efx); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci /* Make sure RX packets are really delivered */ 13918c2ecf20Sopenharmony_ci efx_ptp_deliver_rx_queue(&efx->ptp_data->rxq); 13928c2ecf20Sopenharmony_ci skb_queue_purge(&efx->ptp_data->txq); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci /* Drop any pending receive events */ 13958c2ecf20Sopenharmony_ci spin_lock_bh(&efx->ptp_data->evt_lock); 13968c2ecf20Sopenharmony_ci list_for_each_safe(cursor, next, &efx->ptp_data->evt_list) { 13978c2ecf20Sopenharmony_ci list_move(cursor, &efx->ptp_data->evt_free_list); 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci spin_unlock_bh(&efx->ptp_data->evt_lock); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci return rc; 14028c2ecf20Sopenharmony_ci} 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_cistatic int efx_ptp_restart(struct efx_nic *efx) 14058c2ecf20Sopenharmony_ci{ 14068c2ecf20Sopenharmony_ci if (efx->ptp_data && efx->ptp_data->enabled) 14078c2ecf20Sopenharmony_ci return efx_ptp_start(efx); 14088c2ecf20Sopenharmony_ci return 0; 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic void efx_ptp_pps_worker(struct work_struct *work) 14128c2ecf20Sopenharmony_ci{ 14138c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = 14148c2ecf20Sopenharmony_ci container_of(work, struct efx_ptp_data, pps_work); 14158c2ecf20Sopenharmony_ci struct efx_nic *efx = ptp->efx; 14168c2ecf20Sopenharmony_ci struct ptp_clock_event ptp_evt; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci if (efx_ptp_synchronize(efx, PTP_SYNC_ATTEMPTS)) 14198c2ecf20Sopenharmony_ci return; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci ptp_evt.type = PTP_CLOCK_PPSUSR; 14228c2ecf20Sopenharmony_ci ptp_evt.pps_times = ptp->host_time_pps; 14238c2ecf20Sopenharmony_ci ptp_clock_event(ptp->phc_clock, &ptp_evt); 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic void efx_ptp_worker(struct work_struct *work) 14278c2ecf20Sopenharmony_ci{ 14288c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp_data = 14298c2ecf20Sopenharmony_ci container_of(work, struct efx_ptp_data, work); 14308c2ecf20Sopenharmony_ci struct efx_nic *efx = ptp_data->efx; 14318c2ecf20Sopenharmony_ci struct sk_buff *skb; 14328c2ecf20Sopenharmony_ci struct sk_buff_head tempq; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci if (ptp_data->reset_required) { 14358c2ecf20Sopenharmony_ci efx_ptp_stop(efx); 14368c2ecf20Sopenharmony_ci efx_ptp_start(efx); 14378c2ecf20Sopenharmony_ci return; 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci efx_ptp_drop_time_expired_events(efx); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci __skb_queue_head_init(&tempq); 14438c2ecf20Sopenharmony_ci efx_ptp_process_events(efx, &tempq); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&ptp_data->txq))) 14468c2ecf20Sopenharmony_ci ptp_data->xmit_skb(efx, skb); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci while ((skb = __skb_dequeue(&tempq))) 14498c2ecf20Sopenharmony_ci efx_ptp_process_rx(efx, skb); 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_cistatic const struct ptp_clock_info efx_phc_clock_info = { 14538c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 14548c2ecf20Sopenharmony_ci .name = "sfc", 14558c2ecf20Sopenharmony_ci .max_adj = MAX_PPB, 14568c2ecf20Sopenharmony_ci .n_alarm = 0, 14578c2ecf20Sopenharmony_ci .n_ext_ts = 0, 14588c2ecf20Sopenharmony_ci .n_per_out = 0, 14598c2ecf20Sopenharmony_ci .n_pins = 0, 14608c2ecf20Sopenharmony_ci .pps = 1, 14618c2ecf20Sopenharmony_ci .adjfreq = efx_phc_adjfreq, 14628c2ecf20Sopenharmony_ci .adjtime = efx_phc_adjtime, 14638c2ecf20Sopenharmony_ci .gettime64 = efx_phc_gettime, 14648c2ecf20Sopenharmony_ci .settime64 = efx_phc_settime, 14658c2ecf20Sopenharmony_ci .enable = efx_phc_enable, 14668c2ecf20Sopenharmony_ci}; 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci/* Initialise PTP state. */ 14698c2ecf20Sopenharmony_ciint efx_ptp_probe(struct efx_nic *efx, struct efx_channel *channel) 14708c2ecf20Sopenharmony_ci{ 14718c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp; 14728c2ecf20Sopenharmony_ci int rc = 0; 14738c2ecf20Sopenharmony_ci unsigned int pos; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci if (efx->ptp_data) { 14768c2ecf20Sopenharmony_ci efx->ptp_data->channel = channel; 14778c2ecf20Sopenharmony_ci return 0; 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci ptp = kzalloc(sizeof(struct efx_ptp_data), GFP_KERNEL); 14818c2ecf20Sopenharmony_ci efx->ptp_data = ptp; 14828c2ecf20Sopenharmony_ci if (!efx->ptp_data) 14838c2ecf20Sopenharmony_ci return -ENOMEM; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci ptp->efx = efx; 14868c2ecf20Sopenharmony_ci ptp->channel = channel; 14878c2ecf20Sopenharmony_ci ptp->rx_ts_inline = efx_nic_rev(efx) >= EFX_REV_HUNT_A0; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci rc = efx_nic_alloc_buffer(efx, &ptp->start, sizeof(int), GFP_KERNEL); 14908c2ecf20Sopenharmony_ci if (rc != 0) 14918c2ecf20Sopenharmony_ci goto fail1; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci skb_queue_head_init(&ptp->rxq); 14948c2ecf20Sopenharmony_ci skb_queue_head_init(&ptp->txq); 14958c2ecf20Sopenharmony_ci ptp->workwq = create_singlethread_workqueue("sfc_ptp"); 14968c2ecf20Sopenharmony_ci if (!ptp->workwq) { 14978c2ecf20Sopenharmony_ci rc = -ENOMEM; 14988c2ecf20Sopenharmony_ci goto fail2; 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci if (efx_ptp_use_mac_tx_timestamps(efx)) { 15028c2ecf20Sopenharmony_ci ptp->xmit_skb = efx_ptp_xmit_skb_queue; 15038c2ecf20Sopenharmony_ci /* Request sync events on this channel. */ 15048c2ecf20Sopenharmony_ci channel->sync_events_state = SYNC_EVENTS_QUIESCENT; 15058c2ecf20Sopenharmony_ci } else { 15068c2ecf20Sopenharmony_ci ptp->xmit_skb = efx_ptp_xmit_skb_mc; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci INIT_WORK(&ptp->work, efx_ptp_worker); 15108c2ecf20Sopenharmony_ci ptp->config.flags = 0; 15118c2ecf20Sopenharmony_ci ptp->config.tx_type = HWTSTAMP_TX_OFF; 15128c2ecf20Sopenharmony_ci ptp->config.rx_filter = HWTSTAMP_FILTER_NONE; 15138c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ptp->evt_list); 15148c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ptp->evt_free_list); 15158c2ecf20Sopenharmony_ci spin_lock_init(&ptp->evt_lock); 15168c2ecf20Sopenharmony_ci for (pos = 0; pos < MAX_RECEIVE_EVENTS; pos++) 15178c2ecf20Sopenharmony_ci list_add(&ptp->rx_evts[pos].link, &ptp->evt_free_list); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci /* Get the NIC PTP attributes and set up time conversions */ 15208c2ecf20Sopenharmony_ci rc = efx_ptp_get_attributes(efx); 15218c2ecf20Sopenharmony_ci if (rc < 0) 15228c2ecf20Sopenharmony_ci goto fail3; 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci /* Get the timestamp corrections */ 15258c2ecf20Sopenharmony_ci rc = efx_ptp_get_timestamp_corrections(efx); 15268c2ecf20Sopenharmony_ci if (rc < 0) 15278c2ecf20Sopenharmony_ci goto fail3; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci if (efx->mcdi->fn_flags & 15308c2ecf20Sopenharmony_ci (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY)) { 15318c2ecf20Sopenharmony_ci ptp->phc_clock_info = efx_phc_clock_info; 15328c2ecf20Sopenharmony_ci ptp->phc_clock = ptp_clock_register(&ptp->phc_clock_info, 15338c2ecf20Sopenharmony_ci &efx->pci_dev->dev); 15348c2ecf20Sopenharmony_ci if (IS_ERR(ptp->phc_clock)) { 15358c2ecf20Sopenharmony_ci rc = PTR_ERR(ptp->phc_clock); 15368c2ecf20Sopenharmony_ci goto fail3; 15378c2ecf20Sopenharmony_ci } else if (ptp->phc_clock) { 15388c2ecf20Sopenharmony_ci INIT_WORK(&ptp->pps_work, efx_ptp_pps_worker); 15398c2ecf20Sopenharmony_ci ptp->pps_workwq = create_singlethread_workqueue("sfc_pps"); 15408c2ecf20Sopenharmony_ci if (!ptp->pps_workwq) { 15418c2ecf20Sopenharmony_ci rc = -ENOMEM; 15428c2ecf20Sopenharmony_ci goto fail4; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci } 15458c2ecf20Sopenharmony_ci } 15468c2ecf20Sopenharmony_ci ptp->nic_ts_enabled = false; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci return 0; 15498c2ecf20Sopenharmony_cifail4: 15508c2ecf20Sopenharmony_ci ptp_clock_unregister(efx->ptp_data->phc_clock); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_cifail3: 15538c2ecf20Sopenharmony_ci destroy_workqueue(efx->ptp_data->workwq); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_cifail2: 15568c2ecf20Sopenharmony_ci efx_nic_free_buffer(efx, &ptp->start); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_cifail1: 15598c2ecf20Sopenharmony_ci kfree(efx->ptp_data); 15608c2ecf20Sopenharmony_ci efx->ptp_data = NULL; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci return rc; 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci/* Initialise PTP channel. 15668c2ecf20Sopenharmony_ci * 15678c2ecf20Sopenharmony_ci * Setting core_index to zero causes the queue to be initialised and doesn't 15688c2ecf20Sopenharmony_ci * overlap with 'rxq0' because ptp.c doesn't use skb_record_rx_queue. 15698c2ecf20Sopenharmony_ci */ 15708c2ecf20Sopenharmony_cistatic int efx_ptp_probe_channel(struct efx_channel *channel) 15718c2ecf20Sopenharmony_ci{ 15728c2ecf20Sopenharmony_ci struct efx_nic *efx = channel->efx; 15738c2ecf20Sopenharmony_ci int rc; 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci channel->irq_moderation_us = 0; 15768c2ecf20Sopenharmony_ci channel->rx_queue.core_index = 0; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci rc = efx_ptp_probe(efx, channel); 15798c2ecf20Sopenharmony_ci /* Failure to probe PTP is not fatal; this channel will just not be 15808c2ecf20Sopenharmony_ci * used for anything. 15818c2ecf20Sopenharmony_ci * In the case of EPERM, efx_ptp_probe will print its own message (in 15828c2ecf20Sopenharmony_ci * efx_ptp_get_attributes()), so we don't need to. 15838c2ecf20Sopenharmony_ci */ 15848c2ecf20Sopenharmony_ci if (rc && rc != -EPERM) 15858c2ecf20Sopenharmony_ci netif_warn(efx, drv, efx->net_dev, 15868c2ecf20Sopenharmony_ci "Failed to probe PTP, rc=%d\n", rc); 15878c2ecf20Sopenharmony_ci return 0; 15888c2ecf20Sopenharmony_ci} 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_civoid efx_ptp_remove(struct efx_nic *efx) 15918c2ecf20Sopenharmony_ci{ 15928c2ecf20Sopenharmony_ci if (!efx->ptp_data) 15938c2ecf20Sopenharmony_ci return; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci (void)efx_ptp_disable(efx); 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci cancel_work_sync(&efx->ptp_data->work); 15988c2ecf20Sopenharmony_ci if (efx->ptp_data->pps_workwq) 15998c2ecf20Sopenharmony_ci cancel_work_sync(&efx->ptp_data->pps_work); 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci skb_queue_purge(&efx->ptp_data->rxq); 16028c2ecf20Sopenharmony_ci skb_queue_purge(&efx->ptp_data->txq); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci if (efx->ptp_data->phc_clock) { 16058c2ecf20Sopenharmony_ci destroy_workqueue(efx->ptp_data->pps_workwq); 16068c2ecf20Sopenharmony_ci ptp_clock_unregister(efx->ptp_data->phc_clock); 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci destroy_workqueue(efx->ptp_data->workwq); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci efx_nic_free_buffer(efx, &efx->ptp_data->start); 16128c2ecf20Sopenharmony_ci kfree(efx->ptp_data); 16138c2ecf20Sopenharmony_ci efx->ptp_data = NULL; 16148c2ecf20Sopenharmony_ci} 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_cistatic void efx_ptp_remove_channel(struct efx_channel *channel) 16178c2ecf20Sopenharmony_ci{ 16188c2ecf20Sopenharmony_ci efx_ptp_remove(channel->efx); 16198c2ecf20Sopenharmony_ci} 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_cistatic void efx_ptp_get_channel_name(struct efx_channel *channel, 16228c2ecf20Sopenharmony_ci char *buf, size_t len) 16238c2ecf20Sopenharmony_ci{ 16248c2ecf20Sopenharmony_ci snprintf(buf, len, "%s-ptp", channel->efx->name); 16258c2ecf20Sopenharmony_ci} 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci/* Determine whether this packet should be processed by the PTP module 16288c2ecf20Sopenharmony_ci * or transmitted conventionally. 16298c2ecf20Sopenharmony_ci */ 16308c2ecf20Sopenharmony_cibool efx_ptp_is_ptp_tx(struct efx_nic *efx, struct sk_buff *skb) 16318c2ecf20Sopenharmony_ci{ 16328c2ecf20Sopenharmony_ci return efx->ptp_data && 16338c2ecf20Sopenharmony_ci efx->ptp_data->enabled && 16348c2ecf20Sopenharmony_ci skb->len >= PTP_MIN_LENGTH && 16358c2ecf20Sopenharmony_ci skb->len <= MC_CMD_PTP_IN_TRANSMIT_PACKET_MAXNUM && 16368c2ecf20Sopenharmony_ci likely(skb->protocol == htons(ETH_P_IP)) && 16378c2ecf20Sopenharmony_ci skb_transport_header_was_set(skb) && 16388c2ecf20Sopenharmony_ci skb_network_header_len(skb) >= sizeof(struct iphdr) && 16398c2ecf20Sopenharmony_ci ip_hdr(skb)->protocol == IPPROTO_UDP && 16408c2ecf20Sopenharmony_ci skb_headlen(skb) >= 16418c2ecf20Sopenharmony_ci skb_transport_offset(skb) + sizeof(struct udphdr) && 16428c2ecf20Sopenharmony_ci udp_hdr(skb)->dest == htons(PTP_EVENT_PORT); 16438c2ecf20Sopenharmony_ci} 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci/* Receive a PTP packet. Packets are queued until the arrival of 16468c2ecf20Sopenharmony_ci * the receive timestamp from the MC - this will probably occur after the 16478c2ecf20Sopenharmony_ci * packet arrival because of the processing in the MC. 16488c2ecf20Sopenharmony_ci */ 16498c2ecf20Sopenharmony_cistatic bool efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb) 16508c2ecf20Sopenharmony_ci{ 16518c2ecf20Sopenharmony_ci struct efx_nic *efx = channel->efx; 16528c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 16538c2ecf20Sopenharmony_ci struct efx_ptp_match *match = (struct efx_ptp_match *)skb->cb; 16548c2ecf20Sopenharmony_ci u8 *match_data_012, *match_data_345; 16558c2ecf20Sopenharmony_ci unsigned int version; 16568c2ecf20Sopenharmony_ci u8 *data; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci match->expiry = jiffies + msecs_to_jiffies(PKT_EVENT_LIFETIME_MS); 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci /* Correct version? */ 16618c2ecf20Sopenharmony_ci if (ptp->mode == MC_CMD_PTP_MODE_V1) { 16628c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, PTP_V1_MIN_LENGTH)) { 16638c2ecf20Sopenharmony_ci return false; 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci data = skb->data; 16668c2ecf20Sopenharmony_ci version = ntohs(*(__be16 *)&data[PTP_V1_VERSION_OFFSET]); 16678c2ecf20Sopenharmony_ci if (version != PTP_VERSION_V1) { 16688c2ecf20Sopenharmony_ci return false; 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci /* PTP V1 uses all six bytes of the UUID to match the packet 16728c2ecf20Sopenharmony_ci * to the timestamp 16738c2ecf20Sopenharmony_ci */ 16748c2ecf20Sopenharmony_ci match_data_012 = data + PTP_V1_UUID_OFFSET; 16758c2ecf20Sopenharmony_ci match_data_345 = data + PTP_V1_UUID_OFFSET + 3; 16768c2ecf20Sopenharmony_ci } else { 16778c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, PTP_V2_MIN_LENGTH)) { 16788c2ecf20Sopenharmony_ci return false; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci data = skb->data; 16818c2ecf20Sopenharmony_ci version = data[PTP_V2_VERSION_OFFSET]; 16828c2ecf20Sopenharmony_ci if ((version & PTP_VERSION_V2_MASK) != PTP_VERSION_V2) { 16838c2ecf20Sopenharmony_ci return false; 16848c2ecf20Sopenharmony_ci } 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci /* The original V2 implementation uses bytes 2-7 of 16878c2ecf20Sopenharmony_ci * the UUID to match the packet to the timestamp. This 16888c2ecf20Sopenharmony_ci * discards two of the bytes of the MAC address used 16898c2ecf20Sopenharmony_ci * to create the UUID (SF bug 33070). The PTP V2 16908c2ecf20Sopenharmony_ci * enhanced mode fixes this issue and uses bytes 0-2 16918c2ecf20Sopenharmony_ci * and byte 5-7 of the UUID. 16928c2ecf20Sopenharmony_ci */ 16938c2ecf20Sopenharmony_ci match_data_345 = data + PTP_V2_UUID_OFFSET + 5; 16948c2ecf20Sopenharmony_ci if (ptp->mode == MC_CMD_PTP_MODE_V2) { 16958c2ecf20Sopenharmony_ci match_data_012 = data + PTP_V2_UUID_OFFSET + 2; 16968c2ecf20Sopenharmony_ci } else { 16978c2ecf20Sopenharmony_ci match_data_012 = data + PTP_V2_UUID_OFFSET + 0; 16988c2ecf20Sopenharmony_ci BUG_ON(ptp->mode != MC_CMD_PTP_MODE_V2_ENHANCED); 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci } 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci /* Does this packet require timestamping? */ 17038c2ecf20Sopenharmony_ci if (ntohs(*(__be16 *)&data[PTP_DPORT_OFFSET]) == PTP_EVENT_PORT) { 17048c2ecf20Sopenharmony_ci match->state = PTP_PACKET_STATE_UNMATCHED; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci /* We expect the sequence number to be in the same position in 17078c2ecf20Sopenharmony_ci * the packet for PTP V1 and V2 17088c2ecf20Sopenharmony_ci */ 17098c2ecf20Sopenharmony_ci BUILD_BUG_ON(PTP_V1_SEQUENCE_OFFSET != PTP_V2_SEQUENCE_OFFSET); 17108c2ecf20Sopenharmony_ci BUILD_BUG_ON(PTP_V1_SEQUENCE_LENGTH != PTP_V2_SEQUENCE_LENGTH); 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci /* Extract UUID/Sequence information */ 17138c2ecf20Sopenharmony_ci match->words[0] = (match_data_012[0] | 17148c2ecf20Sopenharmony_ci (match_data_012[1] << 8) | 17158c2ecf20Sopenharmony_ci (match_data_012[2] << 16) | 17168c2ecf20Sopenharmony_ci (match_data_345[0] << 24)); 17178c2ecf20Sopenharmony_ci match->words[1] = (match_data_345[1] | 17188c2ecf20Sopenharmony_ci (match_data_345[2] << 8) | 17198c2ecf20Sopenharmony_ci (data[PTP_V1_SEQUENCE_OFFSET + 17208c2ecf20Sopenharmony_ci PTP_V1_SEQUENCE_LENGTH - 1] << 17218c2ecf20Sopenharmony_ci 16)); 17228c2ecf20Sopenharmony_ci } else { 17238c2ecf20Sopenharmony_ci match->state = PTP_PACKET_STATE_MATCH_UNWANTED; 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci skb_queue_tail(&ptp->rxq, skb); 17278c2ecf20Sopenharmony_ci queue_work(ptp->workwq, &ptp->work); 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci return true; 17308c2ecf20Sopenharmony_ci} 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci/* Transmit a PTP packet. This has to be transmitted by the MC 17338c2ecf20Sopenharmony_ci * itself, through an MCDI call. MCDI calls aren't permitted 17348c2ecf20Sopenharmony_ci * in the transmit path so defer the actual transmission to a suitable worker. 17358c2ecf20Sopenharmony_ci */ 17368c2ecf20Sopenharmony_ciint efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb) 17378c2ecf20Sopenharmony_ci{ 17388c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci skb_queue_tail(&ptp->txq, skb); 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci if ((udp_hdr(skb)->dest == htons(PTP_EVENT_PORT)) && 17438c2ecf20Sopenharmony_ci (skb->len <= MC_CMD_PTP_IN_TRANSMIT_PACKET_MAXNUM)) 17448c2ecf20Sopenharmony_ci efx_xmit_hwtstamp_pending(skb); 17458c2ecf20Sopenharmony_ci queue_work(ptp->workwq, &ptp->work); 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 17488c2ecf20Sopenharmony_ci} 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ciint efx_ptp_get_mode(struct efx_nic *efx) 17518c2ecf20Sopenharmony_ci{ 17528c2ecf20Sopenharmony_ci return efx->ptp_data->mode; 17538c2ecf20Sopenharmony_ci} 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ciint efx_ptp_change_mode(struct efx_nic *efx, bool enable_wanted, 17568c2ecf20Sopenharmony_ci unsigned int new_mode) 17578c2ecf20Sopenharmony_ci{ 17588c2ecf20Sopenharmony_ci if ((enable_wanted != efx->ptp_data->enabled) || 17598c2ecf20Sopenharmony_ci (enable_wanted && (efx->ptp_data->mode != new_mode))) { 17608c2ecf20Sopenharmony_ci int rc = 0; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (enable_wanted) { 17638c2ecf20Sopenharmony_ci /* Change of mode requires disable */ 17648c2ecf20Sopenharmony_ci if (efx->ptp_data->enabled && 17658c2ecf20Sopenharmony_ci (efx->ptp_data->mode != new_mode)) { 17668c2ecf20Sopenharmony_ci efx->ptp_data->enabled = false; 17678c2ecf20Sopenharmony_ci rc = efx_ptp_stop(efx); 17688c2ecf20Sopenharmony_ci if (rc != 0) 17698c2ecf20Sopenharmony_ci return rc; 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci /* Set new operating mode and establish 17738c2ecf20Sopenharmony_ci * baseline synchronisation, which must 17748c2ecf20Sopenharmony_ci * succeed. 17758c2ecf20Sopenharmony_ci */ 17768c2ecf20Sopenharmony_ci efx->ptp_data->mode = new_mode; 17778c2ecf20Sopenharmony_ci if (netif_running(efx->net_dev)) 17788c2ecf20Sopenharmony_ci rc = efx_ptp_start(efx); 17798c2ecf20Sopenharmony_ci if (rc == 0) { 17808c2ecf20Sopenharmony_ci rc = efx_ptp_synchronize(efx, 17818c2ecf20Sopenharmony_ci PTP_SYNC_ATTEMPTS * 2); 17828c2ecf20Sopenharmony_ci if (rc != 0) 17838c2ecf20Sopenharmony_ci efx_ptp_stop(efx); 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci } else { 17868c2ecf20Sopenharmony_ci rc = efx_ptp_stop(efx); 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci if (rc != 0) 17908c2ecf20Sopenharmony_ci return rc; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci efx->ptp_data->enabled = enable_wanted; 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci return 0; 17968c2ecf20Sopenharmony_ci} 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_cistatic int efx_ptp_ts_init(struct efx_nic *efx, struct hwtstamp_config *init) 17998c2ecf20Sopenharmony_ci{ 18008c2ecf20Sopenharmony_ci int rc; 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci if (init->flags) 18038c2ecf20Sopenharmony_ci return -EINVAL; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci if ((init->tx_type != HWTSTAMP_TX_OFF) && 18068c2ecf20Sopenharmony_ci (init->tx_type != HWTSTAMP_TX_ON)) 18078c2ecf20Sopenharmony_ci return -ERANGE; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci rc = efx->type->ptp_set_ts_config(efx, init); 18108c2ecf20Sopenharmony_ci if (rc) 18118c2ecf20Sopenharmony_ci return rc; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci efx->ptp_data->config = *init; 18148c2ecf20Sopenharmony_ci return 0; 18158c2ecf20Sopenharmony_ci} 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_civoid efx_ptp_get_ts_info(struct efx_nic *efx, struct ethtool_ts_info *ts_info) 18188c2ecf20Sopenharmony_ci{ 18198c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 18208c2ecf20Sopenharmony_ci struct efx_nic *primary = efx->primary; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci ASSERT_RTNL(); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci if (!ptp) 18258c2ecf20Sopenharmony_ci return; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci ts_info->so_timestamping |= (SOF_TIMESTAMPING_TX_HARDWARE | 18288c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 18298c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE); 18308c2ecf20Sopenharmony_ci /* Check licensed features. If we don't have the license for TX 18318c2ecf20Sopenharmony_ci * timestamps, the NIC will not support them. 18328c2ecf20Sopenharmony_ci */ 18338c2ecf20Sopenharmony_ci if (efx_ptp_use_mac_tx_timestamps(efx)) { 18348c2ecf20Sopenharmony_ci struct efx_ef10_nic_data *nic_data = efx->nic_data; 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci if (!(nic_data->licensed_features & 18378c2ecf20Sopenharmony_ci (1 << LICENSED_V3_FEATURES_TX_TIMESTAMPS_LBN))) 18388c2ecf20Sopenharmony_ci ts_info->so_timestamping &= 18398c2ecf20Sopenharmony_ci ~SOF_TIMESTAMPING_TX_HARDWARE; 18408c2ecf20Sopenharmony_ci } 18418c2ecf20Sopenharmony_ci if (primary && primary->ptp_data && primary->ptp_data->phc_clock) 18428c2ecf20Sopenharmony_ci ts_info->phc_index = 18438c2ecf20Sopenharmony_ci ptp_clock_index(primary->ptp_data->phc_clock); 18448c2ecf20Sopenharmony_ci ts_info->tx_types = 1 << HWTSTAMP_TX_OFF | 1 << HWTSTAMP_TX_ON; 18458c2ecf20Sopenharmony_ci ts_info->rx_filters = ptp->efx->type->hwtstamp_filters; 18468c2ecf20Sopenharmony_ci} 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ciint efx_ptp_set_ts_config(struct efx_nic *efx, struct ifreq *ifr) 18498c2ecf20Sopenharmony_ci{ 18508c2ecf20Sopenharmony_ci struct hwtstamp_config config; 18518c2ecf20Sopenharmony_ci int rc; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci /* Not a PTP enabled port */ 18548c2ecf20Sopenharmony_ci if (!efx->ptp_data) 18558c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 18588c2ecf20Sopenharmony_ci return -EFAULT; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci rc = efx_ptp_ts_init(efx, &config); 18618c2ecf20Sopenharmony_ci if (rc != 0) 18628c2ecf20Sopenharmony_ci return rc; 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci return copy_to_user(ifr->ifr_data, &config, sizeof(config)) 18658c2ecf20Sopenharmony_ci ? -EFAULT : 0; 18668c2ecf20Sopenharmony_ci} 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ciint efx_ptp_get_ts_config(struct efx_nic *efx, struct ifreq *ifr) 18698c2ecf20Sopenharmony_ci{ 18708c2ecf20Sopenharmony_ci if (!efx->ptp_data) 18718c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci return copy_to_user(ifr->ifr_data, &efx->ptp_data->config, 18748c2ecf20Sopenharmony_ci sizeof(efx->ptp_data->config)) ? -EFAULT : 0; 18758c2ecf20Sopenharmony_ci} 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_cistatic void ptp_event_failure(struct efx_nic *efx, int expected_frag_len) 18788c2ecf20Sopenharmony_ci{ 18798c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci netif_err(efx, hw, efx->net_dev, 18828c2ecf20Sopenharmony_ci "PTP unexpected event length: got %d expected %d\n", 18838c2ecf20Sopenharmony_ci ptp->evt_frag_idx, expected_frag_len); 18848c2ecf20Sopenharmony_ci ptp->reset_required = true; 18858c2ecf20Sopenharmony_ci queue_work(ptp->workwq, &ptp->work); 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci/* Process a completed receive event. Put it on the event queue and 18898c2ecf20Sopenharmony_ci * start worker thread. This is required because event and their 18908c2ecf20Sopenharmony_ci * correspoding packets may come in either order. 18918c2ecf20Sopenharmony_ci */ 18928c2ecf20Sopenharmony_cistatic void ptp_event_rx(struct efx_nic *efx, struct efx_ptp_data *ptp) 18938c2ecf20Sopenharmony_ci{ 18948c2ecf20Sopenharmony_ci struct efx_ptp_event_rx *evt = NULL; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(ptp->rx_ts_inline)) 18978c2ecf20Sopenharmony_ci return; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci if (ptp->evt_frag_idx != 3) { 19008c2ecf20Sopenharmony_ci ptp_event_failure(efx, 3); 19018c2ecf20Sopenharmony_ci return; 19028c2ecf20Sopenharmony_ci } 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci spin_lock_bh(&ptp->evt_lock); 19058c2ecf20Sopenharmony_ci if (!list_empty(&ptp->evt_free_list)) { 19068c2ecf20Sopenharmony_ci evt = list_first_entry(&ptp->evt_free_list, 19078c2ecf20Sopenharmony_ci struct efx_ptp_event_rx, link); 19088c2ecf20Sopenharmony_ci list_del(&evt->link); 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci evt->seq0 = EFX_QWORD_FIELD(ptp->evt_frags[2], MCDI_EVENT_DATA); 19118c2ecf20Sopenharmony_ci evt->seq1 = (EFX_QWORD_FIELD(ptp->evt_frags[2], 19128c2ecf20Sopenharmony_ci MCDI_EVENT_SRC) | 19138c2ecf20Sopenharmony_ci (EFX_QWORD_FIELD(ptp->evt_frags[1], 19148c2ecf20Sopenharmony_ci MCDI_EVENT_SRC) << 8) | 19158c2ecf20Sopenharmony_ci (EFX_QWORD_FIELD(ptp->evt_frags[0], 19168c2ecf20Sopenharmony_ci MCDI_EVENT_SRC) << 16)); 19178c2ecf20Sopenharmony_ci evt->hwtimestamp = efx->ptp_data->nic_to_kernel_time( 19188c2ecf20Sopenharmony_ci EFX_QWORD_FIELD(ptp->evt_frags[0], MCDI_EVENT_DATA), 19198c2ecf20Sopenharmony_ci EFX_QWORD_FIELD(ptp->evt_frags[1], MCDI_EVENT_DATA), 19208c2ecf20Sopenharmony_ci ptp->ts_corrections.ptp_rx); 19218c2ecf20Sopenharmony_ci evt->expiry = jiffies + msecs_to_jiffies(PKT_EVENT_LIFETIME_MS); 19228c2ecf20Sopenharmony_ci list_add_tail(&evt->link, &ptp->evt_list); 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci queue_work(ptp->workwq, &ptp->work); 19258c2ecf20Sopenharmony_ci } else if (net_ratelimit()) { 19268c2ecf20Sopenharmony_ci /* Log a rate-limited warning message. */ 19278c2ecf20Sopenharmony_ci netif_err(efx, rx_err, efx->net_dev, "PTP event queue overflow\n"); 19288c2ecf20Sopenharmony_ci } 19298c2ecf20Sopenharmony_ci spin_unlock_bh(&ptp->evt_lock); 19308c2ecf20Sopenharmony_ci} 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_cistatic void ptp_event_fault(struct efx_nic *efx, struct efx_ptp_data *ptp) 19338c2ecf20Sopenharmony_ci{ 19348c2ecf20Sopenharmony_ci int code = EFX_QWORD_FIELD(ptp->evt_frags[0], MCDI_EVENT_DATA); 19358c2ecf20Sopenharmony_ci if (ptp->evt_frag_idx != 1) { 19368c2ecf20Sopenharmony_ci ptp_event_failure(efx, 1); 19378c2ecf20Sopenharmony_ci return; 19388c2ecf20Sopenharmony_ci } 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci netif_err(efx, hw, efx->net_dev, "PTP error %d\n", code); 19418c2ecf20Sopenharmony_ci} 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_cistatic void ptp_event_pps(struct efx_nic *efx, struct efx_ptp_data *ptp) 19448c2ecf20Sopenharmony_ci{ 19458c2ecf20Sopenharmony_ci if (ptp->nic_ts_enabled) 19468c2ecf20Sopenharmony_ci queue_work(ptp->pps_workwq, &ptp->pps_work); 19478c2ecf20Sopenharmony_ci} 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_civoid efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev) 19508c2ecf20Sopenharmony_ci{ 19518c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 19528c2ecf20Sopenharmony_ci int code = EFX_QWORD_FIELD(*ev, MCDI_EVENT_CODE); 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci if (!ptp) { 19558c2ecf20Sopenharmony_ci if (!efx->ptp_warned) { 19568c2ecf20Sopenharmony_ci netif_warn(efx, drv, efx->net_dev, 19578c2ecf20Sopenharmony_ci "Received PTP event but PTP not set up\n"); 19588c2ecf20Sopenharmony_ci efx->ptp_warned = true; 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci return; 19618c2ecf20Sopenharmony_ci } 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci if (!ptp->enabled) 19648c2ecf20Sopenharmony_ci return; 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci if (ptp->evt_frag_idx == 0) { 19678c2ecf20Sopenharmony_ci ptp->evt_code = code; 19688c2ecf20Sopenharmony_ci } else if (ptp->evt_code != code) { 19698c2ecf20Sopenharmony_ci netif_err(efx, hw, efx->net_dev, 19708c2ecf20Sopenharmony_ci "PTP out of sequence event %d\n", code); 19718c2ecf20Sopenharmony_ci ptp->evt_frag_idx = 0; 19728c2ecf20Sopenharmony_ci } 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci ptp->evt_frags[ptp->evt_frag_idx++] = *ev; 19758c2ecf20Sopenharmony_ci if (!MCDI_EVENT_FIELD(*ev, CONT)) { 19768c2ecf20Sopenharmony_ci /* Process resulting event */ 19778c2ecf20Sopenharmony_ci switch (code) { 19788c2ecf20Sopenharmony_ci case MCDI_EVENT_CODE_PTP_RX: 19798c2ecf20Sopenharmony_ci ptp_event_rx(efx, ptp); 19808c2ecf20Sopenharmony_ci break; 19818c2ecf20Sopenharmony_ci case MCDI_EVENT_CODE_PTP_FAULT: 19828c2ecf20Sopenharmony_ci ptp_event_fault(efx, ptp); 19838c2ecf20Sopenharmony_ci break; 19848c2ecf20Sopenharmony_ci case MCDI_EVENT_CODE_PTP_PPS: 19858c2ecf20Sopenharmony_ci ptp_event_pps(efx, ptp); 19868c2ecf20Sopenharmony_ci break; 19878c2ecf20Sopenharmony_ci default: 19888c2ecf20Sopenharmony_ci netif_err(efx, hw, efx->net_dev, 19898c2ecf20Sopenharmony_ci "PTP unknown event %d\n", code); 19908c2ecf20Sopenharmony_ci break; 19918c2ecf20Sopenharmony_ci } 19928c2ecf20Sopenharmony_ci ptp->evt_frag_idx = 0; 19938c2ecf20Sopenharmony_ci } else if (MAX_EVENT_FRAGS == ptp->evt_frag_idx) { 19948c2ecf20Sopenharmony_ci netif_err(efx, hw, efx->net_dev, 19958c2ecf20Sopenharmony_ci "PTP too many event fragments\n"); 19968c2ecf20Sopenharmony_ci ptp->evt_frag_idx = 0; 19978c2ecf20Sopenharmony_ci } 19988c2ecf20Sopenharmony_ci} 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_civoid efx_time_sync_event(struct efx_channel *channel, efx_qword_t *ev) 20018c2ecf20Sopenharmony_ci{ 20028c2ecf20Sopenharmony_ci struct efx_nic *efx = channel->efx; 20038c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci /* When extracting the sync timestamp minor value, we should discard 20068c2ecf20Sopenharmony_ci * the least significant two bits. These are not required in order 20078c2ecf20Sopenharmony_ci * to reconstruct full-range timestamps and they are optionally used 20088c2ecf20Sopenharmony_ci * to report status depending on the options supplied when subscribing 20098c2ecf20Sopenharmony_ci * for sync events. 20108c2ecf20Sopenharmony_ci */ 20118c2ecf20Sopenharmony_ci channel->sync_timestamp_major = MCDI_EVENT_FIELD(*ev, PTP_TIME_MAJOR); 20128c2ecf20Sopenharmony_ci channel->sync_timestamp_minor = 20138c2ecf20Sopenharmony_ci (MCDI_EVENT_FIELD(*ev, PTP_TIME_MINOR_MS_8BITS) & 0xFC) 20148c2ecf20Sopenharmony_ci << ptp->nic_time.sync_event_minor_shift; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci /* if sync events have been disabled then we want to silently ignore 20178c2ecf20Sopenharmony_ci * this event, so throw away result. 20188c2ecf20Sopenharmony_ci */ 20198c2ecf20Sopenharmony_ci (void) cmpxchg(&channel->sync_events_state, SYNC_EVENTS_REQUESTED, 20208c2ecf20Sopenharmony_ci SYNC_EVENTS_VALID); 20218c2ecf20Sopenharmony_ci} 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_cistatic inline u32 efx_rx_buf_timestamp_minor(struct efx_nic *efx, const u8 *eh) 20248c2ecf20Sopenharmony_ci{ 20258c2ecf20Sopenharmony_ci#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) 20268c2ecf20Sopenharmony_ci return __le32_to_cpup((const __le32 *)(eh + efx->rx_packet_ts_offset)); 20278c2ecf20Sopenharmony_ci#else 20288c2ecf20Sopenharmony_ci const u8 *data = eh + efx->rx_packet_ts_offset; 20298c2ecf20Sopenharmony_ci return (u32)data[0] | 20308c2ecf20Sopenharmony_ci (u32)data[1] << 8 | 20318c2ecf20Sopenharmony_ci (u32)data[2] << 16 | 20328c2ecf20Sopenharmony_ci (u32)data[3] << 24; 20338c2ecf20Sopenharmony_ci#endif 20348c2ecf20Sopenharmony_ci} 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_civoid __efx_rx_skb_attach_timestamp(struct efx_channel *channel, 20378c2ecf20Sopenharmony_ci struct sk_buff *skb) 20388c2ecf20Sopenharmony_ci{ 20398c2ecf20Sopenharmony_ci struct efx_nic *efx = channel->efx; 20408c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp = efx->ptp_data; 20418c2ecf20Sopenharmony_ci u32 pkt_timestamp_major, pkt_timestamp_minor; 20428c2ecf20Sopenharmony_ci u32 diff, carry; 20438c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *timestamps; 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci if (channel->sync_events_state != SYNC_EVENTS_VALID) 20468c2ecf20Sopenharmony_ci return; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci pkt_timestamp_minor = efx_rx_buf_timestamp_minor(efx, skb_mac_header(skb)); 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci /* get the difference between the packet and sync timestamps, 20518c2ecf20Sopenharmony_ci * modulo one second 20528c2ecf20Sopenharmony_ci */ 20538c2ecf20Sopenharmony_ci diff = pkt_timestamp_minor - channel->sync_timestamp_minor; 20548c2ecf20Sopenharmony_ci if (pkt_timestamp_minor < channel->sync_timestamp_minor) 20558c2ecf20Sopenharmony_ci diff += ptp->nic_time.minor_max; 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci /* do we roll over a second boundary and need to carry the one? */ 20588c2ecf20Sopenharmony_ci carry = (channel->sync_timestamp_minor >= ptp->nic_time.minor_max - diff) ? 20598c2ecf20Sopenharmony_ci 1 : 0; 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci if (diff <= ptp->nic_time.sync_event_diff_max) { 20628c2ecf20Sopenharmony_ci /* packet is ahead of the sync event by a quarter of a second or 20638c2ecf20Sopenharmony_ci * less (allowing for fuzz) 20648c2ecf20Sopenharmony_ci */ 20658c2ecf20Sopenharmony_ci pkt_timestamp_major = channel->sync_timestamp_major + carry; 20668c2ecf20Sopenharmony_ci } else if (diff >= ptp->nic_time.sync_event_diff_min) { 20678c2ecf20Sopenharmony_ci /* packet is behind the sync event but within the fuzz factor. 20688c2ecf20Sopenharmony_ci * This means the RX packet and sync event crossed as they were 20698c2ecf20Sopenharmony_ci * placed on the event queue, which can sometimes happen. 20708c2ecf20Sopenharmony_ci */ 20718c2ecf20Sopenharmony_ci pkt_timestamp_major = channel->sync_timestamp_major - 1 + carry; 20728c2ecf20Sopenharmony_ci } else { 20738c2ecf20Sopenharmony_ci /* it's outside tolerance in both directions. this might be 20748c2ecf20Sopenharmony_ci * indicative of us missing sync events for some reason, so 20758c2ecf20Sopenharmony_ci * we'll call it an error rather than risk giving a bogus 20768c2ecf20Sopenharmony_ci * timestamp. 20778c2ecf20Sopenharmony_ci */ 20788c2ecf20Sopenharmony_ci netif_vdbg(efx, drv, efx->net_dev, 20798c2ecf20Sopenharmony_ci "packet timestamp %x too far from sync event %x:%x\n", 20808c2ecf20Sopenharmony_ci pkt_timestamp_minor, channel->sync_timestamp_major, 20818c2ecf20Sopenharmony_ci channel->sync_timestamp_minor); 20828c2ecf20Sopenharmony_ci return; 20838c2ecf20Sopenharmony_ci } 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci /* attach the timestamps to the skb */ 20868c2ecf20Sopenharmony_ci timestamps = skb_hwtstamps(skb); 20878c2ecf20Sopenharmony_ci timestamps->hwtstamp = 20888c2ecf20Sopenharmony_ci ptp->nic_to_kernel_time(pkt_timestamp_major, 20898c2ecf20Sopenharmony_ci pkt_timestamp_minor, 20908c2ecf20Sopenharmony_ci ptp->ts_corrections.general_rx); 20918c2ecf20Sopenharmony_ci} 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_cistatic int efx_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta) 20948c2ecf20Sopenharmony_ci{ 20958c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp_data = container_of(ptp, 20968c2ecf20Sopenharmony_ci struct efx_ptp_data, 20978c2ecf20Sopenharmony_ci phc_clock_info); 20988c2ecf20Sopenharmony_ci struct efx_nic *efx = ptp_data->efx; 20998c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inadj, MC_CMD_PTP_IN_ADJUST_LEN); 21008c2ecf20Sopenharmony_ci s64 adjustment_ns; 21018c2ecf20Sopenharmony_ci int rc; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci if (delta > MAX_PPB) 21048c2ecf20Sopenharmony_ci delta = MAX_PPB; 21058c2ecf20Sopenharmony_ci else if (delta < -MAX_PPB) 21068c2ecf20Sopenharmony_ci delta = -MAX_PPB; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci /* Convert ppb to fixed point ns taking care to round correctly. */ 21098c2ecf20Sopenharmony_ci adjustment_ns = ((s64)delta * PPB_SCALE_WORD + 21108c2ecf20Sopenharmony_ci (1 << (ptp_data->adjfreq_ppb_shift - 1))) >> 21118c2ecf20Sopenharmony_ci ptp_data->adjfreq_ppb_shift; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inadj, PTP_IN_OP, MC_CMD_PTP_OP_ADJUST); 21148c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inadj, PTP_IN_PERIPH_ID, 0); 21158c2ecf20Sopenharmony_ci MCDI_SET_QWORD(inadj, PTP_IN_ADJUST_FREQ, adjustment_ns); 21168c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inadj, PTP_IN_ADJUST_SECONDS, 0); 21178c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inadj, PTP_IN_ADJUST_NANOSECONDS, 0); 21188c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc(efx, MC_CMD_PTP, inadj, sizeof(inadj), 21198c2ecf20Sopenharmony_ci NULL, 0, NULL); 21208c2ecf20Sopenharmony_ci if (rc != 0) 21218c2ecf20Sopenharmony_ci return rc; 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci ptp_data->current_adjfreq = adjustment_ns; 21248c2ecf20Sopenharmony_ci return 0; 21258c2ecf20Sopenharmony_ci} 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_cistatic int efx_phc_adjtime(struct ptp_clock_info *ptp, s64 delta) 21288c2ecf20Sopenharmony_ci{ 21298c2ecf20Sopenharmony_ci u32 nic_major, nic_minor; 21308c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp_data = container_of(ptp, 21318c2ecf20Sopenharmony_ci struct efx_ptp_data, 21328c2ecf20Sopenharmony_ci phc_clock_info); 21338c2ecf20Sopenharmony_ci struct efx_nic *efx = ptp_data->efx; 21348c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_ADJUST_LEN); 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci efx->ptp_data->ns_to_nic_time(delta, &nic_major, &nic_minor); 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_ADJUST); 21398c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0); 21408c2ecf20Sopenharmony_ci MCDI_SET_QWORD(inbuf, PTP_IN_ADJUST_FREQ, ptp_data->current_adjfreq); 21418c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_ADJUST_MAJOR, nic_major); 21428c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_ADJUST_MINOR, nic_minor); 21438c2ecf20Sopenharmony_ci return efx_mcdi_rpc(efx, MC_CMD_PTP, inbuf, sizeof(inbuf), 21448c2ecf20Sopenharmony_ci NULL, 0, NULL); 21458c2ecf20Sopenharmony_ci} 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_cistatic int efx_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 21488c2ecf20Sopenharmony_ci{ 21498c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp_data = container_of(ptp, 21508c2ecf20Sopenharmony_ci struct efx_ptp_data, 21518c2ecf20Sopenharmony_ci phc_clock_info); 21528c2ecf20Sopenharmony_ci struct efx_nic *efx = ptp_data->efx; 21538c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_READ_NIC_TIME_LEN); 21548c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_READ_NIC_TIME_LEN); 21558c2ecf20Sopenharmony_ci int rc; 21568c2ecf20Sopenharmony_ci ktime_t kt; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_READ_NIC_TIME); 21598c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0); 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc(efx, MC_CMD_PTP, inbuf, sizeof(inbuf), 21628c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), NULL); 21638c2ecf20Sopenharmony_ci if (rc != 0) 21648c2ecf20Sopenharmony_ci return rc; 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci kt = ptp_data->nic_to_kernel_time( 21678c2ecf20Sopenharmony_ci MCDI_DWORD(outbuf, PTP_OUT_READ_NIC_TIME_MAJOR), 21688c2ecf20Sopenharmony_ci MCDI_DWORD(outbuf, PTP_OUT_READ_NIC_TIME_MINOR), 0); 21698c2ecf20Sopenharmony_ci *ts = ktime_to_timespec64(kt); 21708c2ecf20Sopenharmony_ci return 0; 21718c2ecf20Sopenharmony_ci} 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_cistatic int efx_phc_settime(struct ptp_clock_info *ptp, 21748c2ecf20Sopenharmony_ci const struct timespec64 *e_ts) 21758c2ecf20Sopenharmony_ci{ 21768c2ecf20Sopenharmony_ci /* Get the current NIC time, efx_phc_gettime. 21778c2ecf20Sopenharmony_ci * Subtract from the desired time to get the offset 21788c2ecf20Sopenharmony_ci * call efx_phc_adjtime with the offset 21798c2ecf20Sopenharmony_ci */ 21808c2ecf20Sopenharmony_ci int rc; 21818c2ecf20Sopenharmony_ci struct timespec64 time_now; 21828c2ecf20Sopenharmony_ci struct timespec64 delta; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci rc = efx_phc_gettime(ptp, &time_now); 21858c2ecf20Sopenharmony_ci if (rc != 0) 21868c2ecf20Sopenharmony_ci return rc; 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci delta = timespec64_sub(*e_ts, time_now); 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci rc = efx_phc_adjtime(ptp, timespec64_to_ns(&delta)); 21918c2ecf20Sopenharmony_ci if (rc != 0) 21928c2ecf20Sopenharmony_ci return rc; 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci return 0; 21958c2ecf20Sopenharmony_ci} 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_cistatic int efx_phc_enable(struct ptp_clock_info *ptp, 21988c2ecf20Sopenharmony_ci struct ptp_clock_request *request, 21998c2ecf20Sopenharmony_ci int enable) 22008c2ecf20Sopenharmony_ci{ 22018c2ecf20Sopenharmony_ci struct efx_ptp_data *ptp_data = container_of(ptp, 22028c2ecf20Sopenharmony_ci struct efx_ptp_data, 22038c2ecf20Sopenharmony_ci phc_clock_info); 22048c2ecf20Sopenharmony_ci if (request->type != PTP_CLK_REQ_PPS) 22058c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci ptp_data->nic_ts_enabled = !!enable; 22088c2ecf20Sopenharmony_ci return 0; 22098c2ecf20Sopenharmony_ci} 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_cistatic const struct efx_channel_type efx_ptp_channel_type = { 22128c2ecf20Sopenharmony_ci .handle_no_channel = efx_ptp_handle_no_channel, 22138c2ecf20Sopenharmony_ci .pre_probe = efx_ptp_probe_channel, 22148c2ecf20Sopenharmony_ci .post_remove = efx_ptp_remove_channel, 22158c2ecf20Sopenharmony_ci .get_name = efx_ptp_get_channel_name, 22168c2ecf20Sopenharmony_ci .copy = efx_copy_channel, 22178c2ecf20Sopenharmony_ci .receive_skb = efx_ptp_rx, 22188c2ecf20Sopenharmony_ci .want_txqs = efx_ptp_want_txqs, 22198c2ecf20Sopenharmony_ci .keep_eventq = false, 22208c2ecf20Sopenharmony_ci}; 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_civoid efx_ptp_defer_probe_with_channel(struct efx_nic *efx) 22238c2ecf20Sopenharmony_ci{ 22248c2ecf20Sopenharmony_ci /* Check whether PTP is implemented on this NIC. The DISABLE 22258c2ecf20Sopenharmony_ci * operation will succeed if and only if it is implemented. 22268c2ecf20Sopenharmony_ci */ 22278c2ecf20Sopenharmony_ci if (efx_ptp_disable(efx) == 0) 22288c2ecf20Sopenharmony_ci efx->extra_channel_type[EFX_EXTRA_CHANNEL_PTP] = 22298c2ecf20Sopenharmony_ci &efx_ptp_channel_type; 22308c2ecf20Sopenharmony_ci} 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_civoid efx_ptp_start_datapath(struct efx_nic *efx) 22338c2ecf20Sopenharmony_ci{ 22348c2ecf20Sopenharmony_ci if (efx_ptp_restart(efx)) 22358c2ecf20Sopenharmony_ci netif_err(efx, drv, efx->net_dev, "Failed to restart PTP.\n"); 22368c2ecf20Sopenharmony_ci /* re-enable timestamping if it was previously enabled */ 22378c2ecf20Sopenharmony_ci if (efx->type->ptp_set_ts_sync_events) 22388c2ecf20Sopenharmony_ci efx->type->ptp_set_ts_sync_events(efx, true, true); 22398c2ecf20Sopenharmony_ci} 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_civoid efx_ptp_stop_datapath(struct efx_nic *efx) 22428c2ecf20Sopenharmony_ci{ 22438c2ecf20Sopenharmony_ci /* temporarily disable timestamping */ 22448c2ecf20Sopenharmony_ci if (efx->type->ptp_set_ts_sync_events) 22458c2ecf20Sopenharmony_ci efx->type->ptp_set_ts_sync_events(efx, false, true); 22468c2ecf20Sopenharmony_ci efx_ptp_stop(efx); 22478c2ecf20Sopenharmony_ci} 2248