linux/drivers/clocksource/timer-integrator-ap.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Integrator/AP timer driver
   4 * Copyright (C) 2000-2003 Deep Blue Solutions Ltd
   5 * Copyright (c) 2014, Linaro Limited
   6 */
   7
   8#include <linux/clk.h>
   9#include <linux/clocksource.h>
  10#include <linux/of_irq.h>
  11#include <linux/of_address.h>
  12#include <linux/of_platform.h>
  13#include <linux/clockchips.h>
  14#include <linux/interrupt.h>
  15#include <linux/sched_clock.h>
  16
  17#include "timer-sp.h"
  18
  19static void __iomem * sched_clk_base;
  20
  21static u64 notrace integrator_read_sched_clock(void)
  22{
  23        return -readl(sched_clk_base + TIMER_VALUE);
  24}
  25
  26static int __init integrator_clocksource_init(unsigned long inrate,
  27                                              void __iomem *base)
  28{
  29        u32 ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC;
  30        unsigned long rate = inrate;
  31        int ret;
  32
  33        if (rate >= 1500000) {
  34                rate /= 16;
  35                ctrl |= TIMER_CTRL_DIV16;
  36        }
  37
  38        writel(0xffff, base + TIMER_LOAD);
  39        writel(ctrl, base + TIMER_CTRL);
  40
  41        ret = clocksource_mmio_init(base + TIMER_VALUE, "timer2",
  42                                    rate, 200, 16, clocksource_mmio_readl_down);
  43        if (ret)
  44                return ret;
  45
  46        sched_clk_base = base;
  47        sched_clock_register(integrator_read_sched_clock, 16, rate);
  48
  49        return 0;
  50}
  51
  52static unsigned long timer_reload;
  53static void __iomem * clkevt_base;
  54
  55/*
  56 * IRQ handler for the timer
  57 */
  58static irqreturn_t integrator_timer_interrupt(int irq, void *dev_id)
  59{
  60        struct clock_event_device *evt = dev_id;
  61
  62        /* clear the interrupt */
  63        writel(1, clkevt_base + TIMER_INTCLR);
  64
  65        evt->event_handler(evt);
  66
  67        return IRQ_HANDLED;
  68}
  69
  70static int clkevt_shutdown(struct clock_event_device *evt)
  71{
  72        u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE;
  73
  74        /* Disable timer */
  75        writel(ctrl, clkevt_base + TIMER_CTRL);
  76        return 0;
  77}
  78
  79static int clkevt_set_oneshot(struct clock_event_device *evt)
  80{
  81        u32 ctrl = readl(clkevt_base + TIMER_CTRL) &
  82                   ~(TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC);
  83
  84        /* Leave the timer disabled, .set_next_event will enable it */
  85        writel(ctrl, clkevt_base + TIMER_CTRL);
  86        return 0;
  87}
  88
  89static int clkevt_set_periodic(struct clock_event_device *evt)
  90{
  91        u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE;
  92
  93        /* Disable timer */
  94        writel(ctrl, clkevt_base + TIMER_CTRL);
  95
  96        /* Enable the timer and start the periodic tick */
  97        writel(timer_reload, clkevt_base + TIMER_LOAD);
  98        ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
  99        writel(ctrl, clkevt_base + TIMER_CTRL);
 100        return 0;
 101}
 102
 103static int clkevt_set_next_event(unsigned long next, struct clock_event_device *evt)
 104{
 105        unsigned long ctrl = readl(clkevt_base + TIMER_CTRL);
 106
 107        writel(ctrl & ~TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
 108        writel(next, clkevt_base + TIMER_LOAD);
 109        writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
 110
 111        return 0;
 112}
 113
 114static struct clock_event_device integrator_clockevent = {
 115        .name                   = "timer1",
 116        .features               = CLOCK_EVT_FEAT_PERIODIC |
 117                                  CLOCK_EVT_FEAT_ONESHOT,
 118        .set_state_shutdown     = clkevt_shutdown,
 119        .set_state_periodic     = clkevt_set_periodic,
 120        .set_state_oneshot      = clkevt_set_oneshot,
 121        .tick_resume            = clkevt_shutdown,
 122        .set_next_event         = clkevt_set_next_event,
 123        .rating                 = 300,
 124};
 125
 126static struct irqaction integrator_timer_irq = {
 127        .name           = "timer",
 128        .flags          = IRQF_TIMER | IRQF_IRQPOLL,
 129        .handler        = integrator_timer_interrupt,
 130        .dev_id         = &integrator_clockevent,
 131};
 132
 133static int integrator_clockevent_init(unsigned long inrate,
 134                                      void __iomem *base, int irq)
 135{
 136        unsigned long rate = inrate;
 137        unsigned int ctrl = 0;
 138        int ret;
 139
 140        clkevt_base = base;
 141        /* Calculate and program a divisor */
 142        if (rate > 0x100000 * HZ) {
 143                rate /= 256;
 144                ctrl |= TIMER_CTRL_DIV256;
 145        } else if (rate > 0x10000 * HZ) {
 146                rate /= 16;
 147                ctrl |= TIMER_CTRL_DIV16;
 148        }
 149        timer_reload = rate / HZ;
 150        writel(ctrl, clkevt_base + TIMER_CTRL);
 151
 152        ret = setup_irq(irq, &integrator_timer_irq);
 153        if (ret)
 154                return ret;
 155
 156        clockevents_config_and_register(&integrator_clockevent,
 157                                        rate,
 158                                        1,
 159                                        0xffffU);
 160        return 0;
 161}
 162
 163static int __init integrator_ap_timer_init_of(struct device_node *node)
 164{
 165        const char *path;
 166        void __iomem *base;
 167        int err;
 168        int irq;
 169        struct clk *clk;
 170        unsigned long rate;
 171        struct device_node *alias_node;
 172
 173        base = of_io_request_and_map(node, 0, "integrator-timer");
 174        if (IS_ERR(base))
 175                return PTR_ERR(base);
 176
 177        clk = of_clk_get(node, 0);
 178        if (IS_ERR(clk)) {
 179                pr_err("No clock for %pOFn\n", node);
 180                return PTR_ERR(clk);
 181        }
 182        clk_prepare_enable(clk);
 183        rate = clk_get_rate(clk);
 184        writel(0, base + TIMER_CTRL);
 185
 186        err = of_property_read_string(of_aliases,
 187                                "arm,timer-primary", &path);
 188        if (err) {
 189                pr_warn("Failed to read property\n");
 190                return err;
 191        }
 192
 193        alias_node = of_find_node_by_path(path);
 194
 195        /*
 196         * The pointer is used as an identifier not as a pointer, we
 197         * can drop the refcount on the of__node immediately after
 198         * getting it.
 199         */
 200        of_node_put(alias_node);
 201
 202        if (node == alias_node)
 203                /* The primary timer lacks IRQ, use as clocksource */
 204                return integrator_clocksource_init(rate, base);
 205
 206        err = of_property_read_string(of_aliases,
 207                                "arm,timer-secondary", &path);
 208        if (err) {
 209                pr_warn("Failed to read property\n");
 210                return err;
 211        }
 212
 213        alias_node = of_find_node_by_path(path);
 214
 215        of_node_put(alias_node);
 216
 217        if (node == alias_node) {
 218                /* The secondary timer will drive the clock event */
 219                irq = irq_of_parse_and_map(node, 0);
 220                return integrator_clockevent_init(rate, base, irq);
 221        }
 222
 223        pr_info("Timer @%p unused\n", base);
 224        clk_disable_unprepare(clk);
 225
 226        return 0;
 227}
 228
 229TIMER_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer",
 230                       integrator_ap_timer_init_of);
 231