blob: 92d5aff9b2e93cc817e8a5f16c18ba17881565b6 [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 *
Tarun Kanti DebBarma97933d62011-09-20 17:00:17 +05306 * 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 *
Tony Lindgren92105bb2005-09-07 17:20:26 +010012 * Copyright (C) 2005 Nokia Corporation
Timo Teras77900a22006-06-26 16:16:12 -070013 * OMAP2 support by Juha Yrjola
14 * API improvements and OMAP2 clock framework support by Timo Teras
Tony Lindgren92105bb2005-09-07 17:20:26 +010015 *
Santosh Shilimkar44169072009-05-28 14:16:04 -070016 * Copyright (C) 2009 Texas Instruments
17 * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
18 *
Tony Lindgren92105bb2005-09-07 17:20:26 +010019 * 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
Russell Kingfced80c2008-09-06 12:10:45 +010038#include <linux/io.h>
Timo Kokkonen6c366e32009-03-23 18:07:46 -070039#include <linux/module.h>
Tarun Kanti DebBarmadf284722011-09-20 17:00:19 +053040#include <linux/slab.h>
Russell Kinga09e64f2008-08-05 16:14:15 +010041#include <mach/hardware.h>
Tony Lindgrence491cf2009-10-20 09:40:47 -070042#include <plat/dmtimer.h>
Russell Kinga09e64f2008-08-05 16:14:15 +010043#include <mach/irqs.h>
Tony Lindgren92105bb2005-09-07 17:20:26 +010044
Tony Lindgren882c0512010-02-12 12:26:46 -080045static int dm_timer_count;
46
Tony Lindgren882c0512010-02-12 12:26:46 -080047#ifdef CONFIG_ARCH_OMAP2
Syed Mohammed, Khasim471b3aa2007-06-21 21:48:07 -070048static struct omap_dm_timer omap2_dm_timers[] = {
Timo Teras77900a22006-06-26 16:16:12 -070049 { .phys_base = 0x48028000, .irq = INT_24XX_GPTIMER1 },
50 { .phys_base = 0x4802a000, .irq = INT_24XX_GPTIMER2 },
51 { .phys_base = 0x48078000, .irq = INT_24XX_GPTIMER3 },
52 { .phys_base = 0x4807a000, .irq = INT_24XX_GPTIMER4 },
53 { .phys_base = 0x4807c000, .irq = INT_24XX_GPTIMER5 },
54 { .phys_base = 0x4807e000, .irq = INT_24XX_GPTIMER6 },
55 { .phys_base = 0x48080000, .irq = INT_24XX_GPTIMER7 },
56 { .phys_base = 0x48082000, .irq = INT_24XX_GPTIMER8 },
57 { .phys_base = 0x48084000, .irq = INT_24XX_GPTIMER9 },
58 { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 },
59 { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 },
60 { .phys_base = 0x4808a000, .irq = INT_24XX_GPTIMER12 },
61};
62
Syed Mohammed, Khasim471b3aa2007-06-21 21:48:07 -070063static const char *omap2_dm_source_names[] __initdata = {
Timo Teras83379c82006-06-26 16:16:23 -070064 "sys_ck",
65 "func_32k_ck",
Syed Mohammed, Khasim471b3aa2007-06-21 21:48:07 -070066 "alt_ck",
67 NULL
Timo Teras83379c82006-06-26 16:16:23 -070068};
69
Santosh Shilimkaraea2a5b2009-05-25 11:08:36 -070070static struct clk *omap2_dm_source_clocks[3];
Tony Lindgren882c0512010-02-12 12:26:46 -080071static const int omap2_dm_timer_count = ARRAY_SIZE(omap2_dm_timers);
Timo Teras83379c82006-06-26 16:16:23 -070072
Tony Lindgren882c0512010-02-12 12:26:46 -080073#else
Syed Mohammed, Khasimce2df9c2007-06-25 22:55:39 -070074#define omap2_dm_timers NULL
Tony Lindgren882c0512010-02-12 12:26:46 -080075#define omap2_dm_timer_count 0
Syed Mohammed, Khasimce2df9c2007-06-25 22:55:39 -070076#define omap2_dm_source_names NULL
77#define omap2_dm_source_clocks NULL
Tony Lindgren882c0512010-02-12 12:26:46 -080078#endif /* CONFIG_ARCH_OMAP2 */
Syed Mohammed, Khasimce2df9c2007-06-25 22:55:39 -070079
Tony Lindgren882c0512010-02-12 12:26:46 -080080#ifdef CONFIG_ARCH_OMAP3
Syed Mohammed, Khasimce2df9c2007-06-25 22:55:39 -070081static struct omap_dm_timer omap3_dm_timers[] = {
82 { .phys_base = 0x48318000, .irq = INT_24XX_GPTIMER1 },
83 { .phys_base = 0x49032000, .irq = INT_24XX_GPTIMER2 },
84 { .phys_base = 0x49034000, .irq = INT_24XX_GPTIMER3 },
85 { .phys_base = 0x49036000, .irq = INT_24XX_GPTIMER4 },
86 { .phys_base = 0x49038000, .irq = INT_24XX_GPTIMER5 },
87 { .phys_base = 0x4903A000, .irq = INT_24XX_GPTIMER6 },
88 { .phys_base = 0x4903C000, .irq = INT_24XX_GPTIMER7 },
89 { .phys_base = 0x4903E000, .irq = INT_24XX_GPTIMER8 },
90 { .phys_base = 0x49040000, .irq = INT_24XX_GPTIMER9 },
91 { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 },
92 { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 },
Paul Walmsley9198a402009-04-23 21:11:08 -060093 { .phys_base = 0x48304000, .irq = INT_34XX_GPT12_IRQ },
Syed Mohammed, Khasimce2df9c2007-06-25 22:55:39 -070094};
95
96static const char *omap3_dm_source_names[] __initdata = {
97 "sys_ck",
98 "omap_32k_fck",
99 NULL
100};
101
Santosh Shilimkaraea2a5b2009-05-25 11:08:36 -0700102static struct clk *omap3_dm_source_clocks[2];
Tony Lindgren882c0512010-02-12 12:26:46 -0800103static const int omap3_dm_timer_count = ARRAY_SIZE(omap3_dm_timers);
Syed Mohammed, Khasimce2df9c2007-06-25 22:55:39 -0700104
Tony Lindgren882c0512010-02-12 12:26:46 -0800105#else
Santosh Shilimkar44169072009-05-28 14:16:04 -0700106#define omap3_dm_timers NULL
Tony Lindgren882c0512010-02-12 12:26:46 -0800107#define omap3_dm_timer_count 0
Santosh Shilimkar44169072009-05-28 14:16:04 -0700108#define omap3_dm_source_names NULL
109#define omap3_dm_source_clocks NULL
Tony Lindgren882c0512010-02-12 12:26:46 -0800110#endif /* CONFIG_ARCH_OMAP3 */
Santosh Shilimkar44169072009-05-28 14:16:04 -0700111
Tony Lindgren882c0512010-02-12 12:26:46 -0800112#ifdef CONFIG_ARCH_OMAP4
Santosh Shilimkar44169072009-05-28 14:16:04 -0700113static struct omap_dm_timer omap4_dm_timers[] = {
Santosh Shilimkar5772ca72010-02-18 03:14:12 +0530114 { .phys_base = 0x4a318000, .irq = OMAP44XX_IRQ_GPT1 },
115 { .phys_base = 0x48032000, .irq = OMAP44XX_IRQ_GPT2 },
116 { .phys_base = 0x48034000, .irq = OMAP44XX_IRQ_GPT3 },
117 { .phys_base = 0x48036000, .irq = OMAP44XX_IRQ_GPT4 },
118 { .phys_base = 0x40138000, .irq = OMAP44XX_IRQ_GPT5 },
119 { .phys_base = 0x4013a000, .irq = OMAP44XX_IRQ_GPT6 },
120 { .phys_base = 0x4013a000, .irq = OMAP44XX_IRQ_GPT7 },
121 { .phys_base = 0x4013e000, .irq = OMAP44XX_IRQ_GPT8 },
122 { .phys_base = 0x4803e000, .irq = OMAP44XX_IRQ_GPT9 },
123 { .phys_base = 0x48086000, .irq = OMAP44XX_IRQ_GPT10 },
124 { .phys_base = 0x48088000, .irq = OMAP44XX_IRQ_GPT11 },
125 { .phys_base = 0x4a320000, .irq = OMAP44XX_IRQ_GPT12 },
Santosh Shilimkar44169072009-05-28 14:16:04 -0700126};
127static const char *omap4_dm_source_names[] __initdata = {
Rajendra Nayak1dc993b2010-05-18 20:24:00 -0600128 "sys_clkin_ck",
129 "sys_32k_ck",
Santosh Shilimkar44169072009-05-28 14:16:04 -0700130 NULL
131};
132static struct clk *omap4_dm_source_clocks[2];
Tony Lindgren882c0512010-02-12 12:26:46 -0800133static const int omap4_dm_timer_count = ARRAY_SIZE(omap4_dm_timers);
Santosh Shilimkar44169072009-05-28 14:16:04 -0700134
Timo Teras77900a22006-06-26 16:16:12 -0700135#else
Tony Lindgren882c0512010-02-12 12:26:46 -0800136#define omap4_dm_timers NULL
137#define omap4_dm_timer_count 0
138#define omap4_dm_source_names NULL
139#define omap4_dm_source_clocks NULL
140#endif /* CONFIG_ARCH_OMAP4 */
Timo Teras77900a22006-06-26 16:16:12 -0700141
Syed Mohammed, Khasim471b3aa2007-06-21 21:48:07 -0700142static struct omap_dm_timer *dm_timers;
Santosh Shilimkaraea2a5b2009-05-25 11:08:36 -0700143static const char **dm_source_names;
Syed Mohammed, Khasim471b3aa2007-06-21 21:48:07 -0700144static struct clk **dm_source_clocks;
145
Tony Lindgren92105bb2005-09-07 17:20:26 +0100146static spinlock_t dm_timer_lock;
Tarun Kanti DebBarmadf284722011-09-20 17:00:19 +0530147static LIST_HEAD(omap_timer_list);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100148
Richard Woodruff0f0d0802008-07-03 12:24:30 +0300149/*
150 * Reads timer registers in posted and non-posted mode. The posted mode bit
151 * is encoded in reg. Note that in posted mode write pending bit must be
152 * checked. Otherwise a read of a non completed write will produce an error.
153 */
154static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100155{
Tony Lindgrenee17f112011-09-16 15:44:20 -0700156 WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
157 return __omap_dm_timer_read(timer, reg, timer->posted);
Timo Teras77900a22006-06-26 16:16:12 -0700158}
159
Richard Woodruff0f0d0802008-07-03 12:24:30 +0300160/*
161 * Writes timer registers in posted and non-posted mode. The posted mode bit
162 * is encoded in reg. Note that in posted mode the write pending bit must be
163 * checked. Otherwise a write on a register which has a pending write will be
164 * lost.
165 */
166static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
167 u32 value)
Timo Teras77900a22006-06-26 16:16:12 -0700168{
Tony Lindgrenee17f112011-09-16 15:44:20 -0700169 WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
170 __omap_dm_timer_write(timer, reg, value, timer->posted);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100171}
172
Timo Teras77900a22006-06-26 16:16:12 -0700173static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100174{
Timo Teras77900a22006-06-26 16:16:12 -0700175 int c;
176
Tony Lindgrenee17f112011-09-16 15:44:20 -0700177 if (!timer->sys_stat)
178 return;
179
Timo Teras77900a22006-06-26 16:16:12 -0700180 c = 0;
Tony Lindgrenee17f112011-09-16 15:44:20 -0700181 while (!(__raw_readl(timer->sys_stat) & 1)) {
Timo Teras77900a22006-06-26 16:16:12 -0700182 c++;
183 if (c > 100000) {
184 printk(KERN_ERR "Timer failed to reset\n");
185 return;
186 }
187 }
Tony Lindgren92105bb2005-09-07 17:20:26 +0100188}
189
Timo Teras77900a22006-06-26 16:16:12 -0700190static void omap_dm_timer_reset(struct omap_dm_timer *timer)
191{
Tony Lindgrencaf64f22011-03-29 15:54:48 -0700192 int autoidle = 0, wakeup = 0;
Timo Teras77900a22006-06-26 16:16:12 -0700193
Juha Yrjola39020842006-09-25 12:41:44 +0300194 if (!cpu_class_is_omap2() || timer != &dm_timers[0]) {
Timo Terase32f7ec2006-06-26 16:16:13 -0700195 omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
196 omap_dm_timer_wait_for_reset(timer);
197 }
Timo Teras12583a72006-09-25 12:41:42 +0300198 omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
Timo Teras77900a22006-06-26 16:16:12 -0700199
Ambresh Kba503482011-06-15 21:12:35 +0000200 /* Enable autoidle on OMAP2+ */
201 if (cpu_class_is_omap2())
Tony Lindgrencaf64f22011-03-29 15:54:48 -0700202 autoidle = 1;
Tero Kristo4ce1e5e2011-03-10 03:50:54 -0700203
Richard Woodruff0f0d0802008-07-03 12:24:30 +0300204 /*
Kevin Hilman219c5b92009-04-23 21:11:08 -0600205 * Enable wake-up on OMAP2 CPUs.
Richard Woodruff0f0d0802008-07-03 12:24:30 +0300206 */
Kevin Hilman219c5b92009-04-23 21:11:08 -0600207 if (cpu_class_is_omap2())
Tony Lindgrencaf64f22011-03-29 15:54:48 -0700208 wakeup = 1;
Richard Woodruff0f0d0802008-07-03 12:24:30 +0300209
Tony Lindgrenee17f112011-09-16 15:44:20 -0700210 __omap_dm_timer_reset(timer, autoidle, wakeup);
Richard Woodruff0f0d0802008-07-03 12:24:30 +0300211 timer->posted = 1;
Timo Teras77900a22006-06-26 16:16:12 -0700212}
213
Tony Lindgrencaf64f22011-03-29 15:54:48 -0700214void omap_dm_timer_prepare(struct omap_dm_timer *timer)
Timo Teras77900a22006-06-26 16:16:12 -0700215{
Timo Teras12583a72006-09-25 12:41:42 +0300216 omap_dm_timer_enable(timer);
Timo Teras77900a22006-06-26 16:16:12 -0700217 omap_dm_timer_reset(timer);
218}
219
220struct omap_dm_timer *omap_dm_timer_request(void)
221{
222 struct omap_dm_timer *timer = NULL;
223 unsigned long flags;
224 int i;
225
226 spin_lock_irqsave(&dm_timer_lock, flags);
227 for (i = 0; i < dm_timer_count; i++) {
228 if (dm_timers[i].reserved)
229 continue;
230
231 timer = &dm_timers[i];
Timo Teras83379c82006-06-26 16:16:23 -0700232 timer->reserved = 1;
Timo Teras77900a22006-06-26 16:16:12 -0700233 break;
234 }
235 spin_unlock_irqrestore(&dm_timer_lock, flags);
236
Timo Teras83379c82006-06-26 16:16:23 -0700237 if (timer != NULL)
238 omap_dm_timer_prepare(timer);
239
Timo Teras77900a22006-06-26 16:16:12 -0700240 return timer;
241}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700242EXPORT_SYMBOL_GPL(omap_dm_timer_request);
Timo Teras77900a22006-06-26 16:16:12 -0700243
244struct omap_dm_timer *omap_dm_timer_request_specific(int id)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100245{
246 struct omap_dm_timer *timer;
Timo Teras77900a22006-06-26 16:16:12 -0700247 unsigned long flags;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100248
Timo Teras77900a22006-06-26 16:16:12 -0700249 spin_lock_irqsave(&dm_timer_lock, flags);
250 if (id <= 0 || id > dm_timer_count || dm_timers[id-1].reserved) {
251 spin_unlock_irqrestore(&dm_timer_lock, flags);
252 printk("BUG: warning at %s:%d/%s(): unable to get timer %d\n",
Harvey Harrison8e86f422008-03-04 15:08:02 -0800253 __FILE__, __LINE__, __func__, id);
Timo Teras77900a22006-06-26 16:16:12 -0700254 dump_stack();
255 return NULL;
256 }
Tony Lindgren92105bb2005-09-07 17:20:26 +0100257
Timo Teras77900a22006-06-26 16:16:12 -0700258 timer = &dm_timers[id-1];
Timo Teras83379c82006-06-26 16:16:23 -0700259 timer->reserved = 1;
Timo Teras77900a22006-06-26 16:16:12 -0700260 spin_unlock_irqrestore(&dm_timer_lock, flags);
261
Timo Teras83379c82006-06-26 16:16:23 -0700262 omap_dm_timer_prepare(timer);
263
Timo Teras77900a22006-06-26 16:16:12 -0700264 return timer;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100265}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700266EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100267
Timo Teras77900a22006-06-26 16:16:12 -0700268void omap_dm_timer_free(struct omap_dm_timer *timer)
269{
Timo Teras12583a72006-09-25 12:41:42 +0300270 omap_dm_timer_enable(timer);
Timo Teras77900a22006-06-26 16:16:12 -0700271 omap_dm_timer_reset(timer);
Timo Teras12583a72006-09-25 12:41:42 +0300272 omap_dm_timer_disable(timer);
Timo Terasfa4bb622006-09-25 12:41:35 +0300273
Timo Teras77900a22006-06-26 16:16:12 -0700274 WARN_ON(!timer->reserved);
275 timer->reserved = 0;
276}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700277EXPORT_SYMBOL_GPL(omap_dm_timer_free);
Timo Teras77900a22006-06-26 16:16:12 -0700278
Timo Teras12583a72006-09-25 12:41:42 +0300279void omap_dm_timer_enable(struct omap_dm_timer *timer)
280{
281 if (timer->enabled)
282 return;
283
Tony Lindgren882c0512010-02-12 12:26:46 -0800284#ifdef CONFIG_ARCH_OMAP2PLUS
285 if (cpu_class_is_omap2()) {
286 clk_enable(timer->fclk);
287 clk_enable(timer->iclk);
288 }
289#endif
Timo Teras12583a72006-09-25 12:41:42 +0300290
291 timer->enabled = 1;
292}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700293EXPORT_SYMBOL_GPL(omap_dm_timer_enable);
Timo Teras12583a72006-09-25 12:41:42 +0300294
295void omap_dm_timer_disable(struct omap_dm_timer *timer)
296{
297 if (!timer->enabled)
298 return;
299
Tony Lindgren882c0512010-02-12 12:26:46 -0800300#ifdef CONFIG_ARCH_OMAP2PLUS
301 if (cpu_class_is_omap2()) {
302 clk_disable(timer->iclk);
303 clk_disable(timer->fclk);
304 }
305#endif
Timo Teras12583a72006-09-25 12:41:42 +0300306
307 timer->enabled = 0;
308}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700309EXPORT_SYMBOL_GPL(omap_dm_timer_disable);
Timo Teras12583a72006-09-25 12:41:42 +0300310
Timo Teras77900a22006-06-26 16:16:12 -0700311int omap_dm_timer_get_irq(struct omap_dm_timer *timer)
312{
313 return timer->irq;
314}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700315EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq);
Timo Teras77900a22006-06-26 16:16:12 -0700316
317#if defined(CONFIG_ARCH_OMAP1)
318
Tony Lindgrena569c6e2006-04-02 17:46:21 +0100319/**
320 * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR
321 * @inputmask: current value of idlect mask
322 */
323__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
324{
Timo Teras77900a22006-06-26 16:16:12 -0700325 int i;
Tony Lindgrena569c6e2006-04-02 17:46:21 +0100326
327 /* If ARMXOR cannot be idled this function call is unnecessary */
328 if (!(inputmask & (1 << 1)))
329 return inputmask;
330
331 /* If any active timer is using ARMXOR return modified mask */
Timo Teras77900a22006-06-26 16:16:12 -0700332 for (i = 0; i < dm_timer_count; i++) {
333 u32 l;
334
Tony Lindgren35912c72006-07-01 19:56:42 +0100335 l = omap_dm_timer_read_reg(&dm_timers[i], OMAP_TIMER_CTRL_REG);
Timo Teras77900a22006-06-26 16:16:12 -0700336 if (l & OMAP_TIMER_CTRL_ST) {
337 if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0)
Tony Lindgrena569c6e2006-04-02 17:46:21 +0100338 inputmask &= ~(1 << 1);
339 else
340 inputmask &= ~(1 << 2);
341 }
Timo Teras77900a22006-06-26 16:16:12 -0700342 }
Tony Lindgrena569c6e2006-04-02 17:46:21 +0100343
344 return inputmask;
345}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700346EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
Tony Lindgrena569c6e2006-04-02 17:46:21 +0100347
Tony Lindgren140455f2010-02-12 12:26:48 -0800348#else
Timo Teras77900a22006-06-26 16:16:12 -0700349
350struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
351{
Timo Terasfa4bb622006-09-25 12:41:35 +0300352 return timer->fclk;
Timo Teras77900a22006-06-26 16:16:12 -0700353}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700354EXPORT_SYMBOL_GPL(omap_dm_timer_get_fclk);
Timo Teras77900a22006-06-26 16:16:12 -0700355
356__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
357{
358 BUG();
Dirk Behme21218802006-12-06 17:14:00 -0800359
360 return 0;
Timo Teras77900a22006-06-26 16:16:12 -0700361}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700362EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
Timo Teras77900a22006-06-26 16:16:12 -0700363
364#endif
365
366void omap_dm_timer_trigger(struct omap_dm_timer *timer)
367{
368 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
369}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700370EXPORT_SYMBOL_GPL(omap_dm_timer_trigger);
Timo Teras77900a22006-06-26 16:16:12 -0700371
372void omap_dm_timer_start(struct omap_dm_timer *timer)
373{
374 u32 l;
375
376 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
377 if (!(l & OMAP_TIMER_CTRL_ST)) {
378 l |= OMAP_TIMER_CTRL_ST;
379 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
380 }
381}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700382EXPORT_SYMBOL_GPL(omap_dm_timer_start);
Timo Teras77900a22006-06-26 16:16:12 -0700383
384void omap_dm_timer_stop(struct omap_dm_timer *timer)
385{
Tony Lindgrencaf64f22011-03-29 15:54:48 -0700386 unsigned long rate = 0;
Timo Teras77900a22006-06-26 16:16:12 -0700387
Tony Lindgren140455f2010-02-12 12:26:48 -0800388#ifdef CONFIG_ARCH_OMAP2PLUS
Tony Lindgrencaf64f22011-03-29 15:54:48 -0700389 rate = clk_get_rate(timer->fclk);
Tero Kristo5c3db362009-10-23 19:03:47 +0300390#endif
Tony Lindgrencaf64f22011-03-29 15:54:48 -0700391
Tony Lindgrenee17f112011-09-16 15:44:20 -0700392 __omap_dm_timer_stop(timer, timer->posted, rate);
Timo Teras77900a22006-06-26 16:16:12 -0700393}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700394EXPORT_SYMBOL_GPL(omap_dm_timer_stop);
Timo Teras77900a22006-06-26 16:16:12 -0700395
Paul Walmsleyf2480762009-04-23 21:11:10 -0600396int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100397{
Timo Teras77900a22006-06-26 16:16:12 -0700398 if (source < 0 || source >= 3)
Paul Walmsleyf2480762009-04-23 21:11:10 -0600399 return -EINVAL;
Timo Teras77900a22006-06-26 16:16:12 -0700400
Tarun Kanti DebBarma97933d62011-09-20 17:00:17 +0530401#ifdef CONFIG_ARCH_OMAP2PLUS
Tony Lindgrencaf64f22011-03-29 15:54:48 -0700402 return __omap_dm_timer_set_source(timer->fclk,
403 dm_source_clocks[source]);
Tarun Kanti DebBarma97933d62011-09-20 17:00:17 +0530404#else
405 return 0;
406#endif
Timo Teras77900a22006-06-26 16:16:12 -0700407}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700408EXPORT_SYMBOL_GPL(omap_dm_timer_set_source);
Timo Teras77900a22006-06-26 16:16:12 -0700409
Timo Teras77900a22006-06-26 16:16:12 -0700410void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
411 unsigned int load)
412{
413 u32 l;
414
415 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
416 if (autoreload)
417 l |= OMAP_TIMER_CTRL_AR;
418 else
419 l &= ~OMAP_TIMER_CTRL_AR;
420 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
421 omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
Richard Woodruff0f0d0802008-07-03 12:24:30 +0300422
Timo Teras77900a22006-06-26 16:16:12 -0700423 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
424}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700425EXPORT_SYMBOL_GPL(omap_dm_timer_set_load);
Timo Teras77900a22006-06-26 16:16:12 -0700426
Richard Woodruff3fddd092008-07-03 12:24:30 +0300427/* Optimized set_load which removes costly spin wait in timer_start */
428void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
429 unsigned int load)
430{
431 u32 l;
432
433 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
Paul Walmsley64ce2902008-12-10 17:36:34 -0800434 if (autoreload) {
Richard Woodruff3fddd092008-07-03 12:24:30 +0300435 l |= OMAP_TIMER_CTRL_AR;
Paul Walmsley64ce2902008-12-10 17:36:34 -0800436 omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
437 } else {
Richard Woodruff3fddd092008-07-03 12:24:30 +0300438 l &= ~OMAP_TIMER_CTRL_AR;
Paul Walmsley64ce2902008-12-10 17:36:34 -0800439 }
Richard Woodruff3fddd092008-07-03 12:24:30 +0300440 l |= OMAP_TIMER_CTRL_ST;
441
Tony Lindgrenee17f112011-09-16 15:44:20 -0700442 __omap_dm_timer_load_start(timer, l, load, timer->posted);
Richard Woodruff3fddd092008-07-03 12:24:30 +0300443}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700444EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start);
Richard Woodruff3fddd092008-07-03 12:24:30 +0300445
Timo Teras77900a22006-06-26 16:16:12 -0700446void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
447 unsigned int match)
448{
449 u32 l;
450
451 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
Timo Teras83379c82006-06-26 16:16:23 -0700452 if (enable)
Timo Teras77900a22006-06-26 16:16:12 -0700453 l |= OMAP_TIMER_CTRL_CE;
454 else
455 l &= ~OMAP_TIMER_CTRL_CE;
456 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
457 omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100458}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700459EXPORT_SYMBOL_GPL(omap_dm_timer_set_match);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100460
Timo Teras77900a22006-06-26 16:16:12 -0700461void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
462 int toggle, int trigger)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100463{
Timo Teras77900a22006-06-26 16:16:12 -0700464 u32 l;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100465
Timo Teras77900a22006-06-26 16:16:12 -0700466 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
467 l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |
468 OMAP_TIMER_CTRL_PT | (0x03 << 10));
469 if (def_on)
470 l |= OMAP_TIMER_CTRL_SCPWM;
471 if (toggle)
472 l |= OMAP_TIMER_CTRL_PT;
473 l |= trigger << 10;
474 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
475}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700476EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm);
Timo Teras77900a22006-06-26 16:16:12 -0700477
478void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
479{
480 u32 l;
481
482 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
483 l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));
484 if (prescaler >= 0x00 && prescaler <= 0x07) {
485 l |= OMAP_TIMER_CTRL_PRE;
486 l |= prescaler << 2;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100487 }
Timo Teras77900a22006-06-26 16:16:12 -0700488 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100489}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700490EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100491
492void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
Timo Teras77900a22006-06-26 16:16:12 -0700493 unsigned int value)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100494{
Tony Lindgrenee17f112011-09-16 15:44:20 -0700495 __omap_dm_timer_int_enable(timer, value);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100496}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700497EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100498
499unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
500{
Timo Terasfa4bb622006-09-25 12:41:35 +0300501 unsigned int l;
502
Tony Lindgrenee17f112011-09-16 15:44:20 -0700503 l = __raw_readl(timer->irq_stat);
Timo Terasfa4bb622006-09-25 12:41:35 +0300504
505 return l;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100506}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700507EXPORT_SYMBOL_GPL(omap_dm_timer_read_status);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100508
509void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
510{
Tony Lindgrenee17f112011-09-16 15:44:20 -0700511 __omap_dm_timer_write_status(timer, value);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100512}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700513EXPORT_SYMBOL_GPL(omap_dm_timer_write_status);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100514
Tony Lindgren92105bb2005-09-07 17:20:26 +0100515unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
516{
Tony Lindgrenee17f112011-09-16 15:44:20 -0700517 return __omap_dm_timer_read_counter(timer, timer->posted);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100518}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700519EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100520
Timo Teras83379c82006-06-26 16:16:23 -0700521void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
522{
Timo Terasfa4bb622006-09-25 12:41:35 +0300523 omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
Timo Teras83379c82006-06-26 16:16:23 -0700524}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700525EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter);
Timo Teras83379c82006-06-26 16:16:23 -0700526
Timo Teras77900a22006-06-26 16:16:12 -0700527int omap_dm_timers_active(void)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100528{
Timo Teras77900a22006-06-26 16:16:12 -0700529 int i;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100530
Timo Teras77900a22006-06-26 16:16:12 -0700531 for (i = 0; i < dm_timer_count; i++) {
532 struct omap_dm_timer *timer;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100533
Timo Teras77900a22006-06-26 16:16:12 -0700534 timer = &dm_timers[i];
Timo Teras12583a72006-09-25 12:41:42 +0300535
536 if (!timer->enabled)
537 continue;
538
Timo Teras77900a22006-06-26 16:16:12 -0700539 if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
Timo Terasfa4bb622006-09-25 12:41:35 +0300540 OMAP_TIMER_CTRL_ST) {
Timo Teras77900a22006-06-26 16:16:12 -0700541 return 1;
Timo Terasfa4bb622006-09-25 12:41:35 +0300542 }
Tony Lindgren92105bb2005-09-07 17:20:26 +0100543 }
Tony Lindgren92105bb2005-09-07 17:20:26 +0100544 return 0;
545}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700546EXPORT_SYMBOL_GPL(omap_dm_timers_active);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100547
Tarun Kanti DebBarmadf284722011-09-20 17:00:19 +0530548/**
549 * omap_dm_timer_probe - probe function called for every registered device
550 * @pdev: pointer to current timer platform device
551 *
552 * Called by driver framework at the end of device registration for all
553 * timer devices.
554 */
555static int __devinit omap_dm_timer_probe(struct platform_device *pdev)
556{
557 int ret;
558 unsigned long flags;
559 struct omap_dm_timer *timer;
560 struct resource *mem, *irq, *ioarea;
561 struct dmtimer_platform_data *pdata = pdev->dev.platform_data;
562
563 if (!pdata) {
564 dev_err(&pdev->dev, "%s: no platform data.\n", __func__);
565 return -ENODEV;
566 }
567
568 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
569 if (unlikely(!irq)) {
570 dev_err(&pdev->dev, "%s: no IRQ resource.\n", __func__);
571 return -ENODEV;
572 }
573
574 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
575 if (unlikely(!mem)) {
576 dev_err(&pdev->dev, "%s: no memory resource.\n", __func__);
577 return -ENODEV;
578 }
579
580 ioarea = request_mem_region(mem->start, resource_size(mem),
581 pdev->name);
582 if (!ioarea) {
583 dev_err(&pdev->dev, "%s: region already claimed.\n", __func__);
584 return -EBUSY;
585 }
586
587 timer = kzalloc(sizeof(struct omap_dm_timer), GFP_KERNEL);
588 if (!timer) {
589 dev_err(&pdev->dev, "%s: no memory for omap_dm_timer.\n",
590 __func__);
591 ret = -ENOMEM;
592 goto err_free_ioregion;
593 }
594
595 timer->io_base = ioremap(mem->start, resource_size(mem));
596 if (!timer->io_base) {
597 dev_err(&pdev->dev, "%s: ioremap failed.\n", __func__);
598 ret = -ENOMEM;
599 goto err_free_mem;
600 }
601
602 timer->id = pdev->id;
603 timer->irq = irq->start;
604 timer->pdev = pdev;
605 __omap_dm_timer_init_regs(timer);
606
607 /* add the timer element to the list */
608 spin_lock_irqsave(&dm_timer_lock, flags);
609 list_add_tail(&timer->node, &omap_timer_list);
610 spin_unlock_irqrestore(&dm_timer_lock, flags);
611
612 dev_dbg(&pdev->dev, "Device Probed.\n");
613
614 return 0;
615
616err_free_mem:
617 kfree(timer);
618
619err_free_ioregion:
620 release_mem_region(mem->start, resource_size(mem));
621
622 return ret;
623}
624
625/**
626 * omap_dm_timer_remove - cleanup a registered timer device
627 * @pdev: pointer to current timer platform device
628 *
629 * Called by driver framework whenever a timer device is unregistered.
630 * In addition to freeing platform resources it also deletes the timer
631 * entry from the local list.
632 */
633static int __devexit omap_dm_timer_remove(struct platform_device *pdev)
634{
635 struct omap_dm_timer *timer;
636 unsigned long flags;
637 int ret = -EINVAL;
638
639 spin_lock_irqsave(&dm_timer_lock, flags);
640 list_for_each_entry(timer, &omap_timer_list, node)
641 if (timer->pdev->id == pdev->id) {
642 list_del(&timer->node);
643 kfree(timer);
644 ret = 0;
645 break;
646 }
647 spin_unlock_irqrestore(&dm_timer_lock, flags);
648
649 return ret;
650}
651
652static struct platform_driver omap_dm_timer_driver = {
653 .probe = omap_dm_timer_probe,
654 .remove = omap_dm_timer_remove,
655 .driver = {
656 .name = "omap_timer",
657 },
658};
659
660static int __init omap_dm_timer_driver_init(void)
661{
662 return platform_driver_register(&omap_dm_timer_driver);
663}
664
665static void __exit omap_dm_timer_driver_exit(void)
666{
667 platform_driver_unregister(&omap_dm_timer_driver);
668}
669
670early_platform_init("earlytimer", &omap_dm_timer_driver);
671module_init(omap_dm_timer_driver_init);
672module_exit(omap_dm_timer_driver_exit);
673
674MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver");
675MODULE_LICENSE("GPL");
676MODULE_ALIAS("platform:" DRIVER_NAME);
677MODULE_AUTHOR("Texas Instruments Inc");
678
Tony Lindgren11a01862011-03-29 15:54:49 -0700679static int __init omap_dm_timer_init(void)
Timo Teras77900a22006-06-26 16:16:12 -0700680{
681 struct omap_dm_timer *timer;
Tony Lindgren3566fc62009-10-19 15:25:18 -0700682 int i, map_size = SZ_8K; /* Module 4KB + L4 4KB except on omap1 */
Timo Teras77900a22006-06-26 16:16:12 -0700683
Tarun Kanti DebBarma97933d62011-09-20 17:00:17 +0530684 if (!cpu_class_is_omap2())
Timo Teras77900a22006-06-26 16:16:12 -0700685 return -ENODEV;
686
687 spin_lock_init(&dm_timer_lock);
Syed Mohammed, Khasim471b3aa2007-06-21 21:48:07 -0700688
Tarun Kanti DebBarma97933d62011-09-20 17:00:17 +0530689 if (cpu_is_omap24xx()) {
Syed Mohammed, Khasim471b3aa2007-06-21 21:48:07 -0700690 dm_timers = omap2_dm_timers;
Tony Lindgren882c0512010-02-12 12:26:46 -0800691 dm_timer_count = omap2_dm_timer_count;
Santosh Shilimkaraea2a5b2009-05-25 11:08:36 -0700692 dm_source_names = omap2_dm_source_names;
693 dm_source_clocks = omap2_dm_source_clocks;
Syed Mohammed, Khasimce2df9c2007-06-25 22:55:39 -0700694 } else if (cpu_is_omap34xx()) {
695 dm_timers = omap3_dm_timers;
Tony Lindgren882c0512010-02-12 12:26:46 -0800696 dm_timer_count = omap3_dm_timer_count;
Santosh Shilimkaraea2a5b2009-05-25 11:08:36 -0700697 dm_source_names = omap3_dm_source_names;
698 dm_source_clocks = omap3_dm_source_clocks;
Santosh Shilimkar44169072009-05-28 14:16:04 -0700699 } else if (cpu_is_omap44xx()) {
700 dm_timers = omap4_dm_timers;
Tony Lindgren882c0512010-02-12 12:26:46 -0800701 dm_timer_count = omap4_dm_timer_count;
Santosh Shilimkar44169072009-05-28 14:16:04 -0700702 dm_source_names = omap4_dm_source_names;
703 dm_source_clocks = omap4_dm_source_clocks;
Tony Lindgrenee17f112011-09-16 15:44:20 -0700704
705 pr_err("dmtimers disabled for omap4 until hwmod conversion\n");
706 return -ENODEV;
Timo Teras83379c82006-06-26 16:16:23 -0700707 }
Syed Mohammed, Khasim471b3aa2007-06-21 21:48:07 -0700708
709 if (cpu_class_is_omap2())
710 for (i = 0; dm_source_names[i] != NULL; i++)
711 dm_source_clocks[i] = clk_get(NULL, dm_source_names[i]);
712
Syed Mohammed Khasim56a25642006-12-06 17:14:08 -0800713 if (cpu_is_omap243x())
714 dm_timers[0].phys_base = 0x49018000;
Timo Teras83379c82006-06-26 16:16:23 -0700715
Timo Teras77900a22006-06-26 16:16:12 -0700716 for (i = 0; i < dm_timer_count; i++) {
Timo Teras77900a22006-06-26 16:16:12 -0700717 timer = &dm_timers[i];
Tony Lindgren3566fc62009-10-19 15:25:18 -0700718
719 /* Static mapping, never released */
720 timer->io_base = ioremap(timer->phys_base, map_size);
721 BUG_ON(!timer->io_base);
722
Tony Lindgren140455f2010-02-12 12:26:48 -0800723#ifdef CONFIG_ARCH_OMAP2PLUS
Syed Mohammed, Khasim471b3aa2007-06-21 21:48:07 -0700724 if (cpu_class_is_omap2()) {
725 char clk_name[16];
726 sprintf(clk_name, "gpt%d_ick", i + 1);
727 timer->iclk = clk_get(NULL, clk_name);
728 sprintf(clk_name, "gpt%d_fck", i + 1);
729 timer->fclk = clk_get(NULL, clk_name);
730 }
Tony Lindgren11a01862011-03-29 15:54:49 -0700731
732 /* One or two timers may be set up early for sys_timer */
733 if (sys_timer_reserved & (1 << i)) {
734 timer->reserved = 1;
735 timer->posted = 1;
Tony Lindgrenee17f112011-09-16 15:44:20 -0700736 continue;
Tony Lindgren11a01862011-03-29 15:54:49 -0700737 }
Timo Teras77900a22006-06-26 16:16:12 -0700738#endif
Tony Lindgrenee17f112011-09-16 15:44:20 -0700739 omap_dm_timer_enable(timer);
740 __omap_dm_timer_init_regs(timer);
741 omap_dm_timer_disable(timer);
Timo Teras77900a22006-06-26 16:16:12 -0700742 }
743
744 return 0;
745}
Tony Lindgren11a01862011-03-29 15:54:49 -0700746
747arch_initcall(omap_dm_timer_init);