1 /* linux/arch/arm/mach-msm/timer.c
3 * Copyright (C) 2007 Google, Inc.
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.
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.
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>
25 #include <asm/mach/time.h>
26 #include <mach/msm_iomap.h>
28 #define MSM_DGT_BASE (MSM_GPT_BASE + 0x10)
29 #define MSM_DGT_SHIFT (5)
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
38 #define CSR_PROTECTION 0x0020
39 #define CSR_PROTECTION_EN 1
42 #define DGT_HZ 19200000 /* 19.2 MHz or 600 KHz after shift */
45 struct clock_event_device clockevent
;
46 struct clocksource clocksource
;
53 static irqreturn_t
msm_timer_interrupt(int irq
, void *dev_id
)
55 struct clock_event_device
*evt
= dev_id
;
56 evt
->event_handler(evt
);
60 static cycle_t
msm_gpt_read(void)
62 return readl(MSM_GPT_BASE
+ TIMER_COUNT_VAL
);
65 static cycle_t
msm_dgt_read(void)
67 return readl(MSM_DGT_BASE
+ TIMER_COUNT_VAL
) >> MSM_DGT_SHIFT
;
70 static int msm_timer_set_next_event(unsigned long cycles
,
71 struct clock_event_device
*evt
)
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
);
78 writel(alarm
, clock
->regbase
+ TIMER_MATCH_VAL
);
79 now
= readl(clock
->regbase
+ TIMER_COUNT_VAL
);
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
);
90 static void msm_timer_set_mode(enum clock_event_mode mode
,
91 struct clock_event_device
*evt
)
93 struct msm_clock
*clock
= container_of(evt
, struct msm_clock
, clockevent
);
95 case CLOCK_EVT_MODE_RESUME
:
96 case CLOCK_EVT_MODE_PERIODIC
:
98 case CLOCK_EVT_MODE_ONESHOT
:
99 writel(TIMER_ENABLE_EN
, clock
->regbase
+ TIMER_ENABLE
);
101 case CLOCK_EVT_MODE_UNUSED
:
102 case CLOCK_EVT_MODE_SHUTDOWN
:
103 writel(0, clock
->regbase
+ TIMER_ENABLE
);
108 static struct msm_clock msm_clocks
[] = {
112 .features
= CLOCK_EVT_FEAT_ONESHOT
,
115 .set_next_event
= msm_timer_set_next_event
,
116 .set_mode
= msm_timer_set_mode
,
121 .read
= msm_gpt_read
,
122 .mask
= CLOCKSOURCE_MASK(32),
124 .flags
= CLOCK_SOURCE_IS_CONTINUOUS
,
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
133 .regbase
= MSM_GPT_BASE
,
139 .features
= CLOCK_EVT_FEAT_ONESHOT
,
140 .shift
= 32 + MSM_DGT_SHIFT
,
142 .set_next_event
= msm_timer_set_next_event
,
143 .set_mode
= msm_timer_set_mode
,
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
,
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
160 .regbase
= MSM_DGT_BASE
,
161 .freq
= DGT_HZ
>> MSM_DGT_SHIFT
,
162 .shift
= MSM_DGT_SHIFT
166 static void __init
msm_timer_init(void)
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
);
179 ce
->mult
= div_sc(clock
->freq
, NSEC_PER_SEC
, ce
->shift
);
180 /* allow at least 10 seconds to notice that the timer wrapped */
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_cpu(0);
187 cs
->mult
= clocksource_hz2mult(clock
->freq
, cs
->shift
);
188 res
= clocksource_register(cs
);
190 printk(KERN_ERR
"msm_timer_init: clocksource_register "
191 "failed for %s\n", cs
->name
);
193 res
= setup_irq(clock
->irq
.irq
, &clock
->irq
);
195 printk(KERN_ERR
"msm_timer_init: setup_irq "
196 "failed for %s\n", cs
->name
);
198 clockevents_register_device(ce
);
202 struct sys_timer msm_timer
= {
203 .init
= msm_timer_init