]>
Commit | Line | Data |
---|---|---|
92105bb7 TL |
1 | /* |
2 | * linux/arch/arm/plat-omap/dmtimer.c | |
3 | * | |
4 | * OMAP Dual-Mode Timers | |
5 | * | |
97933d6c TKD |
6 | * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ |
7 | * Tarun Kanti DebBarma <tarun.kanti@ti.com> | |
8 | * Thara Gopinath <thara@ti.com> | |
9 | * | |
10 | * dmtimer adaptation to platform_driver. | |
11 | * | |
92105bb7 | 12 | * Copyright (C) 2005 Nokia Corporation |
77900a2f TT |
13 | * OMAP2 support by Juha Yrjola |
14 | * API improvements and OMAP2 clock framework support by Timo Teras | |
92105bb7 | 15 | * |
44169075 SS |
16 | * Copyright (C) 2009 Texas Instruments |
17 | * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> | |
18 | * | |
92105bb7 TL |
19 | * This program is free software; you can redistribute it and/or modify it |
20 | * under the terms of the GNU General Public License as published by the | |
21 | * Free Software Foundation; either version 2 of the License, or (at your | |
22 | * option) any later version. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
25 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | |
27 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
28 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
29 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
31 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
32 | * | |
33 | * You should have received a copy of the GNU General Public License along | |
34 | * with this program; if not, write to the Free Software Foundation, Inc., | |
35 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
36 | */ | |
37 | ||
38 | #include <linux/init.h> | |
77900a2f TT |
39 | #include <linux/spinlock.h> |
40 | #include <linux/errno.h> | |
41 | #include <linux/list.h> | |
42 | #include <linux/clk.h> | |
43 | #include <linux/delay.h> | |
fced80c7 | 44 | #include <linux/io.h> |
6c366e32 | 45 | #include <linux/module.h> |
a09e64fb | 46 | #include <mach/hardware.h> |
ce491cf8 | 47 | #include <plat/dmtimer.h> |
a09e64fb | 48 | #include <mach/irqs.h> |
92105bb7 | 49 | |
882c0518 | 50 | static int dm_timer_count; |
fa4bb626 | 51 | |
882c0518 | 52 | #ifdef CONFIG_ARCH_OMAP2 |
471b3aa7 | 53 | static struct omap_dm_timer omap2_dm_timers[] = { |
77900a2f TT |
54 | { .phys_base = 0x48028000, .irq = INT_24XX_GPTIMER1 }, |
55 | { .phys_base = 0x4802a000, .irq = INT_24XX_GPTIMER2 }, | |
56 | { .phys_base = 0x48078000, .irq = INT_24XX_GPTIMER3 }, | |
57 | { .phys_base = 0x4807a000, .irq = INT_24XX_GPTIMER4 }, | |
58 | { .phys_base = 0x4807c000, .irq = INT_24XX_GPTIMER5 }, | |
59 | { .phys_base = 0x4807e000, .irq = INT_24XX_GPTIMER6 }, | |
60 | { .phys_base = 0x48080000, .irq = INT_24XX_GPTIMER7 }, | |
61 | { .phys_base = 0x48082000, .irq = INT_24XX_GPTIMER8 }, | |
62 | { .phys_base = 0x48084000, .irq = INT_24XX_GPTIMER9 }, | |
63 | { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 }, | |
64 | { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 }, | |
65 | { .phys_base = 0x4808a000, .irq = INT_24XX_GPTIMER12 }, | |
92105bb7 TL |
66 | }; |
67 | ||
471b3aa7 | 68 | static const char *omap2_dm_source_names[] __initdata = { |
83379c81 TT |
69 | "sys_ck", |
70 | "func_32k_ck", | |
471b3aa7 SMK |
71 | "alt_ck", |
72 | NULL | |
83379c81 TT |
73 | }; |
74 | ||
aea2a5b0 | 75 | static struct clk *omap2_dm_source_clocks[3]; |
882c0518 | 76 | static const int omap2_dm_timer_count = ARRAY_SIZE(omap2_dm_timers); |
ce2df9ca | 77 | |
882c0518 | 78 | #else |
ce2df9ca | 79 | #define omap2_dm_timers NULL |
882c0518 | 80 | #define omap2_dm_timer_count 0 |
ce2df9ca SMK |
81 | #define omap2_dm_source_names NULL |
82 | #define omap2_dm_source_clocks NULL | |
882c0518 | 83 | #endif /* CONFIG_ARCH_OMAP2 */ |
ce2df9ca | 84 | |
882c0518 | 85 | #ifdef CONFIG_ARCH_OMAP3 |
ce2df9ca SMK |
86 | static struct omap_dm_timer omap3_dm_timers[] = { |
87 | { .phys_base = 0x48318000, .irq = INT_24XX_GPTIMER1 }, | |
88 | { .phys_base = 0x49032000, .irq = INT_24XX_GPTIMER2 }, | |
89 | { .phys_base = 0x49034000, .irq = INT_24XX_GPTIMER3 }, | |
90 | { .phys_base = 0x49036000, .irq = INT_24XX_GPTIMER4 }, | |
91 | { .phys_base = 0x49038000, .irq = INT_24XX_GPTIMER5 }, | |
92 | { .phys_base = 0x4903A000, .irq = INT_24XX_GPTIMER6 }, | |
93 | { .phys_base = 0x4903C000, .irq = INT_24XX_GPTIMER7 }, | |
94 | { .phys_base = 0x4903E000, .irq = INT_24XX_GPTIMER8 }, | |
95 | { .phys_base = 0x49040000, .irq = INT_24XX_GPTIMER9 }, | |
96 | { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 }, | |
97 | { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 }, | |
9198a406 | 98 | { .phys_base = 0x48304000, .irq = INT_34XX_GPT12_IRQ }, |
ce2df9ca SMK |
99 | }; |
100 | ||
101 | static const char *omap3_dm_source_names[] __initdata = { | |
102 | "sys_ck", | |
103 | "omap_32k_fck", | |
104 | NULL | |
105 | }; | |
106 | ||
aea2a5b0 | 107 | static struct clk *omap3_dm_source_clocks[2]; |
882c0518 | 108 | static const int omap3_dm_timer_count = ARRAY_SIZE(omap3_dm_timers); |
44169075 | 109 | |
882c0518 | 110 | #else |
44169075 | 111 | #define omap3_dm_timers NULL |
882c0518 | 112 | #define omap3_dm_timer_count 0 |
44169075 SS |
113 | #define omap3_dm_source_names NULL |
114 | #define omap3_dm_source_clocks NULL | |
882c0518 | 115 | #endif /* CONFIG_ARCH_OMAP3 */ |
44169075 | 116 | |
882c0518 | 117 | #ifdef CONFIG_ARCH_OMAP4 |
44169075 | 118 | static struct omap_dm_timer omap4_dm_timers[] = { |
5772ca7d SS |
119 | { .phys_base = 0x4a318000, .irq = OMAP44XX_IRQ_GPT1 }, |
120 | { .phys_base = 0x48032000, .irq = OMAP44XX_IRQ_GPT2 }, | |
121 | { .phys_base = 0x48034000, .irq = OMAP44XX_IRQ_GPT3 }, | |
122 | { .phys_base = 0x48036000, .irq = OMAP44XX_IRQ_GPT4 }, | |
123 | { .phys_base = 0x40138000, .irq = OMAP44XX_IRQ_GPT5 }, | |
124 | { .phys_base = 0x4013a000, .irq = OMAP44XX_IRQ_GPT6 }, | |
125 | { .phys_base = 0x4013a000, .irq = OMAP44XX_IRQ_GPT7 }, | |
126 | { .phys_base = 0x4013e000, .irq = OMAP44XX_IRQ_GPT8 }, | |
127 | { .phys_base = 0x4803e000, .irq = OMAP44XX_IRQ_GPT9 }, | |
128 | { .phys_base = 0x48086000, .irq = OMAP44XX_IRQ_GPT10 }, | |
129 | { .phys_base = 0x48088000, .irq = OMAP44XX_IRQ_GPT11 }, | |
130 | { .phys_base = 0x4a320000, .irq = OMAP44XX_IRQ_GPT12 }, | |
44169075 SS |
131 | }; |
132 | static const char *omap4_dm_source_names[] __initdata = { | |
1dc993b2 RN |
133 | "sys_clkin_ck", |
134 | "sys_32k_ck", | |
44169075 SS |
135 | NULL |
136 | }; | |
137 | static struct clk *omap4_dm_source_clocks[2]; | |
882c0518 | 138 | static const int omap4_dm_timer_count = ARRAY_SIZE(omap4_dm_timers); |
44169075 | 139 | |
77900a2f | 140 | #else |
882c0518 TL |
141 | #define omap4_dm_timers NULL |
142 | #define omap4_dm_timer_count 0 | |
143 | #define omap4_dm_source_names NULL | |
144 | #define omap4_dm_source_clocks NULL | |
145 | #endif /* CONFIG_ARCH_OMAP4 */ | |
77900a2f | 146 | |
471b3aa7 | 147 | static struct omap_dm_timer *dm_timers; |
aea2a5b0 | 148 | static const char **dm_source_names; |
471b3aa7 SMK |
149 | static struct clk **dm_source_clocks; |
150 | ||
92105bb7 TL |
151 | static spinlock_t dm_timer_lock; |
152 | ||
0f0d0807 RW |
153 | /* |
154 | * Reads timer registers in posted and non-posted mode. The posted mode bit | |
155 | * is encoded in reg. Note that in posted mode write pending bit must be | |
156 | * checked. Otherwise a read of a non completed write will produce an error. | |
157 | */ | |
158 | static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg) | |
77900a2f | 159 | { |
ee17f114 TL |
160 | WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET); |
161 | return __omap_dm_timer_read(timer, reg, timer->posted); | |
77900a2f | 162 | } |
92105bb7 | 163 | |
0f0d0807 RW |
164 | /* |
165 | * Writes timer registers in posted and non-posted mode. The posted mode bit | |
166 | * is encoded in reg. Note that in posted mode the write pending bit must be | |
167 | * checked. Otherwise a write on a register which has a pending write will be | |
168 | * lost. | |
169 | */ | |
170 | static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg, | |
171 | u32 value) | |
92105bb7 | 172 | { |
ee17f114 TL |
173 | WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET); |
174 | __omap_dm_timer_write(timer, reg, value, timer->posted); | |
92105bb7 TL |
175 | } |
176 | ||
77900a2f | 177 | static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer) |
92105bb7 | 178 | { |
77900a2f TT |
179 | int c; |
180 | ||
ee17f114 TL |
181 | if (!timer->sys_stat) |
182 | return; | |
183 | ||
77900a2f | 184 | c = 0; |
ee17f114 | 185 | while (!(__raw_readl(timer->sys_stat) & 1)) { |
77900a2f TT |
186 | c++; |
187 | if (c > 100000) { | |
188 | printk(KERN_ERR "Timer failed to reset\n"); | |
189 | return; | |
190 | } | |
191 | } | |
92105bb7 TL |
192 | } |
193 | ||
77900a2f TT |
194 | static void omap_dm_timer_reset(struct omap_dm_timer *timer) |
195 | { | |
caf64f2f | 196 | int autoidle = 0, wakeup = 0; |
77900a2f | 197 | |
39020842 | 198 | if (!cpu_class_is_omap2() || timer != &dm_timers[0]) { |
e32f7ec2 TT |
199 | omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); |
200 | omap_dm_timer_wait_for_reset(timer); | |
201 | } | |
12583a70 | 202 | omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); |
77900a2f | 203 | |
ba503484 A |
204 | /* Enable autoidle on OMAP2+ */ |
205 | if (cpu_class_is_omap2()) | |
caf64f2f | 206 | autoidle = 1; |
4ce1e5e1 | 207 | |
0f0d0807 | 208 | /* |
219c5b98 | 209 | * Enable wake-up on OMAP2 CPUs. |
0f0d0807 | 210 | */ |
219c5b98 | 211 | if (cpu_class_is_omap2()) |
caf64f2f | 212 | wakeup = 1; |
0f0d0807 | 213 | |
ee17f114 | 214 | __omap_dm_timer_reset(timer, autoidle, wakeup); |
0f0d0807 | 215 | timer->posted = 1; |
77900a2f TT |
216 | } |
217 | ||
caf64f2f | 218 | void omap_dm_timer_prepare(struct omap_dm_timer *timer) |
77900a2f | 219 | { |
12583a70 | 220 | omap_dm_timer_enable(timer); |
77900a2f TT |
221 | omap_dm_timer_reset(timer); |
222 | } | |
223 | ||
224 | struct omap_dm_timer *omap_dm_timer_request(void) | |
225 | { | |
226 | struct omap_dm_timer *timer = NULL; | |
227 | unsigned long flags; | |
228 | int i; | |
229 | ||
230 | spin_lock_irqsave(&dm_timer_lock, flags); | |
231 | for (i = 0; i < dm_timer_count; i++) { | |
232 | if (dm_timers[i].reserved) | |
233 | continue; | |
234 | ||
235 | timer = &dm_timers[i]; | |
83379c81 | 236 | timer->reserved = 1; |
77900a2f TT |
237 | break; |
238 | } | |
239 | spin_unlock_irqrestore(&dm_timer_lock, flags); | |
240 | ||
83379c81 TT |
241 | if (timer != NULL) |
242 | omap_dm_timer_prepare(timer); | |
243 | ||
77900a2f TT |
244 | return timer; |
245 | } | |
6c366e32 | 246 | EXPORT_SYMBOL_GPL(omap_dm_timer_request); |
77900a2f TT |
247 | |
248 | struct omap_dm_timer *omap_dm_timer_request_specific(int id) | |
92105bb7 TL |
249 | { |
250 | struct omap_dm_timer *timer; | |
77900a2f | 251 | unsigned long flags; |
92105bb7 | 252 | |
77900a2f TT |
253 | spin_lock_irqsave(&dm_timer_lock, flags); |
254 | if (id <= 0 || id > dm_timer_count || dm_timers[id-1].reserved) { | |
255 | spin_unlock_irqrestore(&dm_timer_lock, flags); | |
256 | printk("BUG: warning at %s:%d/%s(): unable to get timer %d\n", | |
8e86f427 | 257 | __FILE__, __LINE__, __func__, id); |
77900a2f TT |
258 | dump_stack(); |
259 | return NULL; | |
260 | } | |
92105bb7 | 261 | |
77900a2f | 262 | timer = &dm_timers[id-1]; |
83379c81 | 263 | timer->reserved = 1; |
77900a2f TT |
264 | spin_unlock_irqrestore(&dm_timer_lock, flags); |
265 | ||
83379c81 TT |
266 | omap_dm_timer_prepare(timer); |
267 | ||
77900a2f | 268 | return timer; |
92105bb7 | 269 | } |
6c366e32 | 270 | EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific); |
92105bb7 | 271 | |
77900a2f TT |
272 | void omap_dm_timer_free(struct omap_dm_timer *timer) |
273 | { | |
12583a70 | 274 | omap_dm_timer_enable(timer); |
77900a2f | 275 | omap_dm_timer_reset(timer); |
12583a70 | 276 | omap_dm_timer_disable(timer); |
fa4bb626 | 277 | |
77900a2f TT |
278 | WARN_ON(!timer->reserved); |
279 | timer->reserved = 0; | |
280 | } | |
6c366e32 | 281 | EXPORT_SYMBOL_GPL(omap_dm_timer_free); |
77900a2f | 282 | |
12583a70 TT |
283 | void omap_dm_timer_enable(struct omap_dm_timer *timer) |
284 | { | |
285 | if (timer->enabled) | |
286 | return; | |
287 | ||
882c0518 TL |
288 | #ifdef CONFIG_ARCH_OMAP2PLUS |
289 | if (cpu_class_is_omap2()) { | |
290 | clk_enable(timer->fclk); | |
291 | clk_enable(timer->iclk); | |
292 | } | |
293 | #endif | |
12583a70 TT |
294 | |
295 | timer->enabled = 1; | |
296 | } | |
6c366e32 | 297 | EXPORT_SYMBOL_GPL(omap_dm_timer_enable); |
12583a70 TT |
298 | |
299 | void omap_dm_timer_disable(struct omap_dm_timer *timer) | |
300 | { | |
301 | if (!timer->enabled) | |
302 | return; | |
303 | ||
882c0518 TL |
304 | #ifdef CONFIG_ARCH_OMAP2PLUS |
305 | if (cpu_class_is_omap2()) { | |
306 | clk_disable(timer->iclk); | |
307 | clk_disable(timer->fclk); | |
308 | } | |
309 | #endif | |
12583a70 TT |
310 | |
311 | timer->enabled = 0; | |
312 | } | |
6c366e32 | 313 | EXPORT_SYMBOL_GPL(omap_dm_timer_disable); |
12583a70 | 314 | |
77900a2f TT |
315 | int omap_dm_timer_get_irq(struct omap_dm_timer *timer) |
316 | { | |
317 | return timer->irq; | |
318 | } | |
6c366e32 | 319 | EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq); |
77900a2f TT |
320 | |
321 | #if defined(CONFIG_ARCH_OMAP1) | |
322 | ||
a569c6ec TL |
323 | /** |
324 | * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR | |
325 | * @inputmask: current value of idlect mask | |
326 | */ | |
327 | __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) | |
328 | { | |
77900a2f | 329 | int i; |
a569c6ec TL |
330 | |
331 | /* If ARMXOR cannot be idled this function call is unnecessary */ | |
332 | if (!(inputmask & (1 << 1))) | |
333 | return inputmask; | |
334 | ||
335 | /* If any active timer is using ARMXOR return modified mask */ | |
77900a2f TT |
336 | for (i = 0; i < dm_timer_count; i++) { |
337 | u32 l; | |
338 | ||
35912c79 | 339 | l = omap_dm_timer_read_reg(&dm_timers[i], OMAP_TIMER_CTRL_REG); |
77900a2f TT |
340 | if (l & OMAP_TIMER_CTRL_ST) { |
341 | if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0) | |
a569c6ec TL |
342 | inputmask &= ~(1 << 1); |
343 | else | |
344 | inputmask &= ~(1 << 2); | |
345 | } | |
77900a2f | 346 | } |
a569c6ec TL |
347 | |
348 | return inputmask; | |
349 | } | |
6c366e32 | 350 | EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask); |
a569c6ec | 351 | |
140455fa | 352 | #else |
a569c6ec | 353 | |
77900a2f | 354 | struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) |
92105bb7 | 355 | { |
fa4bb626 | 356 | return timer->fclk; |
77900a2f | 357 | } |
6c366e32 | 358 | EXPORT_SYMBOL_GPL(omap_dm_timer_get_fclk); |
92105bb7 | 359 | |
77900a2f TT |
360 | __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) |
361 | { | |
362 | BUG(); | |
2121880e DB |
363 | |
364 | return 0; | |
92105bb7 | 365 | } |
6c366e32 | 366 | EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask); |
92105bb7 | 367 | |
77900a2f | 368 | #endif |
92105bb7 | 369 | |
77900a2f | 370 | void omap_dm_timer_trigger(struct omap_dm_timer *timer) |
92105bb7 | 371 | { |
77900a2f | 372 | omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); |
92105bb7 | 373 | } |
6c366e32 | 374 | EXPORT_SYMBOL_GPL(omap_dm_timer_trigger); |
92105bb7 | 375 | |
77900a2f TT |
376 | void omap_dm_timer_start(struct omap_dm_timer *timer) |
377 | { | |
378 | u32 l; | |
92105bb7 | 379 | |
77900a2f TT |
380 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
381 | if (!(l & OMAP_TIMER_CTRL_ST)) { | |
382 | l |= OMAP_TIMER_CTRL_ST; | |
383 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); | |
384 | } | |
385 | } | |
6c366e32 | 386 | EXPORT_SYMBOL_GPL(omap_dm_timer_start); |
92105bb7 | 387 | |
77900a2f | 388 | void omap_dm_timer_stop(struct omap_dm_timer *timer) |
92105bb7 | 389 | { |
caf64f2f | 390 | unsigned long rate = 0; |
92105bb7 | 391 | |
140455fa | 392 | #ifdef CONFIG_ARCH_OMAP2PLUS |
caf64f2f | 393 | rate = clk_get_rate(timer->fclk); |
5c3db36b | 394 | #endif |
caf64f2f | 395 | |
ee17f114 | 396 | __omap_dm_timer_stop(timer, timer->posted, rate); |
92105bb7 | 397 | } |
6c366e32 | 398 | EXPORT_SYMBOL_GPL(omap_dm_timer_stop); |
92105bb7 | 399 | |
f248076c | 400 | int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) |
92105bb7 | 401 | { |
77900a2f | 402 | if (source < 0 || source >= 3) |
f248076c | 403 | return -EINVAL; |
77900a2f | 404 | |
97933d6c | 405 | #ifdef CONFIG_ARCH_OMAP2PLUS |
caf64f2f TL |
406 | return __omap_dm_timer_set_source(timer->fclk, |
407 | dm_source_clocks[source]); | |
97933d6c TKD |
408 | #else |
409 | return 0; | |
410 | #endif | |
92105bb7 | 411 | } |
6c366e32 | 412 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_source); |
92105bb7 | 413 | |
77900a2f TT |
414 | void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, |
415 | unsigned int load) | |
92105bb7 TL |
416 | { |
417 | u32 l; | |
77900a2f | 418 | |
92105bb7 | 419 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
77900a2f TT |
420 | if (autoreload) |
421 | l |= OMAP_TIMER_CTRL_AR; | |
422 | else | |
423 | l &= ~OMAP_TIMER_CTRL_AR; | |
92105bb7 | 424 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
77900a2f | 425 | omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); |
0f0d0807 | 426 | |
77900a2f | 427 | omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); |
92105bb7 | 428 | } |
6c366e32 | 429 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_load); |
92105bb7 | 430 | |
3fddd09e RW |
431 | /* Optimized set_load which removes costly spin wait in timer_start */ |
432 | void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, | |
433 | unsigned int load) | |
434 | { | |
435 | u32 l; | |
436 | ||
437 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); | |
64ce2907 | 438 | if (autoreload) { |
3fddd09e | 439 | l |= OMAP_TIMER_CTRL_AR; |
64ce2907 PW |
440 | omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); |
441 | } else { | |
3fddd09e | 442 | l &= ~OMAP_TIMER_CTRL_AR; |
64ce2907 | 443 | } |
3fddd09e RW |
444 | l |= OMAP_TIMER_CTRL_ST; |
445 | ||
ee17f114 | 446 | __omap_dm_timer_load_start(timer, l, load, timer->posted); |
3fddd09e | 447 | } |
6c366e32 | 448 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start); |
3fddd09e | 449 | |
77900a2f TT |
450 | void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, |
451 | unsigned int match) | |
92105bb7 TL |
452 | { |
453 | u32 l; | |
454 | ||
455 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); | |
83379c81 | 456 | if (enable) |
77900a2f TT |
457 | l |= OMAP_TIMER_CTRL_CE; |
458 | else | |
459 | l &= ~OMAP_TIMER_CTRL_CE; | |
92105bb7 | 460 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
77900a2f | 461 | omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); |
92105bb7 | 462 | } |
6c366e32 | 463 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_match); |
92105bb7 | 464 | |
77900a2f TT |
465 | void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, |
466 | int toggle, int trigger) | |
92105bb7 TL |
467 | { |
468 | u32 l; | |
469 | ||
470 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); | |
77900a2f TT |
471 | l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM | |
472 | OMAP_TIMER_CTRL_PT | (0x03 << 10)); | |
473 | if (def_on) | |
474 | l |= OMAP_TIMER_CTRL_SCPWM; | |
475 | if (toggle) | |
476 | l |= OMAP_TIMER_CTRL_PT; | |
477 | l |= trigger << 10; | |
92105bb7 TL |
478 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
479 | } | |
6c366e32 | 480 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm); |
92105bb7 | 481 | |
77900a2f | 482 | void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler) |
92105bb7 TL |
483 | { |
484 | u32 l; | |
485 | ||
486 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); | |
77900a2f TT |
487 | l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2)); |
488 | if (prescaler >= 0x00 && prescaler <= 0x07) { | |
489 | l |= OMAP_TIMER_CTRL_PRE; | |
490 | l |= prescaler << 2; | |
491 | } | |
92105bb7 TL |
492 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
493 | } | |
6c366e32 | 494 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler); |
92105bb7 | 495 | |
77900a2f TT |
496 | void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, |
497 | unsigned int value) | |
92105bb7 | 498 | { |
ee17f114 | 499 | __omap_dm_timer_int_enable(timer, value); |
92105bb7 | 500 | } |
6c366e32 | 501 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable); |
92105bb7 | 502 | |
77900a2f | 503 | unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer) |
92105bb7 | 504 | { |
fa4bb626 TT |
505 | unsigned int l; |
506 | ||
ee17f114 | 507 | l = __raw_readl(timer->irq_stat); |
fa4bb626 TT |
508 | |
509 | return l; | |
92105bb7 | 510 | } |
6c366e32 | 511 | EXPORT_SYMBOL_GPL(omap_dm_timer_read_status); |
92105bb7 | 512 | |
77900a2f | 513 | void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) |
92105bb7 | 514 | { |
ee17f114 | 515 | __omap_dm_timer_write_status(timer, value); |
92105bb7 | 516 | } |
6c366e32 | 517 | EXPORT_SYMBOL_GPL(omap_dm_timer_write_status); |
92105bb7 | 518 | |
77900a2f | 519 | unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) |
92105bb7 | 520 | { |
ee17f114 | 521 | return __omap_dm_timer_read_counter(timer, timer->posted); |
92105bb7 | 522 | } |
6c366e32 | 523 | EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter); |
92105bb7 | 524 | |
83379c81 TT |
525 | void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value) |
526 | { | |
fa4bb626 | 527 | omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value); |
83379c81 | 528 | } |
6c366e32 | 529 | EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter); |
83379c81 | 530 | |
77900a2f | 531 | int omap_dm_timers_active(void) |
92105bb7 | 532 | { |
77900a2f | 533 | int i; |
92105bb7 | 534 | |
77900a2f TT |
535 | for (i = 0; i < dm_timer_count; i++) { |
536 | struct omap_dm_timer *timer; | |
92105bb7 | 537 | |
77900a2f | 538 | timer = &dm_timers[i]; |
12583a70 TT |
539 | |
540 | if (!timer->enabled) | |
541 | continue; | |
542 | ||
77900a2f | 543 | if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) & |
fa4bb626 | 544 | OMAP_TIMER_CTRL_ST) { |
77900a2f | 545 | return 1; |
fa4bb626 | 546 | } |
77900a2f TT |
547 | } |
548 | return 0; | |
549 | } | |
6c366e32 | 550 | EXPORT_SYMBOL_GPL(omap_dm_timers_active); |
92105bb7 | 551 | |
11a0186f | 552 | static int __init omap_dm_timer_init(void) |
92105bb7 TL |
553 | { |
554 | struct omap_dm_timer *timer; | |
3566fc63 | 555 | int i, map_size = SZ_8K; /* Module 4KB + L4 4KB except on omap1 */ |
77900a2f | 556 | |
97933d6c | 557 | if (!cpu_class_is_omap2()) |
77900a2f | 558 | return -ENODEV; |
92105bb7 TL |
559 | |
560 | spin_lock_init(&dm_timer_lock); | |
471b3aa7 | 561 | |
97933d6c | 562 | if (cpu_is_omap24xx()) { |
471b3aa7 | 563 | dm_timers = omap2_dm_timers; |
882c0518 | 564 | dm_timer_count = omap2_dm_timer_count; |
aea2a5b0 SS |
565 | dm_source_names = omap2_dm_source_names; |
566 | dm_source_clocks = omap2_dm_source_clocks; | |
ce2df9ca SMK |
567 | } else if (cpu_is_omap34xx()) { |
568 | dm_timers = omap3_dm_timers; | |
882c0518 | 569 | dm_timer_count = omap3_dm_timer_count; |
aea2a5b0 SS |
570 | dm_source_names = omap3_dm_source_names; |
571 | dm_source_clocks = omap3_dm_source_clocks; | |
44169075 SS |
572 | } else if (cpu_is_omap44xx()) { |
573 | dm_timers = omap4_dm_timers; | |
882c0518 | 574 | dm_timer_count = omap4_dm_timer_count; |
44169075 SS |
575 | dm_source_names = omap4_dm_source_names; |
576 | dm_source_clocks = omap4_dm_source_clocks; | |
ee17f114 TL |
577 | |
578 | pr_err("dmtimers disabled for omap4 until hwmod conversion\n"); | |
579 | return -ENODEV; | |
83379c81 | 580 | } |
471b3aa7 SMK |
581 | |
582 | if (cpu_class_is_omap2()) | |
583 | for (i = 0; dm_source_names[i] != NULL; i++) | |
584 | dm_source_clocks[i] = clk_get(NULL, dm_source_names[i]); | |
585 | ||
56a25641 SMK |
586 | if (cpu_is_omap243x()) |
587 | dm_timers[0].phys_base = 0x49018000; | |
83379c81 | 588 | |
77900a2f | 589 | for (i = 0; i < dm_timer_count; i++) { |
77900a2f | 590 | timer = &dm_timers[i]; |
3566fc63 TL |
591 | |
592 | /* Static mapping, never released */ | |
593 | timer->io_base = ioremap(timer->phys_base, map_size); | |
594 | BUG_ON(!timer->io_base); | |
595 | ||
140455fa | 596 | #ifdef CONFIG_ARCH_OMAP2PLUS |
471b3aa7 SMK |
597 | if (cpu_class_is_omap2()) { |
598 | char clk_name[16]; | |
599 | sprintf(clk_name, "gpt%d_ick", i + 1); | |
600 | timer->iclk = clk_get(NULL, clk_name); | |
601 | sprintf(clk_name, "gpt%d_fck", i + 1); | |
602 | timer->fclk = clk_get(NULL, clk_name); | |
603 | } | |
11a0186f TL |
604 | |
605 | /* One or two timers may be set up early for sys_timer */ | |
606 | if (sys_timer_reserved & (1 << i)) { | |
607 | timer->reserved = 1; | |
608 | timer->posted = 1; | |
ee17f114 | 609 | continue; |
11a0186f | 610 | } |
77900a2f | 611 | #endif |
ee17f114 TL |
612 | omap_dm_timer_enable(timer); |
613 | __omap_dm_timer_init_regs(timer); | |
614 | omap_dm_timer_disable(timer); | |
92105bb7 | 615 | } |
92105bb7 | 616 | |
92105bb7 TL |
617 | return 0; |
618 | } | |
11a0186f TL |
619 | |
620 | arch_initcall(omap_dm_timer_init); |