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 <asm/hardware/gic.h>
  27
  28#include <mach/msm_iomap.h>
  29#include <mach/cpu.h>
  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#define DGT_CLK_CTL             0x0034
  38enum {
  39        DGT_CLK_CTL_DIV_1 = 0,
  40        DGT_CLK_CTL_DIV_2 = 1,
  41        DGT_CLK_CTL_DIV_3 = 2,
  42        DGT_CLK_CTL_DIV_4 = 3,
  43};
  44#define CSR_PROTECTION          0x0020
  45#define CSR_PROTECTION_EN               1
  46
  47#define GPT_HZ 32768
  48
  49enum timer_location {
  50        LOCAL_TIMER = 0,
  51        GLOBAL_TIMER = 1,
  52};
  53
  54#define MSM_GLOBAL_TIMER MSM_CLOCK_DGT
  55
  56/* TODO: Remove these ifdefs */
  57#if defined(CONFIG_ARCH_QSD8X50)
  58#define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */
  59#define MSM_DGT_SHIFT (0)
  60#elif defined(CONFIG_ARCH_MSM7X30)
  61#define DGT_HZ (24576000 / 4) /* 24.576 MHz (LPXO) / 4 by default */
  62#define MSM_DGT_SHIFT (0)
  63#elif defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960)
  64#define DGT_HZ (27000000 / 4) /* 27 MHz (PXO) / 4 by default */
  65#define MSM_DGT_SHIFT (0)
  66#else
  67#define DGT_HZ 19200000 /* 19.2 MHz or 600 KHz after shift */
  68#define MSM_DGT_SHIFT (5)
  69#endif
  70
  71struct msm_clock {
  72        struct clock_event_device   clockevent;
  73        struct clocksource          clocksource;
  74        unsigned int                irq;
  75        void __iomem                *regbase;
  76        uint32_t                    freq;
  77        uint32_t                    shift;
  78        void __iomem                *global_counter;
  79        void __iomem                *local_counter;
  80        union {
  81                struct clock_event_device               *evt;
  82                struct clock_event_device __percpu      **percpu_evt;
  83        };              
  84};
  85
  86enum {
  87        MSM_CLOCK_GPT,
  88        MSM_CLOCK_DGT,
  89        NR_TIMERS,
  90};
  91
  92
  93static struct msm_clock msm_clocks[];
  94
  95static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
  96{
  97        struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
  98        if (evt->event_handler == NULL)
  99                return IRQ_HANDLED;
 100        evt->event_handler(evt);
 101        return IRQ_HANDLED;
 102}
 103
 104static cycle_t msm_read_timer_count(struct clocksource *cs)
 105{
 106        struct msm_clock *clk = container_of(cs, struct msm_clock, clocksource);
 107
 108        /*
 109         * Shift timer count down by a constant due to unreliable lower bits
 110         * on some targets.
 111         */
 112        return readl(clk->global_counter) >> clk->shift;
 113}
 114
 115static struct msm_clock *clockevent_to_clock(struct clock_event_device *evt)
 116{
 117#ifdef CONFIG_SMP
 118        int i;
 119        for (i = 0; i < NR_TIMERS; i++)
 120                if (evt == &(msm_clocks[i].clockevent))
 121                        return &msm_clocks[i];
 122        return &msm_clocks[MSM_GLOBAL_TIMER];
 123#else
 124        return container_of(evt, struct msm_clock, clockevent);
 125#endif
 126}
 127
 128static int msm_timer_set_next_event(unsigned long cycles,
 129                                    struct clock_event_device *evt)
 130{
 131        struct msm_clock *clock = clockevent_to_clock(evt);
 132        uint32_t now = readl(clock->local_counter);
 133        uint32_t alarm = now + (cycles << clock->shift);
 134
 135        writel(alarm, clock->regbase + TIMER_MATCH_VAL);
 136        return 0;
 137}
 138
 139static void msm_timer_set_mode(enum clock_event_mode mode,
 140                              struct clock_event_device *evt)
 141{
 142        struct msm_clock *clock = clockevent_to_clock(evt);
 143
 144        switch (mode) {
 145        case CLOCK_EVT_MODE_RESUME:
 146        case CLOCK_EVT_MODE_PERIODIC:
 147                break;
 148        case CLOCK_EVT_MODE_ONESHOT:
 149                writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE);
 150                break;
 151        case CLOCK_EVT_MODE_UNUSED:
 152        case CLOCK_EVT_MODE_SHUTDOWN:
 153                writel(0, clock->regbase + TIMER_ENABLE);
 154                break;
 155        }
 156}
 157
 158static struct msm_clock msm_clocks[] = {
 159        [MSM_CLOCK_GPT] = {
 160                .clockevent = {
 161                        .name           = "gp_timer",
 162                        .features       = CLOCK_EVT_FEAT_ONESHOT,
 163                        .shift          = 32,
 164                        .rating         = 200,
 165                        .set_next_event = msm_timer_set_next_event,
 166                        .set_mode       = msm_timer_set_mode,
 167                },
 168                .clocksource = {
 169                        .name           = "gp_timer",
 170                        .rating         = 200,
 171                        .read           = msm_read_timer_count,
 172                        .mask           = CLOCKSOURCE_MASK(32),
 173                        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 174                },
 175                .irq = INT_GP_TIMER_EXP,
 176                .freq = GPT_HZ,
 177        },
 178        [MSM_CLOCK_DGT] = {
 179                .clockevent = {
 180                        .name           = "dg_timer",
 181                        .features       = CLOCK_EVT_FEAT_ONESHOT,
 182                        .shift          = 32 + MSM_DGT_SHIFT,
 183                        .rating         = 300,
 184                        .set_next_event = msm_timer_set_next_event,
 185                        .set_mode       = msm_timer_set_mode,
 186                },
 187                .clocksource = {
 188                        .name           = "dg_timer",
 189                        .rating         = 300,
 190                        .read           = msm_read_timer_count,
 191                        .mask           = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)),
 192                        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 193                },
 194                .irq = INT_DEBUG_TIMER_EXP,
 195                .freq = DGT_HZ >> MSM_DGT_SHIFT,
 196                .shift = MSM_DGT_SHIFT,
 197        }
 198};
 199
 200static void __init msm_timer_init(void)
 201{
 202        int i;
 203        int res;
 204        int global_offset = 0;
 205
 206        if (cpu_is_msm7x01()) {
 207                msm_clocks[MSM_CLOCK_GPT].regbase = MSM_CSR_BASE;
 208                msm_clocks[MSM_CLOCK_DGT].regbase = MSM_CSR_BASE + 0x10;
 209        } else if (cpu_is_msm7x30()) {
 210                msm_clocks[MSM_CLOCK_GPT].regbase = MSM_CSR_BASE + 0x04;
 211                msm_clocks[MSM_CLOCK_DGT].regbase = MSM_CSR_BASE + 0x24;
 212        } else if (cpu_is_qsd8x50()) {
 213                msm_clocks[MSM_CLOCK_GPT].regbase = MSM_CSR_BASE;
 214                msm_clocks[MSM_CLOCK_DGT].regbase = MSM_CSR_BASE + 0x10;
 215        } else if (cpu_is_msm8x60() || cpu_is_msm8960()) {
 216                msm_clocks[MSM_CLOCK_GPT].regbase = MSM_TMR_BASE + 0x04;
 217                msm_clocks[MSM_CLOCK_DGT].regbase = MSM_TMR_BASE + 0x24;
 218
 219                /* Use CPU0's timer as the global timer. */
 220                global_offset = MSM_TMR0_BASE - MSM_TMR_BASE;
 221        } else
 222                BUG();
 223
 224#ifdef CONFIG_ARCH_MSM_SCORPIONMP
 225        writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
 226#endif
 227
 228        for (i = 0; i < ARRAY_SIZE(msm_clocks); i++) {
 229                struct msm_clock *clock = &msm_clocks[i];
 230                struct clock_event_device *ce = &clock->clockevent;
 231                struct clocksource *cs = &clock->clocksource;
 232
 233                clock->local_counter = clock->regbase + TIMER_COUNT_VAL;
 234                clock->global_counter = clock->local_counter + global_offset;
 235
 236                writel(0, clock->regbase + TIMER_ENABLE);
 237                writel(0, clock->regbase + TIMER_CLEAR);
 238                writel(~0, clock->regbase + TIMER_MATCH_VAL);
 239
 240                ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift);
 241                /* allow at least 10 seconds to notice that the timer wrapped */
 242                ce->max_delta_ns =
 243                        clockevent_delta2ns(0xf0000000 >> clock->shift, ce);
 244                /* 4 gets rounded down to 3 */
 245                ce->min_delta_ns = clockevent_delta2ns(4, ce);
 246                ce->cpumask = cpumask_of(0);
 247
 248                res = clocksource_register_hz(cs, clock->freq);
 249                if (res)
 250                        printk(KERN_ERR "msm_timer_init: clocksource_register "
 251                               "failed for %s\n", cs->name);
 252
 253                ce->irq = clock->irq;
 254                if (cpu_is_msm8x60() || cpu_is_msm8960()) {
 255                        clock->percpu_evt = alloc_percpu(struct clock_event_device *);
 256                        if (!clock->percpu_evt) {
 257                                pr_err("msm_timer_init: memory allocation "
 258                                       "failed for %s\n", ce->name);
 259                                continue;
 260                        }
 261
 262                        *__this_cpu_ptr(clock->percpu_evt) = ce;
 263                        res = request_percpu_irq(ce->irq, msm_timer_interrupt,
 264                                                 ce->name, clock->percpu_evt);
 265                        if (!res)
 266                                enable_percpu_irq(ce->irq, 0);
 267                } else {
 268                        clock->evt = ce;
 269                        res = request_irq(ce->irq, msm_timer_interrupt,
 270                                          IRQF_TIMER | IRQF_NOBALANCING | IRQF_TRIGGER_RISING,
 271                                          ce->name, &clock->evt);
 272                }
 273
 274                if (res)
 275                        pr_err("msm_timer_init: request_irq failed for %s\n",
 276                               ce->name);
 277
 278                clockevents_register_device(ce);
 279        }
 280}
 281
 282#ifdef CONFIG_SMP
 283int __cpuinit local_timer_setup(struct clock_event_device *evt)
 284{
 285        static bool local_timer_inited;
 286        struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];
 287
 288        /* Use existing clock_event for cpu 0 */
 289        if (!smp_processor_id())
 290                return 0;
 291
 292        writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
 293
 294        if (!local_timer_inited) {
 295                writel(0, clock->regbase  + TIMER_ENABLE);
 296                writel(0, clock->regbase + TIMER_CLEAR);
 297                writel(~0, clock->regbase + TIMER_MATCH_VAL);
 298                local_timer_inited = true;
 299        }
 300        evt->irq = clock->irq;
 301        evt->name = "local_timer";
 302        evt->features = CLOCK_EVT_FEAT_ONESHOT;
 303        evt->rating = clock->clockevent.rating;
 304        evt->set_mode = msm_timer_set_mode;
 305        evt->set_next_event = msm_timer_set_next_event;
 306        evt->shift = clock->clockevent.shift;
 307        evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift);
 308        evt->max_delta_ns =
 309                clockevent_delta2ns(0xf0000000 >> clock->shift, evt);
 310        evt->min_delta_ns = clockevent_delta2ns(4, evt);
 311
 312        *__this_cpu_ptr(clock->percpu_evt) = evt;
 313        enable_percpu_irq(evt->irq, 0);
 314
 315        clockevents_register_device(evt);
 316        return 0;
 317}
 318
 319void local_timer_stop(struct clock_event_device *evt)
 320{
 321        evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
 322        disable_percpu_irq(evt->irq);
 323}
 324
 325#endif
 326
 327struct sys_timer msm_timer = {
 328        .init = msm_timer_init
 329};
 330