linux/arch/arm/mach-msm/timer.c
<<
>>
Prefs
   1/* linux/arch/arm/mach-msm/timer.c
   2 *
   3 * Copyright (C) 2007 Google, Inc.
   4 *
   5 * This software is licensed under the terms of the GNU General Public
   6 * License version 2, as published by the Free Software Foundation, and
   7 * may be copied, distributed, and modified under those terms.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 */
  15
  16#include <linux/init.h>
  17#include <linux/time.h>
  18#include <linux/interrupt.h>
  19#include <linux/irq.h>
  20#include <linux/clk.h>
  21#include <linux/clockchips.h>
  22#include <linux/delay.h>
  23#include <linux/io.h>
  24
  25#include <asm/mach/time.h>
  26#include <mach/msm_iomap.h>
  27
  28#define MSM_DGT_BASE (MSM_GPT_BASE + 0x10)
  29#define MSM_DGT_SHIFT (5)
  30
  31#define TIMER_MATCH_VAL         0x0000
  32#define TIMER_COUNT_VAL         0x0004
  33#define TIMER_ENABLE            0x0008
  34#define TIMER_ENABLE_CLR_ON_MATCH_EN    2
  35#define TIMER_ENABLE_EN                 1
  36#define TIMER_CLEAR             0x000C
  37
  38#define CSR_PROTECTION          0x0020
  39#define CSR_PROTECTION_EN               1
  40
  41#define GPT_HZ 32768
  42#define DGT_HZ 19200000 /* 19.2 MHz or 600 KHz after shift */
  43
  44struct msm_clock {
  45        struct clock_event_device   clockevent;
  46        struct clocksource          clocksource;
  47        struct irqaction            irq;
  48        void __iomem                *regbase;
  49        uint32_t                    freq;
  50        uint32_t                    shift;
  51};
  52
  53static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
  54{
  55        struct clock_event_device *evt = dev_id;
  56        evt->event_handler(evt);
  57        return IRQ_HANDLED;
  58}
  59
  60static cycle_t msm_gpt_read(void)
  61{
  62        return readl(MSM_GPT_BASE + TIMER_COUNT_VAL);
  63}
  64
  65static cycle_t msm_dgt_read(void)
  66{
  67        return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT;
  68}
  69
  70static int msm_timer_set_next_event(unsigned long cycles,
  71                                    struct clock_event_device *evt)
  72{
  73        struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
  74        uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL);
  75        uint32_t alarm = now + (cycles << clock->shift);
  76        int late;
  77
  78        writel(alarm, clock->regbase + TIMER_MATCH_VAL);
  79        now = readl(clock->regbase + TIMER_COUNT_VAL);
  80        late = now - alarm;
  81        if (late >= (-2 << clock->shift) && late < DGT_HZ*5) {
  82                printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, "
  83                       "alarm already expired, now %x, alarm %x, late %d\n",
  84                       cycles, clock->clockevent.name, now, alarm, late);
  85                return -ETIME;
  86        }
  87        return 0;
  88}
  89
  90static void msm_timer_set_mode(enum clock_event_mode mode,
  91                              struct clock_event_device *evt)
  92{
  93        struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
  94        switch (mode) {
  95        case CLOCK_EVT_MODE_RESUME:
  96        case CLOCK_EVT_MODE_PERIODIC:
  97                break;
  98        case CLOCK_EVT_MODE_ONESHOT:
  99                writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE);
 100                break;
 101        case CLOCK_EVT_MODE_UNUSED:
 102        case CLOCK_EVT_MODE_SHUTDOWN:
 103                writel(0, clock->regbase + TIMER_ENABLE);
 104                break;
 105        }
 106}
 107
 108static struct msm_clock msm_clocks[] = {
 109        {
 110                .clockevent = {
 111                        .name           = "gp_timer",
 112                        .features       = CLOCK_EVT_FEAT_ONESHOT,
 113                        .shift          = 32,
 114                        .rating         = 200,
 115                        .set_next_event = msm_timer_set_next_event,
 116                        .set_mode       = msm_timer_set_mode,
 117                },
 118                .clocksource = {
 119                        .name           = "gp_timer",
 120                        .rating         = 200,
 121                        .read           = msm_gpt_read,
 122                        .mask           = CLOCKSOURCE_MASK(32),
 123                        .shift          = 24,
 124                        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 125                },
 126                .irq = {
 127                        .name    = "gp_timer",
 128                        .flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
 129                        .handler = msm_timer_interrupt,
 130                        .dev_id  = &msm_clocks[0].clockevent,
 131                        .irq     = INT_GP_TIMER_EXP
 132                },
 133                .regbase = MSM_GPT_BASE,
 134                .freq = GPT_HZ
 135        },
 136        {
 137                .clockevent = {
 138                        .name           = "dg_timer",
 139                        .features       = CLOCK_EVT_FEAT_ONESHOT,
 140                        .shift          = 32 + MSM_DGT_SHIFT,
 141                        .rating         = 300,
 142                        .set_next_event = msm_timer_set_next_event,
 143                        .set_mode       = msm_timer_set_mode,
 144                },
 145                .clocksource = {
 146                        .name           = "dg_timer",
 147                        .rating         = 300,
 148                        .read           = msm_dgt_read,
 149                        .mask           = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)),
 150                        .shift          = 24 - MSM_DGT_SHIFT,
 151                        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 152                },
 153                .irq = {
 154                        .name    = "dg_timer",
 155                        .flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
 156                        .handler = msm_timer_interrupt,
 157                        .dev_id  = &msm_clocks[1].clockevent,
 158                        .irq     = INT_DEBUG_TIMER_EXP
 159                },
 160                .regbase = MSM_DGT_BASE,
 161                .freq = DGT_HZ >> MSM_DGT_SHIFT,
 162                .shift = MSM_DGT_SHIFT
 163        }
 164};
 165
 166static void __init msm_timer_init(void)
 167{
 168        int i;
 169        int res;
 170
 171        for (i = 0; i < ARRAY_SIZE(msm_clocks); i++) {
 172                struct msm_clock *clock = &msm_clocks[i];
 173                struct clock_event_device *ce = &clock->clockevent;
 174                struct clocksource *cs = &clock->clocksource;
 175                writel(0, clock->regbase + TIMER_ENABLE);
 176                writel(0, clock->regbase + TIMER_CLEAR);
 177                writel(~0, clock->regbase + TIMER_MATCH_VAL);
 178
 179                ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift);
 180                /* allow at least 10 seconds to notice that the timer wrapped */
 181                ce->max_delta_ns =
 182                        clockevent_delta2ns(0xf0000000 >> clock->shift, ce);
 183                /* 4 gets rounded down to 3 */
 184                ce->min_delta_ns = clockevent_delta2ns(4, ce);
 185                ce->cpumask = cpumask_of(0);
 186
 187                cs->mult = clocksource_hz2mult(clock->freq, cs->shift);
 188                res = clocksource_register(cs);
 189                if (res)
 190                        printk(KERN_ERR "msm_timer_init: clocksource_register "
 191                               "failed for %s\n", cs->name);
 192
 193                res = setup_irq(clock->irq.irq, &clock->irq);
 194                if (res)
 195                        printk(KERN_ERR "msm_timer_init: setup_irq "
 196                               "failed for %s\n", cs->name);
 197
 198                clockevents_register_device(ce);
 199        }
 200}
 201
 202struct sys_timer msm_timer = {
 203        .init = msm_timer_init
 204};
 205
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.