blob: eba3cb52ad878567b39c0d87750e84d2a5e44180 [file] [log] [blame]
Tony Lindgren92105bb2005-09-07 17:20:26 +01001/*
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>
Russell King0a5709b2005-11-16 14:51:20 +000029#include <asm/hardware.h>
Tony Lindgren92105bb2005-09-07 17:20:26 +010030#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
54static struct dmtimer_info_struct {
55 struct list_head unused_timers;
56 struct list_head reserved_timers;
57} dm_timer_info;
58
59static 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
72static spinlock_t dm_timer_lock;
73
74
75inline 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
82u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg)
83{
84 return omap_readl(timer->base + reg);
85}
86
87int 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
Tony Lindgrena569c6e2006-04-02 17:46:21 +0100100/**
101 * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR
102 * @inputmask: current value of idlect mask
103 */
104__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
105{
106 int n;
107
108 /* If ARMXOR cannot be idled this function call is unnecessary */
109 if (!(inputmask & (1 << 1)))
110 return inputmask;
111
112 /* If any active timer is using ARMXOR return modified mask */
113 for (n = 0; dm_timers[n].base; ++n)
114 if (omap_dm_timer_read_reg(&dm_timers[n], OMAP_TIMER_CTRL_REG)&
115 OMAP_TIMER_CTRL_ST) {
116 if (((omap_readl(MOD_CONF_CTRL_1)>>(n*2)) & 0x03) == 0)
117 inputmask &= ~(1 << 1);
118 else
119 inputmask &= ~(1 << 2);
120 }
121
122 return inputmask;
123}
124
125
Tony Lindgren92105bb2005-09-07 17:20:26 +0100126void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
127{
128 int n = (timer - dm_timers) << 1;
129 u32 l;
130
131 l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n);
132 l |= source << n;
133 omap_writel(l, MOD_CONF_CTRL_1);
134}
135
136
137static void omap_dm_timer_reset(struct omap_dm_timer *timer)
138{
139 /* Reset and set posted mode */
140 omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
141 omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, 0x02);
142
143 omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_ARMXOR);
144}
145
146
147
148struct omap_dm_timer * omap_dm_timer_request(void)
149{
150 struct omap_dm_timer *timer = NULL;
151 unsigned long flags;
152
153 spin_lock_irqsave(&dm_timer_lock, flags);
154 if (!list_empty(&dm_timer_info.unused_timers)) {
155 timer = (struct omap_dm_timer *)
156 dm_timer_info.unused_timers.next;
157 list_move_tail((struct list_head *)timer,
158 &dm_timer_info.reserved_timers);
159 }
160 spin_unlock_irqrestore(&dm_timer_lock, flags);
161
162 return timer;
163}
164
165
166void omap_dm_timer_free(struct omap_dm_timer *timer)
167{
168 unsigned long flags;
169
170 omap_dm_timer_reset(timer);
171
172 spin_lock_irqsave(&dm_timer_lock, flags);
173 list_move_tail((struct list_head *)timer, &dm_timer_info.unused_timers);
174 spin_unlock_irqrestore(&dm_timer_lock, flags);
175}
176
177void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
178 unsigned int value)
179{
180 omap_dm_timer_write_reg(timer, OMAP_TIMER_INT_EN_REG, value);
181}
182
183unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
184{
185 return omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG);
186}
187
188void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
189{
190 omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG, value);
191}
192
193void omap_dm_timer_enable_autoreload(struct omap_dm_timer *timer)
194{
195 u32 l;
196 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
197 l |= OMAP_TIMER_CTRL_AR;
198 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
199}
200
201void omap_dm_timer_trigger(struct omap_dm_timer *timer)
202{
203 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 1);
204}
205
206void omap_dm_timer_set_trigger(struct omap_dm_timer *timer, unsigned int value)
207{
208 u32 l;
209
210 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
211 l |= value & 0x3;
212 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
213}
214
215void omap_dm_timer_start(struct omap_dm_timer *timer)
216{
217 u32 l;
218
219 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
220 l |= OMAP_TIMER_CTRL_ST;
221 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
222}
223
224void omap_dm_timer_stop(struct omap_dm_timer *timer)
225{
226 u32 l;
227
228 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
229 l &= ~0x1;
230 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
231}
232
233unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
234{
235 return omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_REG);
236}
237
238void omap_dm_timer_reset_counter(struct omap_dm_timer *timer)
239{
240 omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, 0);
241}
242
243void omap_dm_timer_set_load(struct omap_dm_timer *timer, unsigned int load)
244{
245 omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
246}
247
248void omap_dm_timer_set_match(struct omap_dm_timer *timer, unsigned int match)
249{
250 omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
251}
252
253void omap_dm_timer_enable_compare(struct omap_dm_timer *timer)
254{
255 u32 l;
256
257 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
258 l |= OMAP_TIMER_CTRL_CE;
259 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
260}
261
262
263static inline void __dm_timer_init(void)
264{
265 struct omap_dm_timer *timer;
266
267 spin_lock_init(&dm_timer_lock);
268 INIT_LIST_HEAD(&dm_timer_info.unused_timers);
269 INIT_LIST_HEAD(&dm_timer_info.reserved_timers);
270
271 timer = &dm_timers[0];
272 while (timer->base) {
273 list_add_tail((struct list_head *)timer, &dm_timer_info.unused_timers);
274 omap_dm_timer_reset(timer);
275 timer++;
276 }
277}
278
279static int __init omap_dm_timer_init(void)
280{
281 if (cpu_is_omap16xx())
282 __dm_timer_init();
283 return 0;
284}
285
286arch_initcall(omap_dm_timer_init);