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#include <asm/mach/time.h>
  34
  35#include <linux/of.h>
  36#include <linux/of_address.h>
  37#include <linux/of_irq.h>
  38
  39#define VT8500_TIMER_OFFSET     0x0100
  40#define VT8500_TIMER_HZ         3000000
  41#define TIMER_MATCH_VAL         0x0000
  42#define TIMER_COUNT_VAL         0x0010
  43#define TIMER_STATUS_VAL        0x0014
  44#define TIMER_IER_VAL           0x001c          /* interrupt enable */
  45#define TIMER_CTRL_VAL          0x0020
  46#define TIMER_AS_VAL            0x0024          /* access status */
  47#define TIMER_COUNT_R_ACTIVE    (1 << 5)        /* not ready for read */
  48#define TIMER_COUNT_W_ACTIVE    (1 << 4)        /* not ready for write */
  49#define TIMER_MATCH_W_ACTIVE    (1 << 0)        /* not ready for write */
  50
  51#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
  52
  53static void __iomem *regbase;
  54
  55static cycle_t vt8500_timer_read(struct clocksource *cs)
  56{
  57        int loops = msecs_to_loops(10);
  58        writel(3, regbase + TIMER_CTRL_VAL);
  59        while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
  60                                                && --loops)
  61                cpu_relax();
  62        return readl(regbase + TIMER_COUNT_VAL);
  63}
  64
  65static struct clocksource clocksource = {
  66        .name           = "vt8500_timer",
  67        .rating         = 200,
  68        .read           = vt8500_timer_read,
  69        .mask           = CLOCKSOURCE_MASK(32),
  70        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
  71};
  72
  73static int vt8500_timer_set_next_event(unsigned long cycles,
  74                                    struct clock_event_device *evt)
  75{
  76        int loops = msecs_to_loops(10);
  77        cycle_t alarm = clocksource.read(&clocksource) + cycles;
  78        while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
  79                                                && --loops)
  80                cpu_relax();
  81        writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
  82
  83        if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
  84                return -ETIME;
  85
  86        writel(1, regbase + TIMER_IER_VAL);
  87
  88        return 0;
  89}
  90
  91static void vt8500_timer_set_mode(enum clock_event_mode mode,
  92                              struct clock_event_device *evt)
  93{
  94        switch (mode) {
  95        case CLOCK_EVT_MODE_RESUME:
  96        case CLOCK_EVT_MODE_PERIODIC:
  97                break;
  98        case CLOCK_EVT_MODE_ONESHOT:
  99        case CLOCK_EVT_MODE_UNUSED:
 100        case CLOCK_EVT_MODE_SHUTDOWN:
 101                writel(readl(regbase + TIMER_CTRL_VAL) | 1,
 102                        regbase + TIMER_CTRL_VAL);
 103                writel(0, regbase + TIMER_IER_VAL);
 104                break;
 105        }
 106}
 107
 108static struct clock_event_device clockevent = {
 109        .name           = "vt8500_timer",
 110        .features       = CLOCK_EVT_FEAT_ONESHOT,
 111        .rating         = 200,
 112        .set_next_event = vt8500_timer_set_next_event,
 113        .set_mode       = vt8500_timer_set_mode,
 114};
 115
 116static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
 117{
 118        struct clock_event_device *evt = dev_id;
 119        writel(0xf, regbase + TIMER_STATUS_VAL);
 120        evt->event_handler(evt);
 121
 122        return IRQ_HANDLED;
 123}
 124
 125static struct irqaction irq = {
 126        .name    = "vt8500_timer",
 127        .flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
 128        .handler = vt8500_timer_interrupt,
 129        .dev_id  = &clockevent,
 130};
 131
 132static struct of_device_id vt8500_timer_ids[] = {
 133        { .compatible = "via,vt8500-timer" },
 134        { }
 135};
 136
 137static void __init vt8500_timer_init(void)
 138{
 139        struct device_node *np;
 140        int timer_irq;
 141
 142        np = of_find_matching_node(NULL, vt8500_timer_ids);
 143        if (!np) {
 144                pr_err("%s: Timer description missing from Device Tree\n",
 145                                                                __func__);
 146                return;
 147        }
 148        regbase = of_iomap(np, 0);
 149        if (!regbase) {
 150                pr_err("%s: Missing iobase description in Device Tree\n",
 151                                                                __func__);
 152                of_node_put(np);
 153                return;
 154        }
 155        timer_irq = irq_of_parse_and_map(np, 0);
 156        if (!timer_irq) {
 157                pr_err("%s: Missing irq description in Device Tree\n",
 158                                                                __func__);
 159                of_node_put(np);
 160                return;
 161        }
 162
 163        writel(1, regbase + TIMER_CTRL_VAL);
 164        writel(0xf, regbase + TIMER_STATUS_VAL);
 165        writel(~0, regbase + TIMER_MATCH_VAL);
 166
 167        if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
 168                pr_err("%s: vt8500_timer_init: clocksource_register failed for %s\n",
 169                                        __func__, clocksource.name);
 170
 171        clockevent.cpumask = cpumask_of(0);
 172
 173        if (setup_irq(timer_irq, &irq))
 174                pr_err("%s: setup_irq failed for %s\n", __func__,
 175                                                        clockevent.name);
 176        clockevents_config_and_register(&clockevent, VT8500_TIMER_HZ,
 177                                        4, 0xf0000000);
 178}
 179
 180CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init)
 181
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.