linux/arch/arm/mach-nomadik/timer.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/mach-nomadik/timer.c
   3 *
   4 * Copyright (C) 2008 STMicroelectronics
   5 * Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x
   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 version 2, as
   9 * published by the Free Software Foundation.
  10 */
  11#include <linux/init.h>
  12#include <linux/interrupt.h>
  13#include <linux/irq.h>
  14#include <linux/io.h>
  15#include <linux/clockchips.h>
  16#include <linux/jiffies.h>
  17#include <asm/mach/time.h>
  18#include <mach/mtu.h>
  19
  20#define TIMER_CTRL      0x80    /* No divisor */
  21#define TIMER_PERIODIC  0x40
  22#define TIMER_SZ32BIT   0x02
  23
  24/* Initial value for SRC control register: all timers use MXTAL/8 source */
  25#define SRC_CR_INIT_MASK        0x00007fff
  26#define SRC_CR_INIT_VAL         0x2aaa8000
  27
  28static u32      nmdk_count;             /* accumulated count */
  29static u32      nmdk_cycle;             /* write-once */
  30static __iomem void *mtu_base;
  31
  32/*
  33 * clocksource: the MTU device is a decrementing counters, so we negate
  34 * the value being read.
  35 */
  36static cycle_t nmdk_read_timer(struct clocksource *cs)
  37{
  38        u32 count = readl(mtu_base + MTU_VAL(0));
  39        return nmdk_count + nmdk_cycle - count;
  40
  41}
  42
  43static struct clocksource nmdk_clksrc = {
  44        .name           = "mtu_0",
  45        .rating         = 120,
  46        .read           = nmdk_read_timer,
  47        .shift          = 20,
  48        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
  49};
  50
  51/*
  52 * Clockevent device: currently only periodic mode is supported
  53 */
  54static void nmdk_clkevt_mode(enum clock_event_mode mode,
  55                             struct clock_event_device *dev)
  56{
  57        unsigned long flags;
  58
  59        switch (mode) {
  60        case CLOCK_EVT_MODE_PERIODIC:
  61                /* enable interrupts -- and count current value? */
  62                raw_local_irq_save(flags);
  63                writel(readl(mtu_base + MTU_IMSC) | 1, mtu_base + MTU_IMSC);
  64                raw_local_irq_restore(flags);
  65                break;
  66        case CLOCK_EVT_MODE_ONESHOT:
  67                BUG(); /* Not supported, yet */
  68                /* FALLTHROUGH */
  69        case CLOCK_EVT_MODE_SHUTDOWN:
  70        case CLOCK_EVT_MODE_UNUSED:
  71                /* disable irq */
  72                raw_local_irq_save(flags);
  73                writel(readl(mtu_base + MTU_IMSC) & ~1, mtu_base + MTU_IMSC);
  74                raw_local_irq_restore(flags);
  75                break;
  76        case CLOCK_EVT_MODE_RESUME:
  77                break;
  78        }
  79}
  80
  81static struct clock_event_device nmdk_clkevt = {
  82        .name           = "mtu_0",
  83        .features       = CLOCK_EVT_FEAT_PERIODIC,
  84        .shift          = 32,
  85        .rating         = 100,
  86        .set_mode       = nmdk_clkevt_mode,
  87};
  88
  89/*
  90 * IRQ Handler for the timer 0 of the MTU block. The irq is not shared
  91 * as we are the only users of mtu0 by now.
  92 */
  93static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
  94{
  95        /* ack: "interrupt clear register" */
  96        writel( 1 << 0, mtu_base + MTU_ICR);
  97
  98        /* we can't count lost ticks, unfortunately */
  99        nmdk_count += nmdk_cycle;
 100        nmdk_clkevt.event_handler(&nmdk_clkevt);
 101
 102        return IRQ_HANDLED;
 103}
 104
 105/*
 106 * Set up timer interrupt, and return the current time in seconds.
 107 */
 108static struct irqaction nmdk_timer_irq = {
 109        .name           = "Nomadik Timer Tick",
 110        .flags          = IRQF_DISABLED | IRQF_TIMER,
 111        .handler        = nmdk_timer_interrupt,
 112};
 113
 114static void nmdk_timer_reset(void)
 115{
 116        u32 cr;
 117
 118        writel(0, mtu_base + MTU_CR(0)); /* off */
 119
 120        /* configure load and background-load, and fire it up */
 121        writel(nmdk_cycle, mtu_base + MTU_LR(0));
 122        writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
 123        cr = MTU_CRn_PERIODIC | MTU_CRn_PRESCALE_1 | MTU_CRn_32BITS;
 124        writel(cr, mtu_base + MTU_CR(0));
 125        writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0));
 126}
 127
 128static void __init nmdk_timer_init(void)
 129{
 130        u32 src_cr;
 131        unsigned long rate;
 132        int bits;
 133
 134        rate = CLOCK_TICK_RATE; /* 2.4MHz */
 135        nmdk_cycle = (rate + HZ/2) / HZ;
 136
 137        /* Configure timer sources in "system reset controller" ctrl reg */
 138        src_cr = readl(io_p2v(NOMADIK_SRC_BASE));
 139        src_cr &= SRC_CR_INIT_MASK;
 140        src_cr |= SRC_CR_INIT_VAL;
 141        writel(src_cr, io_p2v(NOMADIK_SRC_BASE));
 142
 143        /* Save global pointer to mtu, used by functions above */
 144        mtu_base = io_p2v(NOMADIK_MTU0_BASE);
 145
 146        /* Init the timer and register clocksource */
 147        nmdk_timer_reset();
 148
 149        nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift);
 150        bits =  8*sizeof(nmdk_count);
 151        nmdk_clksrc.mask = CLOCKSOURCE_MASK(bits);
 152
 153        clocksource_register(&nmdk_clksrc);
 154
 155        /* Register irq and clockevents */
 156        setup_irq(IRQ_MTU0, &nmdk_timer_irq);
 157        nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift);
 158        nmdk_clkevt.cpumask = cpumask_of(0);
 159        clockevents_register_device(&nmdk_clkevt);
 160}
 161
 162struct sys_timer nomadik_timer = {
 163        .init           = nmdk_timer_init,
 164};
 165