162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019 Intel Corporation */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include "igc.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/device.h> 862306a36Sopenharmony_ci#include <linux/pci.h> 962306a36Sopenharmony_ci#include <linux/ptp_classify.h> 1062306a36Sopenharmony_ci#include <linux/clocksource.h> 1162306a36Sopenharmony_ci#include <linux/ktime.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/iopoll.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define INCVALUE_MASK 0x7fffffff 1662306a36Sopenharmony_ci#define ISGN 0x80000000 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define IGC_PTP_TX_TIMEOUT (HZ * 15) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define IGC_PTM_STAT_SLEEP 2 2162306a36Sopenharmony_ci#define IGC_PTM_STAT_TIMEOUT 100 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* SYSTIM read access for I225 */ 2462306a36Sopenharmony_civoid igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 2762306a36Sopenharmony_ci u32 sec, nsec; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci /* The timestamp is latched when SYSTIML is read. */ 3062306a36Sopenharmony_ci nsec = rd32(IGC_SYSTIML); 3162306a36Sopenharmony_ci sec = rd32(IGC_SYSTIMH); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci ts->tv_sec = sec; 3462306a36Sopenharmony_ci ts->tv_nsec = nsec; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void igc_ptp_write_i225(struct igc_adapter *adapter, 3862306a36Sopenharmony_ci const struct timespec64 *ts) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci wr32(IGC_SYSTIML, ts->tv_nsec); 4362306a36Sopenharmony_ci wr32(IGC_SYSTIMH, ts->tv_sec); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int igc_ptp_adjfine_i225(struct ptp_clock_info *ptp, long scaled_ppm) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct igc_adapter *igc = container_of(ptp, struct igc_adapter, 4962306a36Sopenharmony_ci ptp_caps); 5062306a36Sopenharmony_ci struct igc_hw *hw = &igc->hw; 5162306a36Sopenharmony_ci int neg_adj = 0; 5262306a36Sopenharmony_ci u64 rate; 5362306a36Sopenharmony_ci u32 inca; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (scaled_ppm < 0) { 5662306a36Sopenharmony_ci neg_adj = 1; 5762306a36Sopenharmony_ci scaled_ppm = -scaled_ppm; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci rate = scaled_ppm; 6062306a36Sopenharmony_ci rate <<= 14; 6162306a36Sopenharmony_ci rate = div_u64(rate, 78125); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci inca = rate & INCVALUE_MASK; 6462306a36Sopenharmony_ci if (neg_adj) 6562306a36Sopenharmony_ci inca |= ISGN; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci wr32(IGC_TIMINCA, inca); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int igc_ptp_adjtime_i225(struct ptp_clock_info *ptp, s64 delta) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct igc_adapter *igc = container_of(ptp, struct igc_adapter, 7562306a36Sopenharmony_ci ptp_caps); 7662306a36Sopenharmony_ci struct timespec64 now, then = ns_to_timespec64(delta); 7762306a36Sopenharmony_ci unsigned long flags; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci spin_lock_irqsave(&igc->tmreg_lock, flags); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci igc_ptp_read(igc, &now); 8262306a36Sopenharmony_ci now = timespec64_add(now, then); 8362306a36Sopenharmony_ci igc_ptp_write_i225(igc, (const struct timespec64 *)&now); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci spin_unlock_irqrestore(&igc->tmreg_lock, flags); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int igc_ptp_gettimex64_i225(struct ptp_clock_info *ptp, 9162306a36Sopenharmony_ci struct timespec64 *ts, 9262306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct igc_adapter *igc = container_of(ptp, struct igc_adapter, 9562306a36Sopenharmony_ci ptp_caps); 9662306a36Sopenharmony_ci struct igc_hw *hw = &igc->hw; 9762306a36Sopenharmony_ci unsigned long flags; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci spin_lock_irqsave(&igc->tmreg_lock, flags); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci ptp_read_system_prets(sts); 10262306a36Sopenharmony_ci ts->tv_nsec = rd32(IGC_SYSTIML); 10362306a36Sopenharmony_ci ts->tv_sec = rd32(IGC_SYSTIMH); 10462306a36Sopenharmony_ci ptp_read_system_postts(sts); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci spin_unlock_irqrestore(&igc->tmreg_lock, flags); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int igc_ptp_settime_i225(struct ptp_clock_info *ptp, 11262306a36Sopenharmony_ci const struct timespec64 *ts) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct igc_adapter *igc = container_of(ptp, struct igc_adapter, 11562306a36Sopenharmony_ci ptp_caps); 11662306a36Sopenharmony_ci unsigned long flags; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci spin_lock_irqsave(&igc->tmreg_lock, flags); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci igc_ptp_write_i225(igc, ts); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci spin_unlock_irqrestore(&igc->tmreg_lock, flags); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void igc_pin_direction(int pin, int input, u32 *ctrl, u32 *ctrl_ext) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci u32 *ptr = pin < 2 ? ctrl : ctrl_ext; 13062306a36Sopenharmony_ci static const u32 mask[IGC_N_SDP] = { 13162306a36Sopenharmony_ci IGC_CTRL_SDP0_DIR, 13262306a36Sopenharmony_ci IGC_CTRL_SDP1_DIR, 13362306a36Sopenharmony_ci IGC_CTRL_EXT_SDP2_DIR, 13462306a36Sopenharmony_ci IGC_CTRL_EXT_SDP3_DIR, 13562306a36Sopenharmony_ci }; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (input) 13862306a36Sopenharmony_ci *ptr &= ~mask[pin]; 13962306a36Sopenharmony_ci else 14062306a36Sopenharmony_ci *ptr |= mask[pin]; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void igc_pin_perout(struct igc_adapter *igc, int chan, int pin, int freq) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci static const u32 igc_aux0_sel_sdp[IGC_N_SDP] = { 14662306a36Sopenharmony_ci IGC_AUX0_SEL_SDP0, IGC_AUX0_SEL_SDP1, IGC_AUX0_SEL_SDP2, IGC_AUX0_SEL_SDP3, 14762306a36Sopenharmony_ci }; 14862306a36Sopenharmony_ci static const u32 igc_aux1_sel_sdp[IGC_N_SDP] = { 14962306a36Sopenharmony_ci IGC_AUX1_SEL_SDP0, IGC_AUX1_SEL_SDP1, IGC_AUX1_SEL_SDP2, IGC_AUX1_SEL_SDP3, 15062306a36Sopenharmony_ci }; 15162306a36Sopenharmony_ci static const u32 igc_ts_sdp_en[IGC_N_SDP] = { 15262306a36Sopenharmony_ci IGC_TS_SDP0_EN, IGC_TS_SDP1_EN, IGC_TS_SDP2_EN, IGC_TS_SDP3_EN, 15362306a36Sopenharmony_ci }; 15462306a36Sopenharmony_ci static const u32 igc_ts_sdp_sel_tt0[IGC_N_SDP] = { 15562306a36Sopenharmony_ci IGC_TS_SDP0_SEL_TT0, IGC_TS_SDP1_SEL_TT0, 15662306a36Sopenharmony_ci IGC_TS_SDP2_SEL_TT0, IGC_TS_SDP3_SEL_TT0, 15762306a36Sopenharmony_ci }; 15862306a36Sopenharmony_ci static const u32 igc_ts_sdp_sel_tt1[IGC_N_SDP] = { 15962306a36Sopenharmony_ci IGC_TS_SDP0_SEL_TT1, IGC_TS_SDP1_SEL_TT1, 16062306a36Sopenharmony_ci IGC_TS_SDP2_SEL_TT1, IGC_TS_SDP3_SEL_TT1, 16162306a36Sopenharmony_ci }; 16262306a36Sopenharmony_ci static const u32 igc_ts_sdp_sel_fc0[IGC_N_SDP] = { 16362306a36Sopenharmony_ci IGC_TS_SDP0_SEL_FC0, IGC_TS_SDP1_SEL_FC0, 16462306a36Sopenharmony_ci IGC_TS_SDP2_SEL_FC0, IGC_TS_SDP3_SEL_FC0, 16562306a36Sopenharmony_ci }; 16662306a36Sopenharmony_ci static const u32 igc_ts_sdp_sel_fc1[IGC_N_SDP] = { 16762306a36Sopenharmony_ci IGC_TS_SDP0_SEL_FC1, IGC_TS_SDP1_SEL_FC1, 16862306a36Sopenharmony_ci IGC_TS_SDP2_SEL_FC1, IGC_TS_SDP3_SEL_FC1, 16962306a36Sopenharmony_ci }; 17062306a36Sopenharmony_ci static const u32 igc_ts_sdp_sel_clr[IGC_N_SDP] = { 17162306a36Sopenharmony_ci IGC_TS_SDP0_SEL_FC1, IGC_TS_SDP1_SEL_FC1, 17262306a36Sopenharmony_ci IGC_TS_SDP2_SEL_FC1, IGC_TS_SDP3_SEL_FC1, 17362306a36Sopenharmony_ci }; 17462306a36Sopenharmony_ci struct igc_hw *hw = &igc->hw; 17562306a36Sopenharmony_ci u32 ctrl, ctrl_ext, tssdp = 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ctrl = rd32(IGC_CTRL); 17862306a36Sopenharmony_ci ctrl_ext = rd32(IGC_CTRL_EXT); 17962306a36Sopenharmony_ci tssdp = rd32(IGC_TSSDP); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci igc_pin_direction(pin, 0, &ctrl, &ctrl_ext); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Make sure this pin is not enabled as an input. */ 18462306a36Sopenharmony_ci if ((tssdp & IGC_AUX0_SEL_SDP3) == igc_aux0_sel_sdp[pin]) 18562306a36Sopenharmony_ci tssdp &= ~IGC_AUX0_TS_SDP_EN; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if ((tssdp & IGC_AUX1_SEL_SDP3) == igc_aux1_sel_sdp[pin]) 18862306a36Sopenharmony_ci tssdp &= ~IGC_AUX1_TS_SDP_EN; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci tssdp &= ~igc_ts_sdp_sel_clr[pin]; 19162306a36Sopenharmony_ci if (freq) { 19262306a36Sopenharmony_ci if (chan == 1) 19362306a36Sopenharmony_ci tssdp |= igc_ts_sdp_sel_fc1[pin]; 19462306a36Sopenharmony_ci else 19562306a36Sopenharmony_ci tssdp |= igc_ts_sdp_sel_fc0[pin]; 19662306a36Sopenharmony_ci } else { 19762306a36Sopenharmony_ci if (chan == 1) 19862306a36Sopenharmony_ci tssdp |= igc_ts_sdp_sel_tt1[pin]; 19962306a36Sopenharmony_ci else 20062306a36Sopenharmony_ci tssdp |= igc_ts_sdp_sel_tt0[pin]; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci tssdp |= igc_ts_sdp_en[pin]; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci wr32(IGC_TSSDP, tssdp); 20562306a36Sopenharmony_ci wr32(IGC_CTRL, ctrl); 20662306a36Sopenharmony_ci wr32(IGC_CTRL_EXT, ctrl_ext); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic void igc_pin_extts(struct igc_adapter *igc, int chan, int pin) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci static const u32 igc_aux0_sel_sdp[IGC_N_SDP] = { 21262306a36Sopenharmony_ci IGC_AUX0_SEL_SDP0, IGC_AUX0_SEL_SDP1, IGC_AUX0_SEL_SDP2, IGC_AUX0_SEL_SDP3, 21362306a36Sopenharmony_ci }; 21462306a36Sopenharmony_ci static const u32 igc_aux1_sel_sdp[IGC_N_SDP] = { 21562306a36Sopenharmony_ci IGC_AUX1_SEL_SDP0, IGC_AUX1_SEL_SDP1, IGC_AUX1_SEL_SDP2, IGC_AUX1_SEL_SDP3, 21662306a36Sopenharmony_ci }; 21762306a36Sopenharmony_ci static const u32 igc_ts_sdp_en[IGC_N_SDP] = { 21862306a36Sopenharmony_ci IGC_TS_SDP0_EN, IGC_TS_SDP1_EN, IGC_TS_SDP2_EN, IGC_TS_SDP3_EN, 21962306a36Sopenharmony_ci }; 22062306a36Sopenharmony_ci struct igc_hw *hw = &igc->hw; 22162306a36Sopenharmony_ci u32 ctrl, ctrl_ext, tssdp = 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ctrl = rd32(IGC_CTRL); 22462306a36Sopenharmony_ci ctrl_ext = rd32(IGC_CTRL_EXT); 22562306a36Sopenharmony_ci tssdp = rd32(IGC_TSSDP); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci igc_pin_direction(pin, 1, &ctrl, &ctrl_ext); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Make sure this pin is not enabled as an output. */ 23062306a36Sopenharmony_ci tssdp &= ~igc_ts_sdp_en[pin]; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (chan == 1) { 23362306a36Sopenharmony_ci tssdp &= ~IGC_AUX1_SEL_SDP3; 23462306a36Sopenharmony_ci tssdp |= igc_aux1_sel_sdp[pin] | IGC_AUX1_TS_SDP_EN; 23562306a36Sopenharmony_ci } else { 23662306a36Sopenharmony_ci tssdp &= ~IGC_AUX0_SEL_SDP3; 23762306a36Sopenharmony_ci tssdp |= igc_aux0_sel_sdp[pin] | IGC_AUX0_TS_SDP_EN; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci wr32(IGC_TSSDP, tssdp); 24162306a36Sopenharmony_ci wr32(IGC_CTRL, ctrl); 24262306a36Sopenharmony_ci wr32(IGC_CTRL_EXT, ctrl_ext); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp, 24662306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct igc_adapter *igc = 24962306a36Sopenharmony_ci container_of(ptp, struct igc_adapter, ptp_caps); 25062306a36Sopenharmony_ci struct igc_hw *hw = &igc->hw; 25162306a36Sopenharmony_ci unsigned long flags; 25262306a36Sopenharmony_ci struct timespec64 ts; 25362306a36Sopenharmony_ci int use_freq = 0, pin = -1; 25462306a36Sopenharmony_ci u32 tsim, tsauxc, tsauxc_mask, tsim_mask, trgttiml, trgttimh, freqout; 25562306a36Sopenharmony_ci s64 ns; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci switch (rq->type) { 25862306a36Sopenharmony_ci case PTP_CLK_REQ_EXTTS: 25962306a36Sopenharmony_ci /* Reject requests with unsupported flags */ 26062306a36Sopenharmony_ci if (rq->extts.flags & ~(PTP_ENABLE_FEATURE | 26162306a36Sopenharmony_ci PTP_RISING_EDGE | 26262306a36Sopenharmony_ci PTP_FALLING_EDGE | 26362306a36Sopenharmony_ci PTP_STRICT_FLAGS)) 26462306a36Sopenharmony_ci return -EOPNOTSUPP; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Reject requests failing to enable both edges. */ 26762306a36Sopenharmony_ci if ((rq->extts.flags & PTP_STRICT_FLAGS) && 26862306a36Sopenharmony_ci (rq->extts.flags & PTP_ENABLE_FEATURE) && 26962306a36Sopenharmony_ci (rq->extts.flags & PTP_EXTTS_EDGES) != PTP_EXTTS_EDGES) 27062306a36Sopenharmony_ci return -EOPNOTSUPP; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (on) { 27362306a36Sopenharmony_ci pin = ptp_find_pin(igc->ptp_clock, PTP_PF_EXTTS, 27462306a36Sopenharmony_ci rq->extts.index); 27562306a36Sopenharmony_ci if (pin < 0) 27662306a36Sopenharmony_ci return -EBUSY; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci if (rq->extts.index == 1) { 27962306a36Sopenharmony_ci tsauxc_mask = IGC_TSAUXC_EN_TS1; 28062306a36Sopenharmony_ci tsim_mask = IGC_TSICR_AUTT1; 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci tsauxc_mask = IGC_TSAUXC_EN_TS0; 28362306a36Sopenharmony_ci tsim_mask = IGC_TSICR_AUTT0; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci spin_lock_irqsave(&igc->tmreg_lock, flags); 28662306a36Sopenharmony_ci tsauxc = rd32(IGC_TSAUXC); 28762306a36Sopenharmony_ci tsim = rd32(IGC_TSIM); 28862306a36Sopenharmony_ci if (on) { 28962306a36Sopenharmony_ci igc_pin_extts(igc, rq->extts.index, pin); 29062306a36Sopenharmony_ci tsauxc |= tsauxc_mask; 29162306a36Sopenharmony_ci tsim |= tsim_mask; 29262306a36Sopenharmony_ci } else { 29362306a36Sopenharmony_ci tsauxc &= ~tsauxc_mask; 29462306a36Sopenharmony_ci tsim &= ~tsim_mask; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci wr32(IGC_TSAUXC, tsauxc); 29762306a36Sopenharmony_ci wr32(IGC_TSIM, tsim); 29862306a36Sopenharmony_ci spin_unlock_irqrestore(&igc->tmreg_lock, flags); 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci case PTP_CLK_REQ_PEROUT: 30262306a36Sopenharmony_ci /* Reject requests with unsupported flags */ 30362306a36Sopenharmony_ci if (rq->perout.flags) 30462306a36Sopenharmony_ci return -EOPNOTSUPP; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (on) { 30762306a36Sopenharmony_ci pin = ptp_find_pin(igc->ptp_clock, PTP_PF_PEROUT, 30862306a36Sopenharmony_ci rq->perout.index); 30962306a36Sopenharmony_ci if (pin < 0) 31062306a36Sopenharmony_ci return -EBUSY; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci ts.tv_sec = rq->perout.period.sec; 31362306a36Sopenharmony_ci ts.tv_nsec = rq->perout.period.nsec; 31462306a36Sopenharmony_ci ns = timespec64_to_ns(&ts); 31562306a36Sopenharmony_ci ns = ns >> 1; 31662306a36Sopenharmony_ci if (on && (ns <= 70000000LL || ns == 125000000LL || 31762306a36Sopenharmony_ci ns == 250000000LL || ns == 500000000LL)) { 31862306a36Sopenharmony_ci if (ns < 8LL) 31962306a36Sopenharmony_ci return -EINVAL; 32062306a36Sopenharmony_ci use_freq = 1; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci ts = ns_to_timespec64(ns); 32362306a36Sopenharmony_ci if (rq->perout.index == 1) { 32462306a36Sopenharmony_ci if (use_freq) { 32562306a36Sopenharmony_ci tsauxc_mask = IGC_TSAUXC_EN_CLK1 | IGC_TSAUXC_ST1; 32662306a36Sopenharmony_ci tsim_mask = 0; 32762306a36Sopenharmony_ci } else { 32862306a36Sopenharmony_ci tsauxc_mask = IGC_TSAUXC_EN_TT1; 32962306a36Sopenharmony_ci tsim_mask = IGC_TSICR_TT1; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci trgttiml = IGC_TRGTTIML1; 33262306a36Sopenharmony_ci trgttimh = IGC_TRGTTIMH1; 33362306a36Sopenharmony_ci freqout = IGC_FREQOUT1; 33462306a36Sopenharmony_ci } else { 33562306a36Sopenharmony_ci if (use_freq) { 33662306a36Sopenharmony_ci tsauxc_mask = IGC_TSAUXC_EN_CLK0 | IGC_TSAUXC_ST0; 33762306a36Sopenharmony_ci tsim_mask = 0; 33862306a36Sopenharmony_ci } else { 33962306a36Sopenharmony_ci tsauxc_mask = IGC_TSAUXC_EN_TT0; 34062306a36Sopenharmony_ci tsim_mask = IGC_TSICR_TT0; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci trgttiml = IGC_TRGTTIML0; 34362306a36Sopenharmony_ci trgttimh = IGC_TRGTTIMH0; 34462306a36Sopenharmony_ci freqout = IGC_FREQOUT0; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci spin_lock_irqsave(&igc->tmreg_lock, flags); 34762306a36Sopenharmony_ci tsauxc = rd32(IGC_TSAUXC); 34862306a36Sopenharmony_ci tsim = rd32(IGC_TSIM); 34962306a36Sopenharmony_ci if (rq->perout.index == 1) { 35062306a36Sopenharmony_ci tsauxc &= ~(IGC_TSAUXC_EN_TT1 | IGC_TSAUXC_EN_CLK1 | 35162306a36Sopenharmony_ci IGC_TSAUXC_ST1); 35262306a36Sopenharmony_ci tsim &= ~IGC_TSICR_TT1; 35362306a36Sopenharmony_ci } else { 35462306a36Sopenharmony_ci tsauxc &= ~(IGC_TSAUXC_EN_TT0 | IGC_TSAUXC_EN_CLK0 | 35562306a36Sopenharmony_ci IGC_TSAUXC_ST0); 35662306a36Sopenharmony_ci tsim &= ~IGC_TSICR_TT0; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci if (on) { 35962306a36Sopenharmony_ci struct timespec64 safe_start; 36062306a36Sopenharmony_ci int i = rq->perout.index; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci igc_pin_perout(igc, i, pin, use_freq); 36362306a36Sopenharmony_ci igc_ptp_read(igc, &safe_start); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* PPS output start time is triggered by Target time(TT) 36662306a36Sopenharmony_ci * register. Programming any past time value into TT 36762306a36Sopenharmony_ci * register will cause PPS to never start. Need to make 36862306a36Sopenharmony_ci * sure we program the TT register a time ahead in 36962306a36Sopenharmony_ci * future. There isn't a stringent need to fire PPS out 37062306a36Sopenharmony_ci * right away. Adding +2 seconds should take care of 37162306a36Sopenharmony_ci * corner cases. Let's say if the SYSTIML is close to 37262306a36Sopenharmony_ci * wrap up and the timer keeps ticking as we program the 37362306a36Sopenharmony_ci * register, adding +2seconds is safe bet. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci safe_start.tv_sec += 2; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (rq->perout.start.sec < safe_start.tv_sec) 37862306a36Sopenharmony_ci igc->perout[i].start.tv_sec = safe_start.tv_sec; 37962306a36Sopenharmony_ci else 38062306a36Sopenharmony_ci igc->perout[i].start.tv_sec = rq->perout.start.sec; 38162306a36Sopenharmony_ci igc->perout[i].start.tv_nsec = rq->perout.start.nsec; 38262306a36Sopenharmony_ci igc->perout[i].period.tv_sec = ts.tv_sec; 38362306a36Sopenharmony_ci igc->perout[i].period.tv_nsec = ts.tv_nsec; 38462306a36Sopenharmony_ci wr32(trgttimh, (u32)igc->perout[i].start.tv_sec); 38562306a36Sopenharmony_ci /* For now, always select timer 0 as source. */ 38662306a36Sopenharmony_ci wr32(trgttiml, (u32)(igc->perout[i].start.tv_nsec | 38762306a36Sopenharmony_ci IGC_TT_IO_TIMER_SEL_SYSTIM0)); 38862306a36Sopenharmony_ci if (use_freq) 38962306a36Sopenharmony_ci wr32(freqout, ns); 39062306a36Sopenharmony_ci tsauxc |= tsauxc_mask; 39162306a36Sopenharmony_ci tsim |= tsim_mask; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci wr32(IGC_TSAUXC, tsauxc); 39462306a36Sopenharmony_ci wr32(IGC_TSIM, tsim); 39562306a36Sopenharmony_ci spin_unlock_irqrestore(&igc->tmreg_lock, flags); 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci case PTP_CLK_REQ_PPS: 39962306a36Sopenharmony_ci spin_lock_irqsave(&igc->tmreg_lock, flags); 40062306a36Sopenharmony_ci tsim = rd32(IGC_TSIM); 40162306a36Sopenharmony_ci if (on) 40262306a36Sopenharmony_ci tsim |= IGC_TSICR_SYS_WRAP; 40362306a36Sopenharmony_ci else 40462306a36Sopenharmony_ci tsim &= ~IGC_TSICR_SYS_WRAP; 40562306a36Sopenharmony_ci igc->pps_sys_wrap_on = on; 40662306a36Sopenharmony_ci wr32(IGC_TSIM, tsim); 40762306a36Sopenharmony_ci spin_unlock_irqrestore(&igc->tmreg_lock, flags); 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci default: 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return -EOPNOTSUPP; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int igc_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin, 41862306a36Sopenharmony_ci enum ptp_pin_function func, unsigned int chan) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci switch (func) { 42162306a36Sopenharmony_ci case PTP_PF_NONE: 42262306a36Sopenharmony_ci case PTP_PF_EXTTS: 42362306a36Sopenharmony_ci case PTP_PF_PEROUT: 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci case PTP_PF_PHYSYNC: 42662306a36Sopenharmony_ci return -1; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci/** 43262306a36Sopenharmony_ci * igc_ptp_systim_to_hwtstamp - convert system time value to HW timestamp 43362306a36Sopenharmony_ci * @adapter: board private structure 43462306a36Sopenharmony_ci * @hwtstamps: timestamp structure to update 43562306a36Sopenharmony_ci * @systim: unsigned 64bit system time value 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * We need to convert the system time value stored in the RX/TXSTMP registers 43862306a36Sopenharmony_ci * into a hwtstamp which can be used by the upper level timestamping functions. 43962306a36Sopenharmony_ci * 44062306a36Sopenharmony_ci * Returns 0 on success. 44162306a36Sopenharmony_ci **/ 44262306a36Sopenharmony_cistatic int igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter, 44362306a36Sopenharmony_ci struct skb_shared_hwtstamps *hwtstamps, 44462306a36Sopenharmony_ci u64 systim) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci switch (adapter->hw.mac.type) { 44762306a36Sopenharmony_ci case igc_i225: 44862306a36Sopenharmony_ci memset(hwtstamps, 0, sizeof(*hwtstamps)); 44962306a36Sopenharmony_ci /* Upper 32 bits contain s, lower 32 bits contain ns. */ 45062306a36Sopenharmony_ci hwtstamps->hwtstamp = ktime_set(systim >> 32, 45162306a36Sopenharmony_ci systim & 0xFFFFFFFF); 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci default: 45462306a36Sopenharmony_ci return -EINVAL; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci return 0; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/** 46062306a36Sopenharmony_ci * igc_ptp_rx_pktstamp - Retrieve timestamp from Rx packet buffer 46162306a36Sopenharmony_ci * @adapter: Pointer to adapter the packet buffer belongs to 46262306a36Sopenharmony_ci * @buf: Pointer to packet buffer 46362306a36Sopenharmony_ci * 46462306a36Sopenharmony_ci * This function retrieves the timestamp saved in the beginning of packet 46562306a36Sopenharmony_ci * buffer. While two timestamps are available, one in timer0 reference and the 46662306a36Sopenharmony_ci * other in timer1 reference, this function considers only the timestamp in 46762306a36Sopenharmony_ci * timer0 reference. 46862306a36Sopenharmony_ci * 46962306a36Sopenharmony_ci * Returns timestamp value. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ciktime_t igc_ptp_rx_pktstamp(struct igc_adapter *adapter, __le32 *buf) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci ktime_t timestamp; 47462306a36Sopenharmony_ci u32 secs, nsecs; 47562306a36Sopenharmony_ci int adjust; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* Timestamps are saved in little endian at the beginning of the packet 47862306a36Sopenharmony_ci * buffer following the layout: 47962306a36Sopenharmony_ci * 48062306a36Sopenharmony_ci * DWORD: | 0 | 1 | 2 | 3 | 48162306a36Sopenharmony_ci * Field: | Timer1 SYSTIML | Timer1 SYSTIMH | Timer0 SYSTIML | Timer0 SYSTIMH | 48262306a36Sopenharmony_ci * 48362306a36Sopenharmony_ci * SYSTIML holds the nanoseconds part while SYSTIMH holds the seconds 48462306a36Sopenharmony_ci * part of the timestamp. 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_ci nsecs = le32_to_cpu(buf[2]); 48762306a36Sopenharmony_ci secs = le32_to_cpu(buf[3]); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci timestamp = ktime_set(secs, nsecs); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* Adjust timestamp for the RX latency based on link speed */ 49262306a36Sopenharmony_ci switch (adapter->link_speed) { 49362306a36Sopenharmony_ci case SPEED_10: 49462306a36Sopenharmony_ci adjust = IGC_I225_RX_LATENCY_10; 49562306a36Sopenharmony_ci break; 49662306a36Sopenharmony_ci case SPEED_100: 49762306a36Sopenharmony_ci adjust = IGC_I225_RX_LATENCY_100; 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci case SPEED_1000: 50062306a36Sopenharmony_ci adjust = IGC_I225_RX_LATENCY_1000; 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci case SPEED_2500: 50362306a36Sopenharmony_ci adjust = IGC_I225_RX_LATENCY_2500; 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci default: 50662306a36Sopenharmony_ci adjust = 0; 50762306a36Sopenharmony_ci netdev_warn_once(adapter->netdev, "Imprecise timestamp\n"); 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return ktime_sub_ns(timestamp, adjust); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void igc_ptp_disable_rx_timestamp(struct igc_adapter *adapter) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 51762306a36Sopenharmony_ci u32 val; 51862306a36Sopenharmony_ci int i; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci wr32(IGC_TSYNCRXCTL, 0); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 52362306a36Sopenharmony_ci val = rd32(IGC_SRRCTL(i)); 52462306a36Sopenharmony_ci val &= ~IGC_SRRCTL_TIMESTAMP; 52562306a36Sopenharmony_ci wr32(IGC_SRRCTL(i), val); 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci val = rd32(IGC_RXPBS); 52962306a36Sopenharmony_ci val &= ~IGC_RXPBS_CFG_TS_EN; 53062306a36Sopenharmony_ci wr32(IGC_RXPBS, val); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void igc_ptp_enable_rx_timestamp(struct igc_adapter *adapter) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 53662306a36Sopenharmony_ci u32 val; 53762306a36Sopenharmony_ci int i; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci val = rd32(IGC_RXPBS); 54062306a36Sopenharmony_ci val |= IGC_RXPBS_CFG_TS_EN; 54162306a36Sopenharmony_ci wr32(IGC_RXPBS, val); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci for (i = 0; i < adapter->num_rx_queues; i++) { 54462306a36Sopenharmony_ci val = rd32(IGC_SRRCTL(i)); 54562306a36Sopenharmony_ci /* FIXME: For now, only support retrieving RX timestamps from 54662306a36Sopenharmony_ci * timer 0. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci val |= IGC_SRRCTL_TIMER1SEL(0) | IGC_SRRCTL_TIMER0SEL(0) | 54962306a36Sopenharmony_ci IGC_SRRCTL_TIMESTAMP; 55062306a36Sopenharmony_ci wr32(IGC_SRRCTL(i), val); 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci val = IGC_TSYNCRXCTL_ENABLED | IGC_TSYNCRXCTL_TYPE_ALL | 55462306a36Sopenharmony_ci IGC_TSYNCRXCTL_RXSYNSIG; 55562306a36Sopenharmony_ci wr32(IGC_TSYNCRXCTL, val); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic void igc_ptp_clear_tx_tstamp(struct igc_adapter *adapter) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci unsigned long flags; 56162306a36Sopenharmony_ci int i; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci spin_lock_irqsave(&adapter->ptp_tx_lock, flags); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci for (i = 0; i < IGC_MAX_TX_TSTAMP_REGS; i++) { 56662306a36Sopenharmony_ci struct igc_tx_timestamp_request *tstamp = &adapter->tx_tstamp[i]; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci dev_kfree_skb_any(tstamp->skb); 56962306a36Sopenharmony_ci tstamp->skb = NULL; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags); 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 57862306a36Sopenharmony_ci int i; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* Clear the flags first to avoid new packets to be enqueued 58162306a36Sopenharmony_ci * for TX timestamping. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 58462306a36Sopenharmony_ci struct igc_ring *tx_ring = adapter->tx_ring[i]; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci clear_bit(IGC_RING_FLAG_TX_HWTSTAMP, &tx_ring->flags); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* Now we can clean the pending TX timestamp requests. */ 59062306a36Sopenharmony_ci igc_ptp_clear_tx_tstamp(adapter); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci wr32(IGC_TSYNCTXCTL, 0); 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic void igc_ptp_enable_tx_timestamp(struct igc_adapter *adapter) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 59862306a36Sopenharmony_ci int i; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci wr32(IGC_TSYNCTXCTL, IGC_TSYNCTXCTL_ENABLED | IGC_TSYNCTXCTL_TXSYNSIG); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* Read TXSTMP registers to discard any timestamp previously stored. */ 60362306a36Sopenharmony_ci rd32(IGC_TXSTMPL); 60462306a36Sopenharmony_ci rd32(IGC_TXSTMPH); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* The hardware is ready to accept TX timestamp requests, 60762306a36Sopenharmony_ci * notify the transmit path. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 61062306a36Sopenharmony_ci struct igc_ring *tx_ring = adapter->tx_ring[i]; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci set_bit(IGC_RING_FLAG_TX_HWTSTAMP, &tx_ring->flags); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/** 61862306a36Sopenharmony_ci * igc_ptp_set_timestamp_mode - setup hardware for timestamping 61962306a36Sopenharmony_ci * @adapter: networking device structure 62062306a36Sopenharmony_ci * @config: hwtstamp configuration 62162306a36Sopenharmony_ci * 62262306a36Sopenharmony_ci * Return: 0 in case of success, negative errno code otherwise. 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_cistatic int igc_ptp_set_timestamp_mode(struct igc_adapter *adapter, 62562306a36Sopenharmony_ci struct hwtstamp_config *config) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci switch (config->tx_type) { 62862306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 62962306a36Sopenharmony_ci igc_ptp_disable_tx_timestamp(adapter); 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 63262306a36Sopenharmony_ci igc_ptp_enable_tx_timestamp(adapter); 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci default: 63562306a36Sopenharmony_ci return -ERANGE; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci switch (config->rx_filter) { 63962306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 64062306a36Sopenharmony_ci igc_ptp_disable_rx_timestamp(adapter); 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 64362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 64462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 64562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 64662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 64762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 64862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 64962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 65062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 65162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 65262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 65362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 65462306a36Sopenharmony_ci case HWTSTAMP_FILTER_NTP_ALL: 65562306a36Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 65662306a36Sopenharmony_ci igc_ptp_enable_rx_timestamp(adapter); 65762306a36Sopenharmony_ci config->rx_filter = HWTSTAMP_FILTER_ALL; 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci default: 66062306a36Sopenharmony_ci return -ERANGE; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci/* Requires adapter->ptp_tx_lock held by caller. */ 66762306a36Sopenharmony_cistatic void igc_ptp_tx_timeout(struct igc_adapter *adapter, 66862306a36Sopenharmony_ci struct igc_tx_timestamp_request *tstamp) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci dev_kfree_skb_any(tstamp->skb); 67162306a36Sopenharmony_ci tstamp->skb = NULL; 67262306a36Sopenharmony_ci adapter->tx_hwtstamp_timeouts++; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci netdev_warn(adapter->netdev, "Tx timestamp timeout\n"); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_civoid igc_ptp_tx_hang(struct igc_adapter *adapter) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct igc_tx_timestamp_request *tstamp; 68062306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 68162306a36Sopenharmony_ci unsigned long flags; 68262306a36Sopenharmony_ci bool found = false; 68362306a36Sopenharmony_ci int i; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci spin_lock_irqsave(&adapter->ptp_tx_lock, flags); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci for (i = 0; i < IGC_MAX_TX_TSTAMP_REGS; i++) { 68862306a36Sopenharmony_ci tstamp = &adapter->tx_tstamp[i]; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (!tstamp->skb) 69162306a36Sopenharmony_ci continue; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (time_is_after_jiffies(tstamp->start + IGC_PTP_TX_TIMEOUT)) 69462306a36Sopenharmony_ci continue; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci igc_ptp_tx_timeout(adapter, tstamp); 69762306a36Sopenharmony_ci found = true; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (found) { 70162306a36Sopenharmony_ci /* Reading the high register of the first set of timestamp registers 70262306a36Sopenharmony_ci * clears all the equivalent bits in the TSYNCTXCTL register. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ci rd32(IGC_TXSTMPH_0); 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags); 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic void igc_ptp_tx_reg_to_stamp(struct igc_adapter *adapter, 71162306a36Sopenharmony_ci struct igc_tx_timestamp_request *tstamp, u64 regval) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 71462306a36Sopenharmony_ci struct sk_buff *skb; 71562306a36Sopenharmony_ci int adjust = 0; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci skb = tstamp->skb; 71862306a36Sopenharmony_ci if (!skb) 71962306a36Sopenharmony_ci return; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (igc_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval)) 72262306a36Sopenharmony_ci return; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci switch (adapter->link_speed) { 72562306a36Sopenharmony_ci case SPEED_10: 72662306a36Sopenharmony_ci adjust = IGC_I225_TX_LATENCY_10; 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci case SPEED_100: 72962306a36Sopenharmony_ci adjust = IGC_I225_TX_LATENCY_100; 73062306a36Sopenharmony_ci break; 73162306a36Sopenharmony_ci case SPEED_1000: 73262306a36Sopenharmony_ci adjust = IGC_I225_TX_LATENCY_1000; 73362306a36Sopenharmony_ci break; 73462306a36Sopenharmony_ci case SPEED_2500: 73562306a36Sopenharmony_ci adjust = IGC_I225_TX_LATENCY_2500; 73662306a36Sopenharmony_ci break; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci shhwtstamps.hwtstamp = 74062306a36Sopenharmony_ci ktime_add_ns(shhwtstamps.hwtstamp, adjust); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci tstamp->skb = NULL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci skb_tstamp_tx(skb, &shhwtstamps); 74562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/** 74962306a36Sopenharmony_ci * igc_ptp_tx_hwtstamp - utility function which checks for TX time stamp 75062306a36Sopenharmony_ci * @adapter: Board private structure 75162306a36Sopenharmony_ci * 75262306a36Sopenharmony_ci * Check against the ready mask for which of the timestamp register 75362306a36Sopenharmony_ci * sets are ready to be retrieved, then retrieve that and notify the 75462306a36Sopenharmony_ci * rest of the stack. 75562306a36Sopenharmony_ci * 75662306a36Sopenharmony_ci * Context: Expects adapter->ptp_tx_lock to be held by caller. 75762306a36Sopenharmony_ci */ 75862306a36Sopenharmony_cistatic void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 76162306a36Sopenharmony_ci u64 regval; 76262306a36Sopenharmony_ci u32 mask; 76362306a36Sopenharmony_ci int i; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci mask = rd32(IGC_TSYNCTXCTL) & IGC_TSYNCTXCTL_TXTT_ANY; 76662306a36Sopenharmony_ci if (mask & IGC_TSYNCTXCTL_TXTT_0) { 76762306a36Sopenharmony_ci regval = rd32(IGC_TXSTMPL); 76862306a36Sopenharmony_ci regval |= (u64)rd32(IGC_TXSTMPH) << 32; 76962306a36Sopenharmony_ci } else { 77062306a36Sopenharmony_ci /* There's a bug in the hardware that could cause 77162306a36Sopenharmony_ci * missing interrupts for TX timestamping. The issue 77262306a36Sopenharmony_ci * is that for new interrupts to be triggered, the 77362306a36Sopenharmony_ci * IGC_TXSTMPH_0 register must be read. 77462306a36Sopenharmony_ci * 77562306a36Sopenharmony_ci * To avoid discarding a valid timestamp that just 77662306a36Sopenharmony_ci * happened at the "wrong" time, we need to confirm 77762306a36Sopenharmony_ci * that there was no timestamp captured, we do that by 77862306a36Sopenharmony_ci * assuming that no two timestamps in sequence have 77962306a36Sopenharmony_ci * the same nanosecond value. 78062306a36Sopenharmony_ci * 78162306a36Sopenharmony_ci * So, we read the "low" register, read the "high" 78262306a36Sopenharmony_ci * register (to latch a new timestamp) and read the 78362306a36Sopenharmony_ci * "low" register again, if "old" and "new" versions 78462306a36Sopenharmony_ci * of the "low" register are different, a valid 78562306a36Sopenharmony_ci * timestamp was captured, we can read the "high" 78662306a36Sopenharmony_ci * register again. 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_ci u32 txstmpl_old, txstmpl_new; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci txstmpl_old = rd32(IGC_TXSTMPL); 79162306a36Sopenharmony_ci rd32(IGC_TXSTMPH); 79262306a36Sopenharmony_ci txstmpl_new = rd32(IGC_TXSTMPL); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (txstmpl_old == txstmpl_new) 79562306a36Sopenharmony_ci goto done; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci regval = txstmpl_new; 79862306a36Sopenharmony_ci regval |= (u64)rd32(IGC_TXSTMPH) << 32; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci igc_ptp_tx_reg_to_stamp(adapter, &adapter->tx_tstamp[0], regval); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cidone: 80462306a36Sopenharmony_ci /* Now that the problematic first register was handled, we can 80562306a36Sopenharmony_ci * use retrieve the timestamps from the other registers 80662306a36Sopenharmony_ci * (starting from '1') with less complications. 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci for (i = 1; i < IGC_MAX_TX_TSTAMP_REGS; i++) { 80962306a36Sopenharmony_ci struct igc_tx_timestamp_request *tstamp = &adapter->tx_tstamp[i]; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (!(tstamp->mask & mask)) 81262306a36Sopenharmony_ci continue; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci regval = rd32(tstamp->regl); 81562306a36Sopenharmony_ci regval |= (u64)rd32(tstamp->regh) << 32; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci igc_ptp_tx_reg_to_stamp(adapter, tstamp, regval); 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci/** 82262306a36Sopenharmony_ci * igc_ptp_tx_tstamp_event 82362306a36Sopenharmony_ci * @adapter: board private structure 82462306a36Sopenharmony_ci * 82562306a36Sopenharmony_ci * Called when a TX timestamp interrupt happens to retrieve the 82662306a36Sopenharmony_ci * timestamp and send it up to the socket. 82762306a36Sopenharmony_ci */ 82862306a36Sopenharmony_civoid igc_ptp_tx_tstamp_event(struct igc_adapter *adapter) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci unsigned long flags; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci spin_lock_irqsave(&adapter->ptp_tx_lock, flags); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci igc_ptp_tx_hwtstamp(adapter); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags); 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci/** 84062306a36Sopenharmony_ci * igc_ptp_set_ts_config - set hardware time stamping config 84162306a36Sopenharmony_ci * @netdev: network interface device structure 84262306a36Sopenharmony_ci * @ifr: interface request data 84362306a36Sopenharmony_ci * 84462306a36Sopenharmony_ci **/ 84562306a36Sopenharmony_ciint igc_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct igc_adapter *adapter = netdev_priv(netdev); 84862306a36Sopenharmony_ci struct hwtstamp_config config; 84962306a36Sopenharmony_ci int err; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 85262306a36Sopenharmony_ci return -EFAULT; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci err = igc_ptp_set_timestamp_mode(adapter, &config); 85562306a36Sopenharmony_ci if (err) 85662306a36Sopenharmony_ci return err; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* save these settings for future reference */ 85962306a36Sopenharmony_ci memcpy(&adapter->tstamp_config, &config, 86062306a36Sopenharmony_ci sizeof(adapter->tstamp_config)); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? 86362306a36Sopenharmony_ci -EFAULT : 0; 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci/** 86762306a36Sopenharmony_ci * igc_ptp_get_ts_config - get hardware time stamping config 86862306a36Sopenharmony_ci * @netdev: network interface device structure 86962306a36Sopenharmony_ci * @ifr: interface request data 87062306a36Sopenharmony_ci * 87162306a36Sopenharmony_ci * Get the hwtstamp_config settings to return to the user. Rather than attempt 87262306a36Sopenharmony_ci * to deconstruct the settings from the registers, just return a shadow copy 87362306a36Sopenharmony_ci * of the last known settings. 87462306a36Sopenharmony_ci **/ 87562306a36Sopenharmony_ciint igc_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct igc_adapter *adapter = netdev_priv(netdev); 87862306a36Sopenharmony_ci struct hwtstamp_config *config = &adapter->tstamp_config; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ? 88162306a36Sopenharmony_ci -EFAULT : 0; 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci/* The two conditions below must be met for cross timestamping via 88562306a36Sopenharmony_ci * PCIe PTM: 88662306a36Sopenharmony_ci * 88762306a36Sopenharmony_ci * 1. We have an way to convert the timestamps in the PTM messages 88862306a36Sopenharmony_ci * to something related to the system clocks (right now, only 88962306a36Sopenharmony_ci * X86 systems with support for the Always Running Timer allow that); 89062306a36Sopenharmony_ci * 89162306a36Sopenharmony_ci * 2. We have PTM enabled in the path from the device to the PCIe root port. 89262306a36Sopenharmony_ci */ 89362306a36Sopenharmony_cistatic bool igc_is_crosststamp_supported(struct igc_adapter *adapter) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_X86_TSC)) 89662306a36Sopenharmony_ci return false; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* FIXME: it was noticed that enabling support for PCIe PTM in 89962306a36Sopenharmony_ci * some i225-V models could cause lockups when bringing the 90062306a36Sopenharmony_ci * interface up/down. There should be no downsides to 90162306a36Sopenharmony_ci * disabling crosstimestamping support for i225-V, as it 90262306a36Sopenharmony_ci * doesn't have any PTP support. That way we gain some time 90362306a36Sopenharmony_ci * while root causing the issue. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ci if (adapter->pdev->device == IGC_DEV_ID_I225_V) 90662306a36Sopenharmony_ci return false; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci return pcie_ptm_enabled(adapter->pdev); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic struct system_counterval_t igc_device_tstamp_to_system(u64 tstamp) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_X86_TSC) && !defined(CONFIG_UML) 91462306a36Sopenharmony_ci return convert_art_ns_to_tsc(tstamp); 91562306a36Sopenharmony_ci#else 91662306a36Sopenharmony_ci return (struct system_counterval_t) { }; 91762306a36Sopenharmony_ci#endif 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic void igc_ptm_log_error(struct igc_adapter *adapter, u32 ptm_stat) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci switch (ptm_stat) { 92562306a36Sopenharmony_ci case IGC_PTM_STAT_RET_ERR: 92662306a36Sopenharmony_ci netdev_err(netdev, "PTM Error: Root port timeout\n"); 92762306a36Sopenharmony_ci break; 92862306a36Sopenharmony_ci case IGC_PTM_STAT_BAD_PTM_RES: 92962306a36Sopenharmony_ci netdev_err(netdev, "PTM Error: Bad response, PTM Response Data expected\n"); 93062306a36Sopenharmony_ci break; 93162306a36Sopenharmony_ci case IGC_PTM_STAT_T4M1_OVFL: 93262306a36Sopenharmony_ci netdev_err(netdev, "PTM Error: T4 minus T1 overflow\n"); 93362306a36Sopenharmony_ci break; 93462306a36Sopenharmony_ci case IGC_PTM_STAT_ADJUST_1ST: 93562306a36Sopenharmony_ci netdev_err(netdev, "PTM Error: 1588 timer adjusted during first PTM cycle\n"); 93662306a36Sopenharmony_ci break; 93762306a36Sopenharmony_ci case IGC_PTM_STAT_ADJUST_CYC: 93862306a36Sopenharmony_ci netdev_err(netdev, "PTM Error: 1588 timer adjusted during non-first PTM cycle\n"); 93962306a36Sopenharmony_ci break; 94062306a36Sopenharmony_ci default: 94162306a36Sopenharmony_ci netdev_err(netdev, "PTM Error: Unknown error (%#x)\n", ptm_stat); 94262306a36Sopenharmony_ci break; 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic int igc_phc_get_syncdevicetime(ktime_t *device, 94762306a36Sopenharmony_ci struct system_counterval_t *system, 94862306a36Sopenharmony_ci void *ctx) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci u32 stat, t2_curr_h, t2_curr_l, ctrl; 95162306a36Sopenharmony_ci struct igc_adapter *adapter = ctx; 95262306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 95362306a36Sopenharmony_ci int err, count = 100; 95462306a36Sopenharmony_ci ktime_t t1, t2_curr; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* Get a snapshot of system clocks to use as historic value. */ 95762306a36Sopenharmony_ci ktime_get_snapshot(&adapter->snapshot); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci do { 96062306a36Sopenharmony_ci /* Doing this in a loop because in the event of a 96162306a36Sopenharmony_ci * badly timed (ha!) system clock adjustment, we may 96262306a36Sopenharmony_ci * get PTM errors from the PCI root, but these errors 96362306a36Sopenharmony_ci * are transitory. Repeating the process returns valid 96462306a36Sopenharmony_ci * data eventually. 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* To "manually" start the PTM cycle we need to clear and 96862306a36Sopenharmony_ci * then set again the TRIG bit. 96962306a36Sopenharmony_ci */ 97062306a36Sopenharmony_ci ctrl = rd32(IGC_PTM_CTRL); 97162306a36Sopenharmony_ci ctrl &= ~IGC_PTM_CTRL_TRIG; 97262306a36Sopenharmony_ci wr32(IGC_PTM_CTRL, ctrl); 97362306a36Sopenharmony_ci ctrl |= IGC_PTM_CTRL_TRIG; 97462306a36Sopenharmony_ci wr32(IGC_PTM_CTRL, ctrl); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* The cycle only starts "for real" when software notifies 97762306a36Sopenharmony_ci * that it has read the registers, this is done by setting 97862306a36Sopenharmony_ci * VALID bit. 97962306a36Sopenharmony_ci */ 98062306a36Sopenharmony_ci wr32(IGC_PTM_STAT, IGC_PTM_STAT_VALID); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci err = readx_poll_timeout(rd32, IGC_PTM_STAT, stat, 98362306a36Sopenharmony_ci stat, IGC_PTM_STAT_SLEEP, 98462306a36Sopenharmony_ci IGC_PTM_STAT_TIMEOUT); 98562306a36Sopenharmony_ci if (err < 0) { 98662306a36Sopenharmony_ci netdev_err(adapter->netdev, "Timeout reading IGC_PTM_STAT register\n"); 98762306a36Sopenharmony_ci return err; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if ((stat & IGC_PTM_STAT_VALID) == IGC_PTM_STAT_VALID) 99162306a36Sopenharmony_ci break; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (stat & ~IGC_PTM_STAT_VALID) { 99462306a36Sopenharmony_ci /* An error occurred, log it. */ 99562306a36Sopenharmony_ci igc_ptm_log_error(adapter, stat); 99662306a36Sopenharmony_ci /* The STAT register is write-1-to-clear (W1C), 99762306a36Sopenharmony_ci * so write the previous error status to clear it. 99862306a36Sopenharmony_ci */ 99962306a36Sopenharmony_ci wr32(IGC_PTM_STAT, stat); 100062306a36Sopenharmony_ci continue; 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci } while (--count); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (!count) { 100562306a36Sopenharmony_ci netdev_err(adapter->netdev, "Exceeded number of tries for PTM cycle\n"); 100662306a36Sopenharmony_ci return -ETIMEDOUT; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci t1 = ktime_set(rd32(IGC_PTM_T1_TIM0_H), rd32(IGC_PTM_T1_TIM0_L)); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci t2_curr_l = rd32(IGC_PTM_CURR_T2_L); 101262306a36Sopenharmony_ci t2_curr_h = rd32(IGC_PTM_CURR_T2_H); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci /* FIXME: When the register that tells the endianness of the 101562306a36Sopenharmony_ci * PTM registers are implemented, check them here and add the 101662306a36Sopenharmony_ci * appropriate conversion. 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_ci t2_curr_h = swab32(t2_curr_h); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci t2_curr = ((s64)t2_curr_h << 32 | t2_curr_l); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci *device = t1; 102362306a36Sopenharmony_ci *system = igc_device_tstamp_to_system(t2_curr); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci return 0; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cistatic int igc_ptp_getcrosststamp(struct ptp_clock_info *ptp, 102962306a36Sopenharmony_ci struct system_device_crosststamp *cts) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci struct igc_adapter *adapter = container_of(ptp, struct igc_adapter, 103262306a36Sopenharmony_ci ptp_caps); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci return get_device_system_crosststamp(igc_phc_get_syncdevicetime, 103562306a36Sopenharmony_ci adapter, &adapter->snapshot, cts); 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci/** 103962306a36Sopenharmony_ci * igc_ptp_init - Initialize PTP functionality 104062306a36Sopenharmony_ci * @adapter: Board private structure 104162306a36Sopenharmony_ci * 104262306a36Sopenharmony_ci * This function is called at device probe to initialize the PTP 104362306a36Sopenharmony_ci * functionality. 104462306a36Sopenharmony_ci */ 104562306a36Sopenharmony_civoid igc_ptp_init(struct igc_adapter *adapter) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 104862306a36Sopenharmony_ci struct igc_tx_timestamp_request *tstamp; 104962306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 105062306a36Sopenharmony_ci int i; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci tstamp = &adapter->tx_tstamp[0]; 105362306a36Sopenharmony_ci tstamp->mask = IGC_TSYNCTXCTL_TXTT_0; 105462306a36Sopenharmony_ci tstamp->regl = IGC_TXSTMPL_0; 105562306a36Sopenharmony_ci tstamp->regh = IGC_TXSTMPH_0; 105662306a36Sopenharmony_ci tstamp->flags = 0; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci tstamp = &adapter->tx_tstamp[1]; 105962306a36Sopenharmony_ci tstamp->mask = IGC_TSYNCTXCTL_TXTT_1; 106062306a36Sopenharmony_ci tstamp->regl = IGC_TXSTMPL_1; 106162306a36Sopenharmony_ci tstamp->regh = IGC_TXSTMPH_1; 106262306a36Sopenharmony_ci tstamp->flags = IGC_TX_FLAGS_TSTAMP_1; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci tstamp = &adapter->tx_tstamp[2]; 106562306a36Sopenharmony_ci tstamp->mask = IGC_TSYNCTXCTL_TXTT_2; 106662306a36Sopenharmony_ci tstamp->regl = IGC_TXSTMPL_2; 106762306a36Sopenharmony_ci tstamp->regh = IGC_TXSTMPH_2; 106862306a36Sopenharmony_ci tstamp->flags = IGC_TX_FLAGS_TSTAMP_2; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci tstamp = &adapter->tx_tstamp[3]; 107162306a36Sopenharmony_ci tstamp->mask = IGC_TSYNCTXCTL_TXTT_3; 107262306a36Sopenharmony_ci tstamp->regl = IGC_TXSTMPL_3; 107362306a36Sopenharmony_ci tstamp->regh = IGC_TXSTMPH_3; 107462306a36Sopenharmony_ci tstamp->flags = IGC_TX_FLAGS_TSTAMP_3; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci switch (hw->mac.type) { 107762306a36Sopenharmony_ci case igc_i225: 107862306a36Sopenharmony_ci for (i = 0; i < IGC_N_SDP; i++) { 107962306a36Sopenharmony_ci struct ptp_pin_desc *ppd = &adapter->sdp_config[i]; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci snprintf(ppd->name, sizeof(ppd->name), "SDP%d", i); 108262306a36Sopenharmony_ci ppd->index = i; 108362306a36Sopenharmony_ci ppd->func = PTP_PF_NONE; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr); 108662306a36Sopenharmony_ci adapter->ptp_caps.owner = THIS_MODULE; 108762306a36Sopenharmony_ci adapter->ptp_caps.max_adj = 62499999; 108862306a36Sopenharmony_ci adapter->ptp_caps.adjfine = igc_ptp_adjfine_i225; 108962306a36Sopenharmony_ci adapter->ptp_caps.adjtime = igc_ptp_adjtime_i225; 109062306a36Sopenharmony_ci adapter->ptp_caps.gettimex64 = igc_ptp_gettimex64_i225; 109162306a36Sopenharmony_ci adapter->ptp_caps.settime64 = igc_ptp_settime_i225; 109262306a36Sopenharmony_ci adapter->ptp_caps.enable = igc_ptp_feature_enable_i225; 109362306a36Sopenharmony_ci adapter->ptp_caps.pps = 1; 109462306a36Sopenharmony_ci adapter->ptp_caps.pin_config = adapter->sdp_config; 109562306a36Sopenharmony_ci adapter->ptp_caps.n_ext_ts = IGC_N_EXTTS; 109662306a36Sopenharmony_ci adapter->ptp_caps.n_per_out = IGC_N_PEROUT; 109762306a36Sopenharmony_ci adapter->ptp_caps.n_pins = IGC_N_SDP; 109862306a36Sopenharmony_ci adapter->ptp_caps.verify = igc_ptp_verify_pin; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (!igc_is_crosststamp_supported(adapter)) 110162306a36Sopenharmony_ci break; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci adapter->ptp_caps.getcrosststamp = igc_ptp_getcrosststamp; 110462306a36Sopenharmony_ci break; 110562306a36Sopenharmony_ci default: 110662306a36Sopenharmony_ci adapter->ptp_clock = NULL; 110762306a36Sopenharmony_ci return; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci spin_lock_init(&adapter->ptp_tx_lock); 111162306a36Sopenharmony_ci spin_lock_init(&adapter->tmreg_lock); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; 111462306a36Sopenharmony_ci adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci adapter->prev_ptp_time = ktime_to_timespec64(ktime_get_real()); 111762306a36Sopenharmony_ci adapter->ptp_reset_start = ktime_get(); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps, 112062306a36Sopenharmony_ci &adapter->pdev->dev); 112162306a36Sopenharmony_ci if (IS_ERR(adapter->ptp_clock)) { 112262306a36Sopenharmony_ci adapter->ptp_clock = NULL; 112362306a36Sopenharmony_ci netdev_err(netdev, "ptp_clock_register failed\n"); 112462306a36Sopenharmony_ci } else if (adapter->ptp_clock) { 112562306a36Sopenharmony_ci netdev_info(netdev, "PHC added\n"); 112662306a36Sopenharmony_ci adapter->ptp_flags |= IGC_PTP_ENABLED; 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic void igc_ptp_time_save(struct igc_adapter *adapter) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci igc_ptp_read(adapter, &adapter->prev_ptp_time); 113362306a36Sopenharmony_ci adapter->ptp_reset_start = ktime_get(); 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_cistatic void igc_ptp_time_restore(struct igc_adapter *adapter) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci struct timespec64 ts = adapter->prev_ptp_time; 113962306a36Sopenharmony_ci ktime_t delta; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci delta = ktime_sub(ktime_get(), adapter->ptp_reset_start); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci timespec64_add_ns(&ts, ktime_to_ns(delta)); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci igc_ptp_write_i225(adapter, &ts); 114662306a36Sopenharmony_ci} 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_cistatic void igc_ptm_stop(struct igc_adapter *adapter) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 115162306a36Sopenharmony_ci u32 ctrl; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci ctrl = rd32(IGC_PTM_CTRL); 115462306a36Sopenharmony_ci ctrl &= ~IGC_PTM_CTRL_EN; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci wr32(IGC_PTM_CTRL, ctrl); 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci/** 116062306a36Sopenharmony_ci * igc_ptp_suspend - Disable PTP work items and prepare for suspend 116162306a36Sopenharmony_ci * @adapter: Board private structure 116262306a36Sopenharmony_ci * 116362306a36Sopenharmony_ci * This function stops the overflow check work and PTP Tx timestamp work, and 116462306a36Sopenharmony_ci * will prepare the device for OS suspend. 116562306a36Sopenharmony_ci */ 116662306a36Sopenharmony_civoid igc_ptp_suspend(struct igc_adapter *adapter) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci if (!(adapter->ptp_flags & IGC_PTP_ENABLED)) 116962306a36Sopenharmony_ci return; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci igc_ptp_clear_tx_tstamp(adapter); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci if (pci_device_is_present(adapter->pdev)) { 117462306a36Sopenharmony_ci igc_ptp_time_save(adapter); 117562306a36Sopenharmony_ci igc_ptm_stop(adapter); 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci/** 118062306a36Sopenharmony_ci * igc_ptp_stop - Disable PTP device and stop the overflow check. 118162306a36Sopenharmony_ci * @adapter: Board private structure. 118262306a36Sopenharmony_ci * 118362306a36Sopenharmony_ci * This function stops the PTP support and cancels the delayed work. 118462306a36Sopenharmony_ci **/ 118562306a36Sopenharmony_civoid igc_ptp_stop(struct igc_adapter *adapter) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci igc_ptp_suspend(adapter); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (adapter->ptp_clock) { 119062306a36Sopenharmony_ci ptp_clock_unregister(adapter->ptp_clock); 119162306a36Sopenharmony_ci netdev_info(adapter->netdev, "PHC removed\n"); 119262306a36Sopenharmony_ci adapter->ptp_flags &= ~IGC_PTP_ENABLED; 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci/** 119762306a36Sopenharmony_ci * igc_ptp_reset - Re-enable the adapter for PTP following a reset. 119862306a36Sopenharmony_ci * @adapter: Board private structure. 119962306a36Sopenharmony_ci * 120062306a36Sopenharmony_ci * This function handles the reset work required to re-enable the PTP device. 120162306a36Sopenharmony_ci **/ 120262306a36Sopenharmony_civoid igc_ptp_reset(struct igc_adapter *adapter) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 120562306a36Sopenharmony_ci u32 cycle_ctrl, ctrl; 120662306a36Sopenharmony_ci unsigned long flags; 120762306a36Sopenharmony_ci u32 timadj; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci /* reset the tstamp_config */ 121062306a36Sopenharmony_ci igc_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci spin_lock_irqsave(&adapter->tmreg_lock, flags); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci switch (adapter->hw.mac.type) { 121562306a36Sopenharmony_ci case igc_i225: 121662306a36Sopenharmony_ci timadj = rd32(IGC_TIMADJ); 121762306a36Sopenharmony_ci timadj |= IGC_TIMADJ_ADJUST_METH; 121862306a36Sopenharmony_ci wr32(IGC_TIMADJ, timadj); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci wr32(IGC_TSAUXC, 0x0); 122162306a36Sopenharmony_ci wr32(IGC_TSSDP, 0x0); 122262306a36Sopenharmony_ci wr32(IGC_TSIM, 122362306a36Sopenharmony_ci IGC_TSICR_INTERRUPTS | 122462306a36Sopenharmony_ci (adapter->pps_sys_wrap_on ? IGC_TSICR_SYS_WRAP : 0)); 122562306a36Sopenharmony_ci wr32(IGC_IMS, IGC_IMS_TS); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (!igc_is_crosststamp_supported(adapter)) 122862306a36Sopenharmony_ci break; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci wr32(IGC_PCIE_DIG_DELAY, IGC_PCIE_DIG_DELAY_DEFAULT); 123162306a36Sopenharmony_ci wr32(IGC_PCIE_PHY_DELAY, IGC_PCIE_PHY_DELAY_DEFAULT); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci cycle_ctrl = IGC_PTM_CYCLE_CTRL_CYC_TIME(IGC_PTM_CYC_TIME_DEFAULT); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci wr32(IGC_PTM_CYCLE_CTRL, cycle_ctrl); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci ctrl = IGC_PTM_CTRL_EN | 123862306a36Sopenharmony_ci IGC_PTM_CTRL_START_NOW | 123962306a36Sopenharmony_ci IGC_PTM_CTRL_SHRT_CYC(IGC_PTM_SHORT_CYC_DEFAULT) | 124062306a36Sopenharmony_ci IGC_PTM_CTRL_PTM_TO(IGC_PTM_TIMEOUT_DEFAULT) | 124162306a36Sopenharmony_ci IGC_PTM_CTRL_TRIG; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci wr32(IGC_PTM_CTRL, ctrl); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci /* Force the first cycle to run. */ 124662306a36Sopenharmony_ci wr32(IGC_PTM_STAT, IGC_PTM_STAT_VALID); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci break; 124962306a36Sopenharmony_ci default: 125062306a36Sopenharmony_ci /* No work to do. */ 125162306a36Sopenharmony_ci goto out; 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci /* Re-initialize the timer. */ 125562306a36Sopenharmony_ci if (hw->mac.type == igc_i225) { 125662306a36Sopenharmony_ci igc_ptp_time_restore(adapter); 125762306a36Sopenharmony_ci } else { 125862306a36Sopenharmony_ci timecounter_init(&adapter->tc, &adapter->cc, 125962306a36Sopenharmony_ci ktime_to_ns(ktime_get_real())); 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ciout: 126262306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->tmreg_lock, flags); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci wrfl(); 126562306a36Sopenharmony_ci} 1266