162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Marvell PP2.2 TAI support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Note:
662306a36Sopenharmony_ci *   Do NOT use the event capture support.
762306a36Sopenharmony_ci *   Do Not even set the MPP muxes to allow PTP_EVENT_REQ to be used.
862306a36Sopenharmony_ci *   It will disrupt the operation of this driver, and there is nothing
962306a36Sopenharmony_ci *   that this driver can do to prevent that.  Even using PTP_EVENT_REQ
1062306a36Sopenharmony_ci *   as an output will be seen as a trigger input, which can't be masked.
1162306a36Sopenharmony_ci *   When ever a trigger input is seen, the action in the TCFCR0_TCF
1262306a36Sopenharmony_ci *   field will be performed - whether it is a set, increment, decrement
1362306a36Sopenharmony_ci *   read, or frequency update.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Other notes (useful, not specified in the documentation):
1662306a36Sopenharmony_ci * - PTP_PULSE_OUT (PTP_EVENT_REQ MPP)
1762306a36Sopenharmony_ci *   It looks like the hardware can't generate a pulse at nsec=0. (The
1862306a36Sopenharmony_ci *   output doesn't trigger if the nsec field is zero.)
1962306a36Sopenharmony_ci *   Note: when configured as an output via the register at 0xfX441120,
2062306a36Sopenharmony_ci *   the input is still very much alive, and will trigger the current TCF
2162306a36Sopenharmony_ci *   function.
2262306a36Sopenharmony_ci * - PTP_CLK_OUT (PTP_TRIG_GEN MPP)
2362306a36Sopenharmony_ci *   This generates a "PPS" signal determined by the CCC registers. It
2462306a36Sopenharmony_ci *   seems this is not aligned to the TOD counter in any way (it may be
2562306a36Sopenharmony_ci *   initially, but if you specify a non-round second interval, it won't,
2662306a36Sopenharmony_ci *   and you can't easily get it back.)
2762306a36Sopenharmony_ci * - PTP_PCLK_OUT
2862306a36Sopenharmony_ci *   This generates a 50% duty cycle clock based on the TOD counter, and
2962306a36Sopenharmony_ci *   seems it can be set to any period of 1ns resolution. It is probably
3062306a36Sopenharmony_ci *   limited by the TOD step size. Its period is defined by the PCLK_CCC
3162306a36Sopenharmony_ci *   registers. Again, its alignment to the second is questionable.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * Consequently, we support none of these.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_ci#include <linux/io.h>
3662306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h>
3762306a36Sopenharmony_ci#include <linux/slab.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#include "mvpp2.h"
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define CR0_SW_NRESET			BIT(0)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define TCFCR0_PHASE_UPDATE_ENABLE	BIT(8)
4462306a36Sopenharmony_ci#define TCFCR0_TCF_MASK			(7 << 2)
4562306a36Sopenharmony_ci#define TCFCR0_TCF_UPDATE		(0 << 2)
4662306a36Sopenharmony_ci#define TCFCR0_TCF_FREQUPDATE		(1 << 2)
4762306a36Sopenharmony_ci#define TCFCR0_TCF_INCREMENT		(2 << 2)
4862306a36Sopenharmony_ci#define TCFCR0_TCF_DECREMENT		(3 << 2)
4962306a36Sopenharmony_ci#define TCFCR0_TCF_CAPTURE		(4 << 2)
5062306a36Sopenharmony_ci#define TCFCR0_TCF_NOP			(7 << 2)
5162306a36Sopenharmony_ci#define TCFCR0_TCF_TRIGGER		BIT(0)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define TCSR_CAPTURE_1_VALID		BIT(1)
5462306a36Sopenharmony_ci#define TCSR_CAPTURE_0_VALID		BIT(0)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistruct mvpp2_tai {
5762306a36Sopenharmony_ci	struct ptp_clock_info caps;
5862306a36Sopenharmony_ci	struct ptp_clock *ptp_clock;
5962306a36Sopenharmony_ci	void __iomem *base;
6062306a36Sopenharmony_ci	spinlock_t lock;
6162306a36Sopenharmony_ci	u64 period;		// nanosecond period in 32.32 fixed point
6262306a36Sopenharmony_ci	/* This timestamp is updated every two seconds */
6362306a36Sopenharmony_ci	struct timespec64 stamp;
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void mvpp2_tai_modify(void __iomem *reg, u32 mask, u32 set)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	u32 val;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	val = readl_relaxed(reg) & ~mask;
7162306a36Sopenharmony_ci	val |= set & mask;
7262306a36Sopenharmony_ci	writel(val, reg);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic void mvpp2_tai_write(u32 val, void __iomem *reg)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	writel_relaxed(val & 0xffff, reg);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic u32 mvpp2_tai_read(void __iomem *reg)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	return readl_relaxed(reg) & 0xffff;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic struct mvpp2_tai *ptp_to_tai(struct ptp_clock_info *ptp)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	return container_of(ptp, struct mvpp2_tai, caps);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic void mvpp22_tai_read_ts(struct timespec64 *ts, void __iomem *base)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	ts->tv_sec = (u64)mvpp2_tai_read(base + 0) << 32 |
9362306a36Sopenharmony_ci		     mvpp2_tai_read(base + 4) << 16 |
9462306a36Sopenharmony_ci		     mvpp2_tai_read(base + 8);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	ts->tv_nsec = mvpp2_tai_read(base + 12) << 16 |
9762306a36Sopenharmony_ci		      mvpp2_tai_read(base + 16);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* Read and discard fractional part */
10062306a36Sopenharmony_ci	readl_relaxed(base + 20);
10162306a36Sopenharmony_ci	readl_relaxed(base + 24);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void mvpp2_tai_write_tlv(const struct timespec64 *ts, u32 frac,
10562306a36Sopenharmony_ci			        void __iomem *base)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	mvpp2_tai_write(ts->tv_sec >> 32, base + MVPP22_TAI_TLV_SEC_HIGH);
10862306a36Sopenharmony_ci	mvpp2_tai_write(ts->tv_sec >> 16, base + MVPP22_TAI_TLV_SEC_MED);
10962306a36Sopenharmony_ci	mvpp2_tai_write(ts->tv_sec, base + MVPP22_TAI_TLV_SEC_LOW);
11062306a36Sopenharmony_ci	mvpp2_tai_write(ts->tv_nsec >> 16, base + MVPP22_TAI_TLV_NANO_HIGH);
11162306a36Sopenharmony_ci	mvpp2_tai_write(ts->tv_nsec, base + MVPP22_TAI_TLV_NANO_LOW);
11262306a36Sopenharmony_ci	mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TLV_FRAC_HIGH);
11362306a36Sopenharmony_ci	mvpp2_tai_write(frac, base + MVPP22_TAI_TLV_FRAC_LOW);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void mvpp2_tai_op(u32 op, void __iomem *base)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	/* Trigger the operation. Note that an external unmaskable
11962306a36Sopenharmony_ci	 * event on PTP_EVENT_REQ will also trigger this action.
12062306a36Sopenharmony_ci	 */
12162306a36Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
12262306a36Sopenharmony_ci			 TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
12362306a36Sopenharmony_ci			 op | TCFCR0_TCF_TRIGGER);
12462306a36Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
12562306a36Sopenharmony_ci			 TCFCR0_TCF_NOP);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/* The adjustment has a range of +0.5ns to -0.5ns in 2^32 steps, so has units
12962306a36Sopenharmony_ci * of 2^-32 ns.
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci * units(s) = 1 / (2^32 * 10^9)
13262306a36Sopenharmony_ci * fractional = abs_scaled_ppm / (2^16 * 10^6)
13362306a36Sopenharmony_ci *
13462306a36Sopenharmony_ci * What we want to achieve:
13562306a36Sopenharmony_ci *  freq_adjusted = freq_nominal * (1 + fractional)
13662306a36Sopenharmony_ci *  freq_delta = freq_adjusted - freq_nominal => positive = faster
13762306a36Sopenharmony_ci *  freq_delta = freq_nominal * (1 + fractional) - freq_nominal
13862306a36Sopenharmony_ci * So: freq_delta = freq_nominal * fractional
13962306a36Sopenharmony_ci *
14062306a36Sopenharmony_ci * However, we are dealing with periods, so:
14162306a36Sopenharmony_ci *  period_adjusted = period_nominal / (1 + fractional)
14262306a36Sopenharmony_ci *  period_delta = period_nominal - period_adjusted => positive = faster
14362306a36Sopenharmony_ci *  period_delta = period_nominal * fractional / (1 + fractional)
14462306a36Sopenharmony_ci *
14562306a36Sopenharmony_ci * Hence:
14662306a36Sopenharmony_ci *  period_delta = period_nominal * abs_scaled_ppm /
14762306a36Sopenharmony_ci *		   (2^16 * 10^6 + abs_scaled_ppm)
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci * To avoid overflow, we reduce both sides of the divide operation by a factor
15062306a36Sopenharmony_ci * of 16.
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_cistatic u64 mvpp22_calc_frac_ppm(struct mvpp2_tai *tai, long abs_scaled_ppm)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	u64 val = tai->period * abs_scaled_ppm >> 4;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return div_u64(val, (1000000 << 12) + (abs_scaled_ppm >> 4));
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic s32 mvpp22_calc_max_adj(struct mvpp2_tai *tai)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	return 1000000;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic int mvpp22_tai_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct mvpp2_tai *tai = ptp_to_tai(ptp);
16762306a36Sopenharmony_ci	unsigned long flags;
16862306a36Sopenharmony_ci	void __iomem *base;
16962306a36Sopenharmony_ci	bool neg_adj;
17062306a36Sopenharmony_ci	s32 frac;
17162306a36Sopenharmony_ci	u64 val;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	neg_adj = scaled_ppm < 0;
17462306a36Sopenharmony_ci	if (neg_adj)
17562306a36Sopenharmony_ci		scaled_ppm = -scaled_ppm;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	val = mvpp22_calc_frac_ppm(tai, scaled_ppm);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Convert to a signed 32-bit adjustment */
18062306a36Sopenharmony_ci	if (neg_adj) {
18162306a36Sopenharmony_ci		/* -S32_MIN warns, -val < S32_MIN fails, so go for the easy
18262306a36Sopenharmony_ci		 * solution.
18362306a36Sopenharmony_ci		 */
18462306a36Sopenharmony_ci		if (val > 0x80000000)
18562306a36Sopenharmony_ci			return -ERANGE;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci		frac = -val;
18862306a36Sopenharmony_ci	} else {
18962306a36Sopenharmony_ci		if (val > S32_MAX)
19062306a36Sopenharmony_ci			return -ERANGE;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		frac = val;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	base = tai->base;
19662306a36Sopenharmony_ci	spin_lock_irqsave(&tai->lock, flags);
19762306a36Sopenharmony_ci	mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TLV_FRAC_HIGH);
19862306a36Sopenharmony_ci	mvpp2_tai_write(frac, base + MVPP22_TAI_TLV_FRAC_LOW);
19962306a36Sopenharmony_ci	mvpp2_tai_op(TCFCR0_TCF_FREQUPDATE, base);
20062306a36Sopenharmony_ci	spin_unlock_irqrestore(&tai->lock, flags);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return 0;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int mvpp22_tai_adjtime(struct ptp_clock_info *ptp, s64 delta)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct mvpp2_tai *tai = ptp_to_tai(ptp);
20862306a36Sopenharmony_ci	struct timespec64 ts;
20962306a36Sopenharmony_ci	unsigned long flags;
21062306a36Sopenharmony_ci	void __iomem *base;
21162306a36Sopenharmony_ci	u32 tcf;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* We can't deal with S64_MIN */
21462306a36Sopenharmony_ci	if (delta == S64_MIN)
21562306a36Sopenharmony_ci		return -ERANGE;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (delta < 0) {
21862306a36Sopenharmony_ci		delta = -delta;
21962306a36Sopenharmony_ci		tcf = TCFCR0_TCF_DECREMENT;
22062306a36Sopenharmony_ci	} else {
22162306a36Sopenharmony_ci		tcf = TCFCR0_TCF_INCREMENT;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	ts = ns_to_timespec64(delta);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	base = tai->base;
22762306a36Sopenharmony_ci	spin_lock_irqsave(&tai->lock, flags);
22862306a36Sopenharmony_ci	mvpp2_tai_write_tlv(&ts, 0, base);
22962306a36Sopenharmony_ci	mvpp2_tai_op(tcf, base);
23062306a36Sopenharmony_ci	spin_unlock_irqrestore(&tai->lock, flags);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return 0;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int mvpp22_tai_gettimex64(struct ptp_clock_info *ptp,
23662306a36Sopenharmony_ci				 struct timespec64 *ts,
23762306a36Sopenharmony_ci				 struct ptp_system_timestamp *sts)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct mvpp2_tai *tai = ptp_to_tai(ptp);
24062306a36Sopenharmony_ci	unsigned long flags;
24162306a36Sopenharmony_ci	void __iomem *base;
24262306a36Sopenharmony_ci	u32 tcsr;
24362306a36Sopenharmony_ci	int ret;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	base = tai->base;
24662306a36Sopenharmony_ci	spin_lock_irqsave(&tai->lock, flags);
24762306a36Sopenharmony_ci	/* XXX: the only way to read the PTP time is for the CPU to trigger
24862306a36Sopenharmony_ci	 * an event. However, there is no way to distinguish between the CPU
24962306a36Sopenharmony_ci	 * triggered event, and an external event on PTP_EVENT_REQ. So this
25062306a36Sopenharmony_ci	 * is incompatible with external use of PTP_EVENT_REQ.
25162306a36Sopenharmony_ci	 */
25262306a36Sopenharmony_ci	ptp_read_system_prets(sts);
25362306a36Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
25462306a36Sopenharmony_ci			 TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
25562306a36Sopenharmony_ci			 TCFCR0_TCF_CAPTURE | TCFCR0_TCF_TRIGGER);
25662306a36Sopenharmony_ci	ptp_read_system_postts(sts);
25762306a36Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
25862306a36Sopenharmony_ci			 TCFCR0_TCF_NOP);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	tcsr = readl(base + MVPP22_TAI_TCSR);
26162306a36Sopenharmony_ci	if (tcsr & TCSR_CAPTURE_1_VALID) {
26262306a36Sopenharmony_ci		mvpp22_tai_read_ts(ts, base + MVPP22_TAI_TCV1_SEC_HIGH);
26362306a36Sopenharmony_ci		ret = 0;
26462306a36Sopenharmony_ci	} else if (tcsr & TCSR_CAPTURE_0_VALID) {
26562306a36Sopenharmony_ci		mvpp22_tai_read_ts(ts, base + MVPP22_TAI_TCV0_SEC_HIGH);
26662306a36Sopenharmony_ci		ret = 0;
26762306a36Sopenharmony_ci	} else {
26862306a36Sopenharmony_ci		/* We don't seem to have a reading... */
26962306a36Sopenharmony_ci		ret = -EBUSY;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci	spin_unlock_irqrestore(&tai->lock, flags);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return ret;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic int mvpp22_tai_settime64(struct ptp_clock_info *ptp,
27762306a36Sopenharmony_ci				const struct timespec64 *ts)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct mvpp2_tai *tai = ptp_to_tai(ptp);
28062306a36Sopenharmony_ci	unsigned long flags;
28162306a36Sopenharmony_ci	void __iomem *base;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	base = tai->base;
28462306a36Sopenharmony_ci	spin_lock_irqsave(&tai->lock, flags);
28562306a36Sopenharmony_ci	mvpp2_tai_write_tlv(ts, 0, base);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* Trigger an update to load the value from the TLV registers
28862306a36Sopenharmony_ci	 * into the TOD counter. Note that an external unmaskable event on
28962306a36Sopenharmony_ci	 * PTP_EVENT_REQ will also trigger this action.
29062306a36Sopenharmony_ci	 */
29162306a36Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
29262306a36Sopenharmony_ci			 TCFCR0_PHASE_UPDATE_ENABLE |
29362306a36Sopenharmony_ci			 TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
29462306a36Sopenharmony_ci			 TCFCR0_TCF_UPDATE | TCFCR0_TCF_TRIGGER);
29562306a36Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
29662306a36Sopenharmony_ci			 TCFCR0_TCF_NOP);
29762306a36Sopenharmony_ci	spin_unlock_irqrestore(&tai->lock, flags);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return 0;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic long mvpp22_tai_aux_work(struct ptp_clock_info *ptp)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct mvpp2_tai *tai = ptp_to_tai(ptp);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	mvpp22_tai_gettimex64(ptp, &tai->stamp, NULL);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return msecs_to_jiffies(2000);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic void mvpp22_tai_set_step(struct mvpp2_tai *tai)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	void __iomem *base = tai->base;
31462306a36Sopenharmony_ci	u32 nano, frac;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	nano = upper_32_bits(tai->period);
31762306a36Sopenharmony_ci	frac = lower_32_bits(tai->period);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* As the fractional nanosecond is a signed offset, if the MSB (sign)
32062306a36Sopenharmony_ci	 * bit is set, we have to increment the whole nanoseconds.
32162306a36Sopenharmony_ci	 */
32262306a36Sopenharmony_ci	if (frac >= 0x80000000)
32362306a36Sopenharmony_ci		nano += 1;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	mvpp2_tai_write(nano, base + MVPP22_TAI_TOD_STEP_NANO_CR);
32662306a36Sopenharmony_ci	mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TOD_STEP_FRAC_HIGH);
32762306a36Sopenharmony_ci	mvpp2_tai_write(frac, base + MVPP22_TAI_TOD_STEP_FRAC_LOW);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic void mvpp22_tai_init(struct mvpp2_tai *tai)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	void __iomem *base = tai->base;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	mvpp22_tai_set_step(tai);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* Release the TAI reset */
33762306a36Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_CR0, CR0_SW_NRESET, CR0_SW_NRESET);
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ciint mvpp22_tai_ptp_clock_index(struct mvpp2_tai *tai)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	return ptp_clock_index(tai->ptp_clock);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_civoid mvpp22_tai_tstamp(struct mvpp2_tai *tai, u32 tstamp,
34662306a36Sopenharmony_ci		       struct skb_shared_hwtstamps *hwtstamp)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct timespec64 ts;
34962306a36Sopenharmony_ci	int delta;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* The tstamp consists of 2 bits of seconds and 30 bits of nanoseconds.
35262306a36Sopenharmony_ci	 * We use our stored timestamp (tai->stamp) to form a full timestamp,
35362306a36Sopenharmony_ci	 * and we must read the seconds exactly once.
35462306a36Sopenharmony_ci	 */
35562306a36Sopenharmony_ci	ts.tv_sec = READ_ONCE(tai->stamp.tv_sec);
35662306a36Sopenharmony_ci	ts.tv_nsec = tstamp & 0x3fffffff;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* Calculate the delta in seconds between our stored timestamp and
35962306a36Sopenharmony_ci	 * the value read from the queue. Allow timestamps one second in the
36062306a36Sopenharmony_ci	 * past, otherwise consider them to be in the future.
36162306a36Sopenharmony_ci	 */
36262306a36Sopenharmony_ci	delta = ((tstamp >> 30) - (ts.tv_sec & 3)) & 3;
36362306a36Sopenharmony_ci	if (delta == 3)
36462306a36Sopenharmony_ci		delta -= 4;
36562306a36Sopenharmony_ci	ts.tv_sec += delta;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	memset(hwtstamp, 0, sizeof(*hwtstamp));
36862306a36Sopenharmony_ci	hwtstamp->hwtstamp = timespec64_to_ktime(ts);
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_civoid mvpp22_tai_start(struct mvpp2_tai *tai)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	long delay;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	delay = mvpp22_tai_aux_work(&tai->caps);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	ptp_schedule_worker(tai->ptp_clock, delay);
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_civoid mvpp22_tai_stop(struct mvpp2_tai *tai)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	ptp_cancel_worker_sync(tai->ptp_clock);
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic void mvpp22_tai_remove(void *priv)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct mvpp2_tai *tai = priv;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (!IS_ERR(tai->ptp_clock))
39062306a36Sopenharmony_ci		ptp_clock_unregister(tai->ptp_clock);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ciint mvpp22_tai_probe(struct device *dev, struct mvpp2 *priv)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct mvpp2_tai *tai;
39662306a36Sopenharmony_ci	int ret;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	tai = devm_kzalloc(dev, sizeof(*tai), GFP_KERNEL);
39962306a36Sopenharmony_ci	if (!tai)
40062306a36Sopenharmony_ci		return -ENOMEM;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	spin_lock_init(&tai->lock);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	tai->base = priv->iface_base;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/* The step size consists of three registers - a 16-bit nanosecond step
40762306a36Sopenharmony_ci	 * size, and a 32-bit fractional nanosecond step size split over two
40862306a36Sopenharmony_ci	 * registers. The fractional nanosecond step size has units of 2^-32ns.
40962306a36Sopenharmony_ci	 *
41062306a36Sopenharmony_ci	 * To calculate this, we calculate:
41162306a36Sopenharmony_ci	 *   (10^9 + freq / 2) / (freq * 2^-32)
41262306a36Sopenharmony_ci	 * which gives us the nanosecond step to the nearest integer in 16.32
41362306a36Sopenharmony_ci	 * fixed point format, and the fractional part of the step size with
41462306a36Sopenharmony_ci	 * the MSB inverted.  With rounding of the fractional nanosecond, and
41562306a36Sopenharmony_ci	 * simplification, this becomes:
41662306a36Sopenharmony_ci	 *   (10^9 << 32 + freq << 31 + (freq + 1) >> 1) / freq
41762306a36Sopenharmony_ci	 *
41862306a36Sopenharmony_ci	 * So:
41962306a36Sopenharmony_ci	 *   div = (10^9 << 32 + freq << 31 + (freq + 1) >> 1) / freq
42062306a36Sopenharmony_ci	 *   nano = upper_32_bits(div);
42162306a36Sopenharmony_ci	 *   frac = lower_32_bits(div) ^ 0x80000000;
42262306a36Sopenharmony_ci	 * Will give the values for the registers.
42362306a36Sopenharmony_ci	 *
42462306a36Sopenharmony_ci	 * This is all seems perfect, but alas it is not when considering the
42562306a36Sopenharmony_ci	 * whole story.  The system is clocked from 25MHz, which is multiplied
42662306a36Sopenharmony_ci	 * by a PLL to 1GHz, and then divided by three, giving 333333333Hz
42762306a36Sopenharmony_ci	 * (recurring).  This gives exactly 3ns, but using 333333333Hz with
42862306a36Sopenharmony_ci	 * the above gives an error of 13*2^-32ns.
42962306a36Sopenharmony_ci	 *
43062306a36Sopenharmony_ci	 * Consequently, we use the period rather than calculating from the
43162306a36Sopenharmony_ci	 * frequency.
43262306a36Sopenharmony_ci	 */
43362306a36Sopenharmony_ci	tai->period = 3ULL << 32;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	mvpp22_tai_init(tai);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	tai->caps.owner = THIS_MODULE;
43862306a36Sopenharmony_ci	strscpy(tai->caps.name, "Marvell PP2.2", sizeof(tai->caps.name));
43962306a36Sopenharmony_ci	tai->caps.max_adj = mvpp22_calc_max_adj(tai);
44062306a36Sopenharmony_ci	tai->caps.adjfine = mvpp22_tai_adjfine;
44162306a36Sopenharmony_ci	tai->caps.adjtime = mvpp22_tai_adjtime;
44262306a36Sopenharmony_ci	tai->caps.gettimex64 = mvpp22_tai_gettimex64;
44362306a36Sopenharmony_ci	tai->caps.settime64 = mvpp22_tai_settime64;
44462306a36Sopenharmony_ci	tai->caps.do_aux_work = mvpp22_tai_aux_work;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	ret = devm_add_action(dev, mvpp22_tai_remove, tai);
44762306a36Sopenharmony_ci	if (ret)
44862306a36Sopenharmony_ci		return ret;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	tai->ptp_clock = ptp_clock_register(&tai->caps, dev);
45162306a36Sopenharmony_ci	if (IS_ERR(tai->ptp_clock))
45262306a36Sopenharmony_ci		return PTR_ERR(tai->ptp_clock);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	priv->tai = tai;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return 0;
45762306a36Sopenharmony_ci}
458