Commit | Line | Data |
---|---|---|
92105bb7 TL |
1 | /* |
2 | * linux/arch/arm/plat-omap/dmtimer.c | |
3 | * | |
4 | * OMAP Dual-Mode Timers | |
5 | * | |
6 | * Copyright (C) 2005 Nokia Corporation | |
77900a2f TT |
7 | * OMAP2 support by Juha Yrjola |
8 | * API improvements and OMAP2 clock framework support by Timo Teras | |
92105bb7 TL |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation; either version 2 of the License, or (at your | |
13 | * option) any later version. | |
14 | * | |
15 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
16 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
17 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | |
18 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
20 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
23 | * | |
24 | * You should have received a copy of the GNU General Public License along | |
25 | * with this program; if not, write to the Free Software Foundation, Inc., | |
26 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
27 | */ | |
28 | ||
29 | #include <linux/init.h> | |
77900a2f TT |
30 | #include <linux/spinlock.h> |
31 | #include <linux/errno.h> | |
32 | #include <linux/list.h> | |
33 | #include <linux/clk.h> | |
34 | #include <linux/delay.h> | |
be509729 | 35 | #include <asm/arch/hardware.h> |
92105bb7 TL |
36 | #include <asm/arch/dmtimer.h> |
37 | #include <asm/io.h> | |
38 | #include <asm/arch/irqs.h> | |
92105bb7 | 39 | |
77900a2f | 40 | /* register offsets */ |
0f0d0807 RW |
41 | #define _OMAP_TIMER_ID_OFFSET 0x00 |
42 | #define _OMAP_TIMER_OCP_CFG_OFFSET 0x10 | |
43 | #define _OMAP_TIMER_SYS_STAT_OFFSET 0x14 | |
44 | #define _OMAP_TIMER_STAT_OFFSET 0x18 | |
45 | #define _OMAP_TIMER_INT_EN_OFFSET 0x1c | |
46 | #define _OMAP_TIMER_WAKEUP_EN_OFFSET 0x20 | |
47 | #define _OMAP_TIMER_CTRL_OFFSET 0x24 | |
48 | #define OMAP_TIMER_CTRL_GPOCFG (1 << 14) | |
49 | #define OMAP_TIMER_CTRL_CAPTMODE (1 << 13) | |
50 | #define OMAP_TIMER_CTRL_PT (1 << 12) | |
51 | #define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8) | |
52 | #define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8) | |
53 | #define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8) | |
54 | #define OMAP_TIMER_CTRL_SCPWM (1 << 7) | |
55 | #define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */ | |
56 | #define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */ | |
57 | #define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* prescaler value shift */ | |
58 | #define OMAP_TIMER_CTRL_POSTED (1 << 2) | |
59 | #define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */ | |
60 | #define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */ | |
61 | #define _OMAP_TIMER_COUNTER_OFFSET 0x28 | |
62 | #define _OMAP_TIMER_LOAD_OFFSET 0x2c | |
63 | #define _OMAP_TIMER_TRIGGER_OFFSET 0x30 | |
64 | #define _OMAP_TIMER_WRITE_PEND_OFFSET 0x34 | |
65 | #define WP_NONE 0 /* no write pending bit */ | |
66 | #define WP_TCLR (1 << 0) | |
67 | #define WP_TCRR (1 << 1) | |
68 | #define WP_TLDR (1 << 2) | |
69 | #define WP_TTGR (1 << 3) | |
70 | #define WP_TMAR (1 << 4) | |
71 | #define WP_TPIR (1 << 5) | |
72 | #define WP_TNIR (1 << 6) | |
73 | #define WP_TCVR (1 << 7) | |
74 | #define WP_TOCR (1 << 8) | |
75 | #define WP_TOWR (1 << 9) | |
76 | #define _OMAP_TIMER_MATCH_OFFSET 0x38 | |
77 | #define _OMAP_TIMER_CAPTURE_OFFSET 0x3c | |
78 | #define _OMAP_TIMER_IF_CTRL_OFFSET 0x40 | |
79 | #define _OMAP_TIMER_CAPTURE2_OFFSET 0x44 /* TCAR2, 34xx only */ | |
80 | #define _OMAP_TIMER_TICK_POS_OFFSET 0x48 /* TPIR, 34xx only */ | |
81 | #define _OMAP_TIMER_TICK_NEG_OFFSET 0x4c /* TNIR, 34xx only */ | |
82 | #define _OMAP_TIMER_TICK_COUNT_OFFSET 0x50 /* TCVR, 34xx only */ | |
83 | #define _OMAP_TIMER_TICK_INT_MASK_SET_OFFSET 0x54 /* TOCR, 34xx only */ | |
84 | #define _OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET 0x58 /* TOWR, 34xx only */ | |
85 | ||
86 | /* register offsets with the write pending bit encoded */ | |
87 | #define WPSHIFT 16 | |
88 | ||
89 | #define OMAP_TIMER_ID_REG (_OMAP_TIMER_ID_OFFSET \ | |
90 | | (WP_NONE << WPSHIFT)) | |
91 | ||
92 | #define OMAP_TIMER_OCP_CFG_REG (_OMAP_TIMER_OCP_CFG_OFFSET \ | |
93 | | (WP_NONE << WPSHIFT)) | |
94 | ||
95 | #define OMAP_TIMER_SYS_STAT_REG (_OMAP_TIMER_SYS_STAT_OFFSET \ | |
96 | | (WP_NONE << WPSHIFT)) | |
97 | ||
98 | #define OMAP_TIMER_STAT_REG (_OMAP_TIMER_STAT_OFFSET \ | |
99 | | (WP_NONE << WPSHIFT)) | |
100 | ||
101 | #define OMAP_TIMER_INT_EN_REG (_OMAP_TIMER_INT_EN_OFFSET \ | |
102 | | (WP_NONE << WPSHIFT)) | |
103 | ||
104 | #define OMAP_TIMER_WAKEUP_EN_REG (_OMAP_TIMER_WAKEUP_EN_OFFSET \ | |
105 | | (WP_NONE << WPSHIFT)) | |
106 | ||
107 | #define OMAP_TIMER_CTRL_REG (_OMAP_TIMER_CTRL_OFFSET \ | |
108 | | (WP_TCLR << WPSHIFT)) | |
109 | ||
110 | #define OMAP_TIMER_COUNTER_REG (_OMAP_TIMER_COUNTER_OFFSET \ | |
111 | | (WP_TCRR << WPSHIFT)) | |
112 | ||
113 | #define OMAP_TIMER_LOAD_REG (_OMAP_TIMER_LOAD_OFFSET \ | |
114 | | (WP_TLDR << WPSHIFT)) | |
115 | ||
116 | #define OMAP_TIMER_TRIGGER_REG (_OMAP_TIMER_TRIGGER_OFFSET \ | |
117 | | (WP_TTGR << WPSHIFT)) | |
118 | ||
119 | #define OMAP_TIMER_WRITE_PEND_REG (_OMAP_TIMER_WRITE_PEND_OFFSET \ | |
120 | | (WP_NONE << WPSHIFT)) | |
121 | ||
122 | #define OMAP_TIMER_MATCH_REG (_OMAP_TIMER_MATCH_OFFSET \ | |
123 | | (WP_TMAR << WPSHIFT)) | |
124 | ||
125 | #define OMAP_TIMER_CAPTURE_REG (_OMAP_TIMER_CAPTURE_OFFSET \ | |
126 | | (WP_NONE << WPSHIFT)) | |
127 | ||
128 | #define OMAP_TIMER_IF_CTRL_REG (_OMAP_TIMER_IF_CTRL_OFFSET \ | |
129 | | (WP_NONE << WPSHIFT)) | |
130 | ||
131 | #define OMAP_TIMER_CAPTURE2_REG (_OMAP_TIMER_CAPTURE2_OFFSET \ | |
132 | | (WP_NONE << WPSHIFT)) | |
133 | ||
134 | #define OMAP_TIMER_TICK_POS_REG (_OMAP_TIMER_TICK_POS_OFFSET \ | |
135 | | (WP_TPIR << WPSHIFT)) | |
136 | ||
137 | #define OMAP_TIMER_TICK_NEG_REG (_OMAP_TIMER_TICK_NEG_OFFSET \ | |
138 | | (WP_TNIR << WPSHIFT)) | |
139 | ||
140 | #define OMAP_TIMER_TICK_COUNT_REG (_OMAP_TIMER_TICK_COUNT_OFFSET \ | |
141 | | (WP_TCVR << WPSHIFT)) | |
142 | ||
143 | #define OMAP_TIMER_TICK_INT_MASK_SET_REG \ | |
144 | (_OMAP_TIMER_TICK_INT_MASK_SET_OFFSET | (WP_TOCR << WPSHIFT)) | |
145 | ||
146 | #define OMAP_TIMER_TICK_INT_MASK_COUNT_REG \ | |
147 | (_OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET | (WP_TOWR << WPSHIFT)) | |
77900a2f TT |
148 | |
149 | struct omap_dm_timer { | |
150 | unsigned long phys_base; | |
151 | int irq; | |
ce2df9ca | 152 | #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) |
77900a2f TT |
153 | struct clk *iclk, *fclk; |
154 | #endif | |
155 | void __iomem *io_base; | |
156 | unsigned reserved:1; | |
12583a70 | 157 | unsigned enabled:1; |
0f0d0807 | 158 | unsigned posted:1; |
77900a2f TT |
159 | }; |
160 | ||
161 | #ifdef CONFIG_ARCH_OMAP1 | |
162 | ||
fa4bb626 TT |
163 | #define omap_dm_clk_enable(x) |
164 | #define omap_dm_clk_disable(x) | |
471b3aa7 SMK |
165 | #define omap2_dm_timers NULL |
166 | #define omap2_dm_source_names NULL | |
167 | #define omap2_dm_source_clocks NULL | |
ce2df9ca SMK |
168 | #define omap3_dm_timers NULL |
169 | #define omap3_dm_source_names NULL | |
170 | #define omap3_dm_source_clocks NULL | |
fa4bb626 | 171 | |
471b3aa7 | 172 | static struct omap_dm_timer omap1_dm_timers[] = { |
77900a2f TT |
173 | { .phys_base = 0xfffb1400, .irq = INT_1610_GPTIMER1 }, |
174 | { .phys_base = 0xfffb1c00, .irq = INT_1610_GPTIMER2 }, | |
175 | { .phys_base = 0xfffb2400, .irq = INT_1610_GPTIMER3 }, | |
176 | { .phys_base = 0xfffb2c00, .irq = INT_1610_GPTIMER4 }, | |
177 | { .phys_base = 0xfffb3400, .irq = INT_1610_GPTIMER5 }, | |
178 | { .phys_base = 0xfffb3c00, .irq = INT_1610_GPTIMER6 }, | |
53037f4c MP |
179 | { .phys_base = 0xfffb7400, .irq = INT_1610_GPTIMER7 }, |
180 | { .phys_base = 0xfffbd400, .irq = INT_1610_GPTIMER8 }, | |
77900a2f | 181 | }; |
92105bb7 | 182 | |
471b3aa7 SMK |
183 | static const int dm_timer_count = ARRAY_SIZE(omap1_dm_timers); |
184 | ||
77900a2f | 185 | #elif defined(CONFIG_ARCH_OMAP2) |
92105bb7 | 186 | |
471b3aa7 SMK |
187 | #define omap_dm_clk_enable(x) clk_enable(x) |
188 | #define omap_dm_clk_disable(x) clk_disable(x) | |
189 | #define omap1_dm_timers NULL | |
ce2df9ca SMK |
190 | #define omap3_dm_timers NULL |
191 | #define omap3_dm_source_names NULL | |
192 | #define omap3_dm_source_clocks NULL | |
fa4bb626 | 193 | |
471b3aa7 | 194 | static struct omap_dm_timer omap2_dm_timers[] = { |
77900a2f TT |
195 | { .phys_base = 0x48028000, .irq = INT_24XX_GPTIMER1 }, |
196 | { .phys_base = 0x4802a000, .irq = INT_24XX_GPTIMER2 }, | |
197 | { .phys_base = 0x48078000, .irq = INT_24XX_GPTIMER3 }, | |
198 | { .phys_base = 0x4807a000, .irq = INT_24XX_GPTIMER4 }, | |
199 | { .phys_base = 0x4807c000, .irq = INT_24XX_GPTIMER5 }, | |
200 | { .phys_base = 0x4807e000, .irq = INT_24XX_GPTIMER6 }, | |
201 | { .phys_base = 0x48080000, .irq = INT_24XX_GPTIMER7 }, | |
202 | { .phys_base = 0x48082000, .irq = INT_24XX_GPTIMER8 }, | |
203 | { .phys_base = 0x48084000, .irq = INT_24XX_GPTIMER9 }, | |
204 | { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 }, | |
205 | { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 }, | |
206 | { .phys_base = 0x4808a000, .irq = INT_24XX_GPTIMER12 }, | |
92105bb7 TL |
207 | }; |
208 | ||
471b3aa7 | 209 | static const char *omap2_dm_source_names[] __initdata = { |
83379c81 TT |
210 | "sys_ck", |
211 | "func_32k_ck", | |
471b3aa7 SMK |
212 | "alt_ck", |
213 | NULL | |
83379c81 TT |
214 | }; |
215 | ||
471b3aa7 SMK |
216 | static struct clk **omap2_dm_source_clocks[3]; |
217 | static const int dm_timer_count = ARRAY_SIZE(omap2_dm_timers); | |
83379c81 | 218 | |
ce2df9ca SMK |
219 | #elif defined(CONFIG_ARCH_OMAP3) |
220 | ||
221 | #define omap_dm_clk_enable(x) clk_enable(x) | |
222 | #define omap_dm_clk_disable(x) clk_disable(x) | |
223 | #define omap1_dm_timers NULL | |
224 | #define omap2_dm_timers NULL | |
225 | #define omap2_dm_source_names NULL | |
226 | #define omap2_dm_source_clocks NULL | |
227 | ||
228 | static struct omap_dm_timer omap3_dm_timers[] = { | |
229 | { .phys_base = 0x48318000, .irq = INT_24XX_GPTIMER1 }, | |
230 | { .phys_base = 0x49032000, .irq = INT_24XX_GPTIMER2 }, | |
231 | { .phys_base = 0x49034000, .irq = INT_24XX_GPTIMER3 }, | |
232 | { .phys_base = 0x49036000, .irq = INT_24XX_GPTIMER4 }, | |
233 | { .phys_base = 0x49038000, .irq = INT_24XX_GPTIMER5 }, | |
234 | { .phys_base = 0x4903A000, .irq = INT_24XX_GPTIMER6 }, | |
235 | { .phys_base = 0x4903C000, .irq = INT_24XX_GPTIMER7 }, | |
236 | { .phys_base = 0x4903E000, .irq = INT_24XX_GPTIMER8 }, | |
237 | { .phys_base = 0x49040000, .irq = INT_24XX_GPTIMER9 }, | |
238 | { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 }, | |
239 | { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 }, | |
240 | { .phys_base = 0x48304000, .irq = INT_24XX_GPTIMER12 }, | |
241 | }; | |
242 | ||
243 | static const char *omap3_dm_source_names[] __initdata = { | |
244 | "sys_ck", | |
245 | "omap_32k_fck", | |
246 | NULL | |
247 | }; | |
248 | ||
249 | static struct clk **omap3_dm_source_clocks[2]; | |
250 | static const int dm_timer_count = ARRAY_SIZE(omap3_dm_timers); | |
251 | ||
77900a2f TT |
252 | #else |
253 | ||
254 | #error OMAP architecture not supported! | |
255 | ||
256 | #endif | |
257 | ||
471b3aa7 SMK |
258 | static struct omap_dm_timer *dm_timers; |
259 | static char **dm_source_names; | |
260 | static struct clk **dm_source_clocks; | |
261 | ||
92105bb7 TL |
262 | static spinlock_t dm_timer_lock; |
263 | ||
0f0d0807 RW |
264 | /* |
265 | * Reads timer registers in posted and non-posted mode. The posted mode bit | |
266 | * is encoded in reg. Note that in posted mode write pending bit must be | |
267 | * checked. Otherwise a read of a non completed write will produce an error. | |
268 | */ | |
269 | static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg) | |
77900a2f | 270 | { |
0f0d0807 RW |
271 | if (timer->posted) |
272 | while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff)) | |
273 | & (reg >> WPSHIFT)) | |
274 | cpu_relax(); | |
275 | return readl(timer->io_base + (reg & 0xff)); | |
77900a2f | 276 | } |
92105bb7 | 277 | |
0f0d0807 RW |
278 | /* |
279 | * Writes timer registers in posted and non-posted mode. The posted mode bit | |
280 | * is encoded in reg. Note that in posted mode the write pending bit must be | |
281 | * checked. Otherwise a write on a register which has a pending write will be | |
282 | * lost. | |
283 | */ | |
284 | static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg, | |
285 | u32 value) | |
92105bb7 | 286 | { |
0f0d0807 RW |
287 | if (timer->posted) |
288 | while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff)) | |
289 | & (reg >> WPSHIFT)) | |
290 | cpu_relax(); | |
291 | writel(value, timer->io_base + (reg & 0xff)); | |
92105bb7 TL |
292 | } |
293 | ||
77900a2f | 294 | static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer) |
92105bb7 | 295 | { |
77900a2f TT |
296 | int c; |
297 | ||
298 | c = 0; | |
299 | while (!(omap_dm_timer_read_reg(timer, OMAP_TIMER_SYS_STAT_REG) & 1)) { | |
300 | c++; | |
301 | if (c > 100000) { | |
302 | printk(KERN_ERR "Timer failed to reset\n"); | |
303 | return; | |
304 | } | |
305 | } | |
92105bb7 TL |
306 | } |
307 | ||
77900a2f TT |
308 | static void omap_dm_timer_reset(struct omap_dm_timer *timer) |
309 | { | |
310 | u32 l; | |
311 | ||
39020842 | 312 | if (!cpu_class_is_omap2() || timer != &dm_timers[0]) { |
e32f7ec2 TT |
313 | omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); |
314 | omap_dm_timer_wait_for_reset(timer); | |
315 | } | |
12583a70 | 316 | omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); |
77900a2f | 317 | |
77900a2f | 318 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_OCP_CFG_REG); |
0f0d0807 RW |
319 | l |= 0x02 << 3; /* Set to smart-idle mode */ |
320 | l |= 0x2 << 8; /* Set clock activity to perserve f-clock on idle */ | |
321 | ||
322 | /* | |
323 | * Enable wake-up only for GPT1 on OMAP2 CPUs. | |
324 | * FIXME: All timers should have wake-up enabled and clear | |
325 | * PRCM status. | |
326 | */ | |
327 | if (cpu_class_is_omap2() && (timer == &dm_timers[0])) | |
39020842 | 328 | l |= 1 << 2; |
77900a2f | 329 | omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, l); |
0f0d0807 RW |
330 | |
331 | /* Match hardware reset default of posted mode */ | |
332 | omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, | |
333 | OMAP_TIMER_CTRL_POSTED); | |
334 | timer->posted = 1; | |
77900a2f TT |
335 | } |
336 | ||
83379c81 | 337 | static void omap_dm_timer_prepare(struct omap_dm_timer *timer) |
77900a2f | 338 | { |
12583a70 | 339 | omap_dm_timer_enable(timer); |
77900a2f TT |
340 | omap_dm_timer_reset(timer); |
341 | } | |
342 | ||
343 | struct omap_dm_timer *omap_dm_timer_request(void) | |
344 | { | |
345 | struct omap_dm_timer *timer = NULL; | |
346 | unsigned long flags; | |
347 | int i; | |
348 | ||
349 | spin_lock_irqsave(&dm_timer_lock, flags); | |
350 | for (i = 0; i < dm_timer_count; i++) { | |
351 | if (dm_timers[i].reserved) | |
352 | continue; | |
353 | ||
354 | timer = &dm_timers[i]; | |
83379c81 | 355 | timer->reserved = 1; |
77900a2f TT |
356 | break; |
357 | } | |
358 | spin_unlock_irqrestore(&dm_timer_lock, flags); | |
359 | ||
83379c81 TT |
360 | if (timer != NULL) |
361 | omap_dm_timer_prepare(timer); | |
362 | ||
77900a2f TT |
363 | return timer; |
364 | } | |
365 | ||
366 | struct omap_dm_timer *omap_dm_timer_request_specific(int id) | |
92105bb7 TL |
367 | { |
368 | struct omap_dm_timer *timer; | |
77900a2f | 369 | unsigned long flags; |
92105bb7 | 370 | |
77900a2f TT |
371 | spin_lock_irqsave(&dm_timer_lock, flags); |
372 | if (id <= 0 || id > dm_timer_count || dm_timers[id-1].reserved) { | |
373 | spin_unlock_irqrestore(&dm_timer_lock, flags); | |
374 | printk("BUG: warning at %s:%d/%s(): unable to get timer %d\n", | |
8e86f427 | 375 | __FILE__, __LINE__, __func__, id); |
77900a2f TT |
376 | dump_stack(); |
377 | return NULL; | |
378 | } | |
92105bb7 | 379 | |
77900a2f | 380 | timer = &dm_timers[id-1]; |
83379c81 | 381 | timer->reserved = 1; |
77900a2f TT |
382 | spin_unlock_irqrestore(&dm_timer_lock, flags); |
383 | ||
83379c81 TT |
384 | omap_dm_timer_prepare(timer); |
385 | ||
77900a2f | 386 | return timer; |
92105bb7 TL |
387 | } |
388 | ||
77900a2f TT |
389 | void omap_dm_timer_free(struct omap_dm_timer *timer) |
390 | { | |
12583a70 | 391 | omap_dm_timer_enable(timer); |
77900a2f | 392 | omap_dm_timer_reset(timer); |
12583a70 | 393 | omap_dm_timer_disable(timer); |
fa4bb626 | 394 | |
77900a2f TT |
395 | WARN_ON(!timer->reserved); |
396 | timer->reserved = 0; | |
397 | } | |
398 | ||
12583a70 TT |
399 | void omap_dm_timer_enable(struct omap_dm_timer *timer) |
400 | { | |
401 | if (timer->enabled) | |
402 | return; | |
403 | ||
404 | omap_dm_clk_enable(timer->fclk); | |
405 | omap_dm_clk_enable(timer->iclk); | |
406 | ||
407 | timer->enabled = 1; | |
408 | } | |
409 | ||
410 | void omap_dm_timer_disable(struct omap_dm_timer *timer) | |
411 | { | |
412 | if (!timer->enabled) | |
413 | return; | |
414 | ||
415 | omap_dm_clk_disable(timer->iclk); | |
416 | omap_dm_clk_disable(timer->fclk); | |
417 | ||
418 | timer->enabled = 0; | |
419 | } | |
420 | ||
77900a2f TT |
421 | int omap_dm_timer_get_irq(struct omap_dm_timer *timer) |
422 | { | |
423 | return timer->irq; | |
424 | } | |
425 | ||
426 | #if defined(CONFIG_ARCH_OMAP1) | |
427 | ||
a569c6ec TL |
428 | /** |
429 | * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR | |
430 | * @inputmask: current value of idlect mask | |
431 | */ | |
432 | __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) | |
433 | { | |
77900a2f | 434 | int i; |
a569c6ec TL |
435 | |
436 | /* If ARMXOR cannot be idled this function call is unnecessary */ | |
437 | if (!(inputmask & (1 << 1))) | |
438 | return inputmask; | |
439 | ||
440 | /* If any active timer is using ARMXOR return modified mask */ | |
77900a2f TT |
441 | for (i = 0; i < dm_timer_count; i++) { |
442 | u32 l; | |
443 | ||
35912c79 | 444 | l = omap_dm_timer_read_reg(&dm_timers[i], OMAP_TIMER_CTRL_REG); |
77900a2f TT |
445 | if (l & OMAP_TIMER_CTRL_ST) { |
446 | if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0) | |
a569c6ec TL |
447 | inputmask &= ~(1 << 1); |
448 | else | |
449 | inputmask &= ~(1 << 2); | |
450 | } | |
77900a2f | 451 | } |
a569c6ec TL |
452 | |
453 | return inputmask; | |
454 | } | |
455 | ||
ce2df9ca | 456 | #elif defined(CONFIG_ARCH_OMAP2) || defined (CONFIG_ARCH_OMAP3) |
a569c6ec | 457 | |
77900a2f | 458 | struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) |
92105bb7 | 459 | { |
fa4bb626 | 460 | return timer->fclk; |
77900a2f | 461 | } |
92105bb7 | 462 | |
77900a2f TT |
463 | __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) |
464 | { | |
465 | BUG(); | |
2121880e DB |
466 | |
467 | return 0; | |
92105bb7 TL |
468 | } |
469 | ||
77900a2f | 470 | #endif |
92105bb7 | 471 | |
77900a2f | 472 | void omap_dm_timer_trigger(struct omap_dm_timer *timer) |
92105bb7 | 473 | { |
77900a2f | 474 | omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); |
92105bb7 TL |
475 | } |
476 | ||
77900a2f TT |
477 | void omap_dm_timer_start(struct omap_dm_timer *timer) |
478 | { | |
479 | u32 l; | |
92105bb7 | 480 | |
77900a2f TT |
481 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
482 | if (!(l & OMAP_TIMER_CTRL_ST)) { | |
483 | l |= OMAP_TIMER_CTRL_ST; | |
484 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); | |
485 | } | |
486 | } | |
92105bb7 | 487 | |
77900a2f | 488 | void omap_dm_timer_stop(struct omap_dm_timer *timer) |
92105bb7 | 489 | { |
77900a2f | 490 | u32 l; |
92105bb7 | 491 | |
77900a2f TT |
492 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
493 | if (l & OMAP_TIMER_CTRL_ST) { | |
494 | l &= ~0x1; | |
495 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); | |
92105bb7 | 496 | } |
92105bb7 TL |
497 | } |
498 | ||
77900a2f | 499 | #ifdef CONFIG_ARCH_OMAP1 |
92105bb7 | 500 | |
77900a2f | 501 | void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) |
92105bb7 | 502 | { |
77900a2f TT |
503 | int n = (timer - dm_timers) << 1; |
504 | u32 l; | |
92105bb7 | 505 | |
77900a2f TT |
506 | l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n); |
507 | l |= source << n; | |
508 | omap_writel(l, MOD_CONF_CTRL_1); | |
92105bb7 TL |
509 | } |
510 | ||
77900a2f | 511 | #else |
92105bb7 | 512 | |
77900a2f | 513 | void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) |
92105bb7 | 514 | { |
77900a2f TT |
515 | if (source < 0 || source >= 3) |
516 | return; | |
517 | ||
77900a2f | 518 | clk_disable(timer->fclk); |
83379c81 | 519 | clk_set_parent(timer->fclk, dm_source_clocks[source]); |
77900a2f | 520 | clk_enable(timer->fclk); |
77900a2f TT |
521 | |
522 | /* When the functional clock disappears, too quick writes seem to | |
523 | * cause an abort. */ | |
c40fae95 | 524 | __delay(150000); |
92105bb7 TL |
525 | } |
526 | ||
77900a2f | 527 | #endif |
92105bb7 | 528 | |
77900a2f TT |
529 | void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, |
530 | unsigned int load) | |
92105bb7 TL |
531 | { |
532 | u32 l; | |
77900a2f | 533 | |
92105bb7 | 534 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
77900a2f TT |
535 | if (autoreload) |
536 | l |= OMAP_TIMER_CTRL_AR; | |
537 | else | |
538 | l &= ~OMAP_TIMER_CTRL_AR; | |
92105bb7 | 539 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
77900a2f | 540 | omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); |
0f0d0807 RW |
541 | |
542 | /* REVISIT: hw feature, ttgr overtaking tldr? */ | |
543 | while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff))) | |
544 | cpu_relax(); | |
545 | ||
77900a2f | 546 | omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); |
92105bb7 TL |
547 | } |
548 | ||
3fddd09e RW |
549 | /* Optimized set_load which removes costly spin wait in timer_start */ |
550 | void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, | |
551 | unsigned int load) | |
552 | { | |
553 | u32 l; | |
554 | ||
555 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); | |
556 | if (autoreload) | |
557 | l |= OMAP_TIMER_CTRL_AR; | |
558 | else | |
559 | l &= ~OMAP_TIMER_CTRL_AR; | |
560 | l |= OMAP_TIMER_CTRL_ST; | |
561 | ||
562 | omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, load); | |
563 | omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); | |
564 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); | |
565 | } | |
566 | ||
77900a2f TT |
567 | void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, |
568 | unsigned int match) | |
92105bb7 TL |
569 | { |
570 | u32 l; | |
571 | ||
572 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); | |
83379c81 | 573 | if (enable) |
77900a2f TT |
574 | l |= OMAP_TIMER_CTRL_CE; |
575 | else | |
576 | l &= ~OMAP_TIMER_CTRL_CE; | |
92105bb7 | 577 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
77900a2f | 578 | omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); |
92105bb7 TL |
579 | } |
580 | ||
77900a2f TT |
581 | void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, |
582 | int toggle, int trigger) | |
92105bb7 TL |
583 | { |
584 | u32 l; | |
585 | ||
586 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); | |
77900a2f TT |
587 | l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM | |
588 | OMAP_TIMER_CTRL_PT | (0x03 << 10)); | |
589 | if (def_on) | |
590 | l |= OMAP_TIMER_CTRL_SCPWM; | |
591 | if (toggle) | |
592 | l |= OMAP_TIMER_CTRL_PT; | |
593 | l |= trigger << 10; | |
92105bb7 TL |
594 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
595 | } | |
596 | ||
77900a2f | 597 | void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler) |
92105bb7 TL |
598 | { |
599 | u32 l; | |
600 | ||
601 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); | |
77900a2f TT |
602 | l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2)); |
603 | if (prescaler >= 0x00 && prescaler <= 0x07) { | |
604 | l |= OMAP_TIMER_CTRL_PRE; | |
605 | l |= prescaler << 2; | |
606 | } | |
92105bb7 TL |
607 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
608 | } | |
609 | ||
77900a2f TT |
610 | void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, |
611 | unsigned int value) | |
92105bb7 | 612 | { |
77900a2f | 613 | omap_dm_timer_write_reg(timer, OMAP_TIMER_INT_EN_REG, value); |
39020842 | 614 | omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, value); |
92105bb7 TL |
615 | } |
616 | ||
77900a2f | 617 | unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer) |
92105bb7 | 618 | { |
fa4bb626 TT |
619 | unsigned int l; |
620 | ||
fa4bb626 | 621 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG); |
fa4bb626 TT |
622 | |
623 | return l; | |
92105bb7 TL |
624 | } |
625 | ||
77900a2f | 626 | void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) |
92105bb7 | 627 | { |
77900a2f | 628 | omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG, value); |
92105bb7 TL |
629 | } |
630 | ||
77900a2f | 631 | unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) |
92105bb7 | 632 | { |
fa4bb626 TT |
633 | unsigned int l; |
634 | ||
fa4bb626 | 635 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_REG); |
fa4bb626 TT |
636 | |
637 | return l; | |
92105bb7 TL |
638 | } |
639 | ||
83379c81 TT |
640 | void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value) |
641 | { | |
fa4bb626 | 642 | omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value); |
83379c81 TT |
643 | } |
644 | ||
77900a2f | 645 | int omap_dm_timers_active(void) |
92105bb7 | 646 | { |
77900a2f | 647 | int i; |
92105bb7 | 648 | |
77900a2f TT |
649 | for (i = 0; i < dm_timer_count; i++) { |
650 | struct omap_dm_timer *timer; | |
92105bb7 | 651 | |
77900a2f | 652 | timer = &dm_timers[i]; |
12583a70 TT |
653 | |
654 | if (!timer->enabled) | |
655 | continue; | |
656 | ||
77900a2f | 657 | if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) & |
fa4bb626 | 658 | OMAP_TIMER_CTRL_ST) { |
77900a2f | 659 | return 1; |
fa4bb626 | 660 | } |
77900a2f TT |
661 | } |
662 | return 0; | |
663 | } | |
92105bb7 | 664 | |
471b3aa7 | 665 | int __init omap_dm_timer_init(void) |
92105bb7 TL |
666 | { |
667 | struct omap_dm_timer *timer; | |
77900a2f TT |
668 | int i; |
669 | ||
ce2df9ca | 670 | if (!(cpu_is_omap16xx() || cpu_class_is_omap2())) |
77900a2f | 671 | return -ENODEV; |
92105bb7 TL |
672 | |
673 | spin_lock_init(&dm_timer_lock); | |
471b3aa7 SMK |
674 | |
675 | if (cpu_class_is_omap1()) | |
676 | dm_timers = omap1_dm_timers; | |
677 | else if (cpu_is_omap24xx()) { | |
678 | dm_timers = omap2_dm_timers; | |
679 | dm_source_names = (char **)omap2_dm_source_names; | |
680 | dm_source_clocks = (struct clk **)omap2_dm_source_clocks; | |
ce2df9ca SMK |
681 | } else if (cpu_is_omap34xx()) { |
682 | dm_timers = omap3_dm_timers; | |
683 | dm_source_names = (char **)omap3_dm_source_names; | |
684 | dm_source_clocks = (struct clk **)omap3_dm_source_clocks; | |
83379c81 | 685 | } |
471b3aa7 SMK |
686 | |
687 | if (cpu_class_is_omap2()) | |
688 | for (i = 0; dm_source_names[i] != NULL; i++) | |
689 | dm_source_clocks[i] = clk_get(NULL, dm_source_names[i]); | |
690 | ||
56a25641 SMK |
691 | if (cpu_is_omap243x()) |
692 | dm_timers[0].phys_base = 0x49018000; | |
83379c81 | 693 | |
77900a2f | 694 | for (i = 0; i < dm_timer_count; i++) { |
77900a2f | 695 | timer = &dm_timers[i]; |
471b3aa7 | 696 | timer->io_base = (void __iomem *)io_p2v(timer->phys_base); |
ce2df9ca | 697 | #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) |
471b3aa7 SMK |
698 | if (cpu_class_is_omap2()) { |
699 | char clk_name[16]; | |
700 | sprintf(clk_name, "gpt%d_ick", i + 1); | |
701 | timer->iclk = clk_get(NULL, clk_name); | |
702 | sprintf(clk_name, "gpt%d_fck", i + 1); | |
703 | timer->fclk = clk_get(NULL, clk_name); | |
704 | } | |
77900a2f | 705 | #endif |
92105bb7 | 706 | } |
92105bb7 | 707 | |
92105bb7 TL |
708 | return 0; |
709 | } |