linux/drivers/clocksource/timer-vt8500.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  arch/arm/mach-vt8500/timer.c
   4 *
   5 *  Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
   6 *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
   7 */
   8
   9/*
  10 * This file is copied and modified from the original timer.c provided by
  11 * Alexey Charkov. Minor changes have been made for Device Tree Support.
  12 */
  13
  14#include <linux/io.h>
  15#include <linux/irq.h>
  16#include <linux/interrupt.h>
  17#include <linux/clocksource.h>
  18#include <linux/clockchips.h>
  19#include <linux/delay.h>
  20
  21#include <linux/of.h>
  22#include <linux/of_address.h>
  23#include <linux/of_irq.h>
  24
  25#define VT8500_TIMER_OFFSET     0x0100
  26#define VT8500_TIMER_HZ         3000000
  27#define TIMER_MATCH_VAL         0x0000
  28#define TIMER_COUNT_VAL         0x0010
  29#define TIMER_STATUS_VAL        0x0014
  30#define TIMER_IER_VAL           0x001c          /* interrupt enable */
  31#define TIMER_CTRL_VAL          0x0020
  32#define TIMER_AS_VAL            0x0024          /* access status */
  33#define TIMER_COUNT_R_ACTIVE    (1 << 5)        /* not ready for read */
  34#define TIMER_COUNT_W_ACTIVE    (1 << 4)        /* not ready for write */
  35#define TIMER_MATCH_W_ACTIVE    (1 << 0)        /* not ready for write */
  36
  37#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
  38
  39#define MIN_OSCR_DELTA          16
  40
  41static void __iomem *regbase;
  42
  43static u64 vt8500_timer_read(struct clocksource *cs)
  44{
  45        int loops = msecs_to_loops(10);
  46        writel(3, regbase + TIMER_CTRL_VAL);
  47        while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
  48                                                && --loops)
  49                cpu_relax();
  50        return readl(regbase + TIMER_COUNT_VAL);
  51}
  52
  53static struct clocksource clocksource = {
  54        .name           = "vt8500_timer",
  55        .rating         = 200,
  56        .read           = vt8500_timer_read,
  57        .mask           = CLOCKSOURCE_MASK(32),
  58        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
  59};
  60
  61static int vt8500_timer_set_next_event(unsigned long cycles,
  62                                    struct clock_event_device *evt)
  63{
  64        int loops = msecs_to_loops(10);
  65        u64 alarm = clocksource.read(&clocksource) + cycles;
  66        while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
  67                                                && --loops)
  68                cpu_relax();
  69        writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
  70
  71        if ((signed)(alarm - clocksource.read(&clocksource)) <= MIN_OSCR_DELTA)
  72                return -ETIME;
  73
  74        writel(1, regbase + TIMER_IER_VAL);
  75
  76        return 0;
  77}
  78
  79static int vt8500_shutdown(struct clock_event_device *evt)
  80{
  81        writel(readl(regbase + TIMER_CTRL_VAL) | 1, regbase + TIMER_CTRL_VAL);
  82        writel(0, regbase + TIMER_IER_VAL);
  83        return 0;
  84}
  85
  86static struct clock_event_device clockevent = {
  87        .name                   = "vt8500_timer",
  88        .features               = CLOCK_EVT_FEAT_ONESHOT,
  89        .rating                 = 200,
  90        .set_next_event         = vt8500_timer_set_next_event,
  91        .set_state_shutdown     = vt8500_shutdown,
  92        .set_state_oneshot      = vt8500_shutdown,
  93};
  94
  95static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
  96{
  97        struct clock_event_device *evt = dev_id;
  98        writel(0xf, regbase + TIMER_STATUS_VAL);
  99        evt->event_handler(evt);
 100
 101        return IRQ_HANDLED;
 102}
 103
 104static int __init vt8500_timer_init(struct device_node *np)
 105{
 106        int timer_irq, ret;
 107
 108        regbase = of_iomap(np, 0);
 109        if (!regbase) {
 110                pr_err("%s: Missing iobase description in Device Tree\n",
 111                                                                __func__);
 112                return -ENXIO;
 113        }
 114
 115        timer_irq = irq_of_parse_and_map(np, 0);
 116        if (!timer_irq) {
 117                pr_err("%s: Missing irq description in Device Tree\n",
 118                                                                __func__);
 119                return -EINVAL;
 120        }
 121
 122        writel(1, regbase + TIMER_CTRL_VAL);
 123        writel(0xf, regbase + TIMER_STATUS_VAL);
 124        writel(~0, regbase + TIMER_MATCH_VAL);
 125
 126        ret = clocksource_register_hz(&clocksource, VT8500_TIMER_HZ);
 127        if (ret) {
 128                pr_err("%s: clocksource_register failed for %s\n",
 129                       __func__, clocksource.name);
 130                return ret;
 131        }
 132
 133        clockevent.cpumask = cpumask_of(0);
 134
 135        ret = request_irq(timer_irq, vt8500_timer_interrupt,
 136                          IRQF_TIMER | IRQF_IRQPOLL, "vt8500_timer",
 137                          &clockevent);
 138        if (ret) {
 139                pr_err("%s: setup_irq failed for %s\n", __func__,
 140                                                        clockevent.name);
 141                return ret;
 142        }
 143
 144        clockevents_config_and_register(&clockevent, VT8500_TIMER_HZ,
 145                                        MIN_OSCR_DELTA * 2, 0xf0000000);
 146
 147        return 0;
 148}
 149
 150TIMER_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init);
 151