root/drivers/clocksource/timer-rda.c
// SPDX-License-Identifier: GPL-2.0+
/*
 * RDA8810PL SoC timer driver
 *
 * Copyright RDA Microelectronics Company Limited
 * Copyright (c) 2017 Andreas Färber
 * Copyright (c) 2018 Manivannan Sadhasivam
 *
 * RDA8810PL has two independent timers: OSTIMER (56 bit) and HWTIMER (64 bit).
 * Each timer provides optional interrupt support. In this driver, OSTIMER is
 * used for clockevents and HWTIMER is used for clocksource.
 */

#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/sched_clock.h>

#include "timer-of.h"

#define RDA_OSTIMER_LOADVAL_L   0x000
#define RDA_OSTIMER_CTRL        0x004
#define RDA_HWTIMER_LOCKVAL_L   0x024
#define RDA_HWTIMER_LOCKVAL_H   0x028
#define RDA_TIMER_IRQ_MASK_SET  0x02c
#define RDA_TIMER_IRQ_MASK_CLR  0x030
#define RDA_TIMER_IRQ_CLR       0x034

#define RDA_OSTIMER_CTRL_ENABLE         BIT(24)
#define RDA_OSTIMER_CTRL_REPEAT         BIT(28)
#define RDA_OSTIMER_CTRL_LOAD           BIT(30)

#define RDA_TIMER_IRQ_MASK_OSTIMER      BIT(0)

#define RDA_TIMER_IRQ_CLR_OSTIMER       BIT(0)

static int rda_ostimer_start(void __iomem *base, bool periodic, u64 cycles)
{
        u32 ctrl, load_l;

        load_l = (u32)cycles;
        ctrl = ((cycles >> 32) & 0xffffff);
        ctrl |= RDA_OSTIMER_CTRL_LOAD | RDA_OSTIMER_CTRL_ENABLE;
        if (periodic)
                ctrl |= RDA_OSTIMER_CTRL_REPEAT;

        /* Enable ostimer interrupt first */
        writel_relaxed(RDA_TIMER_IRQ_MASK_OSTIMER,
                       base + RDA_TIMER_IRQ_MASK_SET);

        /* Write low 32 bits first, high 24 bits are with ctrl */
        writel_relaxed(load_l, base + RDA_OSTIMER_LOADVAL_L);
        writel_relaxed(ctrl, base + RDA_OSTIMER_CTRL);

        return 0;
}

static int rda_ostimer_stop(void __iomem *base)
{
        /* Disable ostimer interrupt first */
        writel_relaxed(RDA_TIMER_IRQ_MASK_OSTIMER,
                       base + RDA_TIMER_IRQ_MASK_CLR);

        writel_relaxed(0, base + RDA_OSTIMER_CTRL);

        return 0;
}

static int rda_ostimer_set_state_shutdown(struct clock_event_device *evt)
{
        struct timer_of *to = to_timer_of(evt);

        rda_ostimer_stop(timer_of_base(to));

        return 0;
}

static int rda_ostimer_set_state_oneshot(struct clock_event_device *evt)
{
        struct timer_of *to = to_timer_of(evt);

        rda_ostimer_stop(timer_of_base(to));

        return 0;
}

static int rda_ostimer_set_state_periodic(struct clock_event_device *evt)
{
        struct timer_of *to = to_timer_of(evt);
        unsigned long cycles_per_jiffy;

        rda_ostimer_stop(timer_of_base(to));

        cycles_per_jiffy = ((unsigned long long)NSEC_PER_SEC / HZ *
                             evt->mult) >> evt->shift;
        rda_ostimer_start(timer_of_base(to), true, cycles_per_jiffy);

        return 0;
}

static int rda_ostimer_tick_resume(struct clock_event_device *evt)
{
        return 0;
}

static int rda_ostimer_set_next_event(unsigned long evt,
                                      struct clock_event_device *ev)
{
        struct timer_of *to = to_timer_of(ev);

        rda_ostimer_start(timer_of_base(to), false, evt);

        return 0;
}

static irqreturn_t rda_ostimer_interrupt(int irq, void *dev_id)
{
        struct clock_event_device *evt = dev_id;
        struct timer_of *to = to_timer_of(evt);

        /* clear timer int */
        writel_relaxed(RDA_TIMER_IRQ_CLR_OSTIMER,
                       timer_of_base(to) + RDA_TIMER_IRQ_CLR);

        if (evt->event_handler)
                evt->event_handler(evt);

        return IRQ_HANDLED;
}

static struct timer_of rda_ostimer_of = {
        .flags = TIMER_OF_IRQ | TIMER_OF_BASE,

        .clkevt = {
                .name = "rda-ostimer",
                .rating = 250,
                .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
                            CLOCK_EVT_FEAT_DYNIRQ,
                .set_state_shutdown = rda_ostimer_set_state_shutdown,
                .set_state_oneshot = rda_ostimer_set_state_oneshot,
                .set_state_periodic = rda_ostimer_set_state_periodic,
                .tick_resume = rda_ostimer_tick_resume,
                .set_next_event = rda_ostimer_set_next_event,
        },

        .of_base = {
                .name = "rda-timer",
                .index = 0,
        },

        .of_irq = {
                .name = "ostimer",
                .handler = rda_ostimer_interrupt,
                .flags = IRQF_TIMER,
        },
};

static u64 rda_hwtimer_clocksource_read(void)
{
        void __iomem *base = timer_of_base(&rda_ostimer_of);
        u32 lo, hi;

        /* Always read low 32 bits first */
        do {
                lo = readl_relaxed(base + RDA_HWTIMER_LOCKVAL_L);
                hi = readl_relaxed(base + RDA_HWTIMER_LOCKVAL_H);
        } while (hi != readl_relaxed(base + RDA_HWTIMER_LOCKVAL_H));

        return ((u64)hi << 32) | lo;
}

static u64 rda_hwtimer_read(struct clocksource *cs)
{
        return rda_hwtimer_clocksource_read();
}

static struct clocksource rda_hwtimer_clocksource = {
        .name           = "rda-timer",
        .rating         = 400,
        .read           = rda_hwtimer_read,
        .mask           = CLOCKSOURCE_MASK(64),
        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
};

static int __init rda_timer_init(struct device_node *np)
{
        unsigned long rate = 2000000;
        int ret;

        ret = timer_of_init(np, &rda_ostimer_of);
        if (ret)
                return ret;

        clocksource_register_hz(&rda_hwtimer_clocksource, rate);
        sched_clock_register(rda_hwtimer_clocksource_read, 64, rate);

        clockevents_config_and_register(&rda_ostimer_of.clkevt, rate,
                                        0x2, UINT_MAX);

        return 0;
}

TIMER_OF_DECLARE(rda8810pl, "rda,8810pl-timer", rda_timer_init);