root/drivers/net/ethernet/marvell/mvpp2/mvpp2_tai.c
// 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                   BIT(0)

#define TCFCR0_PHASE_UPDATE_ENABLE      BIT(8)
#define TCFCR0_TCF_MASK                 (7 << 2)
#define TCFCR0_TCF_UPDATE               (0 << 2)
#define TCFCR0_TCF_FREQUPDATE           (1 << 2)
#define TCFCR0_TCF_INCREMENT            (2 << 2)
#define TCFCR0_TCF_DECREMENT            (3 << 2)
#define TCFCR0_TCF_CAPTURE              (4 << 2)
#define TCFCR0_TCF_NOP                  (7 << 2)
#define TCFCR0_TCF_TRIGGER              BIT(0)

#define TCSR_CAPTURE_1_VALID            BIT(1)
#define TCSR_CAPTURE_0_VALID            BIT(0)

struct mvpp2_tai {
        struct ptp_clock_info caps;
        struct ptp_clock *ptp_clock;
        void __iomem *base;
        spinlock_t lock;
        u64 period;             // nanosecond period in 32.32 fixed point
        /* This timestamp is updated every two seconds */
        struct timespec64 stamp;
};

static void mvpp2_tai_modify(void __iomem *reg, u32 mask, u32 set)
{
        u32 val;

        val = readl_relaxed(reg) & ~mask;
        val |= set & mask;
        writel(val, reg);
}

static void mvpp2_tai_write(u32 val, void __iomem *reg)
{
        writel_relaxed(val & 0xffff, reg);
}

static u32 mvpp2_tai_read(void __iomem *reg)
{
        return readl_relaxed(reg) & 0xffff;
}

static struct mvpp2_tai *ptp_to_tai(struct ptp_clock_info *ptp)
{
        return container_of(ptp, struct mvpp2_tai, caps);
}

static void mvpp22_tai_read_ts(struct timespec64 *ts, void __iomem *base)
{
        ts->tv_sec = (u64)mvpp2_tai_read(base + 0) << 32 |
                     mvpp2_tai_read(base + 4) << 16 |
                     mvpp2_tai_read(base + 8);

        ts->tv_nsec = mvpp2_tai_read(base + 12) << 16 |
                      mvpp2_tai_read(base + 16);

        /* Read and discard fractional part */
        readl_relaxed(base + 20);
        readl_relaxed(base + 24);
}

static void mvpp2_tai_write_tlv(const struct timespec64 *ts, u32 frac,
                                void __iomem *base)
{
        mvpp2_tai_write(ts->tv_sec >> 32, base + MVPP22_TAI_TLV_SEC_HIGH);
        mvpp2_tai_write(ts->tv_sec >> 16, base + MVPP22_TAI_TLV_SEC_MED);
        mvpp2_tai_write(ts->tv_sec, base + MVPP22_TAI_TLV_SEC_LOW);
        mvpp2_tai_write(ts->tv_nsec >> 16, base + MVPP22_TAI_TLV_NANO_HIGH);
        mvpp2_tai_write(ts->tv_nsec, base + MVPP22_TAI_TLV_NANO_LOW);
        mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TLV_FRAC_HIGH);
        mvpp2_tai_write(frac, base + MVPP22_TAI_TLV_FRAC_LOW);
}

static void mvpp2_tai_op(u32 op, void __iomem *base)
{
        /* Trigger the operation. Note that an external unmaskable
         * event on PTP_EVENT_REQ will also trigger this action.
         */
        mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
                         TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
                         op | TCFCR0_TCF_TRIGGER);
        mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
                         TCFCR0_TCF_NOP);
}

/* 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)
{
        u64 val = tai->period * abs_scaled_ppm >> 4;

        return div_u64(val, (1000000 << 12) + (abs_scaled_ppm >> 4));
}

static s32 mvpp22_calc_max_adj(struct mvpp2_tai *tai)
{
        return 1000000;
}

static int mvpp22_tai_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
        struct mvpp2_tai *tai = ptp_to_tai(ptp);
        unsigned long flags;
        void __iomem *base;
        bool neg_adj;
        s32 frac;
        u64 val;

        neg_adj = scaled_ppm < 0;
        if (neg_adj)
                scaled_ppm = -scaled_ppm;

        val = mvpp22_calc_frac_ppm(tai, scaled_ppm);

        /* Convert to a signed 32-bit adjustment */
        if (neg_adj) {
                /* -S32_MIN warns, -val < S32_MIN fails, so go for the easy
                 * solution.
                 */
                if (val > 0x80000000)
                        return -ERANGE;

                frac = -val;
        } else {
                if (val > S32_MAX)
                        return -ERANGE;

                frac = val;
        }

        base = tai->base;
        spin_lock_irqsave(&tai->lock, flags);
        mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TLV_FRAC_HIGH);
        mvpp2_tai_write(frac, base + MVPP22_TAI_TLV_FRAC_LOW);
        mvpp2_tai_op(TCFCR0_TCF_FREQUPDATE, base);
        spin_unlock_irqrestore(&tai->lock, flags);

        return 0;
}

static int mvpp22_tai_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
        struct mvpp2_tai *tai = ptp_to_tai(ptp);
        struct timespec64 ts;
        unsigned long flags;
        void __iomem *base;
        u32 tcf;

        /* We can't deal with S64_MIN */
        if (delta == S64_MIN)
                return -ERANGE;

        if (delta < 0) {
                delta = -delta;
                tcf = TCFCR0_TCF_DECREMENT;
        } else {
                tcf = TCFCR0_TCF_INCREMENT;
        }

        ts = ns_to_timespec64(delta);

        base = tai->base;
        spin_lock_irqsave(&tai->lock, flags);
        mvpp2_tai_write_tlv(&ts, 0, base);
        mvpp2_tai_op(tcf, base);
        spin_unlock_irqrestore(&tai->lock, flags);

        return 0;
}

static int mvpp22_tai_gettimex64(struct ptp_clock_info *ptp,
                                 struct timespec64 *ts,
                                 struct ptp_system_timestamp *sts)
{
        struct mvpp2_tai *tai = ptp_to_tai(ptp);
        unsigned long flags;
        void __iomem *base;
        u32 tcsr;
        int ret;

        base = tai->base;
        spin_lock_irqsave(&tai->lock, flags);
        /* XXX: the only way to read the PTP time is for the CPU to trigger
         * an event. However, there is no way to distinguish between the CPU
         * triggered event, and an external event on PTP_EVENT_REQ. So this
         * is incompatible with external use of PTP_EVENT_REQ.
         */
        ptp_read_system_prets(sts);
        mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
                         TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
                         TCFCR0_TCF_CAPTURE | TCFCR0_TCF_TRIGGER);
        ptp_read_system_postts(sts);
        mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
                         TCFCR0_TCF_NOP);

        tcsr = readl(base + MVPP22_TAI_TCSR);
        if (tcsr & TCSR_CAPTURE_1_VALID) {
                mvpp22_tai_read_ts(ts, base + MVPP22_TAI_TCV1_SEC_HIGH);
                ret = 0;
        } else if (tcsr & TCSR_CAPTURE_0_VALID) {
                mvpp22_tai_read_ts(ts, base + MVPP22_TAI_TCV0_SEC_HIGH);
                ret = 0;
        } else {
                /* We don't seem to have a reading... */
                ret = -EBUSY;
        }
        spin_unlock_irqrestore(&tai->lock, flags);

        return ret;
}

static int mvpp22_tai_settime64(struct ptp_clock_info *ptp,
                                const struct timespec64 *ts)
{
        struct mvpp2_tai *tai = ptp_to_tai(ptp);
        unsigned long flags;
        void __iomem *base;

        base = tai->base;
        spin_lock_irqsave(&tai->lock, flags);
        mvpp2_tai_write_tlv(ts, 0, base);

        /* Trigger an update to load the value from the TLV registers
         * into the TOD counter. Note that an external unmaskable event on
         * PTP_EVENT_REQ will also trigger this action.
         */
        mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
                         TCFCR0_PHASE_UPDATE_ENABLE |
                         TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
                         TCFCR0_TCF_UPDATE | TCFCR0_TCF_TRIGGER);
        mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
                         TCFCR0_TCF_NOP);
        spin_unlock_irqrestore(&tai->lock, flags);

        return 0;
}

static long mvpp22_tai_aux_work(struct ptp_clock_info *ptp)
{
        struct mvpp2_tai *tai = ptp_to_tai(ptp);

        mvpp22_tai_gettimex64(ptp, &tai->stamp, NULL);

        return msecs_to_jiffies(2000);
}

static void mvpp22_tai_set_step(struct mvpp2_tai *tai)
{
        void __iomem *base = tai->base;
        u32 nano, frac;

        nano = upper_32_bits(tai->period);
        frac = lower_32_bits(tai->period);

        /* As the fractional nanosecond is a signed offset, if the MSB (sign)
         * bit is set, we have to increment the whole nanoseconds.
         */
        if (frac >= 0x80000000)
                nano += 1;

        mvpp2_tai_write(nano, base + MVPP22_TAI_TOD_STEP_NANO_CR);
        mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TOD_STEP_FRAC_HIGH);
        mvpp2_tai_write(frac, base + MVPP22_TAI_TOD_STEP_FRAC_LOW);
}

static void mvpp22_tai_init(struct mvpp2_tai *tai)
{
        void __iomem *base = tai->base;

        mvpp22_tai_set_step(tai);

        /* Release the TAI reset */
        mvpp2_tai_modify(base + MVPP22_TAI_CR0, CR0_SW_NRESET, CR0_SW_NRESET);
}

int mvpp22_tai_ptp_clock_index(struct mvpp2_tai *tai)
{
        return ptp_clock_index(tai->ptp_clock);
}

void mvpp22_tai_tstamp(struct mvpp2_tai *tai, u32 tstamp,
                       struct skb_shared_hwtstamps *hwtstamp)
{
        struct timespec64 ts;
        int delta;

        /* The tstamp consists of 2 bits of seconds and 30 bits of nanoseconds.
         * We use our stored timestamp (tai->stamp) to form a full timestamp,
         * and we must read the seconds exactly once.
         */
        ts.tv_sec = READ_ONCE(tai->stamp.tv_sec);
        ts.tv_nsec = tstamp & 0x3fffffff;

        /* Calculate the delta in seconds between our stored timestamp and
         * the value read from the queue. Allow timestamps one second in the
         * past, otherwise consider them to be in the future.
         */
        delta = ((tstamp >> 30) - (ts.tv_sec & 3)) & 3;
        if (delta == 3)
                delta -= 4;
        ts.tv_sec += delta;

        memset(hwtstamp, 0, sizeof(*hwtstamp));
        hwtstamp->hwtstamp = timespec64_to_ktime(ts);
}

void mvpp22_tai_start(struct mvpp2_tai *tai)
{
        long delay;

        delay = mvpp22_tai_aux_work(&tai->caps);

        ptp_schedule_worker(tai->ptp_clock, delay);
}

void mvpp22_tai_stop(struct mvpp2_tai *tai)
{
        ptp_cancel_worker_sync(tai->ptp_clock);
}

static void mvpp22_tai_remove(void *priv)
{
        struct mvpp2_tai *tai = priv;

        if (!IS_ERR(tai->ptp_clock))
                ptp_clock_unregister(tai->ptp_clock);
}

int mvpp22_tai_probe(struct device *dev, struct mvpp2 *priv)
{
        struct mvpp2_tai *tai;
        int ret;

        tai = devm_kzalloc(dev, sizeof(*tai), GFP_KERNEL);
        if (!tai)
                return -ENOMEM;

        spin_lock_init(&tai->lock);

        tai->base = priv->iface_base;

        /* The step size consists of three registers - a 16-bit nanosecond step
         * size, and a 32-bit fractional nanosecond step size split over two
         * registers. The fractional nanosecond step size has units of 2^-32ns.
         *
         * To calculate this, we calculate:
         *   (10^9 + freq / 2) / (freq * 2^-32)
         * which gives us the nanosecond step to the nearest integer in 16.32
         * fixed point format, and the fractional part of the step size with
         * the MSB inverted.  With rounding of the fractional nanosecond, and
         * simplification, this becomes:
         *   (10^9 << 32 + freq << 31 + (freq + 1) >> 1) / freq
         *
         * So:
         *   div = (10^9 << 32 + freq << 31 + (freq + 1) >> 1) / freq
         *   nano = upper_32_bits(div);
         *   frac = lower_32_bits(div) ^ 0x80000000;
         * Will give the values for the registers.
         *
         * This is all seems perfect, but alas it is not when considering the
         * whole story.  The system is clocked from 25MHz, which is multiplied
         * by a PLL to 1GHz, and then divided by three, giving 333333333Hz
         * (recurring).  This gives exactly 3ns, but using 333333333Hz with
         * the above gives an error of 13*2^-32ns.
         *
         * Consequently, we use the period rather than calculating from the
         * frequency.
         */
        tai->period = 3ULL << 32;

        mvpp22_tai_init(tai);

        tai->caps.owner = THIS_MODULE;
        strscpy(tai->caps.name, "Marvell PP2.2", sizeof(tai->caps.name));
        tai->caps.max_adj = mvpp22_calc_max_adj(tai);
        tai->caps.adjfine = mvpp22_tai_adjfine;
        tai->caps.adjtime = mvpp22_tai_adjtime;
        tai->caps.gettimex64 = mvpp22_tai_gettimex64;
        tai->caps.settime64 = mvpp22_tai_settime64;
        tai->caps.do_aux_work = mvpp22_tai_aux_work;

        ret = devm_add_action(dev, mvpp22_tai_remove, tai);
        if (ret)
                return ret;

        tai->ptp_clock = ptp_clock_register(&tai->caps, dev);
        if (IS_ERR(tai->ptp_clock))
                return PTR_ERR(tai->ptp_clock);

        priv->tai = tai;

        return 0;
}