162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/* Copyright (C) 2011 Richard Cochran <richardcochran@gmail.com> */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/device.h>
662306a36Sopenharmony_ci#include <linux/pci.h>
762306a36Sopenharmony_ci#include <linux/ptp_classify.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "igb.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define INCVALUE_MASK		0x7fffffff
1262306a36Sopenharmony_ci#define ISGN			0x80000000
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* The 82580 timesync updates the system timer every 8ns by 8ns,
1562306a36Sopenharmony_ci * and this update value cannot be reprogrammed.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * Neither the 82576 nor the 82580 offer registers wide enough to hold
1862306a36Sopenharmony_ci * nanoseconds time values for very long. For the 82580, SYSTIM always
1962306a36Sopenharmony_ci * counts nanoseconds, but the upper 24 bits are not available. The
2062306a36Sopenharmony_ci * frequency is adjusted by changing the 32 bit fractional nanoseconds
2162306a36Sopenharmony_ci * register, TIMINCA.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * For the 82576, the SYSTIM register time unit is affect by the
2462306a36Sopenharmony_ci * choice of the 24 bit TININCA:IV (incvalue) field. Five bits of this
2562306a36Sopenharmony_ci * field are needed to provide the nominal 16 nanosecond period,
2662306a36Sopenharmony_ci * leaving 19 bits for fractional nanoseconds.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * We scale the NIC clock cycle by a large factor so that relatively
2962306a36Sopenharmony_ci * small clock corrections can be added or subtracted at each clock
3062306a36Sopenharmony_ci * tick. The drawbacks of a large factor are a) that the clock
3162306a36Sopenharmony_ci * register overflows more quickly (not such a big deal) and b) that
3262306a36Sopenharmony_ci * the increment per tick has to fit into 24 bits.  As a result we
3362306a36Sopenharmony_ci * need to use a shift of 19 so we can fit a value of 16 into the
3462306a36Sopenharmony_ci * TIMINCA register.
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci *             SYSTIMH            SYSTIML
3862306a36Sopenharmony_ci *        +--------------+   +---+---+------+
3962306a36Sopenharmony_ci *  82576 |      32      |   | 8 | 5 |  19  |
4062306a36Sopenharmony_ci *        +--------------+   +---+---+------+
4162306a36Sopenharmony_ci *         \________ 45 bits _______/  fract
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci *        +----------+---+   +--------------+
4462306a36Sopenharmony_ci *  82580 |    24    | 8 |   |      32      |
4562306a36Sopenharmony_ci *        +----------+---+   +--------------+
4662306a36Sopenharmony_ci *          reserved  \______ 40 bits _____/
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * The 45 bit 82576 SYSTIM overflows every
5062306a36Sopenharmony_ci *   2^45 * 10^-9 / 3600 = 9.77 hours.
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * The 40 bit 82580 SYSTIM overflows every
5362306a36Sopenharmony_ci *   2^40 * 10^-9 /  60  = 18.3 minutes.
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * SYSTIM is converted to real time using a timecounter. As
5662306a36Sopenharmony_ci * timecounter_cyc2time() allows old timestamps, the timecounter needs
5762306a36Sopenharmony_ci * to be updated at least once per half of the SYSTIM interval.
5862306a36Sopenharmony_ci * Scheduling of delayed work is not very accurate, and also the NIC
5962306a36Sopenharmony_ci * clock can be adjusted to run up to 6% faster and the system clock
6062306a36Sopenharmony_ci * up to 10% slower, so we aim for 6 minutes to be sure the actual
6162306a36Sopenharmony_ci * interval in the NIC time is shorter than 9.16 minutes.
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define IGB_SYSTIM_OVERFLOW_PERIOD	(HZ * 60 * 6)
6562306a36Sopenharmony_ci#define IGB_PTP_TX_TIMEOUT		(HZ * 15)
6662306a36Sopenharmony_ci#define INCPERIOD_82576			BIT(E1000_TIMINCA_16NS_SHIFT)
6762306a36Sopenharmony_ci#define INCVALUE_82576_MASK		GENMASK(E1000_TIMINCA_16NS_SHIFT - 1, 0)
6862306a36Sopenharmony_ci#define INCVALUE_82576			(16u << IGB_82576_TSYNC_SHIFT)
6962306a36Sopenharmony_ci#define IGB_NBITS_82580			40
7062306a36Sopenharmony_ci#define IGB_82580_BASE_PERIOD		0x800000000
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter);
7362306a36Sopenharmony_cistatic void igb_ptp_sdp_init(struct igb_adapter *adapter);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* SYSTIM read access for the 82576 */
7662306a36Sopenharmony_cistatic u64 igb_ptp_read_82576(const struct cyclecounter *cc)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct igb_adapter *igb = container_of(cc, struct igb_adapter, cc);
7962306a36Sopenharmony_ci	struct e1000_hw *hw = &igb->hw;
8062306a36Sopenharmony_ci	u64 val;
8162306a36Sopenharmony_ci	u32 lo, hi;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	lo = rd32(E1000_SYSTIML);
8462306a36Sopenharmony_ci	hi = rd32(E1000_SYSTIMH);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	val = ((u64) hi) << 32;
8762306a36Sopenharmony_ci	val |= lo;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return val;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* SYSTIM read access for the 82580 */
9362306a36Sopenharmony_cistatic u64 igb_ptp_read_82580(const struct cyclecounter *cc)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct igb_adapter *igb = container_of(cc, struct igb_adapter, cc);
9662306a36Sopenharmony_ci	struct e1000_hw *hw = &igb->hw;
9762306a36Sopenharmony_ci	u32 lo, hi;
9862306a36Sopenharmony_ci	u64 val;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* The timestamp latches on lowest register read. For the 82580
10162306a36Sopenharmony_ci	 * the lowest register is SYSTIMR instead of SYSTIML.  However we only
10262306a36Sopenharmony_ci	 * need to provide nanosecond resolution, so we just ignore it.
10362306a36Sopenharmony_ci	 */
10462306a36Sopenharmony_ci	rd32(E1000_SYSTIMR);
10562306a36Sopenharmony_ci	lo = rd32(E1000_SYSTIML);
10662306a36Sopenharmony_ci	hi = rd32(E1000_SYSTIMH);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	val = ((u64) hi) << 32;
10962306a36Sopenharmony_ci	val |= lo;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return val;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/* SYSTIM read access for I210/I211 */
11562306a36Sopenharmony_cistatic void igb_ptp_read_i210(struct igb_adapter *adapter,
11662306a36Sopenharmony_ci			      struct timespec64 *ts)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
11962306a36Sopenharmony_ci	u32 sec, nsec;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* The timestamp latches on lowest register read. For I210/I211, the
12262306a36Sopenharmony_ci	 * lowest register is SYSTIMR. Since we only need to provide nanosecond
12362306a36Sopenharmony_ci	 * resolution, we can ignore it.
12462306a36Sopenharmony_ci	 */
12562306a36Sopenharmony_ci	rd32(E1000_SYSTIMR);
12662306a36Sopenharmony_ci	nsec = rd32(E1000_SYSTIML);
12762306a36Sopenharmony_ci	sec = rd32(E1000_SYSTIMH);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	ts->tv_sec = sec;
13062306a36Sopenharmony_ci	ts->tv_nsec = nsec;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void igb_ptp_write_i210(struct igb_adapter *adapter,
13462306a36Sopenharmony_ci			       const struct timespec64 *ts)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* Writing the SYSTIMR register is not necessary as it only provides
13962306a36Sopenharmony_ci	 * sub-nanosecond resolution.
14062306a36Sopenharmony_ci	 */
14162306a36Sopenharmony_ci	wr32(E1000_SYSTIML, ts->tv_nsec);
14262306a36Sopenharmony_ci	wr32(E1000_SYSTIMH, (u32)ts->tv_sec);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/**
14662306a36Sopenharmony_ci * igb_ptp_systim_to_hwtstamp - convert system time value to hw timestamp
14762306a36Sopenharmony_ci * @adapter: board private structure
14862306a36Sopenharmony_ci * @hwtstamps: timestamp structure to update
14962306a36Sopenharmony_ci * @systim: unsigned 64bit system time value.
15062306a36Sopenharmony_ci *
15162306a36Sopenharmony_ci * We need to convert the system time value stored in the RX/TXSTMP registers
15262306a36Sopenharmony_ci * into a hwtstamp which can be used by the upper level timestamping functions.
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * The 'tmreg_lock' spinlock is used to protect the consistency of the
15562306a36Sopenharmony_ci * system time value. This is needed because reading the 64 bit time
15662306a36Sopenharmony_ci * value involves reading two (or three) 32 bit registers. The first
15762306a36Sopenharmony_ci * read latches the value. Ditto for writing.
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * In addition, here have extended the system time with an overflow
16062306a36Sopenharmony_ci * counter in software.
16162306a36Sopenharmony_ci **/
16262306a36Sopenharmony_cistatic void igb_ptp_systim_to_hwtstamp(struct igb_adapter *adapter,
16362306a36Sopenharmony_ci				       struct skb_shared_hwtstamps *hwtstamps,
16462306a36Sopenharmony_ci				       u64 systim)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	unsigned long flags;
16762306a36Sopenharmony_ci	u64 ns;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	memset(hwtstamps, 0, sizeof(*hwtstamps));
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	switch (adapter->hw.mac.type) {
17262306a36Sopenharmony_ci	case e1000_82576:
17362306a36Sopenharmony_ci	case e1000_82580:
17462306a36Sopenharmony_ci	case e1000_i354:
17562306a36Sopenharmony_ci	case e1000_i350:
17662306a36Sopenharmony_ci		spin_lock_irqsave(&adapter->tmreg_lock, flags);
17762306a36Sopenharmony_ci		ns = timecounter_cyc2time(&adapter->tc, systim);
17862306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		hwtstamps->hwtstamp = ns_to_ktime(ns);
18162306a36Sopenharmony_ci		break;
18262306a36Sopenharmony_ci	case e1000_i210:
18362306a36Sopenharmony_ci	case e1000_i211:
18462306a36Sopenharmony_ci		/* Upper 32 bits contain s, lower 32 bits contain ns. */
18562306a36Sopenharmony_ci		hwtstamps->hwtstamp = ktime_set(systim >> 32,
18662306a36Sopenharmony_ci						systim & 0xFFFFFFFF);
18762306a36Sopenharmony_ci		break;
18862306a36Sopenharmony_ci	default:
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/* PTP clock operations */
19462306a36Sopenharmony_cistatic int igb_ptp_adjfine_82576(struct ptp_clock_info *ptp, long scaled_ppm)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
19762306a36Sopenharmony_ci					       ptp_caps);
19862306a36Sopenharmony_ci	struct e1000_hw *hw = &igb->hw;
19962306a36Sopenharmony_ci	u64 incvalue;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	incvalue = adjust_by_scaled_ppm(INCVALUE_82576, scaled_ppm);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	wr32(E1000_TIMINCA, INCPERIOD_82576 | (incvalue & INCVALUE_82576_MASK));
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return 0;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic int igb_ptp_adjfine_82580(struct ptp_clock_info *ptp, long scaled_ppm)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
21162306a36Sopenharmony_ci					       ptp_caps);
21262306a36Sopenharmony_ci	struct e1000_hw *hw = &igb->hw;
21362306a36Sopenharmony_ci	bool neg_adj;
21462306a36Sopenharmony_ci	u64 rate;
21562306a36Sopenharmony_ci	u32 inca;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	neg_adj = diff_by_scaled_ppm(IGB_82580_BASE_PERIOD, scaled_ppm, &rate);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	inca = rate & INCVALUE_MASK;
22062306a36Sopenharmony_ci	if (neg_adj)
22162306a36Sopenharmony_ci		inca |= ISGN;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	wr32(E1000_TIMINCA, inca);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int igb_ptp_adjtime_82576(struct ptp_clock_info *ptp, s64 delta)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
23162306a36Sopenharmony_ci					       ptp_caps);
23262306a36Sopenharmony_ci	unsigned long flags;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	spin_lock_irqsave(&igb->tmreg_lock, flags);
23562306a36Sopenharmony_ci	timecounter_adjtime(&igb->tc, delta);
23662306a36Sopenharmony_ci	spin_unlock_irqrestore(&igb->tmreg_lock, flags);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return 0;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int igb_ptp_adjtime_i210(struct ptp_clock_info *ptp, s64 delta)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
24462306a36Sopenharmony_ci					       ptp_caps);
24562306a36Sopenharmony_ci	unsigned long flags;
24662306a36Sopenharmony_ci	struct timespec64 now, then = ns_to_timespec64(delta);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	spin_lock_irqsave(&igb->tmreg_lock, flags);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	igb_ptp_read_i210(igb, &now);
25162306a36Sopenharmony_ci	now = timespec64_add(now, then);
25262306a36Sopenharmony_ci	igb_ptp_write_i210(igb, (const struct timespec64 *)&now);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	spin_unlock_irqrestore(&igb->tmreg_lock, flags);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return 0;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int igb_ptp_gettimex_82576(struct ptp_clock_info *ptp,
26062306a36Sopenharmony_ci				  struct timespec64 *ts,
26162306a36Sopenharmony_ci				  struct ptp_system_timestamp *sts)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
26462306a36Sopenharmony_ci					       ptp_caps);
26562306a36Sopenharmony_ci	struct e1000_hw *hw = &igb->hw;
26662306a36Sopenharmony_ci	unsigned long flags;
26762306a36Sopenharmony_ci	u32 lo, hi;
26862306a36Sopenharmony_ci	u64 ns;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	spin_lock_irqsave(&igb->tmreg_lock, flags);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	ptp_read_system_prets(sts);
27362306a36Sopenharmony_ci	lo = rd32(E1000_SYSTIML);
27462306a36Sopenharmony_ci	ptp_read_system_postts(sts);
27562306a36Sopenharmony_ci	hi = rd32(E1000_SYSTIMH);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	ns = timecounter_cyc2time(&igb->tc, ((u64)hi << 32) | lo);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	spin_unlock_irqrestore(&igb->tmreg_lock, flags);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	*ts = ns_to_timespec64(ns);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	return 0;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int igb_ptp_gettimex_82580(struct ptp_clock_info *ptp,
28762306a36Sopenharmony_ci				  struct timespec64 *ts,
28862306a36Sopenharmony_ci				  struct ptp_system_timestamp *sts)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
29162306a36Sopenharmony_ci					       ptp_caps);
29262306a36Sopenharmony_ci	struct e1000_hw *hw = &igb->hw;
29362306a36Sopenharmony_ci	unsigned long flags;
29462306a36Sopenharmony_ci	u32 lo, hi;
29562306a36Sopenharmony_ci	u64 ns;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	spin_lock_irqsave(&igb->tmreg_lock, flags);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	ptp_read_system_prets(sts);
30062306a36Sopenharmony_ci	rd32(E1000_SYSTIMR);
30162306a36Sopenharmony_ci	ptp_read_system_postts(sts);
30262306a36Sopenharmony_ci	lo = rd32(E1000_SYSTIML);
30362306a36Sopenharmony_ci	hi = rd32(E1000_SYSTIMH);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ns = timecounter_cyc2time(&igb->tc, ((u64)hi << 32) | lo);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	spin_unlock_irqrestore(&igb->tmreg_lock, flags);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	*ts = ns_to_timespec64(ns);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return 0;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic int igb_ptp_gettimex_i210(struct ptp_clock_info *ptp,
31562306a36Sopenharmony_ci				 struct timespec64 *ts,
31662306a36Sopenharmony_ci				 struct ptp_system_timestamp *sts)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
31962306a36Sopenharmony_ci					       ptp_caps);
32062306a36Sopenharmony_ci	struct e1000_hw *hw = &igb->hw;
32162306a36Sopenharmony_ci	unsigned long flags;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	spin_lock_irqsave(&igb->tmreg_lock, flags);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	ptp_read_system_prets(sts);
32662306a36Sopenharmony_ci	rd32(E1000_SYSTIMR);
32762306a36Sopenharmony_ci	ptp_read_system_postts(sts);
32862306a36Sopenharmony_ci	ts->tv_nsec = rd32(E1000_SYSTIML);
32962306a36Sopenharmony_ci	ts->tv_sec = rd32(E1000_SYSTIMH);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	spin_unlock_irqrestore(&igb->tmreg_lock, flags);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return 0;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic int igb_ptp_settime_82576(struct ptp_clock_info *ptp,
33762306a36Sopenharmony_ci				 const struct timespec64 *ts)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
34062306a36Sopenharmony_ci					       ptp_caps);
34162306a36Sopenharmony_ci	unsigned long flags;
34262306a36Sopenharmony_ci	u64 ns;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	ns = timespec64_to_ns(ts);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	spin_lock_irqsave(&igb->tmreg_lock, flags);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	timecounter_init(&igb->tc, &igb->cc, ns);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	spin_unlock_irqrestore(&igb->tmreg_lock, flags);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return 0;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic int igb_ptp_settime_i210(struct ptp_clock_info *ptp,
35662306a36Sopenharmony_ci				const struct timespec64 *ts)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
35962306a36Sopenharmony_ci					       ptp_caps);
36062306a36Sopenharmony_ci	unsigned long flags;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	spin_lock_irqsave(&igb->tmreg_lock, flags);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	igb_ptp_write_i210(igb, ts);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	spin_unlock_irqrestore(&igb->tmreg_lock, flags);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic void igb_pin_direction(int pin, int input, u32 *ctrl, u32 *ctrl_ext)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	u32 *ptr = pin < 2 ? ctrl : ctrl_ext;
37462306a36Sopenharmony_ci	static const u32 mask[IGB_N_SDP] = {
37562306a36Sopenharmony_ci		E1000_CTRL_SDP0_DIR,
37662306a36Sopenharmony_ci		E1000_CTRL_SDP1_DIR,
37762306a36Sopenharmony_ci		E1000_CTRL_EXT_SDP2_DIR,
37862306a36Sopenharmony_ci		E1000_CTRL_EXT_SDP3_DIR,
37962306a36Sopenharmony_ci	};
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (input)
38262306a36Sopenharmony_ci		*ptr &= ~mask[pin];
38362306a36Sopenharmony_ci	else
38462306a36Sopenharmony_ci		*ptr |= mask[pin];
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic void igb_pin_extts(struct igb_adapter *igb, int chan, int pin)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	static const u32 aux0_sel_sdp[IGB_N_SDP] = {
39062306a36Sopenharmony_ci		AUX0_SEL_SDP0, AUX0_SEL_SDP1, AUX0_SEL_SDP2, AUX0_SEL_SDP3,
39162306a36Sopenharmony_ci	};
39262306a36Sopenharmony_ci	static const u32 aux1_sel_sdp[IGB_N_SDP] = {
39362306a36Sopenharmony_ci		AUX1_SEL_SDP0, AUX1_SEL_SDP1, AUX1_SEL_SDP2, AUX1_SEL_SDP3,
39462306a36Sopenharmony_ci	};
39562306a36Sopenharmony_ci	static const u32 ts_sdp_en[IGB_N_SDP] = {
39662306a36Sopenharmony_ci		TS_SDP0_EN, TS_SDP1_EN, TS_SDP2_EN, TS_SDP3_EN,
39762306a36Sopenharmony_ci	};
39862306a36Sopenharmony_ci	struct e1000_hw *hw = &igb->hw;
39962306a36Sopenharmony_ci	u32 ctrl, ctrl_ext, tssdp = 0;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	ctrl = rd32(E1000_CTRL);
40262306a36Sopenharmony_ci	ctrl_ext = rd32(E1000_CTRL_EXT);
40362306a36Sopenharmony_ci	tssdp = rd32(E1000_TSSDP);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	igb_pin_direction(pin, 1, &ctrl, &ctrl_ext);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* Make sure this pin is not enabled as an output. */
40862306a36Sopenharmony_ci	tssdp &= ~ts_sdp_en[pin];
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (chan == 1) {
41162306a36Sopenharmony_ci		tssdp &= ~AUX1_SEL_SDP3;
41262306a36Sopenharmony_ci		tssdp |= aux1_sel_sdp[pin] | AUX1_TS_SDP_EN;
41362306a36Sopenharmony_ci	} else {
41462306a36Sopenharmony_ci		tssdp &= ~AUX0_SEL_SDP3;
41562306a36Sopenharmony_ci		tssdp |= aux0_sel_sdp[pin] | AUX0_TS_SDP_EN;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	wr32(E1000_TSSDP, tssdp);
41962306a36Sopenharmony_ci	wr32(E1000_CTRL, ctrl);
42062306a36Sopenharmony_ci	wr32(E1000_CTRL_EXT, ctrl_ext);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic void igb_pin_perout(struct igb_adapter *igb, int chan, int pin, int freq)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	static const u32 aux0_sel_sdp[IGB_N_SDP] = {
42662306a36Sopenharmony_ci		AUX0_SEL_SDP0, AUX0_SEL_SDP1, AUX0_SEL_SDP2, AUX0_SEL_SDP3,
42762306a36Sopenharmony_ci	};
42862306a36Sopenharmony_ci	static const u32 aux1_sel_sdp[IGB_N_SDP] = {
42962306a36Sopenharmony_ci		AUX1_SEL_SDP0, AUX1_SEL_SDP1, AUX1_SEL_SDP2, AUX1_SEL_SDP3,
43062306a36Sopenharmony_ci	};
43162306a36Sopenharmony_ci	static const u32 ts_sdp_en[IGB_N_SDP] = {
43262306a36Sopenharmony_ci		TS_SDP0_EN, TS_SDP1_EN, TS_SDP2_EN, TS_SDP3_EN,
43362306a36Sopenharmony_ci	};
43462306a36Sopenharmony_ci	static const u32 ts_sdp_sel_tt0[IGB_N_SDP] = {
43562306a36Sopenharmony_ci		TS_SDP0_SEL_TT0, TS_SDP1_SEL_TT0,
43662306a36Sopenharmony_ci		TS_SDP2_SEL_TT0, TS_SDP3_SEL_TT0,
43762306a36Sopenharmony_ci	};
43862306a36Sopenharmony_ci	static const u32 ts_sdp_sel_tt1[IGB_N_SDP] = {
43962306a36Sopenharmony_ci		TS_SDP0_SEL_TT1, TS_SDP1_SEL_TT1,
44062306a36Sopenharmony_ci		TS_SDP2_SEL_TT1, TS_SDP3_SEL_TT1,
44162306a36Sopenharmony_ci	};
44262306a36Sopenharmony_ci	static const u32 ts_sdp_sel_fc0[IGB_N_SDP] = {
44362306a36Sopenharmony_ci		TS_SDP0_SEL_FC0, TS_SDP1_SEL_FC0,
44462306a36Sopenharmony_ci		TS_SDP2_SEL_FC0, TS_SDP3_SEL_FC0,
44562306a36Sopenharmony_ci	};
44662306a36Sopenharmony_ci	static const u32 ts_sdp_sel_fc1[IGB_N_SDP] = {
44762306a36Sopenharmony_ci		TS_SDP0_SEL_FC1, TS_SDP1_SEL_FC1,
44862306a36Sopenharmony_ci		TS_SDP2_SEL_FC1, TS_SDP3_SEL_FC1,
44962306a36Sopenharmony_ci	};
45062306a36Sopenharmony_ci	static const u32 ts_sdp_sel_clr[IGB_N_SDP] = {
45162306a36Sopenharmony_ci		TS_SDP0_SEL_FC1, TS_SDP1_SEL_FC1,
45262306a36Sopenharmony_ci		TS_SDP2_SEL_FC1, TS_SDP3_SEL_FC1,
45362306a36Sopenharmony_ci	};
45462306a36Sopenharmony_ci	struct e1000_hw *hw = &igb->hw;
45562306a36Sopenharmony_ci	u32 ctrl, ctrl_ext, tssdp = 0;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	ctrl = rd32(E1000_CTRL);
45862306a36Sopenharmony_ci	ctrl_ext = rd32(E1000_CTRL_EXT);
45962306a36Sopenharmony_ci	tssdp = rd32(E1000_TSSDP);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	igb_pin_direction(pin, 0, &ctrl, &ctrl_ext);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/* Make sure this pin is not enabled as an input. */
46462306a36Sopenharmony_ci	if ((tssdp & AUX0_SEL_SDP3) == aux0_sel_sdp[pin])
46562306a36Sopenharmony_ci		tssdp &= ~AUX0_TS_SDP_EN;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if ((tssdp & AUX1_SEL_SDP3) == aux1_sel_sdp[pin])
46862306a36Sopenharmony_ci		tssdp &= ~AUX1_TS_SDP_EN;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	tssdp &= ~ts_sdp_sel_clr[pin];
47162306a36Sopenharmony_ci	if (freq) {
47262306a36Sopenharmony_ci		if (chan == 1)
47362306a36Sopenharmony_ci			tssdp |= ts_sdp_sel_fc1[pin];
47462306a36Sopenharmony_ci		else
47562306a36Sopenharmony_ci			tssdp |= ts_sdp_sel_fc0[pin];
47662306a36Sopenharmony_ci	} else {
47762306a36Sopenharmony_ci		if (chan == 1)
47862306a36Sopenharmony_ci			tssdp |= ts_sdp_sel_tt1[pin];
47962306a36Sopenharmony_ci		else
48062306a36Sopenharmony_ci			tssdp |= ts_sdp_sel_tt0[pin];
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci	tssdp |= ts_sdp_en[pin];
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	wr32(E1000_TSSDP, tssdp);
48562306a36Sopenharmony_ci	wr32(E1000_CTRL, ctrl);
48662306a36Sopenharmony_ci	wr32(E1000_CTRL_EXT, ctrl_ext);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int igb_ptp_feature_enable_82580(struct ptp_clock_info *ptp,
49062306a36Sopenharmony_ci					struct ptp_clock_request *rq, int on)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct igb_adapter *igb =
49362306a36Sopenharmony_ci		container_of(ptp, struct igb_adapter, ptp_caps);
49462306a36Sopenharmony_ci	u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh, systiml,
49562306a36Sopenharmony_ci		systimh, level_mask, level, rem;
49662306a36Sopenharmony_ci	struct e1000_hw *hw = &igb->hw;
49762306a36Sopenharmony_ci	struct timespec64 ts, start;
49862306a36Sopenharmony_ci	unsigned long flags;
49962306a36Sopenharmony_ci	u64 systim, now;
50062306a36Sopenharmony_ci	int pin = -1;
50162306a36Sopenharmony_ci	s64 ns;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	switch (rq->type) {
50462306a36Sopenharmony_ci	case PTP_CLK_REQ_EXTTS:
50562306a36Sopenharmony_ci		/* Reject requests with unsupported flags */
50662306a36Sopenharmony_ci		if (rq->extts.flags & ~(PTP_ENABLE_FEATURE |
50762306a36Sopenharmony_ci					PTP_RISING_EDGE |
50862306a36Sopenharmony_ci					PTP_FALLING_EDGE |
50962306a36Sopenharmony_ci					PTP_STRICT_FLAGS))
51062306a36Sopenharmony_ci			return -EOPNOTSUPP;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		if (on) {
51362306a36Sopenharmony_ci			pin = ptp_find_pin(igb->ptp_clock, PTP_PF_EXTTS,
51462306a36Sopenharmony_ci					   rq->extts.index);
51562306a36Sopenharmony_ci			if (pin < 0)
51662306a36Sopenharmony_ci				return -EBUSY;
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci		if (rq->extts.index == 1) {
51962306a36Sopenharmony_ci			tsauxc_mask = TSAUXC_EN_TS1;
52062306a36Sopenharmony_ci			tsim_mask = TSINTR_AUTT1;
52162306a36Sopenharmony_ci		} else {
52262306a36Sopenharmony_ci			tsauxc_mask = TSAUXC_EN_TS0;
52362306a36Sopenharmony_ci			tsim_mask = TSINTR_AUTT0;
52462306a36Sopenharmony_ci		}
52562306a36Sopenharmony_ci		spin_lock_irqsave(&igb->tmreg_lock, flags);
52662306a36Sopenharmony_ci		tsauxc = rd32(E1000_TSAUXC);
52762306a36Sopenharmony_ci		tsim = rd32(E1000_TSIM);
52862306a36Sopenharmony_ci		if (on) {
52962306a36Sopenharmony_ci			igb_pin_extts(igb, rq->extts.index, pin);
53062306a36Sopenharmony_ci			tsauxc |= tsauxc_mask;
53162306a36Sopenharmony_ci			tsim |= tsim_mask;
53262306a36Sopenharmony_ci		} else {
53362306a36Sopenharmony_ci			tsauxc &= ~tsauxc_mask;
53462306a36Sopenharmony_ci			tsim &= ~tsim_mask;
53562306a36Sopenharmony_ci		}
53662306a36Sopenharmony_ci		wr32(E1000_TSAUXC, tsauxc);
53762306a36Sopenharmony_ci		wr32(E1000_TSIM, tsim);
53862306a36Sopenharmony_ci		spin_unlock_irqrestore(&igb->tmreg_lock, flags);
53962306a36Sopenharmony_ci		return 0;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	case PTP_CLK_REQ_PEROUT:
54262306a36Sopenharmony_ci		/* Reject requests with unsupported flags */
54362306a36Sopenharmony_ci		if (rq->perout.flags)
54462306a36Sopenharmony_ci			return -EOPNOTSUPP;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		if (on) {
54762306a36Sopenharmony_ci			pin = ptp_find_pin(igb->ptp_clock, PTP_PF_PEROUT,
54862306a36Sopenharmony_ci					   rq->perout.index);
54962306a36Sopenharmony_ci			if (pin < 0)
55062306a36Sopenharmony_ci				return -EBUSY;
55162306a36Sopenharmony_ci		}
55262306a36Sopenharmony_ci		ts.tv_sec = rq->perout.period.sec;
55362306a36Sopenharmony_ci		ts.tv_nsec = rq->perout.period.nsec;
55462306a36Sopenharmony_ci		ns = timespec64_to_ns(&ts);
55562306a36Sopenharmony_ci		ns = ns >> 1;
55662306a36Sopenharmony_ci		if (on && ns < 8LL)
55762306a36Sopenharmony_ci			return -EINVAL;
55862306a36Sopenharmony_ci		ts = ns_to_timespec64(ns);
55962306a36Sopenharmony_ci		if (rq->perout.index == 1) {
56062306a36Sopenharmony_ci			tsauxc_mask = TSAUXC_EN_TT1;
56162306a36Sopenharmony_ci			tsim_mask = TSINTR_TT1;
56262306a36Sopenharmony_ci			trgttiml = E1000_TRGTTIML1;
56362306a36Sopenharmony_ci			trgttimh = E1000_TRGTTIMH1;
56462306a36Sopenharmony_ci		} else {
56562306a36Sopenharmony_ci			tsauxc_mask = TSAUXC_EN_TT0;
56662306a36Sopenharmony_ci			tsim_mask = TSINTR_TT0;
56762306a36Sopenharmony_ci			trgttiml = E1000_TRGTTIML0;
56862306a36Sopenharmony_ci			trgttimh = E1000_TRGTTIMH0;
56962306a36Sopenharmony_ci		}
57062306a36Sopenharmony_ci		spin_lock_irqsave(&igb->tmreg_lock, flags);
57162306a36Sopenharmony_ci		tsauxc = rd32(E1000_TSAUXC);
57262306a36Sopenharmony_ci		tsim = rd32(E1000_TSIM);
57362306a36Sopenharmony_ci		if (rq->perout.index == 1) {
57462306a36Sopenharmony_ci			tsauxc &= ~(TSAUXC_EN_TT1 | TSAUXC_EN_CLK1 | TSAUXC_ST1);
57562306a36Sopenharmony_ci			tsim &= ~TSINTR_TT1;
57662306a36Sopenharmony_ci		} else {
57762306a36Sopenharmony_ci			tsauxc &= ~(TSAUXC_EN_TT0 | TSAUXC_EN_CLK0 | TSAUXC_ST0);
57862306a36Sopenharmony_ci			tsim &= ~TSINTR_TT0;
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci		if (on) {
58162306a36Sopenharmony_ci			int i = rq->perout.index;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci			/* read systim registers in sequence */
58462306a36Sopenharmony_ci			rd32(E1000_SYSTIMR);
58562306a36Sopenharmony_ci			systiml = rd32(E1000_SYSTIML);
58662306a36Sopenharmony_ci			systimh = rd32(E1000_SYSTIMH);
58762306a36Sopenharmony_ci			systim = (((u64)(systimh & 0xFF)) << 32) | ((u64)systiml);
58862306a36Sopenharmony_ci			now = timecounter_cyc2time(&igb->tc, systim);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci			if (pin < 2) {
59162306a36Sopenharmony_ci				level_mask = (i == 1) ? 0x80000 : 0x40000;
59262306a36Sopenharmony_ci				level = (rd32(E1000_CTRL) & level_mask) ? 1 : 0;
59362306a36Sopenharmony_ci			} else {
59462306a36Sopenharmony_ci				level_mask = (i == 1) ? 0x80 : 0x40;
59562306a36Sopenharmony_ci				level = (rd32(E1000_CTRL_EXT) & level_mask) ? 1 : 0;
59662306a36Sopenharmony_ci			}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci			div_u64_rem(now, ns, &rem);
59962306a36Sopenharmony_ci			systim = systim + (ns - rem);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci			/* synchronize pin level with rising/falling edges */
60262306a36Sopenharmony_ci			div_u64_rem(now, ns << 1, &rem);
60362306a36Sopenharmony_ci			if (rem < ns) {
60462306a36Sopenharmony_ci				/* first half of period */
60562306a36Sopenharmony_ci				if (level == 0) {
60662306a36Sopenharmony_ci					/* output is already low, skip this period */
60762306a36Sopenharmony_ci					systim += ns;
60862306a36Sopenharmony_ci				}
60962306a36Sopenharmony_ci			} else {
61062306a36Sopenharmony_ci				/* second half of period */
61162306a36Sopenharmony_ci				if (level == 1) {
61262306a36Sopenharmony_ci					/* output is already high, skip this period */
61362306a36Sopenharmony_ci					systim += ns;
61462306a36Sopenharmony_ci				}
61562306a36Sopenharmony_ci			}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci			start = ns_to_timespec64(systim + (ns - rem));
61862306a36Sopenharmony_ci			igb_pin_perout(igb, i, pin, 0);
61962306a36Sopenharmony_ci			igb->perout[i].start.tv_sec = start.tv_sec;
62062306a36Sopenharmony_ci			igb->perout[i].start.tv_nsec = start.tv_nsec;
62162306a36Sopenharmony_ci			igb->perout[i].period.tv_sec = ts.tv_sec;
62262306a36Sopenharmony_ci			igb->perout[i].period.tv_nsec = ts.tv_nsec;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci			wr32(trgttiml, (u32)systim);
62562306a36Sopenharmony_ci			wr32(trgttimh, ((u32)(systim >> 32)) & 0xFF);
62662306a36Sopenharmony_ci			tsauxc |= tsauxc_mask;
62762306a36Sopenharmony_ci			tsim |= tsim_mask;
62862306a36Sopenharmony_ci		}
62962306a36Sopenharmony_ci		wr32(E1000_TSAUXC, tsauxc);
63062306a36Sopenharmony_ci		wr32(E1000_TSIM, tsim);
63162306a36Sopenharmony_ci		spin_unlock_irqrestore(&igb->tmreg_lock, flags);
63262306a36Sopenharmony_ci		return 0;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	case PTP_CLK_REQ_PPS:
63562306a36Sopenharmony_ci		return -EOPNOTSUPP;
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	return -EOPNOTSUPP;
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp,
64262306a36Sopenharmony_ci				       struct ptp_clock_request *rq, int on)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct igb_adapter *igb =
64562306a36Sopenharmony_ci		container_of(ptp, struct igb_adapter, ptp_caps);
64662306a36Sopenharmony_ci	struct e1000_hw *hw = &igb->hw;
64762306a36Sopenharmony_ci	u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh, freqout;
64862306a36Sopenharmony_ci	unsigned long flags;
64962306a36Sopenharmony_ci	struct timespec64 ts;
65062306a36Sopenharmony_ci	int use_freq = 0, pin = -1;
65162306a36Sopenharmony_ci	s64 ns;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	switch (rq->type) {
65462306a36Sopenharmony_ci	case PTP_CLK_REQ_EXTTS:
65562306a36Sopenharmony_ci		/* Reject requests with unsupported flags */
65662306a36Sopenharmony_ci		if (rq->extts.flags & ~(PTP_ENABLE_FEATURE |
65762306a36Sopenharmony_ci					PTP_RISING_EDGE |
65862306a36Sopenharmony_ci					PTP_FALLING_EDGE |
65962306a36Sopenharmony_ci					PTP_STRICT_FLAGS))
66062306a36Sopenharmony_ci			return -EOPNOTSUPP;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		/* Reject requests failing to enable both edges. */
66362306a36Sopenharmony_ci		if ((rq->extts.flags & PTP_STRICT_FLAGS) &&
66462306a36Sopenharmony_ci		    (rq->extts.flags & PTP_ENABLE_FEATURE) &&
66562306a36Sopenharmony_ci		    (rq->extts.flags & PTP_EXTTS_EDGES) != PTP_EXTTS_EDGES)
66662306a36Sopenharmony_ci			return -EOPNOTSUPP;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci		if (on) {
66962306a36Sopenharmony_ci			pin = ptp_find_pin(igb->ptp_clock, PTP_PF_EXTTS,
67062306a36Sopenharmony_ci					   rq->extts.index);
67162306a36Sopenharmony_ci			if (pin < 0)
67262306a36Sopenharmony_ci				return -EBUSY;
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci		if (rq->extts.index == 1) {
67562306a36Sopenharmony_ci			tsauxc_mask = TSAUXC_EN_TS1;
67662306a36Sopenharmony_ci			tsim_mask = TSINTR_AUTT1;
67762306a36Sopenharmony_ci		} else {
67862306a36Sopenharmony_ci			tsauxc_mask = TSAUXC_EN_TS0;
67962306a36Sopenharmony_ci			tsim_mask = TSINTR_AUTT0;
68062306a36Sopenharmony_ci		}
68162306a36Sopenharmony_ci		spin_lock_irqsave(&igb->tmreg_lock, flags);
68262306a36Sopenharmony_ci		tsauxc = rd32(E1000_TSAUXC);
68362306a36Sopenharmony_ci		tsim = rd32(E1000_TSIM);
68462306a36Sopenharmony_ci		if (on) {
68562306a36Sopenharmony_ci			igb_pin_extts(igb, rq->extts.index, pin);
68662306a36Sopenharmony_ci			tsauxc |= tsauxc_mask;
68762306a36Sopenharmony_ci			tsim |= tsim_mask;
68862306a36Sopenharmony_ci		} else {
68962306a36Sopenharmony_ci			tsauxc &= ~tsauxc_mask;
69062306a36Sopenharmony_ci			tsim &= ~tsim_mask;
69162306a36Sopenharmony_ci		}
69262306a36Sopenharmony_ci		wr32(E1000_TSAUXC, tsauxc);
69362306a36Sopenharmony_ci		wr32(E1000_TSIM, tsim);
69462306a36Sopenharmony_ci		spin_unlock_irqrestore(&igb->tmreg_lock, flags);
69562306a36Sopenharmony_ci		return 0;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	case PTP_CLK_REQ_PEROUT:
69862306a36Sopenharmony_ci		/* Reject requests with unsupported flags */
69962306a36Sopenharmony_ci		if (rq->perout.flags)
70062306a36Sopenharmony_ci			return -EOPNOTSUPP;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		if (on) {
70362306a36Sopenharmony_ci			pin = ptp_find_pin(igb->ptp_clock, PTP_PF_PEROUT,
70462306a36Sopenharmony_ci					   rq->perout.index);
70562306a36Sopenharmony_ci			if (pin < 0)
70662306a36Sopenharmony_ci				return -EBUSY;
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci		ts.tv_sec = rq->perout.period.sec;
70962306a36Sopenharmony_ci		ts.tv_nsec = rq->perout.period.nsec;
71062306a36Sopenharmony_ci		ns = timespec64_to_ns(&ts);
71162306a36Sopenharmony_ci		ns = ns >> 1;
71262306a36Sopenharmony_ci		if (on && ((ns <= 70000000LL) || (ns == 125000000LL) ||
71362306a36Sopenharmony_ci			   (ns == 250000000LL) || (ns == 500000000LL))) {
71462306a36Sopenharmony_ci			if (ns < 8LL)
71562306a36Sopenharmony_ci				return -EINVAL;
71662306a36Sopenharmony_ci			use_freq = 1;
71762306a36Sopenharmony_ci		}
71862306a36Sopenharmony_ci		ts = ns_to_timespec64(ns);
71962306a36Sopenharmony_ci		if (rq->perout.index == 1) {
72062306a36Sopenharmony_ci			if (use_freq) {
72162306a36Sopenharmony_ci				tsauxc_mask = TSAUXC_EN_CLK1 | TSAUXC_ST1;
72262306a36Sopenharmony_ci				tsim_mask = 0;
72362306a36Sopenharmony_ci			} else {
72462306a36Sopenharmony_ci				tsauxc_mask = TSAUXC_EN_TT1;
72562306a36Sopenharmony_ci				tsim_mask = TSINTR_TT1;
72662306a36Sopenharmony_ci			}
72762306a36Sopenharmony_ci			trgttiml = E1000_TRGTTIML1;
72862306a36Sopenharmony_ci			trgttimh = E1000_TRGTTIMH1;
72962306a36Sopenharmony_ci			freqout = E1000_FREQOUT1;
73062306a36Sopenharmony_ci		} else {
73162306a36Sopenharmony_ci			if (use_freq) {
73262306a36Sopenharmony_ci				tsauxc_mask = TSAUXC_EN_CLK0 | TSAUXC_ST0;
73362306a36Sopenharmony_ci				tsim_mask = 0;
73462306a36Sopenharmony_ci			} else {
73562306a36Sopenharmony_ci				tsauxc_mask = TSAUXC_EN_TT0;
73662306a36Sopenharmony_ci				tsim_mask = TSINTR_TT0;
73762306a36Sopenharmony_ci			}
73862306a36Sopenharmony_ci			trgttiml = E1000_TRGTTIML0;
73962306a36Sopenharmony_ci			trgttimh = E1000_TRGTTIMH0;
74062306a36Sopenharmony_ci			freqout = E1000_FREQOUT0;
74162306a36Sopenharmony_ci		}
74262306a36Sopenharmony_ci		spin_lock_irqsave(&igb->tmreg_lock, flags);
74362306a36Sopenharmony_ci		tsauxc = rd32(E1000_TSAUXC);
74462306a36Sopenharmony_ci		tsim = rd32(E1000_TSIM);
74562306a36Sopenharmony_ci		if (rq->perout.index == 1) {
74662306a36Sopenharmony_ci			tsauxc &= ~(TSAUXC_EN_TT1 | TSAUXC_EN_CLK1 | TSAUXC_ST1);
74762306a36Sopenharmony_ci			tsim &= ~TSINTR_TT1;
74862306a36Sopenharmony_ci		} else {
74962306a36Sopenharmony_ci			tsauxc &= ~(TSAUXC_EN_TT0 | TSAUXC_EN_CLK0 | TSAUXC_ST0);
75062306a36Sopenharmony_ci			tsim &= ~TSINTR_TT0;
75162306a36Sopenharmony_ci		}
75262306a36Sopenharmony_ci		if (on) {
75362306a36Sopenharmony_ci			int i = rq->perout.index;
75462306a36Sopenharmony_ci			igb_pin_perout(igb, i, pin, use_freq);
75562306a36Sopenharmony_ci			igb->perout[i].start.tv_sec = rq->perout.start.sec;
75662306a36Sopenharmony_ci			igb->perout[i].start.tv_nsec = rq->perout.start.nsec;
75762306a36Sopenharmony_ci			igb->perout[i].period.tv_sec = ts.tv_sec;
75862306a36Sopenharmony_ci			igb->perout[i].period.tv_nsec = ts.tv_nsec;
75962306a36Sopenharmony_ci			wr32(trgttimh, rq->perout.start.sec);
76062306a36Sopenharmony_ci			wr32(trgttiml, rq->perout.start.nsec);
76162306a36Sopenharmony_ci			if (use_freq)
76262306a36Sopenharmony_ci				wr32(freqout, ns);
76362306a36Sopenharmony_ci			tsauxc |= tsauxc_mask;
76462306a36Sopenharmony_ci			tsim |= tsim_mask;
76562306a36Sopenharmony_ci		}
76662306a36Sopenharmony_ci		wr32(E1000_TSAUXC, tsauxc);
76762306a36Sopenharmony_ci		wr32(E1000_TSIM, tsim);
76862306a36Sopenharmony_ci		spin_unlock_irqrestore(&igb->tmreg_lock, flags);
76962306a36Sopenharmony_ci		return 0;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	case PTP_CLK_REQ_PPS:
77262306a36Sopenharmony_ci		spin_lock_irqsave(&igb->tmreg_lock, flags);
77362306a36Sopenharmony_ci		tsim = rd32(E1000_TSIM);
77462306a36Sopenharmony_ci		if (on)
77562306a36Sopenharmony_ci			tsim |= TSINTR_SYS_WRAP;
77662306a36Sopenharmony_ci		else
77762306a36Sopenharmony_ci			tsim &= ~TSINTR_SYS_WRAP;
77862306a36Sopenharmony_ci		igb->pps_sys_wrap_on = !!on;
77962306a36Sopenharmony_ci		wr32(E1000_TSIM, tsim);
78062306a36Sopenharmony_ci		spin_unlock_irqrestore(&igb->tmreg_lock, flags);
78162306a36Sopenharmony_ci		return 0;
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	return -EOPNOTSUPP;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_cistatic int igb_ptp_feature_enable(struct ptp_clock_info *ptp,
78862306a36Sopenharmony_ci				  struct ptp_clock_request *rq, int on)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	return -EOPNOTSUPP;
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_cistatic int igb_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
79462306a36Sopenharmony_ci			      enum ptp_pin_function func, unsigned int chan)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	switch (func) {
79762306a36Sopenharmony_ci	case PTP_PF_NONE:
79862306a36Sopenharmony_ci	case PTP_PF_EXTTS:
79962306a36Sopenharmony_ci	case PTP_PF_PEROUT:
80062306a36Sopenharmony_ci		break;
80162306a36Sopenharmony_ci	case PTP_PF_PHYSYNC:
80262306a36Sopenharmony_ci		return -1;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci	return 0;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci/**
80862306a36Sopenharmony_ci * igb_ptp_tx_work
80962306a36Sopenharmony_ci * @work: pointer to work struct
81062306a36Sopenharmony_ci *
81162306a36Sopenharmony_ci * This work function polls the TSYNCTXCTL valid bit to determine when a
81262306a36Sopenharmony_ci * timestamp has been taken for the current stored skb.
81362306a36Sopenharmony_ci **/
81462306a36Sopenharmony_cistatic void igb_ptp_tx_work(struct work_struct *work)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	struct igb_adapter *adapter = container_of(work, struct igb_adapter,
81762306a36Sopenharmony_ci						   ptp_tx_work);
81862306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
81962306a36Sopenharmony_ci	u32 tsynctxctl;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	if (!adapter->ptp_tx_skb)
82262306a36Sopenharmony_ci		return;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	if (time_is_before_jiffies(adapter->ptp_tx_start +
82562306a36Sopenharmony_ci				   IGB_PTP_TX_TIMEOUT)) {
82662306a36Sopenharmony_ci		dev_kfree_skb_any(adapter->ptp_tx_skb);
82762306a36Sopenharmony_ci		adapter->ptp_tx_skb = NULL;
82862306a36Sopenharmony_ci		clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state);
82962306a36Sopenharmony_ci		adapter->tx_hwtstamp_timeouts++;
83062306a36Sopenharmony_ci		/* Clear the tx valid bit in TSYNCTXCTL register to enable
83162306a36Sopenharmony_ci		 * interrupt
83262306a36Sopenharmony_ci		 */
83362306a36Sopenharmony_ci		rd32(E1000_TXSTMPH);
83462306a36Sopenharmony_ci		dev_warn(&adapter->pdev->dev, "clearing Tx timestamp hang\n");
83562306a36Sopenharmony_ci		return;
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	tsynctxctl = rd32(E1000_TSYNCTXCTL);
83962306a36Sopenharmony_ci	if (tsynctxctl & E1000_TSYNCTXCTL_VALID)
84062306a36Sopenharmony_ci		igb_ptp_tx_hwtstamp(adapter);
84162306a36Sopenharmony_ci	else
84262306a36Sopenharmony_ci		/* reschedule to check later */
84362306a36Sopenharmony_ci		schedule_work(&adapter->ptp_tx_work);
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic void igb_ptp_overflow_check(struct work_struct *work)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct igb_adapter *igb =
84962306a36Sopenharmony_ci		container_of(work, struct igb_adapter, ptp_overflow_work.work);
85062306a36Sopenharmony_ci	struct timespec64 ts;
85162306a36Sopenharmony_ci	u64 ns;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	/* Update the timecounter */
85462306a36Sopenharmony_ci	ns = timecounter_read(&igb->tc);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	ts = ns_to_timespec64(ns);
85762306a36Sopenharmony_ci	pr_debug("igb overflow check at %lld.%09lu\n",
85862306a36Sopenharmony_ci		 (long long) ts.tv_sec, ts.tv_nsec);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	schedule_delayed_work(&igb->ptp_overflow_work,
86162306a36Sopenharmony_ci			      IGB_SYSTIM_OVERFLOW_PERIOD);
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci/**
86562306a36Sopenharmony_ci * igb_ptp_rx_hang - detect error case when Rx timestamp registers latched
86662306a36Sopenharmony_ci * @adapter: private network adapter structure
86762306a36Sopenharmony_ci *
86862306a36Sopenharmony_ci * This watchdog task is scheduled to detect error case where hardware has
86962306a36Sopenharmony_ci * dropped an Rx packet that was timestamped when the ring is full. The
87062306a36Sopenharmony_ci * particular error is rare but leaves the device in a state unable to timestamp
87162306a36Sopenharmony_ci * any future packets.
87262306a36Sopenharmony_ci **/
87362306a36Sopenharmony_civoid igb_ptp_rx_hang(struct igb_adapter *adapter)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
87662306a36Sopenharmony_ci	u32 tsyncrxctl = rd32(E1000_TSYNCRXCTL);
87762306a36Sopenharmony_ci	unsigned long rx_event;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	/* Other hardware uses per-packet timestamps */
88062306a36Sopenharmony_ci	if (hw->mac.type != e1000_82576)
88162306a36Sopenharmony_ci		return;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* If we don't have a valid timestamp in the registers, just update the
88462306a36Sopenharmony_ci	 * timeout counter and exit
88562306a36Sopenharmony_ci	 */
88662306a36Sopenharmony_ci	if (!(tsyncrxctl & E1000_TSYNCRXCTL_VALID)) {
88762306a36Sopenharmony_ci		adapter->last_rx_ptp_check = jiffies;
88862306a36Sopenharmony_ci		return;
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	/* Determine the most recent watchdog or rx_timestamp event */
89262306a36Sopenharmony_ci	rx_event = adapter->last_rx_ptp_check;
89362306a36Sopenharmony_ci	if (time_after(adapter->last_rx_timestamp, rx_event))
89462306a36Sopenharmony_ci		rx_event = adapter->last_rx_timestamp;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	/* Only need to read the high RXSTMP register to clear the lock */
89762306a36Sopenharmony_ci	if (time_is_before_jiffies(rx_event + 5 * HZ)) {
89862306a36Sopenharmony_ci		rd32(E1000_RXSTMPH);
89962306a36Sopenharmony_ci		adapter->last_rx_ptp_check = jiffies;
90062306a36Sopenharmony_ci		adapter->rx_hwtstamp_cleared++;
90162306a36Sopenharmony_ci		dev_warn(&adapter->pdev->dev, "clearing Rx timestamp hang\n");
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci/**
90662306a36Sopenharmony_ci * igb_ptp_tx_hang - detect error case where Tx timestamp never finishes
90762306a36Sopenharmony_ci * @adapter: private network adapter structure
90862306a36Sopenharmony_ci */
90962306a36Sopenharmony_civoid igb_ptp_tx_hang(struct igb_adapter *adapter)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
91262306a36Sopenharmony_ci	bool timeout = time_is_before_jiffies(adapter->ptp_tx_start +
91362306a36Sopenharmony_ci					      IGB_PTP_TX_TIMEOUT);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (!adapter->ptp_tx_skb)
91662306a36Sopenharmony_ci		return;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (!test_bit(__IGB_PTP_TX_IN_PROGRESS, &adapter->state))
91962306a36Sopenharmony_ci		return;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	/* If we haven't received a timestamp within the timeout, it is
92262306a36Sopenharmony_ci	 * reasonable to assume that it will never occur, so we can unlock the
92362306a36Sopenharmony_ci	 * timestamp bit when this occurs.
92462306a36Sopenharmony_ci	 */
92562306a36Sopenharmony_ci	if (timeout) {
92662306a36Sopenharmony_ci		cancel_work_sync(&adapter->ptp_tx_work);
92762306a36Sopenharmony_ci		dev_kfree_skb_any(adapter->ptp_tx_skb);
92862306a36Sopenharmony_ci		adapter->ptp_tx_skb = NULL;
92962306a36Sopenharmony_ci		clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state);
93062306a36Sopenharmony_ci		adapter->tx_hwtstamp_timeouts++;
93162306a36Sopenharmony_ci		/* Clear the tx valid bit in TSYNCTXCTL register to enable
93262306a36Sopenharmony_ci		 * interrupt
93362306a36Sopenharmony_ci		 */
93462306a36Sopenharmony_ci		rd32(E1000_TXSTMPH);
93562306a36Sopenharmony_ci		dev_warn(&adapter->pdev->dev, "clearing Tx timestamp hang\n");
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci/**
94062306a36Sopenharmony_ci * igb_ptp_tx_hwtstamp - utility function which checks for TX time stamp
94162306a36Sopenharmony_ci * @adapter: Board private structure.
94262306a36Sopenharmony_ci *
94362306a36Sopenharmony_ci * If we were asked to do hardware stamping and such a time stamp is
94462306a36Sopenharmony_ci * available, then it must have been for this skb here because we only
94562306a36Sopenharmony_ci * allow only one such packet into the queue.
94662306a36Sopenharmony_ci **/
94762306a36Sopenharmony_cistatic void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct sk_buff *skb = adapter->ptp_tx_skb;
95062306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
95162306a36Sopenharmony_ci	struct skb_shared_hwtstamps shhwtstamps;
95262306a36Sopenharmony_ci	u64 regval;
95362306a36Sopenharmony_ci	int adjust = 0;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	regval = rd32(E1000_TXSTMPL);
95662306a36Sopenharmony_ci	regval |= (u64)rd32(E1000_TXSTMPH) << 32;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	igb_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval);
95962306a36Sopenharmony_ci	/* adjust timestamp for the TX latency based on link speed */
96062306a36Sopenharmony_ci	if (hw->mac.type == e1000_i210 || hw->mac.type == e1000_i211) {
96162306a36Sopenharmony_ci		switch (adapter->link_speed) {
96262306a36Sopenharmony_ci		case SPEED_10:
96362306a36Sopenharmony_ci			adjust = IGB_I210_TX_LATENCY_10;
96462306a36Sopenharmony_ci			break;
96562306a36Sopenharmony_ci		case SPEED_100:
96662306a36Sopenharmony_ci			adjust = IGB_I210_TX_LATENCY_100;
96762306a36Sopenharmony_ci			break;
96862306a36Sopenharmony_ci		case SPEED_1000:
96962306a36Sopenharmony_ci			adjust = IGB_I210_TX_LATENCY_1000;
97062306a36Sopenharmony_ci			break;
97162306a36Sopenharmony_ci		}
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	shhwtstamps.hwtstamp =
97562306a36Sopenharmony_ci		ktime_add_ns(shhwtstamps.hwtstamp, adjust);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	/* Clear the lock early before calling skb_tstamp_tx so that
97862306a36Sopenharmony_ci	 * applications are not woken up before the lock bit is clear. We use
97962306a36Sopenharmony_ci	 * a copy of the skb pointer to ensure other threads can't change it
98062306a36Sopenharmony_ci	 * while we're notifying the stack.
98162306a36Sopenharmony_ci	 */
98262306a36Sopenharmony_ci	adapter->ptp_tx_skb = NULL;
98362306a36Sopenharmony_ci	clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	/* Notify the stack and free the skb after we've unlocked */
98662306a36Sopenharmony_ci	skb_tstamp_tx(skb, &shhwtstamps);
98762306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci/**
99162306a36Sopenharmony_ci * igb_ptp_rx_pktstamp - retrieve Rx per packet timestamp
99262306a36Sopenharmony_ci * @q_vector: Pointer to interrupt specific structure
99362306a36Sopenharmony_ci * @va: Pointer to address containing Rx buffer
99462306a36Sopenharmony_ci * @timestamp: Pointer where timestamp will be stored
99562306a36Sopenharmony_ci *
99662306a36Sopenharmony_ci * This function is meant to retrieve a timestamp from the first buffer of an
99762306a36Sopenharmony_ci * incoming frame.  The value is stored in little endian format starting on
99862306a36Sopenharmony_ci * byte 8
99962306a36Sopenharmony_ci *
100062306a36Sopenharmony_ci * Returns: The timestamp header length or 0 if not available
100162306a36Sopenharmony_ci **/
100262306a36Sopenharmony_ciint igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, void *va,
100362306a36Sopenharmony_ci			ktime_t *timestamp)
100462306a36Sopenharmony_ci{
100562306a36Sopenharmony_ci	struct igb_adapter *adapter = q_vector->adapter;
100662306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
100762306a36Sopenharmony_ci	struct skb_shared_hwtstamps ts;
100862306a36Sopenharmony_ci	__le64 *regval = (__le64 *)va;
100962306a36Sopenharmony_ci	int adjust = 0;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	if (!(adapter->ptp_flags & IGB_PTP_ENABLED))
101262306a36Sopenharmony_ci		return 0;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	/* The timestamp is recorded in little endian format.
101562306a36Sopenharmony_ci	 * DWORD: 0        1        2        3
101662306a36Sopenharmony_ci	 * Field: Reserved Reserved SYSTIML  SYSTIMH
101762306a36Sopenharmony_ci	 */
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	/* check reserved dwords are zero, be/le doesn't matter for zero */
102062306a36Sopenharmony_ci	if (regval[0])
102162306a36Sopenharmony_ci		return 0;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	igb_ptp_systim_to_hwtstamp(adapter, &ts, le64_to_cpu(regval[1]));
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	/* adjust timestamp for the RX latency based on link speed */
102662306a36Sopenharmony_ci	if (hw->mac.type == e1000_i210 || hw->mac.type == e1000_i211) {
102762306a36Sopenharmony_ci		switch (adapter->link_speed) {
102862306a36Sopenharmony_ci		case SPEED_10:
102962306a36Sopenharmony_ci			adjust = IGB_I210_RX_LATENCY_10;
103062306a36Sopenharmony_ci			break;
103162306a36Sopenharmony_ci		case SPEED_100:
103262306a36Sopenharmony_ci			adjust = IGB_I210_RX_LATENCY_100;
103362306a36Sopenharmony_ci			break;
103462306a36Sopenharmony_ci		case SPEED_1000:
103562306a36Sopenharmony_ci			adjust = IGB_I210_RX_LATENCY_1000;
103662306a36Sopenharmony_ci			break;
103762306a36Sopenharmony_ci		}
103862306a36Sopenharmony_ci	}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	*timestamp = ktime_sub_ns(ts.hwtstamp, adjust);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	return IGB_TS_HDR_LEN;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci/**
104662306a36Sopenharmony_ci * igb_ptp_rx_rgtstamp - retrieve Rx timestamp stored in register
104762306a36Sopenharmony_ci * @q_vector: Pointer to interrupt specific structure
104862306a36Sopenharmony_ci * @skb: Buffer containing timestamp and packet
104962306a36Sopenharmony_ci *
105062306a36Sopenharmony_ci * This function is meant to retrieve a timestamp from the internal registers
105162306a36Sopenharmony_ci * of the adapter and store it in the skb.
105262306a36Sopenharmony_ci **/
105362306a36Sopenharmony_civoid igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, struct sk_buff *skb)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	struct igb_adapter *adapter = q_vector->adapter;
105662306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
105762306a36Sopenharmony_ci	int adjust = 0;
105862306a36Sopenharmony_ci	u64 regval;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (!(adapter->ptp_flags & IGB_PTP_ENABLED))
106162306a36Sopenharmony_ci		return;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	/* If this bit is set, then the RX registers contain the time stamp. No
106462306a36Sopenharmony_ci	 * other packet will be time stamped until we read these registers, so
106562306a36Sopenharmony_ci	 * read the registers to make them available again. Because only one
106662306a36Sopenharmony_ci	 * packet can be time stamped at a time, we know that the register
106762306a36Sopenharmony_ci	 * values must belong to this one here and therefore we don't need to
106862306a36Sopenharmony_ci	 * compare any of the additional attributes stored for it.
106962306a36Sopenharmony_ci	 *
107062306a36Sopenharmony_ci	 * If nothing went wrong, then it should have a shared tx_flags that we
107162306a36Sopenharmony_ci	 * can turn into a skb_shared_hwtstamps.
107262306a36Sopenharmony_ci	 */
107362306a36Sopenharmony_ci	if (!(rd32(E1000_TSYNCRXCTL) & E1000_TSYNCRXCTL_VALID))
107462306a36Sopenharmony_ci		return;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	regval = rd32(E1000_RXSTMPL);
107762306a36Sopenharmony_ci	regval |= (u64)rd32(E1000_RXSTMPH) << 32;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	igb_ptp_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	/* adjust timestamp for the RX latency based on link speed */
108262306a36Sopenharmony_ci	if (adapter->hw.mac.type == e1000_i210) {
108362306a36Sopenharmony_ci		switch (adapter->link_speed) {
108462306a36Sopenharmony_ci		case SPEED_10:
108562306a36Sopenharmony_ci			adjust = IGB_I210_RX_LATENCY_10;
108662306a36Sopenharmony_ci			break;
108762306a36Sopenharmony_ci		case SPEED_100:
108862306a36Sopenharmony_ci			adjust = IGB_I210_RX_LATENCY_100;
108962306a36Sopenharmony_ci			break;
109062306a36Sopenharmony_ci		case SPEED_1000:
109162306a36Sopenharmony_ci			adjust = IGB_I210_RX_LATENCY_1000;
109262306a36Sopenharmony_ci			break;
109362306a36Sopenharmony_ci		}
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci	skb_hwtstamps(skb)->hwtstamp =
109662306a36Sopenharmony_ci		ktime_sub_ns(skb_hwtstamps(skb)->hwtstamp, adjust);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	/* Update the last_rx_timestamp timer in order to enable watchdog check
109962306a36Sopenharmony_ci	 * for error case of latched timestamp on a dropped packet.
110062306a36Sopenharmony_ci	 */
110162306a36Sopenharmony_ci	adapter->last_rx_timestamp = jiffies;
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci/**
110562306a36Sopenharmony_ci * igb_ptp_get_ts_config - get hardware time stamping config
110662306a36Sopenharmony_ci * @netdev: netdev struct
110762306a36Sopenharmony_ci * @ifr: interface struct
110862306a36Sopenharmony_ci *
110962306a36Sopenharmony_ci * Get the hwtstamp_config settings to return to the user. Rather than attempt
111062306a36Sopenharmony_ci * to deconstruct the settings from the registers, just return a shadow copy
111162306a36Sopenharmony_ci * of the last known settings.
111262306a36Sopenharmony_ci **/
111362306a36Sopenharmony_ciint igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
111462306a36Sopenharmony_ci{
111562306a36Sopenharmony_ci	struct igb_adapter *adapter = netdev_priv(netdev);
111662306a36Sopenharmony_ci	struct hwtstamp_config *config = &adapter->tstamp_config;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
111962306a36Sopenharmony_ci		-EFAULT : 0;
112062306a36Sopenharmony_ci}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci/**
112362306a36Sopenharmony_ci * igb_ptp_set_timestamp_mode - setup hardware for timestamping
112462306a36Sopenharmony_ci * @adapter: networking device structure
112562306a36Sopenharmony_ci * @config: hwtstamp configuration
112662306a36Sopenharmony_ci *
112762306a36Sopenharmony_ci * Outgoing time stamping can be enabled and disabled. Play nice and
112862306a36Sopenharmony_ci * disable it when requested, although it shouldn't case any overhead
112962306a36Sopenharmony_ci * when no packet needs it. At most one packet in the queue may be
113062306a36Sopenharmony_ci * marked for time stamping, otherwise it would be impossible to tell
113162306a36Sopenharmony_ci * for sure to which packet the hardware time stamp belongs.
113262306a36Sopenharmony_ci *
113362306a36Sopenharmony_ci * Incoming time stamping has to be configured via the hardware
113462306a36Sopenharmony_ci * filters. Not all combinations are supported, in particular event
113562306a36Sopenharmony_ci * type has to be specified. Matching the kind of event packet is
113662306a36Sopenharmony_ci * not supported, with the exception of "all V2 events regardless of
113762306a36Sopenharmony_ci * level 2 or 4".
113862306a36Sopenharmony_ci */
113962306a36Sopenharmony_cistatic int igb_ptp_set_timestamp_mode(struct igb_adapter *adapter,
114062306a36Sopenharmony_ci				      struct hwtstamp_config *config)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
114362306a36Sopenharmony_ci	u32 tsync_tx_ctl = E1000_TSYNCTXCTL_ENABLED;
114462306a36Sopenharmony_ci	u32 tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
114562306a36Sopenharmony_ci	u32 tsync_rx_cfg = 0;
114662306a36Sopenharmony_ci	bool is_l4 = false;
114762306a36Sopenharmony_ci	bool is_l2 = false;
114862306a36Sopenharmony_ci	u32 regval;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	switch (config->tx_type) {
115162306a36Sopenharmony_ci	case HWTSTAMP_TX_OFF:
115262306a36Sopenharmony_ci		tsync_tx_ctl = 0;
115362306a36Sopenharmony_ci		break;
115462306a36Sopenharmony_ci	case HWTSTAMP_TX_ON:
115562306a36Sopenharmony_ci		break;
115662306a36Sopenharmony_ci	default:
115762306a36Sopenharmony_ci		return -ERANGE;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	switch (config->rx_filter) {
116162306a36Sopenharmony_ci	case HWTSTAMP_FILTER_NONE:
116262306a36Sopenharmony_ci		tsync_rx_ctl = 0;
116362306a36Sopenharmony_ci		break;
116462306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
116562306a36Sopenharmony_ci		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L4_V1;
116662306a36Sopenharmony_ci		tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE;
116762306a36Sopenharmony_ci		is_l4 = true;
116862306a36Sopenharmony_ci		break;
116962306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
117062306a36Sopenharmony_ci		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L4_V1;
117162306a36Sopenharmony_ci		tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE;
117262306a36Sopenharmony_ci		is_l4 = true;
117362306a36Sopenharmony_ci		break;
117462306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_EVENT:
117562306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
117662306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
117762306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_SYNC:
117862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
117962306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
118062306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
118162306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
118262306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
118362306a36Sopenharmony_ci		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_EVENT_V2;
118462306a36Sopenharmony_ci		config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
118562306a36Sopenharmony_ci		is_l2 = true;
118662306a36Sopenharmony_ci		is_l4 = true;
118762306a36Sopenharmony_ci		break;
118862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
118962306a36Sopenharmony_ci	case HWTSTAMP_FILTER_NTP_ALL:
119062306a36Sopenharmony_ci	case HWTSTAMP_FILTER_ALL:
119162306a36Sopenharmony_ci		/* 82576 cannot timestamp all packets, which it needs to do to
119262306a36Sopenharmony_ci		 * support both V1 Sync and Delay_Req messages
119362306a36Sopenharmony_ci		 */
119462306a36Sopenharmony_ci		if (hw->mac.type != e1000_82576) {
119562306a36Sopenharmony_ci			tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL;
119662306a36Sopenharmony_ci			config->rx_filter = HWTSTAMP_FILTER_ALL;
119762306a36Sopenharmony_ci			break;
119862306a36Sopenharmony_ci		}
119962306a36Sopenharmony_ci		fallthrough;
120062306a36Sopenharmony_ci	default:
120162306a36Sopenharmony_ci		config->rx_filter = HWTSTAMP_FILTER_NONE;
120262306a36Sopenharmony_ci		return -ERANGE;
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	if (hw->mac.type == e1000_82575) {
120662306a36Sopenharmony_ci		if (tsync_rx_ctl | tsync_tx_ctl)
120762306a36Sopenharmony_ci			return -EINVAL;
120862306a36Sopenharmony_ci		return 0;
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	/* Per-packet timestamping only works if all packets are
121262306a36Sopenharmony_ci	 * timestamped, so enable timestamping in all packets as
121362306a36Sopenharmony_ci	 * long as one Rx filter was configured.
121462306a36Sopenharmony_ci	 */
121562306a36Sopenharmony_ci	if ((hw->mac.type >= e1000_82580) && tsync_rx_ctl) {
121662306a36Sopenharmony_ci		tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
121762306a36Sopenharmony_ci		tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL;
121862306a36Sopenharmony_ci		config->rx_filter = HWTSTAMP_FILTER_ALL;
121962306a36Sopenharmony_ci		is_l2 = true;
122062306a36Sopenharmony_ci		is_l4 = true;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		if ((hw->mac.type == e1000_i210) ||
122362306a36Sopenharmony_ci		    (hw->mac.type == e1000_i211)) {
122462306a36Sopenharmony_ci			regval = rd32(E1000_RXPBS);
122562306a36Sopenharmony_ci			regval |= E1000_RXPBS_CFG_TS_EN;
122662306a36Sopenharmony_ci			wr32(E1000_RXPBS, regval);
122762306a36Sopenharmony_ci		}
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	/* enable/disable TX */
123162306a36Sopenharmony_ci	regval = rd32(E1000_TSYNCTXCTL);
123262306a36Sopenharmony_ci	regval &= ~E1000_TSYNCTXCTL_ENABLED;
123362306a36Sopenharmony_ci	regval |= tsync_tx_ctl;
123462306a36Sopenharmony_ci	wr32(E1000_TSYNCTXCTL, regval);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	/* enable/disable RX */
123762306a36Sopenharmony_ci	regval = rd32(E1000_TSYNCRXCTL);
123862306a36Sopenharmony_ci	regval &= ~(E1000_TSYNCRXCTL_ENABLED | E1000_TSYNCRXCTL_TYPE_MASK);
123962306a36Sopenharmony_ci	regval |= tsync_rx_ctl;
124062306a36Sopenharmony_ci	wr32(E1000_TSYNCRXCTL, regval);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	/* define which PTP packets are time stamped */
124362306a36Sopenharmony_ci	wr32(E1000_TSYNCRXCFG, tsync_rx_cfg);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	/* define ethertype filter for timestamped packets */
124662306a36Sopenharmony_ci	if (is_l2)
124762306a36Sopenharmony_ci		wr32(E1000_ETQF(IGB_ETQF_FILTER_1588),
124862306a36Sopenharmony_ci		     (E1000_ETQF_FILTER_ENABLE | /* enable filter */
124962306a36Sopenharmony_ci		      E1000_ETQF_1588 | /* enable timestamping */
125062306a36Sopenharmony_ci		      ETH_P_1588));     /* 1588 eth protocol type */
125162306a36Sopenharmony_ci	else
125262306a36Sopenharmony_ci		wr32(E1000_ETQF(IGB_ETQF_FILTER_1588), 0);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	/* L4 Queue Filter[3]: filter by destination port and protocol */
125562306a36Sopenharmony_ci	if (is_l4) {
125662306a36Sopenharmony_ci		u32 ftqf = (IPPROTO_UDP /* UDP */
125762306a36Sopenharmony_ci			| E1000_FTQF_VF_BP /* VF not compared */
125862306a36Sopenharmony_ci			| E1000_FTQF_1588_TIME_STAMP /* Enable Timestamping */
125962306a36Sopenharmony_ci			| E1000_FTQF_MASK); /* mask all inputs */
126062306a36Sopenharmony_ci		ftqf &= ~E1000_FTQF_MASK_PROTO_BP; /* enable protocol check */
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci		wr32(E1000_IMIR(3), (__force unsigned int)htons(PTP_EV_PORT));
126362306a36Sopenharmony_ci		wr32(E1000_IMIREXT(3),
126462306a36Sopenharmony_ci		     (E1000_IMIREXT_SIZE_BP | E1000_IMIREXT_CTRL_BP));
126562306a36Sopenharmony_ci		if (hw->mac.type == e1000_82576) {
126662306a36Sopenharmony_ci			/* enable source port check */
126762306a36Sopenharmony_ci			wr32(E1000_SPQF(3), (__force unsigned int)htons(PTP_EV_PORT));
126862306a36Sopenharmony_ci			ftqf &= ~E1000_FTQF_MASK_SOURCE_PORT_BP;
126962306a36Sopenharmony_ci		}
127062306a36Sopenharmony_ci		wr32(E1000_FTQF(3), ftqf);
127162306a36Sopenharmony_ci	} else {
127262306a36Sopenharmony_ci		wr32(E1000_FTQF(3), E1000_FTQF_MASK);
127362306a36Sopenharmony_ci	}
127462306a36Sopenharmony_ci	wrfl();
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	/* clear TX/RX time stamp registers, just to be sure */
127762306a36Sopenharmony_ci	regval = rd32(E1000_TXSTMPL);
127862306a36Sopenharmony_ci	regval = rd32(E1000_TXSTMPH);
127962306a36Sopenharmony_ci	regval = rd32(E1000_RXSTMPL);
128062306a36Sopenharmony_ci	regval = rd32(E1000_RXSTMPH);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	return 0;
128362306a36Sopenharmony_ci}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci/**
128662306a36Sopenharmony_ci * igb_ptp_set_ts_config - set hardware time stamping config
128762306a36Sopenharmony_ci * @netdev: netdev struct
128862306a36Sopenharmony_ci * @ifr: interface struct
128962306a36Sopenharmony_ci *
129062306a36Sopenharmony_ci **/
129162306a36Sopenharmony_ciint igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
129262306a36Sopenharmony_ci{
129362306a36Sopenharmony_ci	struct igb_adapter *adapter = netdev_priv(netdev);
129462306a36Sopenharmony_ci	struct hwtstamp_config config;
129562306a36Sopenharmony_ci	int err;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
129862306a36Sopenharmony_ci		return -EFAULT;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	err = igb_ptp_set_timestamp_mode(adapter, &config);
130162306a36Sopenharmony_ci	if (err)
130262306a36Sopenharmony_ci		return err;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	/* save these settings for future reference */
130562306a36Sopenharmony_ci	memcpy(&adapter->tstamp_config, &config,
130662306a36Sopenharmony_ci	       sizeof(adapter->tstamp_config));
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
130962306a36Sopenharmony_ci		-EFAULT : 0;
131062306a36Sopenharmony_ci}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci/**
131362306a36Sopenharmony_ci * igb_ptp_init - Initialize PTP functionality
131462306a36Sopenharmony_ci * @adapter: Board private structure
131562306a36Sopenharmony_ci *
131662306a36Sopenharmony_ci * This function is called at device probe to initialize the PTP
131762306a36Sopenharmony_ci * functionality.
131862306a36Sopenharmony_ci */
131962306a36Sopenharmony_civoid igb_ptp_init(struct igb_adapter *adapter)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
132262306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	switch (hw->mac.type) {
132562306a36Sopenharmony_ci	case e1000_82576:
132662306a36Sopenharmony_ci		snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);
132762306a36Sopenharmony_ci		adapter->ptp_caps.owner = THIS_MODULE;
132862306a36Sopenharmony_ci		adapter->ptp_caps.max_adj = 999999881;
132962306a36Sopenharmony_ci		adapter->ptp_caps.n_ext_ts = 0;
133062306a36Sopenharmony_ci		adapter->ptp_caps.pps = 0;
133162306a36Sopenharmony_ci		adapter->ptp_caps.adjfine = igb_ptp_adjfine_82576;
133262306a36Sopenharmony_ci		adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
133362306a36Sopenharmony_ci		adapter->ptp_caps.gettimex64 = igb_ptp_gettimex_82576;
133462306a36Sopenharmony_ci		adapter->ptp_caps.settime64 = igb_ptp_settime_82576;
133562306a36Sopenharmony_ci		adapter->ptp_caps.enable = igb_ptp_feature_enable;
133662306a36Sopenharmony_ci		adapter->cc.read = igb_ptp_read_82576;
133762306a36Sopenharmony_ci		adapter->cc.mask = CYCLECOUNTER_MASK(64);
133862306a36Sopenharmony_ci		adapter->cc.mult = 1;
133962306a36Sopenharmony_ci		adapter->cc.shift = IGB_82576_TSYNC_SHIFT;
134062306a36Sopenharmony_ci		adapter->ptp_flags |= IGB_PTP_OVERFLOW_CHECK;
134162306a36Sopenharmony_ci		break;
134262306a36Sopenharmony_ci	case e1000_82580:
134362306a36Sopenharmony_ci	case e1000_i354:
134462306a36Sopenharmony_ci	case e1000_i350:
134562306a36Sopenharmony_ci		igb_ptp_sdp_init(adapter);
134662306a36Sopenharmony_ci		snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);
134762306a36Sopenharmony_ci		adapter->ptp_caps.owner = THIS_MODULE;
134862306a36Sopenharmony_ci		adapter->ptp_caps.max_adj = 62499999;
134962306a36Sopenharmony_ci		adapter->ptp_caps.n_ext_ts = IGB_N_EXTTS;
135062306a36Sopenharmony_ci		adapter->ptp_caps.n_per_out = IGB_N_PEROUT;
135162306a36Sopenharmony_ci		adapter->ptp_caps.n_pins = IGB_N_SDP;
135262306a36Sopenharmony_ci		adapter->ptp_caps.pps = 0;
135362306a36Sopenharmony_ci		adapter->ptp_caps.pin_config = adapter->sdp_config;
135462306a36Sopenharmony_ci		adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580;
135562306a36Sopenharmony_ci		adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
135662306a36Sopenharmony_ci		adapter->ptp_caps.gettimex64 = igb_ptp_gettimex_82580;
135762306a36Sopenharmony_ci		adapter->ptp_caps.settime64 = igb_ptp_settime_82576;
135862306a36Sopenharmony_ci		adapter->ptp_caps.enable = igb_ptp_feature_enable_82580;
135962306a36Sopenharmony_ci		adapter->ptp_caps.verify = igb_ptp_verify_pin;
136062306a36Sopenharmony_ci		adapter->cc.read = igb_ptp_read_82580;
136162306a36Sopenharmony_ci		adapter->cc.mask = CYCLECOUNTER_MASK(IGB_NBITS_82580);
136262306a36Sopenharmony_ci		adapter->cc.mult = 1;
136362306a36Sopenharmony_ci		adapter->cc.shift = 0;
136462306a36Sopenharmony_ci		adapter->ptp_flags |= IGB_PTP_OVERFLOW_CHECK;
136562306a36Sopenharmony_ci		break;
136662306a36Sopenharmony_ci	case e1000_i210:
136762306a36Sopenharmony_ci	case e1000_i211:
136862306a36Sopenharmony_ci		igb_ptp_sdp_init(adapter);
136962306a36Sopenharmony_ci		snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);
137062306a36Sopenharmony_ci		adapter->ptp_caps.owner = THIS_MODULE;
137162306a36Sopenharmony_ci		adapter->ptp_caps.max_adj = 62499999;
137262306a36Sopenharmony_ci		adapter->ptp_caps.n_ext_ts = IGB_N_EXTTS;
137362306a36Sopenharmony_ci		adapter->ptp_caps.n_per_out = IGB_N_PEROUT;
137462306a36Sopenharmony_ci		adapter->ptp_caps.n_pins = IGB_N_SDP;
137562306a36Sopenharmony_ci		adapter->ptp_caps.pps = 1;
137662306a36Sopenharmony_ci		adapter->ptp_caps.pin_config = adapter->sdp_config;
137762306a36Sopenharmony_ci		adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580;
137862306a36Sopenharmony_ci		adapter->ptp_caps.adjtime = igb_ptp_adjtime_i210;
137962306a36Sopenharmony_ci		adapter->ptp_caps.gettimex64 = igb_ptp_gettimex_i210;
138062306a36Sopenharmony_ci		adapter->ptp_caps.settime64 = igb_ptp_settime_i210;
138162306a36Sopenharmony_ci		adapter->ptp_caps.enable = igb_ptp_feature_enable_i210;
138262306a36Sopenharmony_ci		adapter->ptp_caps.verify = igb_ptp_verify_pin;
138362306a36Sopenharmony_ci		break;
138462306a36Sopenharmony_ci	default:
138562306a36Sopenharmony_ci		adapter->ptp_clock = NULL;
138662306a36Sopenharmony_ci		return;
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps,
139062306a36Sopenharmony_ci						&adapter->pdev->dev);
139162306a36Sopenharmony_ci	if (IS_ERR(adapter->ptp_clock)) {
139262306a36Sopenharmony_ci		adapter->ptp_clock = NULL;
139362306a36Sopenharmony_ci		dev_err(&adapter->pdev->dev, "ptp_clock_register failed\n");
139462306a36Sopenharmony_ci	} else if (adapter->ptp_clock) {
139562306a36Sopenharmony_ci		dev_info(&adapter->pdev->dev, "added PHC on %s\n",
139662306a36Sopenharmony_ci			 adapter->netdev->name);
139762306a36Sopenharmony_ci		adapter->ptp_flags |= IGB_PTP_ENABLED;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci		spin_lock_init(&adapter->tmreg_lock);
140062306a36Sopenharmony_ci		INIT_WORK(&adapter->ptp_tx_work, igb_ptp_tx_work);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci		if (adapter->ptp_flags & IGB_PTP_OVERFLOW_CHECK)
140362306a36Sopenharmony_ci			INIT_DELAYED_WORK(&adapter->ptp_overflow_work,
140462306a36Sopenharmony_ci					  igb_ptp_overflow_check);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci		adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
140762306a36Sopenharmony_ci		adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci		igb_ptp_reset(adapter);
141062306a36Sopenharmony_ci	}
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci/**
141462306a36Sopenharmony_ci * igb_ptp_sdp_init - utility function which inits the SDP config structs
141562306a36Sopenharmony_ci * @adapter: Board private structure.
141662306a36Sopenharmony_ci **/
141762306a36Sopenharmony_civoid igb_ptp_sdp_init(struct igb_adapter *adapter)
141862306a36Sopenharmony_ci{
141962306a36Sopenharmony_ci	int i;
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	for (i = 0; i < IGB_N_SDP; i++) {
142262306a36Sopenharmony_ci		struct ptp_pin_desc *ppd = &adapter->sdp_config[i];
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci		snprintf(ppd->name, sizeof(ppd->name), "SDP%d", i);
142562306a36Sopenharmony_ci		ppd->index = i;
142662306a36Sopenharmony_ci		ppd->func = PTP_PF_NONE;
142762306a36Sopenharmony_ci	}
142862306a36Sopenharmony_ci}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci/**
143162306a36Sopenharmony_ci * igb_ptp_suspend - Disable PTP work items and prepare for suspend
143262306a36Sopenharmony_ci * @adapter: Board private structure
143362306a36Sopenharmony_ci *
143462306a36Sopenharmony_ci * This function stops the overflow check work and PTP Tx timestamp work, and
143562306a36Sopenharmony_ci * will prepare the device for OS suspend.
143662306a36Sopenharmony_ci */
143762306a36Sopenharmony_civoid igb_ptp_suspend(struct igb_adapter *adapter)
143862306a36Sopenharmony_ci{
143962306a36Sopenharmony_ci	if (!(adapter->ptp_flags & IGB_PTP_ENABLED))
144062306a36Sopenharmony_ci		return;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	if (adapter->ptp_flags & IGB_PTP_OVERFLOW_CHECK)
144362306a36Sopenharmony_ci		cancel_delayed_work_sync(&adapter->ptp_overflow_work);
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	cancel_work_sync(&adapter->ptp_tx_work);
144662306a36Sopenharmony_ci	if (adapter->ptp_tx_skb) {
144762306a36Sopenharmony_ci		dev_kfree_skb_any(adapter->ptp_tx_skb);
144862306a36Sopenharmony_ci		adapter->ptp_tx_skb = NULL;
144962306a36Sopenharmony_ci		clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state);
145062306a36Sopenharmony_ci	}
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci/**
145462306a36Sopenharmony_ci * igb_ptp_stop - Disable PTP device and stop the overflow check.
145562306a36Sopenharmony_ci * @adapter: Board private structure.
145662306a36Sopenharmony_ci *
145762306a36Sopenharmony_ci * This function stops the PTP support and cancels the delayed work.
145862306a36Sopenharmony_ci **/
145962306a36Sopenharmony_civoid igb_ptp_stop(struct igb_adapter *adapter)
146062306a36Sopenharmony_ci{
146162306a36Sopenharmony_ci	igb_ptp_suspend(adapter);
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	if (adapter->ptp_clock) {
146462306a36Sopenharmony_ci		ptp_clock_unregister(adapter->ptp_clock);
146562306a36Sopenharmony_ci		dev_info(&adapter->pdev->dev, "removed PHC on %s\n",
146662306a36Sopenharmony_ci			 adapter->netdev->name);
146762306a36Sopenharmony_ci		adapter->ptp_flags &= ~IGB_PTP_ENABLED;
146862306a36Sopenharmony_ci	}
146962306a36Sopenharmony_ci}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci/**
147262306a36Sopenharmony_ci * igb_ptp_reset - Re-enable the adapter for PTP following a reset.
147362306a36Sopenharmony_ci * @adapter: Board private structure.
147462306a36Sopenharmony_ci *
147562306a36Sopenharmony_ci * This function handles the reset work required to re-enable the PTP device.
147662306a36Sopenharmony_ci **/
147762306a36Sopenharmony_civoid igb_ptp_reset(struct igb_adapter *adapter)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
148062306a36Sopenharmony_ci	unsigned long flags;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	/* reset the tstamp_config */
148362306a36Sopenharmony_ci	igb_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	spin_lock_irqsave(&adapter->tmreg_lock, flags);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	switch (adapter->hw.mac.type) {
148862306a36Sopenharmony_ci	case e1000_82576:
148962306a36Sopenharmony_ci		/* Dial the nominal frequency. */
149062306a36Sopenharmony_ci		wr32(E1000_TIMINCA, INCPERIOD_82576 | INCVALUE_82576);
149162306a36Sopenharmony_ci		break;
149262306a36Sopenharmony_ci	case e1000_82580:
149362306a36Sopenharmony_ci	case e1000_i354:
149462306a36Sopenharmony_ci	case e1000_i350:
149562306a36Sopenharmony_ci	case e1000_i210:
149662306a36Sopenharmony_ci	case e1000_i211:
149762306a36Sopenharmony_ci		wr32(E1000_TSAUXC, 0x0);
149862306a36Sopenharmony_ci		wr32(E1000_TSSDP, 0x0);
149962306a36Sopenharmony_ci		wr32(E1000_TSIM,
150062306a36Sopenharmony_ci		     TSYNC_INTERRUPTS |
150162306a36Sopenharmony_ci		     (adapter->pps_sys_wrap_on ? TSINTR_SYS_WRAP : 0));
150262306a36Sopenharmony_ci		wr32(E1000_IMS, E1000_IMS_TS);
150362306a36Sopenharmony_ci		break;
150462306a36Sopenharmony_ci	default:
150562306a36Sopenharmony_ci		/* No work to do. */
150662306a36Sopenharmony_ci		goto out;
150762306a36Sopenharmony_ci	}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	/* Re-initialize the timer. */
151062306a36Sopenharmony_ci	if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211)) {
151162306a36Sopenharmony_ci		struct timespec64 ts = ktime_to_timespec64(ktime_get_real());
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci		igb_ptp_write_i210(adapter, &ts);
151462306a36Sopenharmony_ci	} else {
151562306a36Sopenharmony_ci		timecounter_init(&adapter->tc, &adapter->cc,
151662306a36Sopenharmony_ci				 ktime_to_ns(ktime_get_real()));
151762306a36Sopenharmony_ci	}
151862306a36Sopenharmony_ciout:
151962306a36Sopenharmony_ci	spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	wrfl();
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if (adapter->ptp_flags & IGB_PTP_OVERFLOW_CHECK)
152462306a36Sopenharmony_ci		schedule_delayed_work(&adapter->ptp_overflow_work,
152562306a36Sopenharmony_ci				      IGB_SYSTIM_OVERFLOW_PERIOD);
152662306a36Sopenharmony_ci}
1527