linux/arch/arm/mach-omap1/time.c
<<
>>
Prefs
   1/*
   2 * linux/arch/arm/mach-omap1/time.c
   3 *
   4 * OMAP Timers
   5 *
   6 * Copyright (C) 2004 Nokia Corporation
   7 * Partial timer rewrite and additional dynamic tick timer support by
   8 * Tony Lindgen <tony@atomide.com> and
   9 * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
  10 *
  11 * MPU timer code based on the older MPU timer code for OMAP
  12 * Copyright (C) 2000 RidgeRun, Inc.
  13 * Author: Greg Lonnon <glonnon@ridgerun.com>
  14 *
  15 * This program is free software; you can redistribute it and/or modify it
  16 * under the terms of the GNU General Public License as published by the
  17 * Free Software Foundation; either version 2 of the License, or (at your
  18 * option) any later version.
  19 *
  20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  21 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
  23 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  27 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30 *
  31 * You should have received a copy of the  GNU General Public License along
  32 * with this program; if not, write  to the Free Software Foundation, Inc.,
  33 * 675 Mass Ave, Cambridge, MA 02139, USA.
  34 */
  35
  36#include <linux/kernel.h>
  37#include <linux/init.h>
  38#include <linux/delay.h>
  39#include <linux/interrupt.h>
  40#include <linux/sched.h>
  41#include <linux/spinlock.h>
  42#include <linux/clk.h>
  43#include <linux/err.h>
  44#include <linux/clocksource.h>
  45#include <linux/clockchips.h>
  46#include <linux/io.h>
  47
  48#include <asm/system.h>
  49#include <mach/hardware.h>
  50#include <asm/leds.h>
  51#include <asm/irq.h>
  52#include <asm/sched_clock.h>
  53
  54#include <asm/mach/irq.h>
  55#include <asm/mach/time.h>
  56
  57#include <plat/common.h>
  58
  59#ifdef CONFIG_OMAP_MPU_TIMER
  60
  61#define OMAP_MPU_TIMER_BASE             OMAP_MPU_TIMER1_BASE
  62#define OMAP_MPU_TIMER_OFFSET           0x100
  63
  64typedef struct {
  65        u32 cntl;                       /* CNTL_TIMER, R/W */
  66        u32 load_tim;                   /* LOAD_TIM,   W */
  67        u32 read_tim;                   /* READ_TIM,   R */
  68} omap_mpu_timer_regs_t;
  69
  70#define omap_mpu_timer_base(n)                                                  \
  71((volatile omap_mpu_timer_regs_t*)OMAP1_IO_ADDRESS(OMAP_MPU_TIMER_BASE +        \
  72                                 (n)*OMAP_MPU_TIMER_OFFSET))
  73
  74static inline unsigned long notrace omap_mpu_timer_read(int nr)
  75{
  76        volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);
  77        return timer->read_tim;
  78}
  79
  80static inline void omap_mpu_set_autoreset(int nr)
  81{
  82        volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);
  83
  84        timer->cntl = timer->cntl | MPU_TIMER_AR;
  85}
  86
  87static inline void omap_mpu_remove_autoreset(int nr)
  88{
  89        volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);
  90
  91        timer->cntl = timer->cntl & ~MPU_TIMER_AR;
  92}
  93
  94static inline void omap_mpu_timer_start(int nr, unsigned long load_val,
  95                                        int autoreset)
  96{
  97        volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);
  98        unsigned int timerflags = (MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_ST);
  99
 100        if (autoreset) timerflags |= MPU_TIMER_AR;
 101
 102        timer->cntl = MPU_TIMER_CLOCK_ENABLE;
 103        udelay(1);
 104        timer->load_tim = load_val;
 105        udelay(1);
 106        timer->cntl = timerflags;
 107}
 108
 109static inline void omap_mpu_timer_stop(int nr)
 110{
 111        volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);
 112
 113        timer->cntl &= ~MPU_TIMER_ST;
 114}
 115
 116/*
 117 * ---------------------------------------------------------------------------
 118 * MPU timer 1 ... count down to zero, interrupt, reload
 119 * ---------------------------------------------------------------------------
 120 */
 121static int omap_mpu_set_next_event(unsigned long cycles,
 122                                   struct clock_event_device *evt)
 123{
 124        omap_mpu_timer_start(0, cycles, 0);
 125        return 0;
 126}
 127
 128static void omap_mpu_set_mode(enum clock_event_mode mode,
 129                              struct clock_event_device *evt)
 130{
 131        switch (mode) {
 132        case CLOCK_EVT_MODE_PERIODIC:
 133                omap_mpu_set_autoreset(0);
 134                break;
 135        case CLOCK_EVT_MODE_ONESHOT:
 136                omap_mpu_timer_stop(0);
 137                omap_mpu_remove_autoreset(0);
 138                break;
 139        case CLOCK_EVT_MODE_UNUSED:
 140        case CLOCK_EVT_MODE_SHUTDOWN:
 141        case CLOCK_EVT_MODE_RESUME:
 142                break;
 143        }
 144}
 145
 146static struct clock_event_device clockevent_mpu_timer1 = {
 147        .name           = "mpu_timer1",
 148        .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
 149        .shift          = 32,
 150        .set_next_event = omap_mpu_set_next_event,
 151        .set_mode       = omap_mpu_set_mode,
 152};
 153
 154static irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id)
 155{
 156        struct clock_event_device *evt = &clockevent_mpu_timer1;
 157
 158        evt->event_handler(evt);
 159
 160        return IRQ_HANDLED;
 161}
 162
 163static struct irqaction omap_mpu_timer1_irq = {
 164        .name           = "mpu_timer1",
 165        .flags          = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
 166        .handler        = omap_mpu_timer1_interrupt,
 167};
 168
 169static __init void omap_init_mpu_timer(unsigned long rate)
 170{
 171        setup_irq(INT_TIMER1, &omap_mpu_timer1_irq);
 172        omap_mpu_timer_start(0, (rate / HZ) - 1, 1);
 173
 174        clockevent_mpu_timer1.mult = div_sc(rate, NSEC_PER_SEC,
 175                                            clockevent_mpu_timer1.shift);
 176        clockevent_mpu_timer1.max_delta_ns =
 177                clockevent_delta2ns(-1, &clockevent_mpu_timer1);
 178        clockevent_mpu_timer1.min_delta_ns =
 179                clockevent_delta2ns(1, &clockevent_mpu_timer1);
 180
 181        clockevent_mpu_timer1.cpumask = cpumask_of(0);
 182        clockevents_register_device(&clockevent_mpu_timer1);
 183}
 184
 185
 186/*
 187 * ---------------------------------------------------------------------------
 188 * MPU timer 2 ... free running 32-bit clock source and scheduler clock
 189 * ---------------------------------------------------------------------------
 190 */
 191
 192static unsigned long omap_mpu_timer2_overflows;
 193
 194static irqreturn_t omap_mpu_timer2_interrupt(int irq, void *dev_id)
 195{
 196        omap_mpu_timer2_overflows++;
 197        return IRQ_HANDLED;
 198}
 199
 200static struct irqaction omap_mpu_timer2_irq = {
 201        .name           = "mpu_timer2",
 202        .flags          = IRQF_DISABLED,
 203        .handler        = omap_mpu_timer2_interrupt,
 204};
 205
 206static cycle_t mpu_read(struct clocksource *cs)
 207{
 208        return ~omap_mpu_timer_read(1);
 209}
 210
 211static struct clocksource clocksource_mpu = {
 212        .name           = "mpu_timer2",
 213        .rating         = 300,
 214        .read           = mpu_read,
 215        .mask           = CLOCKSOURCE_MASK(32),
 216        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 217};
 218
 219static DEFINE_CLOCK_DATA(cd);
 220
 221static inline unsigned long long notrace _omap_mpu_sched_clock(void)
 222{
 223        u32 cyc = mpu_read(&clocksource_mpu);
 224        return cyc_to_sched_clock(&cd, cyc, (u32)~0);
 225}
 226
 227#ifndef CONFIG_OMAP_32K_TIMER
 228unsigned long long notrace sched_clock(void)
 229{
 230        return _omap_mpu_sched_clock();
 231}
 232#else
 233static unsigned long long notrace omap_mpu_sched_clock(void)
 234{
 235        return _omap_mpu_sched_clock();
 236}
 237#endif
 238
 239static void notrace mpu_update_sched_clock(void)
 240{
 241        u32 cyc = mpu_read(&clocksource_mpu);
 242        update_sched_clock(&cd, cyc, (u32)~0);
 243}
 244
 245static void __init omap_init_clocksource(unsigned long rate)
 246{
 247        static char err[] __initdata = KERN_ERR
 248                        "%s: can't register clocksource!\n";
 249
 250        setup_irq(INT_TIMER2, &omap_mpu_timer2_irq);
 251        omap_mpu_timer_start(1, ~0, 1);
 252        init_sched_clock(&cd, mpu_update_sched_clock, 32, rate);
 253
 254        if (clocksource_register_hz(&clocksource_mpu, rate))
 255                printk(err, clocksource_mpu.name);
 256}
 257
 258static void __init omap_mpu_timer_init(void)
 259{
 260        struct clk      *ck_ref = clk_get(NULL, "ck_ref");
 261        unsigned long   rate;
 262
 263        BUG_ON(IS_ERR(ck_ref));
 264
 265        rate = clk_get_rate(ck_ref);
 266        clk_put(ck_ref);
 267
 268        /* PTV = 0 */
 269        rate /= 2;
 270
 271        omap_init_mpu_timer(rate);
 272        omap_init_clocksource(rate);
 273}
 274
 275#else
 276static inline void omap_mpu_timer_init(void)
 277{
 278        pr_err("Bogus timer, should not happen\n");
 279}
 280#endif  /* CONFIG_OMAP_MPU_TIMER */
 281
 282#if defined(CONFIG_OMAP_MPU_TIMER) && defined(CONFIG_OMAP_32K_TIMER)
 283static unsigned long long (*preferred_sched_clock)(void);
 284
 285unsigned long long notrace sched_clock(void)
 286{
 287        if (!preferred_sched_clock)
 288                return 0;
 289
 290        return preferred_sched_clock();
 291}
 292
 293static inline void preferred_sched_clock_init(bool use_32k_sched_clock)
 294{
 295        if (use_32k_sched_clock)
 296                preferred_sched_clock = omap_32k_sched_clock;
 297        else
 298                preferred_sched_clock = omap_mpu_sched_clock;
 299}
 300#else
 301static inline void preferred_sched_clock_init(bool use_32k_sched_clcok)
 302{
 303}
 304#endif
 305
 306static inline int omap_32k_timer_usable(void)
 307{
 308        int res = false;
 309
 310        if (cpu_is_omap730() || cpu_is_omap15xx())
 311                return res;
 312
 313#ifdef CONFIG_OMAP_32K_TIMER
 314        res = omap_32k_timer_init();
 315#endif
 316
 317        return res;
 318}
 319
 320/*
 321 * ---------------------------------------------------------------------------
 322 * Timer initialization
 323 * ---------------------------------------------------------------------------
 324 */
 325static void __init omap_timer_init(void)
 326{
 327        if (omap_32k_timer_usable()) {
 328                preferred_sched_clock_init(1);
 329        } else {
 330                omap_mpu_timer_init();
 331                preferred_sched_clock_init(0);
 332        }
 333}
 334
 335struct sys_timer omap_timer = {
 336        .init           = omap_timer_init,
 337};
 338