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>
24 #include <asm/mach/time.h>
25 #include <asm/arch/msm_iomap.h>
29 #define MSM_DGT_BASE (MSM_GPT_BASE + 0x10)
30 #define MSM_DGT_SHIFT (5)
32 #define TIMER_MATCH_VAL 0x0000
33 #define TIMER_COUNT_VAL 0x0004
34 #define TIMER_ENABLE 0x0008
35 #define TIMER_ENABLE_CLR_ON_MATCH_EN 2
36 #define TIMER_ENABLE_EN 1
37 #define TIMER_CLEAR 0x000C
39 #define CSR_PROTECTION 0x0020
40 #define CSR_PROTECTION_EN 1
43 #define DGT_HZ 19200000 /* 19.2 MHz or 600 KHz after shift */
46 struct clock_event_device clockevent
;
47 struct clocksource clocksource
;
54 static irqreturn_t
msm_timer_interrupt(int irq
, void *dev_id
)
56 struct clock_event_device
*evt
= dev_id
;
57 evt
->event_handler(evt
);
61 static cycle_t
msm_gpt_read(void)
63 return readl(MSM_GPT_BASE
+ TIMER_COUNT_VAL
);
66 static cycle_t
msm_dgt_read(void)
68 return readl(MSM_DGT_BASE
+ TIMER_COUNT_VAL
) >> MSM_DGT_SHIFT
;
71 static int msm_timer_set_next_event(unsigned long cycles
,
72 struct clock_event_device
*evt
)
74 struct msm_clock
*clock
= container_of(evt
, struct msm_clock
, clockevent
);
75 uint32_t now
= readl(clock
->regbase
+ TIMER_COUNT_VAL
);
76 uint32_t alarm
= now
+ (cycles
<< clock
->shift
);
79 writel(alarm
, clock
->regbase
+ TIMER_MATCH_VAL
);
80 now
= readl(clock
->regbase
+ TIMER_COUNT_VAL
);
82 if (late
>= (-2 << clock
->shift
) && late
< DGT_HZ
*5) {
83 printk(KERN_NOTICE
"msm_timer_set_next_event(%lu) clock %s, "
84 "alarm already expired, now %x, alarm %x, late %d\n",
85 cycles
, clock
->clockevent
.name
, now
, alarm
, late
);
91 static void msm_timer_set_mode(enum clock_event_mode mode
,
92 struct clock_event_device
*evt
)
94 struct msm_clock
*clock
= container_of(evt
, struct msm_clock
, clockevent
);
96 case CLOCK_EVT_MODE_RESUME
:
97 case CLOCK_EVT_MODE_PERIODIC
:
99 case CLOCK_EVT_MODE_ONESHOT
:
100 writel(TIMER_ENABLE_EN
, clock
->regbase
+ TIMER_ENABLE
);
102 case CLOCK_EVT_MODE_UNUSED
:
103 case CLOCK_EVT_MODE_SHUTDOWN
:
104 writel(0, clock
->regbase
+ TIMER_ENABLE
);
109 static struct msm_clock msm_clocks
[] = {
113 .features
= CLOCK_EVT_FEAT_ONESHOT
,
116 .set_next_event
= msm_timer_set_next_event
,
117 .set_mode
= msm_timer_set_mode
,
122 .read
= msm_gpt_read
,
123 .mask
= CLOCKSOURCE_MASK(32),
125 .flags
= CLOCK_SOURCE_IS_CONTINUOUS
,
129 .flags
= IRQF_DISABLED
| IRQF_TIMER
| IRQF_TRIGGER_RISING
,
130 .handler
= msm_timer_interrupt
,
131 .dev_id
= &msm_clocks
[0].clockevent
,
132 .irq
= INT_GP_TIMER_EXP
134 .regbase
= MSM_GPT_BASE
,
140 .features
= CLOCK_EVT_FEAT_ONESHOT
,
141 .shift
= 32 + MSM_DGT_SHIFT
,
143 .set_next_event
= msm_timer_set_next_event
,
144 .set_mode
= msm_timer_set_mode
,
149 .read
= msm_dgt_read
,
150 .mask
= CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT
)),
151 .shift
= 24 - MSM_DGT_SHIFT
,
152 .flags
= CLOCK_SOURCE_IS_CONTINUOUS
,
156 .flags
= IRQF_DISABLED
| IRQF_TIMER
| IRQF_TRIGGER_RISING
,
157 .handler
= msm_timer_interrupt
,
158 .dev_id
= &msm_clocks
[1].clockevent
,
159 .irq
= INT_DEBUG_TIMER_EXP
161 .regbase
= MSM_DGT_BASE
,
162 .freq
= DGT_HZ
>> MSM_DGT_SHIFT
,
163 .shift
= MSM_DGT_SHIFT
167 static void __init
msm_timer_init(void)
172 for (i
= 0; i
< ARRAY_SIZE(msm_clocks
); i
++) {
173 struct msm_clock
*clock
= &msm_clocks
[i
];
174 struct clock_event_device
*ce
= &clock
->clockevent
;
175 struct clocksource
*cs
= &clock
->clocksource
;
176 writel(0, clock
->regbase
+ TIMER_ENABLE
);
177 writel(0, clock
->regbase
+ TIMER_CLEAR
);
178 writel(~0, clock
->regbase
+ TIMER_MATCH_VAL
);
180 ce
->mult
= div_sc(clock
->freq
, NSEC_PER_SEC
, ce
->shift
);
181 /* allow at least 10 seconds to notice that the timer wrapped */
183 clockevent_delta2ns(0xf0000000 >> clock
->shift
, ce
);
184 /* 4 gets rounded down to 3 */
185 ce
->min_delta_ns
= clockevent_delta2ns(4, ce
);
186 ce
->cpumask
= cpumask_of_cpu(0);
188 cs
->mult
= clocksource_hz2mult(clock
->freq
, cs
->shift
);
189 res
= clocksource_register(cs
);
191 printk(KERN_ERR
"msm_timer_init: clocksource_register "
192 "failed for %s\n", cs
->name
);
194 res
= setup_irq(clock
->irq
.irq
, &clock
->irq
);
196 printk(KERN_ERR
"msm_timer_init: setup_irq "
197 "failed for %s\n", cs
->name
);
199 clockevents_register_device(ce
);
203 struct sys_timer msm_timer
= {
204 .init
= msm_timer_init