18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Marvell PP2.2 TAI support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Note:
68c2ecf20Sopenharmony_ci *   Do NOT use the event capture support.
78c2ecf20Sopenharmony_ci *   Do Not even set the MPP muxes to allow PTP_EVENT_REQ to be used.
88c2ecf20Sopenharmony_ci *   It will disrupt the operation of this driver, and there is nothing
98c2ecf20Sopenharmony_ci *   that this driver can do to prevent that.  Even using PTP_EVENT_REQ
108c2ecf20Sopenharmony_ci *   as an output will be seen as a trigger input, which can't be masked.
118c2ecf20Sopenharmony_ci *   When ever a trigger input is seen, the action in the TCFCR0_TCF
128c2ecf20Sopenharmony_ci *   field will be performed - whether it is a set, increment, decrement
138c2ecf20Sopenharmony_ci *   read, or frequency update.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Other notes (useful, not specified in the documentation):
168c2ecf20Sopenharmony_ci * - PTP_PULSE_OUT (PTP_EVENT_REQ MPP)
178c2ecf20Sopenharmony_ci *   It looks like the hardware can't generate a pulse at nsec=0. (The
188c2ecf20Sopenharmony_ci *   output doesn't trigger if the nsec field is zero.)
198c2ecf20Sopenharmony_ci *   Note: when configured as an output via the register at 0xfX441120,
208c2ecf20Sopenharmony_ci *   the input is still very much alive, and will trigger the current TCF
218c2ecf20Sopenharmony_ci *   function.
228c2ecf20Sopenharmony_ci * - PTP_CLK_OUT (PTP_TRIG_GEN MPP)
238c2ecf20Sopenharmony_ci *   This generates a "PPS" signal determined by the CCC registers. It
248c2ecf20Sopenharmony_ci *   seems this is not aligned to the TOD counter in any way (it may be
258c2ecf20Sopenharmony_ci *   initially, but if you specify a non-round second interval, it won't,
268c2ecf20Sopenharmony_ci *   and you can't easily get it back.)
278c2ecf20Sopenharmony_ci * - PTP_PCLK_OUT
288c2ecf20Sopenharmony_ci *   This generates a 50% duty cycle clock based on the TOD counter, and
298c2ecf20Sopenharmony_ci *   seems it can be set to any period of 1ns resolution. It is probably
308c2ecf20Sopenharmony_ci *   limited by the TOD step size. Its period is defined by the PCLK_CCC
318c2ecf20Sopenharmony_ci *   registers. Again, its alignment to the second is questionable.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * Consequently, we support none of these.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci#include <linux/io.h>
368c2ecf20Sopenharmony_ci#include <linux/ptp_clock_kernel.h>
378c2ecf20Sopenharmony_ci#include <linux/slab.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include "mvpp2.h"
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define CR0_SW_NRESET			BIT(0)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define TCFCR0_PHASE_UPDATE_ENABLE	BIT(8)
448c2ecf20Sopenharmony_ci#define TCFCR0_TCF_MASK			(7 << 2)
458c2ecf20Sopenharmony_ci#define TCFCR0_TCF_UPDATE		(0 << 2)
468c2ecf20Sopenharmony_ci#define TCFCR0_TCF_FREQUPDATE		(1 << 2)
478c2ecf20Sopenharmony_ci#define TCFCR0_TCF_INCREMENT		(2 << 2)
488c2ecf20Sopenharmony_ci#define TCFCR0_TCF_DECREMENT		(3 << 2)
498c2ecf20Sopenharmony_ci#define TCFCR0_TCF_CAPTURE		(4 << 2)
508c2ecf20Sopenharmony_ci#define TCFCR0_TCF_NOP			(7 << 2)
518c2ecf20Sopenharmony_ci#define TCFCR0_TCF_TRIGGER		BIT(0)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define TCSR_CAPTURE_1_VALID		BIT(1)
548c2ecf20Sopenharmony_ci#define TCSR_CAPTURE_0_VALID		BIT(0)
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistruct mvpp2_tai {
578c2ecf20Sopenharmony_ci	struct ptp_clock_info caps;
588c2ecf20Sopenharmony_ci	struct ptp_clock *ptp_clock;
598c2ecf20Sopenharmony_ci	void __iomem *base;
608c2ecf20Sopenharmony_ci	spinlock_t lock;
618c2ecf20Sopenharmony_ci	u64 period;		// nanosecond period in 32.32 fixed point
628c2ecf20Sopenharmony_ci	/* This timestamp is updated every two seconds */
638c2ecf20Sopenharmony_ci	struct timespec64 stamp;
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void mvpp2_tai_modify(void __iomem *reg, u32 mask, u32 set)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	u32 val;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	val = readl_relaxed(reg) & ~mask;
718c2ecf20Sopenharmony_ci	val |= set & mask;
728c2ecf20Sopenharmony_ci	writel(val, reg);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic void mvpp2_tai_write(u32 val, void __iomem *reg)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	writel_relaxed(val & 0xffff, reg);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic u32 mvpp2_tai_read(void __iomem *reg)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	return readl_relaxed(reg) & 0xffff;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic struct mvpp2_tai *ptp_to_tai(struct ptp_clock_info *ptp)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	return container_of(ptp, struct mvpp2_tai, caps);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void mvpp22_tai_read_ts(struct timespec64 *ts, void __iomem *base)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	ts->tv_sec = (u64)mvpp2_tai_read(base + 0) << 32 |
938c2ecf20Sopenharmony_ci		     mvpp2_tai_read(base + 4) << 16 |
948c2ecf20Sopenharmony_ci		     mvpp2_tai_read(base + 8);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	ts->tv_nsec = mvpp2_tai_read(base + 12) << 16 |
978c2ecf20Sopenharmony_ci		      mvpp2_tai_read(base + 16);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* Read and discard fractional part */
1008c2ecf20Sopenharmony_ci	readl_relaxed(base + 20);
1018c2ecf20Sopenharmony_ci	readl_relaxed(base + 24);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void mvpp2_tai_write_tlv(const struct timespec64 *ts, u32 frac,
1058c2ecf20Sopenharmony_ci			        void __iomem *base)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	mvpp2_tai_write(ts->tv_sec >> 32, base + MVPP22_TAI_TLV_SEC_HIGH);
1088c2ecf20Sopenharmony_ci	mvpp2_tai_write(ts->tv_sec >> 16, base + MVPP22_TAI_TLV_SEC_MED);
1098c2ecf20Sopenharmony_ci	mvpp2_tai_write(ts->tv_sec, base + MVPP22_TAI_TLV_SEC_LOW);
1108c2ecf20Sopenharmony_ci	mvpp2_tai_write(ts->tv_nsec >> 16, base + MVPP22_TAI_TLV_NANO_HIGH);
1118c2ecf20Sopenharmony_ci	mvpp2_tai_write(ts->tv_nsec, base + MVPP22_TAI_TLV_NANO_LOW);
1128c2ecf20Sopenharmony_ci	mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TLV_FRAC_HIGH);
1138c2ecf20Sopenharmony_ci	mvpp2_tai_write(frac, base + MVPP22_TAI_TLV_FRAC_LOW);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic void mvpp2_tai_op(u32 op, void __iomem *base)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	/* Trigger the operation. Note that an external unmaskable
1198c2ecf20Sopenharmony_ci	 * event on PTP_EVENT_REQ will also trigger this action.
1208c2ecf20Sopenharmony_ci	 */
1218c2ecf20Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
1228c2ecf20Sopenharmony_ci			 TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
1238c2ecf20Sopenharmony_ci			 op | TCFCR0_TCF_TRIGGER);
1248c2ecf20Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
1258c2ecf20Sopenharmony_ci			 TCFCR0_TCF_NOP);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/* The adjustment has a range of +0.5ns to -0.5ns in 2^32 steps, so has units
1298c2ecf20Sopenharmony_ci * of 2^-32 ns.
1308c2ecf20Sopenharmony_ci *
1318c2ecf20Sopenharmony_ci * units(s) = 1 / (2^32 * 10^9)
1328c2ecf20Sopenharmony_ci * fractional = abs_scaled_ppm / (2^16 * 10^6)
1338c2ecf20Sopenharmony_ci *
1348c2ecf20Sopenharmony_ci * What we want to achieve:
1358c2ecf20Sopenharmony_ci *  freq_adjusted = freq_nominal * (1 + fractional)
1368c2ecf20Sopenharmony_ci *  freq_delta = freq_adjusted - freq_nominal => positive = faster
1378c2ecf20Sopenharmony_ci *  freq_delta = freq_nominal * (1 + fractional) - freq_nominal
1388c2ecf20Sopenharmony_ci * So: freq_delta = freq_nominal * fractional
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci * However, we are dealing with periods, so:
1418c2ecf20Sopenharmony_ci *  period_adjusted = period_nominal / (1 + fractional)
1428c2ecf20Sopenharmony_ci *  period_delta = period_nominal - period_adjusted => positive = faster
1438c2ecf20Sopenharmony_ci *  period_delta = period_nominal * fractional / (1 + fractional)
1448c2ecf20Sopenharmony_ci *
1458c2ecf20Sopenharmony_ci * Hence:
1468c2ecf20Sopenharmony_ci *  period_delta = period_nominal * abs_scaled_ppm /
1478c2ecf20Sopenharmony_ci *		   (2^16 * 10^6 + abs_scaled_ppm)
1488c2ecf20Sopenharmony_ci *
1498c2ecf20Sopenharmony_ci * To avoid overflow, we reduce both sides of the divide operation by a factor
1508c2ecf20Sopenharmony_ci * of 16.
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_cistatic u64 mvpp22_calc_frac_ppm(struct mvpp2_tai *tai, long abs_scaled_ppm)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	u64 val = tai->period * abs_scaled_ppm >> 4;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return div_u64(val, (1000000 << 12) + (abs_scaled_ppm >> 4));
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic s32 mvpp22_calc_max_adj(struct mvpp2_tai *tai)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	return 1000000;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic int mvpp22_tai_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct mvpp2_tai *tai = ptp_to_tai(ptp);
1678c2ecf20Sopenharmony_ci	unsigned long flags;
1688c2ecf20Sopenharmony_ci	void __iomem *base;
1698c2ecf20Sopenharmony_ci	bool neg_adj;
1708c2ecf20Sopenharmony_ci	s32 frac;
1718c2ecf20Sopenharmony_ci	u64 val;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	neg_adj = scaled_ppm < 0;
1748c2ecf20Sopenharmony_ci	if (neg_adj)
1758c2ecf20Sopenharmony_ci		scaled_ppm = -scaled_ppm;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	val = mvpp22_calc_frac_ppm(tai, scaled_ppm);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* Convert to a signed 32-bit adjustment */
1808c2ecf20Sopenharmony_ci	if (neg_adj) {
1818c2ecf20Sopenharmony_ci		/* -S32_MIN warns, -val < S32_MIN fails, so go for the easy
1828c2ecf20Sopenharmony_ci		 * solution.
1838c2ecf20Sopenharmony_ci		 */
1848c2ecf20Sopenharmony_ci		if (val > 0x80000000)
1858c2ecf20Sopenharmony_ci			return -ERANGE;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		frac = -val;
1888c2ecf20Sopenharmony_ci	} else {
1898c2ecf20Sopenharmony_ci		if (val > S32_MAX)
1908c2ecf20Sopenharmony_ci			return -ERANGE;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		frac = val;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	base = tai->base;
1968c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tai->lock, flags);
1978c2ecf20Sopenharmony_ci	mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TLV_FRAC_HIGH);
1988c2ecf20Sopenharmony_ci	mvpp2_tai_write(frac, base + MVPP22_TAI_TLV_FRAC_LOW);
1998c2ecf20Sopenharmony_ci	mvpp2_tai_op(TCFCR0_TCF_FREQUPDATE, base);
2008c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tai->lock, flags);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int mvpp22_tai_adjtime(struct ptp_clock_info *ptp, s64 delta)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct mvpp2_tai *tai = ptp_to_tai(ptp);
2088c2ecf20Sopenharmony_ci	struct timespec64 ts;
2098c2ecf20Sopenharmony_ci	unsigned long flags;
2108c2ecf20Sopenharmony_ci	void __iomem *base;
2118c2ecf20Sopenharmony_ci	u32 tcf;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* We can't deal with S64_MIN */
2148c2ecf20Sopenharmony_ci	if (delta == S64_MIN)
2158c2ecf20Sopenharmony_ci		return -ERANGE;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (delta < 0) {
2188c2ecf20Sopenharmony_ci		delta = -delta;
2198c2ecf20Sopenharmony_ci		tcf = TCFCR0_TCF_DECREMENT;
2208c2ecf20Sopenharmony_ci	} else {
2218c2ecf20Sopenharmony_ci		tcf = TCFCR0_TCF_INCREMENT;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	ts = ns_to_timespec64(delta);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	base = tai->base;
2278c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tai->lock, flags);
2288c2ecf20Sopenharmony_ci	mvpp2_tai_write_tlv(&ts, 0, base);
2298c2ecf20Sopenharmony_ci	mvpp2_tai_op(tcf, base);
2308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tai->lock, flags);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	return 0;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int mvpp22_tai_gettimex64(struct ptp_clock_info *ptp,
2368c2ecf20Sopenharmony_ci				 struct timespec64 *ts,
2378c2ecf20Sopenharmony_ci				 struct ptp_system_timestamp *sts)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct mvpp2_tai *tai = ptp_to_tai(ptp);
2408c2ecf20Sopenharmony_ci	unsigned long flags;
2418c2ecf20Sopenharmony_ci	void __iomem *base;
2428c2ecf20Sopenharmony_ci	u32 tcsr;
2438c2ecf20Sopenharmony_ci	int ret;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	base = tai->base;
2468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tai->lock, flags);
2478c2ecf20Sopenharmony_ci	/* XXX: the only way to read the PTP time is for the CPU to trigger
2488c2ecf20Sopenharmony_ci	 * an event. However, there is no way to distinguish between the CPU
2498c2ecf20Sopenharmony_ci	 * triggered event, and an external event on PTP_EVENT_REQ. So this
2508c2ecf20Sopenharmony_ci	 * is incompatible with external use of PTP_EVENT_REQ.
2518c2ecf20Sopenharmony_ci	 */
2528c2ecf20Sopenharmony_ci	ptp_read_system_prets(sts);
2538c2ecf20Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
2548c2ecf20Sopenharmony_ci			 TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
2558c2ecf20Sopenharmony_ci			 TCFCR0_TCF_CAPTURE | TCFCR0_TCF_TRIGGER);
2568c2ecf20Sopenharmony_ci	ptp_read_system_postts(sts);
2578c2ecf20Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
2588c2ecf20Sopenharmony_ci			 TCFCR0_TCF_NOP);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	tcsr = readl(base + MVPP22_TAI_TCSR);
2618c2ecf20Sopenharmony_ci	if (tcsr & TCSR_CAPTURE_1_VALID) {
2628c2ecf20Sopenharmony_ci		mvpp22_tai_read_ts(ts, base + MVPP22_TAI_TCV1_SEC_HIGH);
2638c2ecf20Sopenharmony_ci		ret = 0;
2648c2ecf20Sopenharmony_ci	} else if (tcsr & TCSR_CAPTURE_0_VALID) {
2658c2ecf20Sopenharmony_ci		mvpp22_tai_read_ts(ts, base + MVPP22_TAI_TCV0_SEC_HIGH);
2668c2ecf20Sopenharmony_ci		ret = 0;
2678c2ecf20Sopenharmony_ci	} else {
2688c2ecf20Sopenharmony_ci		/* We don't seem to have a reading... */
2698c2ecf20Sopenharmony_ci		ret = -EBUSY;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tai->lock, flags);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return ret;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int mvpp22_tai_settime64(struct ptp_clock_info *ptp,
2778c2ecf20Sopenharmony_ci				const struct timespec64 *ts)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct mvpp2_tai *tai = ptp_to_tai(ptp);
2808c2ecf20Sopenharmony_ci	unsigned long flags;
2818c2ecf20Sopenharmony_ci	void __iomem *base;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	base = tai->base;
2848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tai->lock, flags);
2858c2ecf20Sopenharmony_ci	mvpp2_tai_write_tlv(ts, 0, base);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* Trigger an update to load the value from the TLV registers
2888c2ecf20Sopenharmony_ci	 * into the TOD counter. Note that an external unmaskable event on
2898c2ecf20Sopenharmony_ci	 * PTP_EVENT_REQ will also trigger this action.
2908c2ecf20Sopenharmony_ci	 */
2918c2ecf20Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
2928c2ecf20Sopenharmony_ci			 TCFCR0_PHASE_UPDATE_ENABLE |
2938c2ecf20Sopenharmony_ci			 TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
2948c2ecf20Sopenharmony_ci			 TCFCR0_TCF_UPDATE | TCFCR0_TCF_TRIGGER);
2958c2ecf20Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
2968c2ecf20Sopenharmony_ci			 TCFCR0_TCF_NOP);
2978c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tai->lock, flags);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return 0;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic long mvpp22_tai_aux_work(struct ptp_clock_info *ptp)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct mvpp2_tai *tai = ptp_to_tai(ptp);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	mvpp22_tai_gettimex64(ptp, &tai->stamp, NULL);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return msecs_to_jiffies(2000);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic void mvpp22_tai_set_step(struct mvpp2_tai *tai)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	void __iomem *base = tai->base;
3148c2ecf20Sopenharmony_ci	u32 nano, frac;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	nano = upper_32_bits(tai->period);
3178c2ecf20Sopenharmony_ci	frac = lower_32_bits(tai->period);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* As the fractional nanosecond is a signed offset, if the MSB (sign)
3208c2ecf20Sopenharmony_ci	 * bit is set, we have to increment the whole nanoseconds.
3218c2ecf20Sopenharmony_ci	 */
3228c2ecf20Sopenharmony_ci	if (frac >= 0x80000000)
3238c2ecf20Sopenharmony_ci		nano += 1;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	mvpp2_tai_write(nano, base + MVPP22_TAI_TOD_STEP_NANO_CR);
3268c2ecf20Sopenharmony_ci	mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TOD_STEP_FRAC_HIGH);
3278c2ecf20Sopenharmony_ci	mvpp2_tai_write(frac, base + MVPP22_TAI_TOD_STEP_FRAC_LOW);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void mvpp22_tai_init(struct mvpp2_tai *tai)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	void __iomem *base = tai->base;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	mvpp22_tai_set_step(tai);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* Release the TAI reset */
3378c2ecf20Sopenharmony_ci	mvpp2_tai_modify(base + MVPP22_TAI_CR0, CR0_SW_NRESET, CR0_SW_NRESET);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ciint mvpp22_tai_ptp_clock_index(struct mvpp2_tai *tai)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	return ptp_clock_index(tai->ptp_clock);
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_civoid mvpp22_tai_tstamp(struct mvpp2_tai *tai, u32 tstamp,
3468c2ecf20Sopenharmony_ci		       struct skb_shared_hwtstamps *hwtstamp)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct timespec64 ts;
3498c2ecf20Sopenharmony_ci	int delta;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* The tstamp consists of 2 bits of seconds and 30 bits of nanoseconds.
3528c2ecf20Sopenharmony_ci	 * We use our stored timestamp (tai->stamp) to form a full timestamp,
3538c2ecf20Sopenharmony_ci	 * and we must read the seconds exactly once.
3548c2ecf20Sopenharmony_ci	 */
3558c2ecf20Sopenharmony_ci	ts.tv_sec = READ_ONCE(tai->stamp.tv_sec);
3568c2ecf20Sopenharmony_ci	ts.tv_nsec = tstamp & 0x3fffffff;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/* Calculate the delta in seconds between our stored timestamp and
3598c2ecf20Sopenharmony_ci	 * the value read from the queue. Allow timestamps one second in the
3608c2ecf20Sopenharmony_ci	 * past, otherwise consider them to be in the future.
3618c2ecf20Sopenharmony_ci	 */
3628c2ecf20Sopenharmony_ci	delta = ((tstamp >> 30) - (ts.tv_sec & 3)) & 3;
3638c2ecf20Sopenharmony_ci	if (delta == 3)
3648c2ecf20Sopenharmony_ci		delta -= 4;
3658c2ecf20Sopenharmony_ci	ts.tv_sec += delta;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	memset(hwtstamp, 0, sizeof(*hwtstamp));
3688c2ecf20Sopenharmony_ci	hwtstamp->hwtstamp = timespec64_to_ktime(ts);
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_civoid mvpp22_tai_start(struct mvpp2_tai *tai)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	long delay;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	delay = mvpp22_tai_aux_work(&tai->caps);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	ptp_schedule_worker(tai->ptp_clock, delay);
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_civoid mvpp22_tai_stop(struct mvpp2_tai *tai)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	ptp_cancel_worker_sync(tai->ptp_clock);
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic void mvpp22_tai_remove(void *priv)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct mvpp2_tai *tai = priv;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (!IS_ERR(tai->ptp_clock))
3908c2ecf20Sopenharmony_ci		ptp_clock_unregister(tai->ptp_clock);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ciint mvpp22_tai_probe(struct device *dev, struct mvpp2 *priv)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct mvpp2_tai *tai;
3968c2ecf20Sopenharmony_ci	int ret;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	tai = devm_kzalloc(dev, sizeof(*tai), GFP_KERNEL);
3998c2ecf20Sopenharmony_ci	if (!tai)
4008c2ecf20Sopenharmony_ci		return -ENOMEM;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	spin_lock_init(&tai->lock);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	tai->base = priv->iface_base;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	/* The step size consists of three registers - a 16-bit nanosecond step
4078c2ecf20Sopenharmony_ci	 * size, and a 32-bit fractional nanosecond step size split over two
4088c2ecf20Sopenharmony_ci	 * registers. The fractional nanosecond step size has units of 2^-32ns.
4098c2ecf20Sopenharmony_ci	 *
4108c2ecf20Sopenharmony_ci	 * To calculate this, we calculate:
4118c2ecf20Sopenharmony_ci	 *   (10^9 + freq / 2) / (freq * 2^-32)
4128c2ecf20Sopenharmony_ci	 * which gives us the nanosecond step to the nearest integer in 16.32
4138c2ecf20Sopenharmony_ci	 * fixed point format, and the fractional part of the step size with
4148c2ecf20Sopenharmony_ci	 * the MSB inverted.  With rounding of the fractional nanosecond, and
4158c2ecf20Sopenharmony_ci	 * simplification, this becomes:
4168c2ecf20Sopenharmony_ci	 *   (10^9 << 32 + freq << 31 + (freq + 1) >> 1) / freq
4178c2ecf20Sopenharmony_ci	 *
4188c2ecf20Sopenharmony_ci	 * So:
4198c2ecf20Sopenharmony_ci	 *   div = (10^9 << 32 + freq << 31 + (freq + 1) >> 1) / freq
4208c2ecf20Sopenharmony_ci	 *   nano = upper_32_bits(div);
4218c2ecf20Sopenharmony_ci	 *   frac = lower_32_bits(div) ^ 0x80000000;
4228c2ecf20Sopenharmony_ci	 * Will give the values for the registers.
4238c2ecf20Sopenharmony_ci	 *
4248c2ecf20Sopenharmony_ci	 * This is all seems perfect, but alas it is not when considering the
4258c2ecf20Sopenharmony_ci	 * whole story.  The system is clocked from 25MHz, which is multiplied
4268c2ecf20Sopenharmony_ci	 * by a PLL to 1GHz, and then divided by three, giving 333333333Hz
4278c2ecf20Sopenharmony_ci	 * (recurring).  This gives exactly 3ns, but using 333333333Hz with
4288c2ecf20Sopenharmony_ci	 * the above gives an error of 13*2^-32ns.
4298c2ecf20Sopenharmony_ci	 *
4308c2ecf20Sopenharmony_ci	 * Consequently, we use the period rather than calculating from the
4318c2ecf20Sopenharmony_ci	 * frequency.
4328c2ecf20Sopenharmony_ci	 */
4338c2ecf20Sopenharmony_ci	tai->period = 3ULL << 32;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	mvpp22_tai_init(tai);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	tai->caps.owner = THIS_MODULE;
4388c2ecf20Sopenharmony_ci	strscpy(tai->caps.name, "Marvell PP2.2", sizeof(tai->caps.name));
4398c2ecf20Sopenharmony_ci	tai->caps.max_adj = mvpp22_calc_max_adj(tai);
4408c2ecf20Sopenharmony_ci	tai->caps.adjfine = mvpp22_tai_adjfine;
4418c2ecf20Sopenharmony_ci	tai->caps.adjtime = mvpp22_tai_adjtime;
4428c2ecf20Sopenharmony_ci	tai->caps.gettimex64 = mvpp22_tai_gettimex64;
4438c2ecf20Sopenharmony_ci	tai->caps.settime64 = mvpp22_tai_settime64;
4448c2ecf20Sopenharmony_ci	tai->caps.do_aux_work = mvpp22_tai_aux_work;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	ret = devm_add_action(dev, mvpp22_tai_remove, tai);
4478c2ecf20Sopenharmony_ci	if (ret)
4488c2ecf20Sopenharmony_ci		return ret;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	tai->ptp_clock = ptp_clock_register(&tai->caps, dev);
4518c2ecf20Sopenharmony_ci	if (IS_ERR(tai->ptp_clock))
4528c2ecf20Sopenharmony_ci		return PTR_ERR(tai->ptp_clock);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	priv->tai = tai;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	return 0;
4578c2ecf20Sopenharmony_ci}
458