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        struct irqaction            irq;
  75        void __iomem                *regbase;
  76        uint32_t                    freq;
  77        uint32_t                    shift;
  78        void __iomem                *global_counter;
  79        void __iomem                *local_counter;
  80};
  81
  82enum {
  83        MSM_CLOCK_GPT,
  84        MSM_CLOCK_DGT,
  85        NR_TIMERS,
  86};
  87
  88
  89static struct msm_clock msm_clocks[];
  90static struct clock_event_device *local_clock_event;
  91
  92static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
  93{
  94        struct clock_event_device *evt = dev_id;
  95        if (smp_processor_id() != 0)
  96                evt = local_clock_event;
  97        if (evt->event_handler == NULL)
  98                return IRQ_HANDLED;
  99        evt->event_handler(evt);
 100        return IRQ_HANDLED;
 101}
 102
 103static cycle_t msm_read_timer_count(struct clocksource *cs)
 104{
 105        struct msm_clock *clk = container_of(cs, struct msm_clock, clocksource);
 106
 107        /*
 108         * Shift timer count down by a constant due to unreliable lower bits
 109         * on some targets.
 110         */
 111        return readl(clk->global_counter) >> clk->shift;
 112}
 113
 114static struct msm_clock *clockevent_to_clock(struct clock_event_device *evt)
 115{
 116#ifdef CONFIG_SMP
 117        int i;
 118        for (i = 0; i < NR_TIMERS; i++)
 119                if (evt == &(msm_clocks[i].clockevent))
 120                        return &msm_clocks[i];
 121        return &msm_clocks[MSM_GLOBAL_TIMER];
 122#else
 123        return container_of(evt, struct msm_clock, clockevent);
 124#endif
 125}
 126
 127static int msm_timer_set_next_event(unsigned long cycles,
 128                                    struct clock_event_device *evt)
 129{
 130        struct msm_clock *clock = clockevent_to_clock(evt);
 131        uint32_t now = readl(clock->local_counter);
 132        uint32_t alarm = now + (cycles << clock->shift);
 133
 134        writel(alarm, clock->regbase + TIMER_MATCH_VAL);
 135        return 0;
 136}
 137
 138static void msm_timer_set_mode(enum clock_event_mode mode,
 139                              struct clock_event_device *evt)
 140{
 141        struct msm_clock *clock = clockevent_to_clock(evt);
 142
 143        switch (mode) {
 144        case CLOCK_EVT_MODE_RESUME:
 145        case CLOCK_EVT_MODE_PERIODIC:
 146                break;
 147        case CLOCK_EVT_MODE_ONESHOT:
 148                writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE);
 149                break;
 150        case CLOCK_EVT_MODE_UNUSED:
 151        case CLOCK_EVT_MODE_SHUTDOWN:
 152                writel(0, clock->regbase + TIMER_ENABLE);
 153                break;
 154        }
 155}
 156
 157static struct msm_clock msm_clocks[] = {
 158        [MSM_CLOCK_GPT] = {
 159                .clockevent = {
 160                        .name           = "gp_timer",
 161                        .features       = CLOCK_EVT_FEAT_ONESHOT,
 162                        .shift          = 32,
 163                        .rating         = 200,
 164                        .set_next_event = msm_timer_set_next_event,
 165                        .set_mode       = msm_timer_set_mode,
 166                },
 167                .clocksource = {
 168                        .name           = "gp_timer",
 169                        .rating         = 200,
 170                        .read           = msm_read_timer_count,
 171                        .mask           = CLOCKSOURCE_MASK(32),
 172                        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 173                },
 174                .irq = {
 175                        .name    = "gp_timer",
 176                        .flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
 177                        .handler = msm_timer_interrupt,
 178                        .dev_id  = &msm_clocks[0].clockevent,
 179                        .irq     = INT_GP_TIMER_EXP
 180                },
 181                .freq = GPT_HZ,
 182        },
 183        [MSM_CLOCK_DGT] = {
 184                .clockevent = {
 185                        .name           = "dg_timer",
 186                        .features       = CLOCK_EVT_FEAT_ONESHOT,
 187                        .shift          = 32 + MSM_DGT_SHIFT,
 188                        .rating         = 300,
 189                        .set_next_event = msm_timer_set_next_event,
 190                        .set_mode       = msm_timer_set_mode,
 191                },
 192                .clocksource = {
 193                        .name           = "dg_timer",
 194                        .rating         = 300,
 195                        .read           = msm_read_timer_count,
 196                        .mask           = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)),
 197                        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 198                },
 199                .irq = {
 200                        .name    = "dg_timer",
 201                        .flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
 202                        .handler = msm_timer_interrupt,
 203                        .dev_id  = &msm_clocks[1].clockevent,
 204                        .irq     = INT_DEBUG_TIMER_EXP
 205                },
 206                .freq = DGT_HZ >> MSM_DGT_SHIFT,
 207                .shift = MSM_DGT_SHIFT,
 208        }
 209};
 210
 211static void __init msm_timer_init(void)
 212{
 213        int i;
 214        int res;
 215        int global_offset = 0;
 216
 217        if (cpu_is_msm7x01()) {
 218                msm_clocks[MSM_CLOCK_GPT].regbase = MSM_CSR_BASE;
 219                msm_clocks[MSM_CLOCK_DGT].regbase = MSM_CSR_BASE + 0x10;
 220        } else if (cpu_is_msm7x30()) {
 221                msm_clocks[MSM_CLOCK_GPT].regbase = MSM_CSR_BASE + 0x04;
 222                msm_clocks[MSM_CLOCK_DGT].regbase = MSM_CSR_BASE + 0x24;
 223        } else if (cpu_is_qsd8x50()) {
 224                msm_clocks[MSM_CLOCK_GPT].regbase = MSM_CSR_BASE;
 225                msm_clocks[MSM_CLOCK_DGT].regbase = MSM_CSR_BASE + 0x10;
 226        } else if (cpu_is_msm8x60() || cpu_is_msm8960()) {
 227                msm_clocks[MSM_CLOCK_GPT].regbase = MSM_TMR_BASE + 0x04;
 228                msm_clocks[MSM_CLOCK_DGT].regbase = MSM_TMR_BASE + 0x24;
 229
 230                /* Use CPU0's timer as the global timer. */
 231                global_offset = MSM_TMR0_BASE - MSM_TMR_BASE;
 232        } else
 233                BUG();
 234
 235#ifdef CONFIG_ARCH_MSM_SCORPIONMP
 236        writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
 237#endif
 238
 239        for (i = 0; i < ARRAY_SIZE(msm_clocks); i++) {
 240                struct msm_clock *clock = &msm_clocks[i];
 241                struct clock_event_device *ce = &clock->clockevent;
 242                struct clocksource *cs = &clock->clocksource;
 243
 244                clock->local_counter = clock->regbase + TIMER_COUNT_VAL;
 245                clock->global_counter = clock->local_counter + global_offset;
 246
 247                writel(0, clock->regbase + TIMER_ENABLE);
 248                writel(0, clock->regbase + TIMER_CLEAR);
 249                writel(~0, clock->regbase + TIMER_MATCH_VAL);
 250
 251                ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift);
 252                /* allow at least 10 seconds to notice that the timer wrapped */
 253                ce->max_delta_ns =
 254                        clockevent_delta2ns(0xf0000000 >> clock->shift, ce);
 255                /* 4 gets rounded down to 3 */
 256                ce->min_delta_ns = clockevent_delta2ns(4, ce);
 257                ce->cpumask = cpumask_of(0);
 258
 259                res = clocksource_register_hz(cs, clock->freq);
 260                if (res)
 261                        printk(KERN_ERR "msm_timer_init: clocksource_register "
 262                               "failed for %s\n", cs->name);
 263
 264                res = setup_irq(clock->irq.irq, &clock->irq);
 265                if (res)
 266                        printk(KERN_ERR "msm_timer_init: setup_irq "
 267                               "failed for %s\n", cs->name);
 268
 269                clockevents_register_device(ce);
 270        }
 271}
 272
 273#ifdef CONFIG_SMP
 274int __cpuinit local_timer_setup(struct clock_event_device *evt)
 275{
 276        struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];
 277
 278        /* Use existing clock_event for cpu 0 */
 279        if (!smp_processor_id())
 280                return 0;
 281
 282        writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
 283
 284        if (!local_clock_event) {
 285                writel(0, clock->regbase  + TIMER_ENABLE);
 286                writel(0, clock->regbase + TIMER_CLEAR);
 287                writel(~0, clock->regbase + TIMER_MATCH_VAL);
 288        }
 289        evt->irq = clock->irq.irq;
 290        evt->name = "local_timer";
 291        evt->features = CLOCK_EVT_FEAT_ONESHOT;
 292        evt->rating = clock->clockevent.rating;
 293        evt->set_mode = msm_timer_set_mode;
 294        evt->set_next_event = msm_timer_set_next_event;
 295        evt->shift = clock->clockevent.shift;
 296        evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift);
 297        evt->max_delta_ns =
 298                clockevent_delta2ns(0xf0000000 >> clock->shift, evt);
 299        evt->min_delta_ns = clockevent_delta2ns(4, evt);
 300
 301        local_clock_event = evt;
 302
 303        gic_enable_ppi(clock->irq.irq);
 304
 305        clockevents_register_device(evt);
 306        return 0;
 307}
 308
 309inline int local_timer_ack(void)
 310{
 311        return 1;
 312}
 313
 314#endif
 315
 316struct sys_timer msm_timer = {
 317        .init = msm_timer_init
 318};
 319