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