18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2019 Intel Corporation */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include "igc.h" 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <linux/device.h> 88c2ecf20Sopenharmony_ci#include <linux/pci.h> 98c2ecf20Sopenharmony_ci#include <linux/ptp_classify.h> 108c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 118c2ecf20Sopenharmony_ci#include <linux/ktime.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define INCVALUE_MASK 0x7fffffff 148c2ecf20Sopenharmony_ci#define ISGN 0x80000000 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define IGC_SYSTIM_OVERFLOW_PERIOD (HZ * 60 * 9) 178c2ecf20Sopenharmony_ci#define IGC_PTP_TX_TIMEOUT (HZ * 15) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* SYSTIM read access for I225 */ 208c2ecf20Sopenharmony_civoid igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 238c2ecf20Sopenharmony_ci u32 sec, nsec; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci /* The timestamp is latched when SYSTIML is read. */ 268c2ecf20Sopenharmony_ci nsec = rd32(IGC_SYSTIML); 278c2ecf20Sopenharmony_ci sec = rd32(IGC_SYSTIMH); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci ts->tv_sec = sec; 308c2ecf20Sopenharmony_ci ts->tv_nsec = nsec; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void igc_ptp_write_i225(struct igc_adapter *adapter, 348c2ecf20Sopenharmony_ci const struct timespec64 *ts) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci wr32(IGC_SYSTIML, ts->tv_nsec); 398c2ecf20Sopenharmony_ci wr32(IGC_SYSTIMH, ts->tv_sec); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int igc_ptp_adjfine_i225(struct ptp_clock_info *ptp, long scaled_ppm) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct igc_adapter *igc = container_of(ptp, struct igc_adapter, 458c2ecf20Sopenharmony_ci ptp_caps); 468c2ecf20Sopenharmony_ci struct igc_hw *hw = &igc->hw; 478c2ecf20Sopenharmony_ci int neg_adj = 0; 488c2ecf20Sopenharmony_ci u64 rate; 498c2ecf20Sopenharmony_ci u32 inca; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (scaled_ppm < 0) { 528c2ecf20Sopenharmony_ci neg_adj = 1; 538c2ecf20Sopenharmony_ci scaled_ppm = -scaled_ppm; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci rate = scaled_ppm; 568c2ecf20Sopenharmony_ci rate <<= 14; 578c2ecf20Sopenharmony_ci rate = div_u64(rate, 78125); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci inca = rate & INCVALUE_MASK; 608c2ecf20Sopenharmony_ci if (neg_adj) 618c2ecf20Sopenharmony_ci inca |= ISGN; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci wr32(IGC_TIMINCA, inca); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int igc_ptp_adjtime_i225(struct ptp_clock_info *ptp, s64 delta) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct igc_adapter *igc = container_of(ptp, struct igc_adapter, 718c2ecf20Sopenharmony_ci ptp_caps); 728c2ecf20Sopenharmony_ci struct timespec64 now, then = ns_to_timespec64(delta); 738c2ecf20Sopenharmony_ci unsigned long flags; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci spin_lock_irqsave(&igc->tmreg_lock, flags); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci igc_ptp_read(igc, &now); 788c2ecf20Sopenharmony_ci now = timespec64_add(now, then); 798c2ecf20Sopenharmony_ci igc_ptp_write_i225(igc, (const struct timespec64 *)&now); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&igc->tmreg_lock, flags); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int igc_ptp_gettimex64_i225(struct ptp_clock_info *ptp, 878c2ecf20Sopenharmony_ci struct timespec64 *ts, 888c2ecf20Sopenharmony_ci struct ptp_system_timestamp *sts) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct igc_adapter *igc = container_of(ptp, struct igc_adapter, 918c2ecf20Sopenharmony_ci ptp_caps); 928c2ecf20Sopenharmony_ci struct igc_hw *hw = &igc->hw; 938c2ecf20Sopenharmony_ci unsigned long flags; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci spin_lock_irqsave(&igc->tmreg_lock, flags); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci ptp_read_system_prets(sts); 988c2ecf20Sopenharmony_ci ts->tv_nsec = rd32(IGC_SYSTIML); 998c2ecf20Sopenharmony_ci ts->tv_sec = rd32(IGC_SYSTIMH); 1008c2ecf20Sopenharmony_ci ptp_read_system_postts(sts); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&igc->tmreg_lock, flags); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int igc_ptp_settime_i225(struct ptp_clock_info *ptp, 1088c2ecf20Sopenharmony_ci const struct timespec64 *ts) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct igc_adapter *igc = container_of(ptp, struct igc_adapter, 1118c2ecf20Sopenharmony_ci ptp_caps); 1128c2ecf20Sopenharmony_ci unsigned long flags; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci spin_lock_irqsave(&igc->tmreg_lock, flags); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci igc_ptp_write_i225(igc, ts); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&igc->tmreg_lock, flags); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp, 1248c2ecf20Sopenharmony_ci struct ptp_clock_request *rq, int on) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/** 1308c2ecf20Sopenharmony_ci * igc_ptp_systim_to_hwtstamp - convert system time value to HW timestamp 1318c2ecf20Sopenharmony_ci * @adapter: board private structure 1328c2ecf20Sopenharmony_ci * @hwtstamps: timestamp structure to update 1338c2ecf20Sopenharmony_ci * @systim: unsigned 64bit system time value 1348c2ecf20Sopenharmony_ci * 1358c2ecf20Sopenharmony_ci * We need to convert the system time value stored in the RX/TXSTMP registers 1368c2ecf20Sopenharmony_ci * into a hwtstamp which can be used by the upper level timestamping functions. 1378c2ecf20Sopenharmony_ci * 1388c2ecf20Sopenharmony_ci * Returns 0 on success. 1398c2ecf20Sopenharmony_ci **/ 1408c2ecf20Sopenharmony_cistatic int igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter, 1418c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *hwtstamps, 1428c2ecf20Sopenharmony_ci u64 systim) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci switch (adapter->hw.mac.type) { 1458c2ecf20Sopenharmony_ci case igc_i225: 1468c2ecf20Sopenharmony_ci memset(hwtstamps, 0, sizeof(*hwtstamps)); 1478c2ecf20Sopenharmony_ci /* Upper 32 bits contain s, lower 32 bits contain ns. */ 1488c2ecf20Sopenharmony_ci hwtstamps->hwtstamp = ktime_set(systim >> 32, 1498c2ecf20Sopenharmony_ci systim & 0xFFFFFFFF); 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci default: 1528c2ecf20Sopenharmony_ci return -EINVAL; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/** 1588c2ecf20Sopenharmony_ci * igc_ptp_rx_pktstamp - Retrieve timestamp from Rx packet buffer 1598c2ecf20Sopenharmony_ci * @q_vector: Pointer to interrupt specific structure 1608c2ecf20Sopenharmony_ci * @va: Pointer to address containing Rx buffer 1618c2ecf20Sopenharmony_ci * @skb: Buffer containing timestamp and packet 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * This function retrieves the timestamp saved in the beginning of packet 1648c2ecf20Sopenharmony_ci * buffer. While two timestamps are available, one in timer0 reference and the 1658c2ecf20Sopenharmony_ci * other in timer1 reference, this function considers only the timestamp in 1668c2ecf20Sopenharmony_ci * timer0 reference. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_civoid igc_ptp_rx_pktstamp(struct igc_q_vector *q_vector, __le32 *va, 1698c2ecf20Sopenharmony_ci struct sk_buff *skb) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct igc_adapter *adapter = q_vector->adapter; 1728c2ecf20Sopenharmony_ci u64 regval; 1738c2ecf20Sopenharmony_ci int adjust; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Timestamps are saved in little endian at the beginning of the packet 1768c2ecf20Sopenharmony_ci * buffer following the layout: 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * DWORD: | 0 | 1 | 2 | 3 | 1798c2ecf20Sopenharmony_ci * Field: | Timer1 SYSTIML | Timer1 SYSTIMH | Timer0 SYSTIML | Timer0 SYSTIMH | 1808c2ecf20Sopenharmony_ci * 1818c2ecf20Sopenharmony_ci * SYSTIML holds the nanoseconds part while SYSTIMH holds the seconds 1828c2ecf20Sopenharmony_ci * part of the timestamp. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci regval = le32_to_cpu(va[2]); 1858c2ecf20Sopenharmony_ci regval |= (u64)le32_to_cpu(va[3]) << 32; 1868c2ecf20Sopenharmony_ci igc_ptp_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Adjust timestamp for the RX latency based on link speed */ 1898c2ecf20Sopenharmony_ci switch (adapter->link_speed) { 1908c2ecf20Sopenharmony_ci case SPEED_10: 1918c2ecf20Sopenharmony_ci adjust = IGC_I225_RX_LATENCY_10; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci case SPEED_100: 1948c2ecf20Sopenharmony_ci adjust = IGC_I225_RX_LATENCY_100; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case SPEED_1000: 1978c2ecf20Sopenharmony_ci adjust = IGC_I225_RX_LATENCY_1000; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case SPEED_2500: 2008c2ecf20Sopenharmony_ci adjust = IGC_I225_RX_LATENCY_2500; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci default: 2038c2ecf20Sopenharmony_ci adjust = 0; 2048c2ecf20Sopenharmony_ci netdev_warn_once(adapter->netdev, "Imprecise timestamp\n"); 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci skb_hwtstamps(skb)->hwtstamp = 2088c2ecf20Sopenharmony_ci ktime_sub_ns(skb_hwtstamps(skb)->hwtstamp, adjust); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void igc_ptp_disable_rx_timestamp(struct igc_adapter *adapter) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 2148c2ecf20Sopenharmony_ci u32 val; 2158c2ecf20Sopenharmony_ci int i; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci wr32(IGC_TSYNCRXCTL, 0); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 2208c2ecf20Sopenharmony_ci val = rd32(IGC_SRRCTL(i)); 2218c2ecf20Sopenharmony_ci val &= ~IGC_SRRCTL_TIMESTAMP; 2228c2ecf20Sopenharmony_ci wr32(IGC_SRRCTL(i), val); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci val = rd32(IGC_RXPBS); 2268c2ecf20Sopenharmony_ci val &= ~IGC_RXPBS_CFG_TS_EN; 2278c2ecf20Sopenharmony_ci wr32(IGC_RXPBS, val); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void igc_ptp_enable_rx_timestamp(struct igc_adapter *adapter) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 2338c2ecf20Sopenharmony_ci u32 val; 2348c2ecf20Sopenharmony_ci int i; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci val = rd32(IGC_RXPBS); 2378c2ecf20Sopenharmony_ci val |= IGC_RXPBS_CFG_TS_EN; 2388c2ecf20Sopenharmony_ci wr32(IGC_RXPBS, val); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 2418c2ecf20Sopenharmony_ci val = rd32(IGC_SRRCTL(i)); 2428c2ecf20Sopenharmony_ci /* FIXME: For now, only support retrieving RX timestamps from 2438c2ecf20Sopenharmony_ci * timer 0. 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci val |= IGC_SRRCTL_TIMER1SEL(0) | IGC_SRRCTL_TIMER0SEL(0) | 2468c2ecf20Sopenharmony_ci IGC_SRRCTL_TIMESTAMP; 2478c2ecf20Sopenharmony_ci wr32(IGC_SRRCTL(i), val); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci val = IGC_TSYNCRXCTL_ENABLED | IGC_TSYNCRXCTL_TYPE_ALL | 2518c2ecf20Sopenharmony_ci IGC_TSYNCRXCTL_RXSYNSIG; 2528c2ecf20Sopenharmony_ci wr32(IGC_TSYNCRXCTL, val); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci wr32(IGC_TSYNCTXCTL, 0); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic void igc_ptp_enable_tx_timestamp(struct igc_adapter *adapter) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci wr32(IGC_TSYNCTXCTL, IGC_TSYNCTXCTL_ENABLED | IGC_TSYNCTXCTL_TXSYNSIG); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Read TXSTMP registers to discard any timestamp previously stored. */ 2698c2ecf20Sopenharmony_ci rd32(IGC_TXSTMPL); 2708c2ecf20Sopenharmony_ci rd32(IGC_TXSTMPH); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/** 2748c2ecf20Sopenharmony_ci * igc_ptp_set_timestamp_mode - setup hardware for timestamping 2758c2ecf20Sopenharmony_ci * @adapter: networking device structure 2768c2ecf20Sopenharmony_ci * @config: hwtstamp configuration 2778c2ecf20Sopenharmony_ci * 2788c2ecf20Sopenharmony_ci * Return: 0 in case of success, negative errno code otherwise. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_cistatic int igc_ptp_set_timestamp_mode(struct igc_adapter *adapter, 2818c2ecf20Sopenharmony_ci struct hwtstamp_config *config) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci /* reserved for future extensions */ 2848c2ecf20Sopenharmony_ci if (config->flags) 2858c2ecf20Sopenharmony_ci return -EINVAL; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci switch (config->tx_type) { 2888c2ecf20Sopenharmony_ci case HWTSTAMP_TX_OFF: 2898c2ecf20Sopenharmony_ci igc_ptp_disable_tx_timestamp(adapter); 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci case HWTSTAMP_TX_ON: 2928c2ecf20Sopenharmony_ci igc_ptp_enable_tx_timestamp(adapter); 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci default: 2958c2ecf20Sopenharmony_ci return -ERANGE; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci switch (config->rx_filter) { 2998c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 3008c2ecf20Sopenharmony_ci igc_ptp_disable_rx_timestamp(adapter); 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 3038c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 3048c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 3058c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 3068c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 3078c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 3088c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 3098c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 3108c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 3118c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 3128c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 3138c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 3148c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_NTP_ALL: 3158c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 3168c2ecf20Sopenharmony_ci igc_ptp_enable_rx_timestamp(adapter); 3178c2ecf20Sopenharmony_ci config->rx_filter = HWTSTAMP_FILTER_ALL; 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci default: 3208c2ecf20Sopenharmony_ci return -ERANGE; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return 0; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/* Requires adapter->ptp_tx_lock held by caller. */ 3278c2ecf20Sopenharmony_cistatic void igc_ptp_tx_timeout(struct igc_adapter *adapter) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci dev_kfree_skb_any(adapter->ptp_tx_skb); 3328c2ecf20Sopenharmony_ci adapter->ptp_tx_skb = NULL; 3338c2ecf20Sopenharmony_ci adapter->tx_hwtstamp_timeouts++; 3348c2ecf20Sopenharmony_ci /* Clear the tx valid bit in TSYNCTXCTL register to enable interrupt. */ 3358c2ecf20Sopenharmony_ci rd32(IGC_TXSTMPH); 3368c2ecf20Sopenharmony_ci netdev_warn(adapter->netdev, "Tx timestamp timeout\n"); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_civoid igc_ptp_tx_hang(struct igc_adapter *adapter) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci unsigned long flags; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->ptp_tx_lock, flags); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (!adapter->ptp_tx_skb) 3468c2ecf20Sopenharmony_ci goto unlock; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (time_is_after_jiffies(adapter->ptp_tx_start + IGC_PTP_TX_TIMEOUT)) 3498c2ecf20Sopenharmony_ci goto unlock; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci igc_ptp_tx_timeout(adapter); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ciunlock: 3548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/** 3588c2ecf20Sopenharmony_ci * igc_ptp_tx_hwtstamp - utility function which checks for TX time stamp 3598c2ecf20Sopenharmony_ci * @adapter: Board private structure 3608c2ecf20Sopenharmony_ci * 3618c2ecf20Sopenharmony_ci * If we were asked to do hardware stamping and such a time stamp is 3628c2ecf20Sopenharmony_ci * available, then it must have been for this skb here because we only 3638c2ecf20Sopenharmony_ci * allow only one such packet into the queue. 3648c2ecf20Sopenharmony_ci * 3658c2ecf20Sopenharmony_ci * Context: Expects adapter->ptp_tx_lock to be held by caller. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_cistatic void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct sk_buff *skb = adapter->ptp_tx_skb; 3708c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 3718c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 3728c2ecf20Sopenharmony_ci int adjust = 0; 3738c2ecf20Sopenharmony_ci u64 regval; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!skb)) 3768c2ecf20Sopenharmony_ci return; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci regval = rd32(IGC_TXSTMPL); 3798c2ecf20Sopenharmony_ci regval |= (u64)rd32(IGC_TXSTMPH) << 32; 3808c2ecf20Sopenharmony_ci if (igc_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval)) 3818c2ecf20Sopenharmony_ci return; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci switch (adapter->link_speed) { 3848c2ecf20Sopenharmony_ci case SPEED_10: 3858c2ecf20Sopenharmony_ci adjust = IGC_I225_TX_LATENCY_10; 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci case SPEED_100: 3888c2ecf20Sopenharmony_ci adjust = IGC_I225_TX_LATENCY_100; 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci case SPEED_1000: 3918c2ecf20Sopenharmony_ci adjust = IGC_I225_TX_LATENCY_1000; 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci case SPEED_2500: 3948c2ecf20Sopenharmony_ci adjust = IGC_I225_TX_LATENCY_2500; 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci shhwtstamps.hwtstamp = 3998c2ecf20Sopenharmony_ci ktime_add_ns(shhwtstamps.hwtstamp, adjust); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci adapter->ptp_tx_skb = NULL; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Notify the stack and free the skb after we've unlocked */ 4048c2ecf20Sopenharmony_ci skb_tstamp_tx(skb, &shhwtstamps); 4058c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci/** 4098c2ecf20Sopenharmony_ci * igc_ptp_tx_work 4108c2ecf20Sopenharmony_ci * @work: pointer to work struct 4118c2ecf20Sopenharmony_ci * 4128c2ecf20Sopenharmony_ci * This work function checks the TSYNCTXCTL valid bit to determine when 4138c2ecf20Sopenharmony_ci * a timestamp has been taken for the current stored skb. 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_cistatic void igc_ptp_tx_work(struct work_struct *work) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct igc_adapter *adapter = container_of(work, struct igc_adapter, 4188c2ecf20Sopenharmony_ci ptp_tx_work); 4198c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 4208c2ecf20Sopenharmony_ci unsigned long flags; 4218c2ecf20Sopenharmony_ci u32 tsynctxctl; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->ptp_tx_lock, flags); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (!adapter->ptp_tx_skb) 4268c2ecf20Sopenharmony_ci goto unlock; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci tsynctxctl = rd32(IGC_TSYNCTXCTL); 4298c2ecf20Sopenharmony_ci tsynctxctl &= IGC_TSYNCTXCTL_TXTT_0; 4308c2ecf20Sopenharmony_ci if (!tsynctxctl) { 4318c2ecf20Sopenharmony_ci WARN_ONCE(1, "Received a TSTAMP interrupt but no TSTAMP is ready.\n"); 4328c2ecf20Sopenharmony_ci goto unlock; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci igc_ptp_tx_hwtstamp(adapter); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ciunlock: 4388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags); 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/** 4428c2ecf20Sopenharmony_ci * igc_ptp_set_ts_config - set hardware time stamping config 4438c2ecf20Sopenharmony_ci * @netdev: network interface device structure 4448c2ecf20Sopenharmony_ci * @ifr: interface request data 4458c2ecf20Sopenharmony_ci * 4468c2ecf20Sopenharmony_ci **/ 4478c2ecf20Sopenharmony_ciint igc_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct igc_adapter *adapter = netdev_priv(netdev); 4508c2ecf20Sopenharmony_ci struct hwtstamp_config config; 4518c2ecf20Sopenharmony_ci int err; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 4548c2ecf20Sopenharmony_ci return -EFAULT; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci err = igc_ptp_set_timestamp_mode(adapter, &config); 4578c2ecf20Sopenharmony_ci if (err) 4588c2ecf20Sopenharmony_ci return err; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* save these settings for future reference */ 4618c2ecf20Sopenharmony_ci memcpy(&adapter->tstamp_config, &config, 4628c2ecf20Sopenharmony_ci sizeof(adapter->tstamp_config)); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? 4658c2ecf20Sopenharmony_ci -EFAULT : 0; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci/** 4698c2ecf20Sopenharmony_ci * igc_ptp_get_ts_config - get hardware time stamping config 4708c2ecf20Sopenharmony_ci * @netdev: network interface device structure 4718c2ecf20Sopenharmony_ci * @ifr: interface request data 4728c2ecf20Sopenharmony_ci * 4738c2ecf20Sopenharmony_ci * Get the hwtstamp_config settings to return to the user. Rather than attempt 4748c2ecf20Sopenharmony_ci * to deconstruct the settings from the registers, just return a shadow copy 4758c2ecf20Sopenharmony_ci * of the last known settings. 4768c2ecf20Sopenharmony_ci **/ 4778c2ecf20Sopenharmony_ciint igc_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct igc_adapter *adapter = netdev_priv(netdev); 4808c2ecf20Sopenharmony_ci struct hwtstamp_config *config = &adapter->tstamp_config; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ? 4838c2ecf20Sopenharmony_ci -EFAULT : 0; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/** 4878c2ecf20Sopenharmony_ci * igc_ptp_init - Initialize PTP functionality 4888c2ecf20Sopenharmony_ci * @adapter: Board private structure 4898c2ecf20Sopenharmony_ci * 4908c2ecf20Sopenharmony_ci * This function is called at device probe to initialize the PTP 4918c2ecf20Sopenharmony_ci * functionality. 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_civoid igc_ptp_init(struct igc_adapter *adapter) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 4968c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci switch (hw->mac.type) { 4998c2ecf20Sopenharmony_ci case igc_i225: 5008c2ecf20Sopenharmony_ci snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr); 5018c2ecf20Sopenharmony_ci adapter->ptp_caps.owner = THIS_MODULE; 5028c2ecf20Sopenharmony_ci adapter->ptp_caps.max_adj = 62499999; 5038c2ecf20Sopenharmony_ci adapter->ptp_caps.adjfine = igc_ptp_adjfine_i225; 5048c2ecf20Sopenharmony_ci adapter->ptp_caps.adjtime = igc_ptp_adjtime_i225; 5058c2ecf20Sopenharmony_ci adapter->ptp_caps.gettimex64 = igc_ptp_gettimex64_i225; 5068c2ecf20Sopenharmony_ci adapter->ptp_caps.settime64 = igc_ptp_settime_i225; 5078c2ecf20Sopenharmony_ci adapter->ptp_caps.enable = igc_ptp_feature_enable_i225; 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci default: 5108c2ecf20Sopenharmony_ci adapter->ptp_clock = NULL; 5118c2ecf20Sopenharmony_ci return; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci spin_lock_init(&adapter->ptp_tx_lock); 5158c2ecf20Sopenharmony_ci spin_lock_init(&adapter->tmreg_lock); 5168c2ecf20Sopenharmony_ci INIT_WORK(&adapter->ptp_tx_work, igc_ptp_tx_work); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; 5198c2ecf20Sopenharmony_ci adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci adapter->prev_ptp_time = ktime_to_timespec64(ktime_get_real()); 5228c2ecf20Sopenharmony_ci adapter->ptp_reset_start = ktime_get(); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps, 5258c2ecf20Sopenharmony_ci &adapter->pdev->dev); 5268c2ecf20Sopenharmony_ci if (IS_ERR(adapter->ptp_clock)) { 5278c2ecf20Sopenharmony_ci adapter->ptp_clock = NULL; 5288c2ecf20Sopenharmony_ci netdev_err(netdev, "ptp_clock_register failed\n"); 5298c2ecf20Sopenharmony_ci } else if (adapter->ptp_clock) { 5308c2ecf20Sopenharmony_ci netdev_info(netdev, "PHC added\n"); 5318c2ecf20Sopenharmony_ci adapter->ptp_flags |= IGC_PTP_ENABLED; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic void igc_ptp_time_save(struct igc_adapter *adapter) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci igc_ptp_read(adapter, &adapter->prev_ptp_time); 5388c2ecf20Sopenharmony_ci adapter->ptp_reset_start = ktime_get(); 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic void igc_ptp_time_restore(struct igc_adapter *adapter) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct timespec64 ts = adapter->prev_ptp_time; 5448c2ecf20Sopenharmony_ci ktime_t delta; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci delta = ktime_sub(ktime_get(), adapter->ptp_reset_start); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci timespec64_add_ns(&ts, ktime_to_ns(delta)); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci igc_ptp_write_i225(adapter, &ts); 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci/** 5548c2ecf20Sopenharmony_ci * igc_ptp_suspend - Disable PTP work items and prepare for suspend 5558c2ecf20Sopenharmony_ci * @adapter: Board private structure 5568c2ecf20Sopenharmony_ci * 5578c2ecf20Sopenharmony_ci * This function stops the overflow check work and PTP Tx timestamp work, and 5588c2ecf20Sopenharmony_ci * will prepare the device for OS suspend. 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_civoid igc_ptp_suspend(struct igc_adapter *adapter) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci if (!(adapter->ptp_flags & IGC_PTP_ENABLED)) 5638c2ecf20Sopenharmony_ci return; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci cancel_work_sync(&adapter->ptp_tx_work); 5668c2ecf20Sopenharmony_ci dev_kfree_skb_any(adapter->ptp_tx_skb); 5678c2ecf20Sopenharmony_ci adapter->ptp_tx_skb = NULL; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (pci_device_is_present(adapter->pdev)) 5708c2ecf20Sopenharmony_ci igc_ptp_time_save(adapter); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci/** 5748c2ecf20Sopenharmony_ci * igc_ptp_stop - Disable PTP device and stop the overflow check. 5758c2ecf20Sopenharmony_ci * @adapter: Board private structure. 5768c2ecf20Sopenharmony_ci * 5778c2ecf20Sopenharmony_ci * This function stops the PTP support and cancels the delayed work. 5788c2ecf20Sopenharmony_ci **/ 5798c2ecf20Sopenharmony_civoid igc_ptp_stop(struct igc_adapter *adapter) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci igc_ptp_suspend(adapter); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (adapter->ptp_clock) { 5848c2ecf20Sopenharmony_ci ptp_clock_unregister(adapter->ptp_clock); 5858c2ecf20Sopenharmony_ci netdev_info(adapter->netdev, "PHC removed\n"); 5868c2ecf20Sopenharmony_ci adapter->ptp_flags &= ~IGC_PTP_ENABLED; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/** 5918c2ecf20Sopenharmony_ci * igc_ptp_reset - Re-enable the adapter for PTP following a reset. 5928c2ecf20Sopenharmony_ci * @adapter: Board private structure. 5938c2ecf20Sopenharmony_ci * 5948c2ecf20Sopenharmony_ci * This function handles the reset work required to re-enable the PTP device. 5958c2ecf20Sopenharmony_ci **/ 5968c2ecf20Sopenharmony_civoid igc_ptp_reset(struct igc_adapter *adapter) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 5998c2ecf20Sopenharmony_ci unsigned long flags; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* reset the tstamp_config */ 6028c2ecf20Sopenharmony_ci igc_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->tmreg_lock, flags); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci switch (adapter->hw.mac.type) { 6078c2ecf20Sopenharmony_ci case igc_i225: 6088c2ecf20Sopenharmony_ci wr32(IGC_TSAUXC, 0x0); 6098c2ecf20Sopenharmony_ci wr32(IGC_TSSDP, 0x0); 6108c2ecf20Sopenharmony_ci wr32(IGC_TSIM, IGC_TSICR_INTERRUPTS); 6118c2ecf20Sopenharmony_ci wr32(IGC_IMS, IGC_IMS_TS); 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci default: 6148c2ecf20Sopenharmony_ci /* No work to do. */ 6158c2ecf20Sopenharmony_ci goto out; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci /* Re-initialize the timer. */ 6198c2ecf20Sopenharmony_ci if (hw->mac.type == igc_i225) { 6208c2ecf20Sopenharmony_ci igc_ptp_time_restore(adapter); 6218c2ecf20Sopenharmony_ci } else { 6228c2ecf20Sopenharmony_ci timecounter_init(&adapter->tc, &adapter->cc, 6238c2ecf20Sopenharmony_ci ktime_to_ns(ktime_get_real())); 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ciout: 6268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->tmreg_lock, flags); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci wrfl(); 6298c2ecf20Sopenharmony_ci} 630