clocksource/drivers/davinci: Add support for clocksource
authorBartosz Golaszewski <bgolaszewski@baylibre.com>
Mon, 24 Jun 2019 09:50:56 +0000 (11:50 +0200)
committerDaniel Lezcano <daniel.lezcano@linaro.org>
Tue, 25 Jun 2019 18:46:14 +0000 (20:46 +0200)
Extend the davinci-timer driver to also register a clock source.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
drivers/clocksource/timer-davinci.c

index 246a5564495d7f74b2b7bfcaa45f6e87470774da..62745c96204982f8b870efa014775b5b41feb867 100644 (file)
@@ -43,6 +43,8 @@
 #define DAVINCI_TIMER_MIN_DELTA                        0x01
 #define DAVINCI_TIMER_MAX_DELTA                        0xfffffffe
 
+#define DAVINCI_TIMER_CLKSRC_BITS              32
+
 #define DAVINCI_TIMER_TGCR_DEFAULT \
                (DAVINCI_TIMER_TIMMODE_32BIT_UNCHAINED | DAVINCI_TIMER_UNRESET)
 
@@ -52,6 +54,16 @@ struct davinci_clockevent {
        unsigned int cmp_off;
 };
 
+/*
+ * This must be globally accessible by davinci_timer_read_sched_clock(), so
+ * let's keep it here.
+ */
+static struct {
+       struct clocksource dev;
+       void __iomem *base;
+       unsigned int tim_off;
+} davinci_clocksource;
+
 static struct davinci_clockevent *
 to_davinci_clockevent(struct clock_event_device *clockevent)
 {
@@ -166,6 +178,53 @@ static irqreturn_t davinci_timer_irq_timer(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+static u64 notrace davinci_timer_read_sched_clock(void)
+{
+       return readl_relaxed(davinci_clocksource.base +
+                            davinci_clocksource.tim_off);
+}
+
+static u64 davinci_clocksource_read(struct clocksource *dev)
+{
+       return davinci_timer_read_sched_clock();
+}
+
+/*
+ * Standard use-case: we're using tim12 for clockevent and tim34 for
+ * clocksource. The default is making the former run in oneshot mode
+ * and the latter in periodic mode.
+ */
+static void davinci_clocksource_init_tim34(void __iomem *base)
+{
+       int tcr;
+
+       tcr = DAVINCI_TIMER_ENAMODE_PERIODIC <<
+               DAVINCI_TIMER_ENAMODE_SHIFT_TIM34;
+       tcr |= DAVINCI_TIMER_ENAMODE_ONESHOT <<
+               DAVINCI_TIMER_ENAMODE_SHIFT_TIM12;
+
+       writel_relaxed(0x0, base + DAVINCI_TIMER_REG_TIM34);
+       writel_relaxed(UINT_MAX, base + DAVINCI_TIMER_REG_PRD34);
+       writel_relaxed(tcr, base + DAVINCI_TIMER_REG_TCR);
+}
+
+/*
+ * Special use-case on da830: the DSP may use tim34. We're using tim12 for
+ * both clocksource and clockevent. We set tim12 to periodic and don't touch
+ * tim34.
+ */
+static void davinci_clocksource_init_tim12(void __iomem *base)
+{
+       unsigned int tcr;
+
+       tcr = DAVINCI_TIMER_ENAMODE_PERIODIC <<
+               DAVINCI_TIMER_ENAMODE_SHIFT_TIM12;
+
+       writel_relaxed(0x0, base + DAVINCI_TIMER_REG_TIM12);
+       writel_relaxed(UINT_MAX, base + DAVINCI_TIMER_REG_PRD12);
+       writel_relaxed(tcr, base + DAVINCI_TIMER_REG_TCR);
+}
+
 static void davinci_timer_init(void __iomem *base)
 {
        /* Set clock to internal mode and disable it. */
@@ -247,6 +306,32 @@ int __init davinci_timer_register(struct clk *clk,
                                        DAVINCI_TIMER_MIN_DELTA,
                                        DAVINCI_TIMER_MAX_DELTA);
 
+       davinci_clocksource.dev.rating = 300;
+       davinci_clocksource.dev.read = davinci_clocksource_read;
+       davinci_clocksource.dev.mask =
+                       CLOCKSOURCE_MASK(DAVINCI_TIMER_CLKSRC_BITS);
+       davinci_clocksource.dev.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+       davinci_clocksource.base = base;
+
+       if (timer_cfg->cmp_off) {
+               davinci_clocksource.dev.name = "tim12";
+               davinci_clocksource.tim_off = DAVINCI_TIMER_REG_TIM12;
+               davinci_clocksource_init_tim12(base);
+       } else {
+               davinci_clocksource.dev.name = "tim34";
+               davinci_clocksource.tim_off = DAVINCI_TIMER_REG_TIM34;
+               davinci_clocksource_init_tim34(base);
+       }
+
+       rv = clocksource_register_hz(&davinci_clocksource.dev, tick_rate);
+       if (rv) {
+               pr_err("Unable to register clocksource");
+               return rv;
+       }
+
+       sched_clock_register(davinci_timer_read_sched_clock,
+                            DAVINCI_TIMER_CLKSRC_BITS, tick_rate);
+
        return 0;
 }