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