linux/drivers/clocksource/vt8500_timer.c
<<
>>
Prefs
   1/*
   2 *  arch/arm/mach-vt8500/timer.c
   3 *
   4 *  Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
   5 *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20 */
  21
  22/*
  23 * This file is copied and modified from the original timer.c provided by
  24 * Alexey Charkov. Minor changes have been made for Device Tree Support.
  25 */
  26
  27#include <linux/io.h>
  28#include <linux/irq.h>
  29#include <linux/interrupt.h>
  30#include <linux/clocksource.h>
  31#include <linux/clockchips.h>
  32#include <linux/delay.h>
  33
  34#include <linux/of.h>
  35#include <linux/of_address.h>
  36#include <linux/of_irq.h>
  37
  38#define VT8500_TIMER_OFFSET     0x0100
  39#define VT8500_TIMER_HZ         3000000
  40#define TIMER_MATCH_VAL         0x0000
  41#define TIMER_COUNT_VAL         0x0010
  42#define TIMER_STATUS_VAL        0x0014
  43#define TIMER_IER_VAL           0x001c          /* interrupt enable */
  44#define TIMER_CTRL_VAL          0x0020
  45#define TIMER_AS_VAL            0x0024          /* access status */
  46#define TIMER_COUNT_R_ACTIVE    (1 << 5)        /* not ready for read */
  47#define TIMER_COUNT_W_ACTIVE    (1 << 4)        /* not ready for write */
  48#define TIMER_MATCH_W_ACTIVE    (1 << 0)        /* not ready for write */
  49
  50#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
  51
  52#define MIN_OSCR_DELTA          16
  53
  54static void __iomem *regbase;
  55
  56static u64 vt8500_timer_read(struct clocksource *cs)
  57{
  58        int loops = msecs_to_loops(10);
  59        writel(3, regbase + TIMER_CTRL_VAL);
  60        while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
  61                                                && --loops)
  62                cpu_relax();
  63        return readl(regbase + TIMER_COUNT_VAL);
  64}
  65
  66static struct clocksource clocksource = {
  67        .name           = "vt8500_timer",
  68        .rating         = 200,
  69        .read           = vt8500_timer_read,
  70        .mask           = CLOCKSOURCE_MASK(32),
  71        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
  72};
  73
  74static int vt8500_timer_set_next_event(unsigned long cycles,
  75                                    struct clock_event_device *evt)
  76{
  77        int loops = msecs_to_loops(10);
  78        u64 alarm = clocksource.read(&clocksource) + cycles;
  79        while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
  80                                                && --loops)
  81                cpu_relax();
  82        writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
  83
  84        if ((signed)(alarm - clocksource.read(&clocksource)) <= MIN_OSCR_DELTA)
  85                return -ETIME;
  86
  87        writel(1, regbase + TIMER_IER_VAL);
  88
  89        return 0;
  90}
  91
  92static int vt8500_shutdown(struct clock_event_device *evt)
  93{
  94        writel(readl(regbase + TIMER_CTRL_VAL) | 1, regbase + TIMER_CTRL_VAL);
  95        writel(0, regbase + TIMER_IER_VAL);
  96        return 0;
  97}
  98
  99static struct clock_event_device clockevent = {
 100        .name                   = "vt8500_timer",
 101        .features               = CLOCK_EVT_FEAT_ONESHOT,
 102        .rating                 = 200,
 103        .set_next_event         = vt8500_timer_set_next_event,
 104        .set_state_shutdown     = vt8500_shutdown,
 105        .set_state_oneshot      = vt8500_shutdown,
 106};
 107
 108static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
 109{
 110        struct clock_event_device *evt = dev_id;
 111        writel(0xf, regbase + TIMER_STATUS_VAL);
 112        evt->event_handler(evt);
 113
 114        return IRQ_HANDLED;
 115}
 116
 117static struct irqaction irq = {
 118        .name    = "vt8500_timer",
 119        .flags   = IRQF_TIMER | IRQF_IRQPOLL,
 120        .handler = vt8500_timer_interrupt,
 121        .dev_id  = &clockevent,
 122};
 123
 124static int __init vt8500_timer_init(struct device_node *np)
 125{
 126        int timer_irq, ret;
 127
 128        regbase = of_iomap(np, 0);
 129        if (!regbase) {
 130                pr_err("%s: Missing iobase description in Device Tree\n",
 131                                                                __func__);
 132                return -ENXIO;
 133        }
 134
 135        timer_irq = irq_of_parse_and_map(np, 0);
 136        if (!timer_irq) {
 137                pr_err("%s: Missing irq description in Device Tree\n",
 138                                                                __func__);
 139                return -EINVAL;
 140        }
 141
 142        writel(1, regbase + TIMER_CTRL_VAL);
 143        writel(0xf, regbase + TIMER_STATUS_VAL);
 144        writel(~0, regbase + TIMER_MATCH_VAL);
 145
 146        ret = clocksource_register_hz(&clocksource, VT8500_TIMER_HZ);
 147        if (ret) {
 148                pr_err("%s: vt8500_timer_init: clocksource_register failed for %s\n",
 149                       __func__, clocksource.name);
 150                return ret;
 151        }
 152
 153        clockevent.cpumask = cpumask_of(0);
 154
 155        ret = setup_irq(timer_irq, &irq);
 156        if (ret) {
 157                pr_err("%s: setup_irq failed for %s\n", __func__,
 158                                                        clockevent.name);
 159                return ret;
 160        }
 161
 162        clockevents_config_and_register(&clockevent, VT8500_TIMER_HZ,
 163                                        MIN_OSCR_DELTA * 2, 0xf0000000);
 164
 165        return 0;
 166}
 167
 168CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init);
 169