// SPDX-License-Identifier: GPL-2.0 /* * Marvell PP2.2 TAI support * * Note: * Do NOT use the event capture support. * Do Not even set the MPP muxes to allow PTP_EVENT_REQ to be used. * It will disrupt the operation of this driver, and there is nothing * that this driver can do to prevent that. Even using PTP_EVENT_REQ * as an output will be seen as a trigger input, which can't be masked. * When ever a trigger input is seen, the action in the TCFCR0_TCF * field will be performed - whether it is a set, increment, decrement * read, or frequency update. * * Other notes (useful, not specified in the documentation): * - PTP_PULSE_OUT (PTP_EVENT_REQ MPP) * It looks like the hardware can't generate a pulse at nsec=0. (The * output doesn't trigger if the nsec field is zero.) * Note: when configured as an output via the register at 0xfX441120, * the input is still very much alive, and will trigger the current TCF * function. * - PTP_CLK_OUT (PTP_TRIG_GEN MPP) * This generates a "PPS" signal determined by the CCC registers. It * seems this is not aligned to the TOD counter in any way (it may be * initially, but if you specify a non-round second interval, it won't, * and you can't easily get it back.) * - PTP_PCLK_OUT * This generates a 50% duty cycle clock based on the TOD counter, and * seems it can be set to any period of 1ns resolution. It is probably * limited by the TOD step size. Its period is defined by the PCLK_CCC * registers. Again, its alignment to the second is questionable. * * Consequently, we support none of these. */ #include <linux/io.h> #include <linux/ptp_clock_kernel.h> #include <linux/slab.h> #include "mvpp2.h" #define CR0_SW_NRESET … #define TCFCR0_PHASE_UPDATE_ENABLE … #define TCFCR0_TCF_MASK … #define TCFCR0_TCF_UPDATE … #define TCFCR0_TCF_FREQUPDATE … #define TCFCR0_TCF_INCREMENT … #define TCFCR0_TCF_DECREMENT … #define TCFCR0_TCF_CAPTURE … #define TCFCR0_TCF_NOP … #define TCFCR0_TCF_TRIGGER … #define TCSR_CAPTURE_1_VALID … #define TCSR_CAPTURE_0_VALID … struct mvpp2_tai { … }; static void mvpp2_tai_modify(void __iomem *reg, u32 mask, u32 set) { … } static void mvpp2_tai_write(u32 val, void __iomem *reg) { … } static u32 mvpp2_tai_read(void __iomem *reg) { … } static struct mvpp2_tai *ptp_to_tai(struct ptp_clock_info *ptp) { … } static void mvpp22_tai_read_ts(struct timespec64 *ts, void __iomem *base) { … } static void mvpp2_tai_write_tlv(const struct timespec64 *ts, u32 frac, void __iomem *base) { … } static void mvpp2_tai_op(u32 op, void __iomem *base) { … } /* The adjustment has a range of +0.5ns to -0.5ns in 2^32 steps, so has units * of 2^-32 ns. * * units(s) = 1 / (2^32 * 10^9) * fractional = abs_scaled_ppm / (2^16 * 10^6) * * What we want to achieve: * freq_adjusted = freq_nominal * (1 + fractional) * freq_delta = freq_adjusted - freq_nominal => positive = faster * freq_delta = freq_nominal * (1 + fractional) - freq_nominal * So: freq_delta = freq_nominal * fractional * * However, we are dealing with periods, so: * period_adjusted = period_nominal / (1 + fractional) * period_delta = period_nominal - period_adjusted => positive = faster * period_delta = period_nominal * fractional / (1 + fractional) * * Hence: * period_delta = period_nominal * abs_scaled_ppm / * (2^16 * 10^6 + abs_scaled_ppm) * * To avoid overflow, we reduce both sides of the divide operation by a factor * of 16. */ static u64 mvpp22_calc_frac_ppm(struct mvpp2_tai *tai, long abs_scaled_ppm) { … } static s32 mvpp22_calc_max_adj(struct mvpp2_tai *tai) { … } static int mvpp22_tai_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { … } static int mvpp22_tai_adjtime(struct ptp_clock_info *ptp, s64 delta) { … } static int mvpp22_tai_gettimex64(struct ptp_clock_info *ptp, struct timespec64 *ts, struct ptp_system_timestamp *sts) { … } static int mvpp22_tai_settime64(struct ptp_clock_info *ptp, const struct timespec64 *ts) { … } static long mvpp22_tai_aux_work(struct ptp_clock_info *ptp) { … } static void mvpp22_tai_set_step(struct mvpp2_tai *tai) { … } static void mvpp22_tai_init(struct mvpp2_tai *tai) { … } int mvpp22_tai_ptp_clock_index(struct mvpp2_tai *tai) { … } void mvpp22_tai_tstamp(struct mvpp2_tai *tai, u32 tstamp, struct skb_shared_hwtstamps *hwtstamp) { … } void mvpp22_tai_start(struct mvpp2_tai *tai) { … } void mvpp22_tai_stop(struct mvpp2_tai *tai) { … } static void mvpp22_tai_remove(void *priv) { … } int mvpp22_tai_probe(struct device *dev, struct mvpp2 *priv) { … }