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