root/drivers/clocksource/timer-keystone.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Keystone broadcast clock-event
 *
 * Copyright 2013 Texas Instruments, Inc.
 *
 * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
 */

#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>

#define TIMER_NAME                      "timer-keystone"

/* Timer register offsets */
#define TIM12                           0x10
#define TIM34                           0x14
#define PRD12                           0x18
#define PRD34                           0x1c
#define TCR                             0x20
#define TGCR                            0x24
#define INTCTLSTAT                      0x44

/* Timer register bitfields */
#define TCR_ENAMODE_MASK                0xC0
#define TCR_ENAMODE_ONESHOT_MASK        0x40
#define TCR_ENAMODE_PERIODIC_MASK       0x80

#define TGCR_TIM_UNRESET_MASK           0x03
#define INTCTLSTAT_ENINT_MASK           0x01

/**
 * struct keystone_timer: holds timer's data
 * @base: timer memory base address
 * @hz_period: cycles per HZ period
 * @event_dev: event device based on timer
 */
static struct keystone_timer {
        void __iomem *base;
        unsigned long hz_period;
        struct clock_event_device event_dev;
} timer;

static inline u32 keystone_timer_readl(unsigned long rg)
{
        return readl_relaxed(timer.base + rg);
}

static inline void keystone_timer_writel(u32 val, unsigned long rg)
{
        writel_relaxed(val, timer.base + rg);
}

/**
 * keystone_timer_barrier: write memory barrier
 * use explicit barrier to avoid using readl/writel non relaxed function
 * variants, because in our case non relaxed variants hide the true places
 * where barrier is needed.
 */
static inline void keystone_timer_barrier(void)
{
        __iowmb();
}

/**
 * keystone_timer_config: configures timer to work in oneshot/periodic modes.
 * @ mask: mask of the mode to configure
 * @ period: cycles number to configure for
 */
static int keystone_timer_config(u64 period, int mask)
{
        u32 tcr;
        u32 off;

        tcr = keystone_timer_readl(TCR);
        off = tcr & ~(TCR_ENAMODE_MASK);

        /* set enable mode */
        tcr |= mask;

        /* disable timer */
        keystone_timer_writel(off, TCR);
        /* here we have to be sure the timer has been disabled */
        keystone_timer_barrier();

        /* reset counter to zero, set new period */
        keystone_timer_writel(0, TIM12);
        keystone_timer_writel(0, TIM34);
        keystone_timer_writel(period & 0xffffffff, PRD12);
        keystone_timer_writel(period >> 32, PRD34);

        /*
         * enable timer
         * here we have to be sure that CNTLO, CNTHI, PRDLO, PRDHI registers
         * have been written.
         */
        keystone_timer_barrier();
        keystone_timer_writel(tcr, TCR);
        return 0;
}

static void keystone_timer_disable(void)
{
        u32 tcr;

        tcr = keystone_timer_readl(TCR);

        /* disable timer */
        tcr &= ~(TCR_ENAMODE_MASK);
        keystone_timer_writel(tcr, TCR);
}

static irqreturn_t keystone_timer_interrupt(int irq, void *dev_id)
{
        struct clock_event_device *evt = dev_id;

        evt->event_handler(evt);
        return IRQ_HANDLED;
}

static int keystone_set_next_event(unsigned long cycles,
                                  struct clock_event_device *evt)
{
        return keystone_timer_config(cycles, TCR_ENAMODE_ONESHOT_MASK);
}

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

static int keystone_set_periodic(struct clock_event_device *evt)
{
        keystone_timer_config(timer.hz_period, TCR_ENAMODE_PERIODIC_MASK);
        return 0;
}

static int __init keystone_timer_init(struct device_node *np)
{
        struct clock_event_device *event_dev = &timer.event_dev;
        unsigned long rate;
        struct clk *clk;
        int irq, error;

        irq  = irq_of_parse_and_map(np, 0);
        if (!irq) {
                pr_err("%s: failed to map interrupts\n", __func__);
                return -EINVAL;
        }

        timer.base = of_iomap(np, 0);
        if (!timer.base) {
                pr_err("%s: failed to map registers\n", __func__);
                return -ENXIO;
        }

        clk = of_clk_get(np, 0);
        if (IS_ERR(clk)) {
                pr_err("%s: failed to get clock\n", __func__);
                iounmap(timer.base);
                return PTR_ERR(clk);
        }

        error = clk_prepare_enable(clk);
        if (error) {
                pr_err("%s: failed to enable clock\n", __func__);
                goto err;
        }

        rate = clk_get_rate(clk);

        /* disable, use internal clock source */
        keystone_timer_writel(0, TCR);
        /* here we have to be sure the timer has been disabled */
        keystone_timer_barrier();

        /* reset timer as 64-bit, no pre-scaler, plus features are disabled */
        keystone_timer_writel(0, TGCR);

        /* unreset timer */
        keystone_timer_writel(TGCR_TIM_UNRESET_MASK, TGCR);

        /* init counter to zero */
        keystone_timer_writel(0, TIM12);
        keystone_timer_writel(0, TIM34);

        timer.hz_period = DIV_ROUND_UP(rate, HZ);

        /* enable timer interrupts */
        keystone_timer_writel(INTCTLSTAT_ENINT_MASK, INTCTLSTAT);

        error = request_irq(irq, keystone_timer_interrupt, IRQF_TIMER,
                            TIMER_NAME, event_dev);
        if (error) {
                pr_err("%s: failed to setup irq\n", __func__);
                goto err;
        }

        /* setup clockevent */
        event_dev->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
        event_dev->set_next_event = keystone_set_next_event;
        event_dev->set_state_shutdown = keystone_shutdown;
        event_dev->set_state_periodic = keystone_set_periodic;
        event_dev->set_state_oneshot = keystone_shutdown;
        event_dev->cpumask = cpu_possible_mask;
        event_dev->owner = THIS_MODULE;
        event_dev->name = TIMER_NAME;
        event_dev->irq = irq;

        clockevents_config_and_register(event_dev, rate, 1, ULONG_MAX);

        pr_info("keystone timer clock @%lu Hz\n", rate);
        return 0;
err:
        clk_put(clk);
        iounmap(timer.base);
        return error;
}

TIMER_OF_DECLARE(keystone_timer, "ti,keystone-timer",
                           keystone_timer_init);