38d7ebf879207cb116ed17fc1c81e2b1ab7ec946
[linux-2.6.git] / arch / arm / plat-omap / dmtimer.c
1 /*
2  * linux/arch/arm/plat-omap/dmtimer.c
3  *
4  * OMAP Dual-Mode Timers
5  *
6  * Copyright (C) 2005 Nokia Corporation
7  * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; either version 2 of the License, or (at your
12  * option) any later version.
13  *
14  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
21  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22  *
23  * You should have received a copy of the  GNU General Public License along
24  * with this program; if not, write  to the Free Software Foundation, Inc.,
25  * 675 Mass Ave, Cambridge, MA 02139, USA.
26  */
27
28 #include <linux/init.h>
29 #include <asm/hardware.h>
30 #include <asm/arch/dmtimer.h>
31 #include <asm/io.h>
32 #include <asm/arch/irqs.h>
33 #include <linux/spinlock.h>
34 #include <linux/list.h>
35
36 #define OMAP_TIMER_COUNT                8
37
38 #define OMAP_TIMER_ID_REG               0x00
39 #define OMAP_TIMER_OCP_CFG_REG          0x10
40 #define OMAP_TIMER_SYS_STAT_REG         0x14
41 #define OMAP_TIMER_STAT_REG             0x18
42 #define OMAP_TIMER_INT_EN_REG           0x1c
43 #define OMAP_TIMER_WAKEUP_EN_REG        0x20
44 #define OMAP_TIMER_CTRL_REG             0x24
45 #define OMAP_TIMER_COUNTER_REG          0x28
46 #define OMAP_TIMER_LOAD_REG             0x2c
47 #define OMAP_TIMER_TRIGGER_REG          0x30
48 #define OMAP_TIMER_WRITE_PEND_REG       0x34
49 #define OMAP_TIMER_MATCH_REG            0x38
50 #define OMAP_TIMER_CAPTURE_REG          0x3c
51 #define OMAP_TIMER_IF_CTRL_REG          0x40
52
53
54 static struct dmtimer_info_struct {
55         struct list_head        unused_timers;
56         struct list_head        reserved_timers;
57 } dm_timer_info;
58
59 static struct omap_dm_timer dm_timers[] = {
60         { .base=0xfffb1400, .irq=INT_1610_GPTIMER1 },
61         { .base=0xfffb1c00, .irq=INT_1610_GPTIMER2 },
62         { .base=0xfffb2400, .irq=INT_1610_GPTIMER3 },
63         { .base=0xfffb2c00, .irq=INT_1610_GPTIMER4 },
64         { .base=0xfffb3400, .irq=INT_1610_GPTIMER5 },
65         { .base=0xfffb3c00, .irq=INT_1610_GPTIMER6 },
66         { .base=0xfffb4400, .irq=INT_1610_GPTIMER7 },
67         { .base=0xfffb4c00, .irq=INT_1610_GPTIMER8 },
68         { .base=0x0 },
69 };
70
71
72 static spinlock_t dm_timer_lock;
73
74
75 inline void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value)
76 {
77         omap_writel(value, timer->base + reg);
78         while (omap_dm_timer_read_reg(timer, OMAP_TIMER_WRITE_PEND_REG))
79                 ;
80 }
81
82 u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg)
83 {
84         return omap_readl(timer->base + reg);
85 }
86
87 int omap_dm_timers_active(void)
88 {
89         struct omap_dm_timer *timer;
90
91         for (timer = &dm_timers[0]; timer->base; ++timer)
92                 if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
93                     OMAP_TIMER_CTRL_ST)
94                         return 1;
95
96         return 0;
97 }
98
99
100 void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
101 {
102         int n = (timer - dm_timers) << 1;
103         u32 l;
104
105         l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n);
106         l |= source << n;
107         omap_writel(l, MOD_CONF_CTRL_1);
108 }
109
110
111 static void omap_dm_timer_reset(struct omap_dm_timer *timer)
112 {
113         /* Reset and set posted mode */
114         omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
115         omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, 0x02);
116
117         omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_ARMXOR);
118 }
119
120
121
122 struct omap_dm_timer * omap_dm_timer_request(void)
123 {
124         struct omap_dm_timer *timer = NULL;
125         unsigned long flags;
126
127         spin_lock_irqsave(&dm_timer_lock, flags);
128         if (!list_empty(&dm_timer_info.unused_timers)) {
129                 timer = (struct omap_dm_timer *)
130                                 dm_timer_info.unused_timers.next;
131                 list_move_tail((struct list_head *)timer,
132                                 &dm_timer_info.reserved_timers);
133         }
134         spin_unlock_irqrestore(&dm_timer_lock, flags);
135
136         return timer;
137 }
138
139
140 void omap_dm_timer_free(struct omap_dm_timer *timer)
141 {
142         unsigned long flags;
143
144         omap_dm_timer_reset(timer);
145
146         spin_lock_irqsave(&dm_timer_lock, flags);
147         list_move_tail((struct list_head *)timer, &dm_timer_info.unused_timers);
148         spin_unlock_irqrestore(&dm_timer_lock, flags);
149 }
150
151 void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
152                                 unsigned int value)
153 {
154         omap_dm_timer_write_reg(timer, OMAP_TIMER_INT_EN_REG, value);
155 }
156
157 unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
158 {
159         return omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG);
160 }
161
162 void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
163 {
164         omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG, value);
165 }
166
167 void omap_dm_timer_enable_autoreload(struct omap_dm_timer *timer)
168 {
169         u32 l;
170         l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
171         l |= OMAP_TIMER_CTRL_AR;
172         omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
173 }
174
175 void omap_dm_timer_trigger(struct omap_dm_timer *timer)
176 {
177         omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 1);
178 }
179
180 void omap_dm_timer_set_trigger(struct omap_dm_timer *timer, unsigned int value)
181 {
182         u32 l;
183
184         l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
185         l |= value & 0x3;
186         omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
187 }
188
189 void omap_dm_timer_start(struct omap_dm_timer *timer)
190 {
191         u32 l;
192
193         l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
194         l |= OMAP_TIMER_CTRL_ST;
195         omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
196 }
197
198 void omap_dm_timer_stop(struct omap_dm_timer *timer)
199 {
200         u32 l;
201
202         l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
203         l &= ~0x1;
204         omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
205 }
206
207 unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
208 {
209         return omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_REG);
210 }
211
212 void omap_dm_timer_reset_counter(struct omap_dm_timer *timer)
213 {
214         omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, 0);
215 }
216
217 void omap_dm_timer_set_load(struct omap_dm_timer *timer, unsigned int load)
218 {
219         omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
220 }
221
222 void omap_dm_timer_set_match(struct omap_dm_timer *timer, unsigned int match)
223 {
224         omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
225 }
226
227 void omap_dm_timer_enable_compare(struct omap_dm_timer *timer)
228 {
229         u32 l;
230
231         l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
232         l |= OMAP_TIMER_CTRL_CE;
233         omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
234 }
235
236
237 static inline void __dm_timer_init(void)
238 {
239         struct omap_dm_timer *timer;
240
241         spin_lock_init(&dm_timer_lock);
242         INIT_LIST_HEAD(&dm_timer_info.unused_timers);
243         INIT_LIST_HEAD(&dm_timer_info.reserved_timers);
244
245         timer = &dm_timers[0];
246         while (timer->base) {
247                 list_add_tail((struct list_head *)timer, &dm_timer_info.unused_timers);
248                 omap_dm_timer_reset(timer);
249                 timer++;
250         }
251 }
252
253 static int __init omap_dm_timer_init(void)
254 {
255         if (cpu_is_omap16xx())
256                 __dm_timer_init();
257         return 0;
258 }
259
260 arch_initcall(omap_dm_timer_init);