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