aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-omap/dmtimer.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-omap/dmtimer.c')
-rw-r--r--arch/arm/plat-omap/dmtimer.c713
1 files changed, 411 insertions, 302 deletions
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index 75a847dd776a..2def4e1990ed 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -3,6 +3,12 @@
3 * 3 *
4 * OMAP Dual-Mode Timers 4 * OMAP Dual-Mode Timers
5 * 5 *
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 *
6 * Copyright (C) 2005 Nokia Corporation 12 * Copyright (C) 2005 Nokia Corporation
7 * OMAP2 support by Juha Yrjola 13 * OMAP2 support by Juha Yrjola
8 * API improvements and OMAP2 clock framework support by Timo Teras 14 * API improvements and OMAP2 clock framework support by Timo Teras
@@ -29,168 +35,80 @@
29 * 675 Mass Ave, Cambridge, MA 02139, USA. 35 * 675 Mass Ave, Cambridge, MA 02139, USA.
30 */ 36 */
31 37
32#include <linux/init.h>
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>
38#include <linux/io.h> 38#include <linux/io.h>
39#include <linux/module.h> 39#include <linux/slab.h>
40#include <mach/hardware.h> 40#include <linux/err.h>
41#include <plat/dmtimer.h> 41#include <linux/pm_runtime.h>
42#include <mach/irqs.h>
43
44static int dm_timer_count;
45
46#ifdef CONFIG_ARCH_OMAP1
47static struct omap_dm_timer omap1_dm_timers[] = {
48 { .phys_base = 0xfffb1400, .irq = INT_1610_GPTIMER1 },
49 { .phys_base = 0xfffb1c00, .irq = INT_1610_GPTIMER2 },
50 { .phys_base = 0xfffb2400, .irq = INT_1610_GPTIMER3 },
51 { .phys_base = 0xfffb2c00, .irq = INT_1610_GPTIMER4 },
52 { .phys_base = 0xfffb3400, .irq = INT_1610_GPTIMER5 },
53 { .phys_base = 0xfffb3c00, .irq = INT_1610_GPTIMER6 },
54 { .phys_base = 0xfffb7400, .irq = INT_1610_GPTIMER7 },
55 { .phys_base = 0xfffbd400, .irq = INT_1610_GPTIMER8 },
56};
57
58static const int omap1_dm_timer_count = ARRAY_SIZE(omap1_dm_timers);
59
60#else
61#define omap1_dm_timers NULL
62#define omap1_dm_timer_count 0
63#endif /* CONFIG_ARCH_OMAP1 */
64
65#ifdef CONFIG_ARCH_OMAP2
66static struct omap_dm_timer omap2_dm_timers[] = {
67 { .phys_base = 0x48028000, .irq = INT_24XX_GPTIMER1 },
68 { .phys_base = 0x4802a000, .irq = INT_24XX_GPTIMER2 },
69 { .phys_base = 0x48078000, .irq = INT_24XX_GPTIMER3 },
70 { .phys_base = 0x4807a000, .irq = INT_24XX_GPTIMER4 },
71 { .phys_base = 0x4807c000, .irq = INT_24XX_GPTIMER5 },
72 { .phys_base = 0x4807e000, .irq = INT_24XX_GPTIMER6 },
73 { .phys_base = 0x48080000, .irq = INT_24XX_GPTIMER7 },
74 { .phys_base = 0x48082000, .irq = INT_24XX_GPTIMER8 },
75 { .phys_base = 0x48084000, .irq = INT_24XX_GPTIMER9 },
76 { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 },
77 { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 },
78 { .phys_base = 0x4808a000, .irq = INT_24XX_GPTIMER12 },
79};
80
81static const char *omap2_dm_source_names[] __initdata = {
82 "sys_ck",
83 "func_32k_ck",
84 "alt_ck",
85 NULL
86};
87
88static struct clk *omap2_dm_source_clocks[3];
89static const int omap2_dm_timer_count = ARRAY_SIZE(omap2_dm_timers);
90
91#else
92#define omap2_dm_timers NULL
93#define omap2_dm_timer_count 0
94#define omap2_dm_source_names NULL
95#define omap2_dm_source_clocks NULL
96#endif /* CONFIG_ARCH_OMAP2 */
97
98#ifdef CONFIG_ARCH_OMAP3
99static struct omap_dm_timer omap3_dm_timers[] = {
100 { .phys_base = 0x48318000, .irq = INT_24XX_GPTIMER1 },
101 { .phys_base = 0x49032000, .irq = INT_24XX_GPTIMER2 },
102 { .phys_base = 0x49034000, .irq = INT_24XX_GPTIMER3 },
103 { .phys_base = 0x49036000, .irq = INT_24XX_GPTIMER4 },
104 { .phys_base = 0x49038000, .irq = INT_24XX_GPTIMER5 },
105 { .phys_base = 0x4903A000, .irq = INT_24XX_GPTIMER6 },
106 { .phys_base = 0x4903C000, .irq = INT_24XX_GPTIMER7 },
107 { .phys_base = 0x4903E000, .irq = INT_24XX_GPTIMER8 },
108 { .phys_base = 0x49040000, .irq = INT_24XX_GPTIMER9 },
109 { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 },
110 { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 },
111 { .phys_base = 0x48304000, .irq = INT_34XX_GPT12_IRQ },
112};
113
114static const char *omap3_dm_source_names[] __initdata = {
115 "sys_ck",
116 "omap_32k_fck",
117 NULL
118};
119
120static struct clk *omap3_dm_source_clocks[2];
121static const int omap3_dm_timer_count = ARRAY_SIZE(omap3_dm_timers);
122 42
123#else 43#include <plat/dmtimer.h>
124#define omap3_dm_timers NULL
125#define omap3_dm_timer_count 0
126#define omap3_dm_source_names NULL
127#define omap3_dm_source_clocks NULL
128#endif /* CONFIG_ARCH_OMAP3 */
129
130#ifdef CONFIG_ARCH_OMAP4
131static struct omap_dm_timer omap4_dm_timers[] = {
132 { .phys_base = 0x4a318000, .irq = OMAP44XX_IRQ_GPT1 },
133 { .phys_base = 0x48032000, .irq = OMAP44XX_IRQ_GPT2 },
134 { .phys_base = 0x48034000, .irq = OMAP44XX_IRQ_GPT3 },
135 { .phys_base = 0x48036000, .irq = OMAP44XX_IRQ_GPT4 },
136 { .phys_base = 0x40138000, .irq = OMAP44XX_IRQ_GPT5 },
137 { .phys_base = 0x4013a000, .irq = OMAP44XX_IRQ_GPT6 },
138 { .phys_base = 0x4013a000, .irq = OMAP44XX_IRQ_GPT7 },
139 { .phys_base = 0x4013e000, .irq = OMAP44XX_IRQ_GPT8 },
140 { .phys_base = 0x4803e000, .irq = OMAP44XX_IRQ_GPT9 },
141 { .phys_base = 0x48086000, .irq = OMAP44XX_IRQ_GPT10 },
142 { .phys_base = 0x48088000, .irq = OMAP44XX_IRQ_GPT11 },
143 { .phys_base = 0x4a320000, .irq = OMAP44XX_IRQ_GPT12 },
144};
145static const char *omap4_dm_source_names[] __initdata = {
146 "sys_clkin_ck",
147 "sys_32k_ck",
148 NULL
149};
150static struct clk *omap4_dm_source_clocks[2];
151static const int omap4_dm_timer_count = ARRAY_SIZE(omap4_dm_timers);
152
153#else
154#define omap4_dm_timers NULL
155#define omap4_dm_timer_count 0
156#define omap4_dm_source_names NULL
157#define omap4_dm_source_clocks NULL
158#endif /* CONFIG_ARCH_OMAP4 */
159
160static struct omap_dm_timer *dm_timers;
161static const char **dm_source_names;
162static struct clk **dm_source_clocks;
163 44
164static spinlock_t dm_timer_lock; 45static LIST_HEAD(omap_timer_list);
46static DEFINE_SPINLOCK(dm_timer_lock);
165 47
166/* 48/**
167 * Reads timer registers in posted and non-posted mode. The posted mode bit 49 * omap_dm_timer_read_reg - read timer registers in posted and non-posted mode
168 * is encoded in reg. Note that in posted mode write pending bit must be 50 * @timer: timer pointer over which read operation to perform
169 * checked. Otherwise a read of a non completed write will produce an error. 51 * @reg: lowest byte holds the register offset
52 *
53 * The posted mode bit is encoded in reg. Note that in posted mode write
54 * pending bit must be checked. Otherwise a read of a non completed write
55 * will produce an error.
170 */ 56 */
171static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg) 57static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
172{ 58{
173 return __omap_dm_timer_read(timer->io_base, reg, timer->posted); 59 WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
60 return __omap_dm_timer_read(timer, reg, timer->posted);
174} 61}
175 62
176/* 63/**
177 * Writes timer registers in posted and non-posted mode. The posted mode bit 64 * omap_dm_timer_write_reg - write timer registers in posted and non-posted mode
178 * is encoded in reg. Note that in posted mode the write pending bit must be 65 * @timer: timer pointer over which write operation is to perform
179 * checked. Otherwise a write on a register which has a pending write will be 66 * @reg: lowest byte holds the register offset
180 * lost. 67 * @value: data to write into the register
68 *
69 * The posted mode bit is encoded in reg. Note that in posted mode the write
70 * pending bit must be checked. Otherwise a write on a register which has a
71 * pending write will be lost.
181 */ 72 */
182static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg, 73static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
183 u32 value) 74 u32 value)
184{ 75{
185 __omap_dm_timer_write(timer->io_base, reg, value, timer->posted); 76 WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
77 __omap_dm_timer_write(timer, reg, value, timer->posted);
78}
79
80static void omap_timer_restore_context(struct omap_dm_timer *timer)
81{
82 omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_OFFSET,
83 timer->context.tiocp_cfg);
84 if (timer->revision > 1)
85 __raw_writel(timer->context.tistat, timer->sys_stat);
86
87 __raw_writel(timer->context.tisr, timer->irq_stat);
88 omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG,
89 timer->context.twer);
90 omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG,
91 timer->context.tcrr);
92 omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG,
93 timer->context.tldr);
94 omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG,
95 timer->context.tmar);
96 omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG,
97 timer->context.tsicr);
98 __raw_writel(timer->context.tier, timer->irq_ena);
99 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG,
100 timer->context.tclr);
186} 101}
187 102
188static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer) 103static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
189{ 104{
190 int c; 105 int c;
191 106
107 if (!timer->sys_stat)
108 return;
109
192 c = 0; 110 c = 0;
193 while (!(omap_dm_timer_read_reg(timer, OMAP_TIMER_SYS_STAT_REG) & 1)) { 111 while (!(__raw_readl(timer->sys_stat) & 1)) {
194 c++; 112 c++;
195 if (c > 100000) { 113 if (c > 100000) {
196 printk(KERN_ERR "Timer failed to reset\n"); 114 printk(KERN_ERR "Timer failed to reset\n");
@@ -201,53 +119,65 @@ static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
201 119
202static void omap_dm_timer_reset(struct omap_dm_timer *timer) 120static void omap_dm_timer_reset(struct omap_dm_timer *timer)
203{ 121{
204 int autoidle = 0, wakeup = 0; 122 omap_dm_timer_enable(timer);
205 123 if (timer->pdev->id != 1) {
206 if (!cpu_class_is_omap2() || timer != &dm_timers[0]) {
207 omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); 124 omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
208 omap_dm_timer_wait_for_reset(timer); 125 omap_dm_timer_wait_for_reset(timer);
209 } 126 }
210 omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
211
212 /* Enable autoidle on OMAP2+ */
213 if (cpu_class_is_omap2())
214 autoidle = 1;
215
216 /*
217 * Enable wake-up on OMAP2 CPUs.
218 */
219 if (cpu_class_is_omap2())
220 wakeup = 1;
221 127
222 __omap_dm_timer_reset(timer->io_base, autoidle, wakeup); 128 __omap_dm_timer_reset(timer, 0, 0);
129 omap_dm_timer_disable(timer);
223 timer->posted = 1; 130 timer->posted = 1;
224} 131}
225 132
226void omap_dm_timer_prepare(struct omap_dm_timer *timer) 133int omap_dm_timer_prepare(struct omap_dm_timer *timer)
227{ 134{
228 omap_dm_timer_enable(timer); 135 struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
229 omap_dm_timer_reset(timer); 136 int ret;
137
138 timer->fclk = clk_get(&timer->pdev->dev, "fck");
139 if (WARN_ON_ONCE(IS_ERR_OR_NULL(timer->fclk))) {
140 timer->fclk = NULL;
141 dev_err(&timer->pdev->dev, ": No fclk handle.\n");
142 return -EINVAL;
143 }
144
145 if (pdata->needs_manual_reset)
146 omap_dm_timer_reset(timer);
147
148 ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
149
150 timer->posted = 1;
151 return ret;
230} 152}
231 153
232struct omap_dm_timer *omap_dm_timer_request(void) 154struct omap_dm_timer *omap_dm_timer_request(void)
233{ 155{
234 struct omap_dm_timer *timer = NULL; 156 struct omap_dm_timer *timer = NULL, *t;
235 unsigned long flags; 157 unsigned long flags;
236 int i; 158 int ret = 0;
237 159
238 spin_lock_irqsave(&dm_timer_lock, flags); 160 spin_lock_irqsave(&dm_timer_lock, flags);
239 for (i = 0; i < dm_timer_count; i++) { 161 list_for_each_entry(t, &omap_timer_list, node) {
240 if (dm_timers[i].reserved) 162 if (t->reserved)
241 continue; 163 continue;
242 164
243 timer = &dm_timers[i]; 165 timer = t;
244 timer->reserved = 1; 166 timer->reserved = 1;
245 break; 167 break;
246 } 168 }
169
170 if (timer) {
171 ret = omap_dm_timer_prepare(timer);
172 if (ret) {
173 timer->reserved = 0;
174 timer = NULL;
175 }
176 }
247 spin_unlock_irqrestore(&dm_timer_lock, flags); 177 spin_unlock_irqrestore(&dm_timer_lock, flags);
248 178
249 if (timer != NULL) 179 if (!timer)
250 omap_dm_timer_prepare(timer); 180 pr_debug("%s: timer request failed!\n", __func__);
251 181
252 return timer; 182 return timer;
253} 183}
@@ -255,74 +185,65 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_request);
255 185
256struct omap_dm_timer *omap_dm_timer_request_specific(int id) 186struct omap_dm_timer *omap_dm_timer_request_specific(int id)
257{ 187{
258 struct omap_dm_timer *timer; 188 struct omap_dm_timer *timer = NULL, *t;
259 unsigned long flags; 189 unsigned long flags;
190 int ret = 0;
260 191
261 spin_lock_irqsave(&dm_timer_lock, flags); 192 spin_lock_irqsave(&dm_timer_lock, flags);
262 if (id <= 0 || id > dm_timer_count || dm_timers[id-1].reserved) { 193 list_for_each_entry(t, &omap_timer_list, node) {
263 spin_unlock_irqrestore(&dm_timer_lock, flags); 194 if (t->pdev->id == id && !t->reserved) {
264 printk("BUG: warning at %s:%d/%s(): unable to get timer %d\n", 195 timer = t;
265 __FILE__, __LINE__, __func__, id); 196 timer->reserved = 1;
266 dump_stack(); 197 break;
267 return NULL; 198 }
268 } 199 }
269 200
270 timer = &dm_timers[id-1]; 201 if (timer) {
271 timer->reserved = 1; 202 ret = omap_dm_timer_prepare(timer);
203 if (ret) {
204 timer->reserved = 0;
205 timer = NULL;
206 }
207 }
272 spin_unlock_irqrestore(&dm_timer_lock, flags); 208 spin_unlock_irqrestore(&dm_timer_lock, flags);
273 209
274 omap_dm_timer_prepare(timer); 210 if (!timer)
211 pr_debug("%s: timer%d request failed!\n", __func__, id);
275 212
276 return timer; 213 return timer;
277} 214}
278EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific); 215EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific);
279 216
280void omap_dm_timer_free(struct omap_dm_timer *timer) 217int omap_dm_timer_free(struct omap_dm_timer *timer)
281{ 218{
282 omap_dm_timer_enable(timer); 219 if (unlikely(!timer))
283 omap_dm_timer_reset(timer); 220 return -EINVAL;
284 omap_dm_timer_disable(timer); 221
222 clk_put(timer->fclk);
285 223
286 WARN_ON(!timer->reserved); 224 WARN_ON(!timer->reserved);
287 timer->reserved = 0; 225 timer->reserved = 0;
226 return 0;
288} 227}
289EXPORT_SYMBOL_GPL(omap_dm_timer_free); 228EXPORT_SYMBOL_GPL(omap_dm_timer_free);
290 229
291void omap_dm_timer_enable(struct omap_dm_timer *timer) 230void omap_dm_timer_enable(struct omap_dm_timer *timer)
292{ 231{
293 if (timer->enabled) 232 pm_runtime_get_sync(&timer->pdev->dev);
294 return;
295
296#ifdef CONFIG_ARCH_OMAP2PLUS
297 if (cpu_class_is_omap2()) {
298 clk_enable(timer->fclk);
299 clk_enable(timer->iclk);
300 }
301#endif
302
303 timer->enabled = 1;
304} 233}
305EXPORT_SYMBOL_GPL(omap_dm_timer_enable); 234EXPORT_SYMBOL_GPL(omap_dm_timer_enable);
306 235
307void omap_dm_timer_disable(struct omap_dm_timer *timer) 236void omap_dm_timer_disable(struct omap_dm_timer *timer)
308{ 237{
309 if (!timer->enabled) 238 pm_runtime_put(&timer->pdev->dev);
310 return;
311
312#ifdef CONFIG_ARCH_OMAP2PLUS
313 if (cpu_class_is_omap2()) {
314 clk_disable(timer->iclk);
315 clk_disable(timer->fclk);
316 }
317#endif
318
319 timer->enabled = 0;
320} 239}
321EXPORT_SYMBOL_GPL(omap_dm_timer_disable); 240EXPORT_SYMBOL_GPL(omap_dm_timer_disable);
322 241
323int omap_dm_timer_get_irq(struct omap_dm_timer *timer) 242int omap_dm_timer_get_irq(struct omap_dm_timer *timer)
324{ 243{
325 return timer->irq; 244 if (timer)
245 return timer->irq;
246 return -EINVAL;
326} 247}
327EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq); 248EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq);
328 249
@@ -334,24 +255,29 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq);
334 */ 255 */
335__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) 256__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
336{ 257{
337 int i; 258 int i = 0;
259 struct omap_dm_timer *timer = NULL;
260 unsigned long flags;
338 261
339 /* If ARMXOR cannot be idled this function call is unnecessary */ 262 /* If ARMXOR cannot be idled this function call is unnecessary */
340 if (!(inputmask & (1 << 1))) 263 if (!(inputmask & (1 << 1)))
341 return inputmask; 264 return inputmask;
342 265
343 /* If any active timer is using ARMXOR return modified mask */ 266 /* If any active timer is using ARMXOR return modified mask */
344 for (i = 0; i < dm_timer_count; i++) { 267 spin_lock_irqsave(&dm_timer_lock, flags);
268 list_for_each_entry(timer, &omap_timer_list, node) {
345 u32 l; 269 u32 l;
346 270
347 l = omap_dm_timer_read_reg(&dm_timers[i], OMAP_TIMER_CTRL_REG); 271 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
348 if (l & OMAP_TIMER_CTRL_ST) { 272 if (l & OMAP_TIMER_CTRL_ST) {
349 if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0) 273 if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0)
350 inputmask &= ~(1 << 1); 274 inputmask &= ~(1 << 1);
351 else 275 else
352 inputmask &= ~(1 << 2); 276 inputmask &= ~(1 << 2);
353 } 277 }
278 i++;
354 } 279 }
280 spin_unlock_irqrestore(&dm_timer_lock, flags);
355 281
356 return inputmask; 282 return inputmask;
357} 283}
@@ -361,7 +287,9 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
361 287
362struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) 288struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
363{ 289{
364 return timer->fclk; 290 if (timer)
291 return timer->fclk;
292 return NULL;
365} 293}
366EXPORT_SYMBOL_GPL(omap_dm_timer_get_fclk); 294EXPORT_SYMBOL_GPL(omap_dm_timer_get_fclk);
367 295
@@ -375,70 +303,91 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
375 303
376#endif 304#endif
377 305
378void omap_dm_timer_trigger(struct omap_dm_timer *timer) 306int omap_dm_timer_trigger(struct omap_dm_timer *timer)
379{ 307{
308 if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
309 pr_err("%s: timer not available or enabled.\n", __func__);
310 return -EINVAL;
311 }
312
380 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); 313 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
314 return 0;
381} 315}
382EXPORT_SYMBOL_GPL(omap_dm_timer_trigger); 316EXPORT_SYMBOL_GPL(omap_dm_timer_trigger);
383 317
384void omap_dm_timer_start(struct omap_dm_timer *timer) 318int omap_dm_timer_start(struct omap_dm_timer *timer)
385{ 319{
386 u32 l; 320 u32 l;
387 321
322 if (unlikely(!timer))
323 return -EINVAL;
324
325 omap_dm_timer_enable(timer);
326
327 if (timer->loses_context) {
328 u32 ctx_loss_cnt_after =
329 timer->get_context_loss_count(&timer->pdev->dev);
330 if (ctx_loss_cnt_after != timer->ctx_loss_count)
331 omap_timer_restore_context(timer);
332 }
333
388 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); 334 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
389 if (!(l & OMAP_TIMER_CTRL_ST)) { 335 if (!(l & OMAP_TIMER_CTRL_ST)) {
390 l |= OMAP_TIMER_CTRL_ST; 336 l |= OMAP_TIMER_CTRL_ST;
391 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); 337 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
392 } 338 }
339
340 /* Save the context */
341 timer->context.tclr = l;
342 return 0;
393} 343}
394EXPORT_SYMBOL_GPL(omap_dm_timer_start); 344EXPORT_SYMBOL_GPL(omap_dm_timer_start);
395 345
396void omap_dm_timer_stop(struct omap_dm_timer *timer) 346int omap_dm_timer_stop(struct omap_dm_timer *timer)
397{ 347{
398 unsigned long rate = 0; 348 unsigned long rate = 0;
349 struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
399 350
400#ifdef CONFIG_ARCH_OMAP2PLUS 351 if (unlikely(!timer))
401 rate = clk_get_rate(timer->fclk); 352 return -EINVAL;
402#endif
403 353
404 __omap_dm_timer_stop(timer->io_base, timer->posted, rate); 354 if (!pdata->needs_manual_reset)
355 rate = clk_get_rate(timer->fclk);
356
357 __omap_dm_timer_stop(timer, timer->posted, rate);
358
359 return 0;
405} 360}
406EXPORT_SYMBOL_GPL(omap_dm_timer_stop); 361EXPORT_SYMBOL_GPL(omap_dm_timer_stop);
407 362
408#ifdef CONFIG_ARCH_OMAP1
409
410int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) 363int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
411{ 364{
412 int n = (timer - dm_timers) << 1; 365 int ret;
413 u32 l; 366 struct dmtimer_platform_data *pdata;
414
415 l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n);
416 l |= source << n;
417 omap_writel(l, MOD_CONF_CTRL_1);
418 367
419 return 0; 368 if (unlikely(!timer))
420} 369 return -EINVAL;
421EXPORT_SYMBOL_GPL(omap_dm_timer_set_source);
422 370
423#else 371 pdata = timer->pdev->dev.platform_data;
424 372
425int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
426{
427 if (source < 0 || source >= 3) 373 if (source < 0 || source >= 3)
428 return -EINVAL; 374 return -EINVAL;
429 375
430 return __omap_dm_timer_set_source(timer->fclk, 376 ret = pdata->set_timer_src(timer->pdev, source);
431 dm_source_clocks[source]); 377
378 return ret;
432} 379}
433EXPORT_SYMBOL_GPL(omap_dm_timer_set_source); 380EXPORT_SYMBOL_GPL(omap_dm_timer_set_source);
434 381
435#endif 382int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
436
437void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
438 unsigned int load) 383 unsigned int load)
439{ 384{
440 u32 l; 385 u32 l;
441 386
387 if (unlikely(!timer))
388 return -EINVAL;
389
390 omap_dm_timer_enable(timer);
442 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); 391 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
443 if (autoreload) 392 if (autoreload)
444 l |= OMAP_TIMER_CTRL_AR; 393 l |= OMAP_TIMER_CTRL_AR;
@@ -448,15 +397,32 @@ void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
448 omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); 397 omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
449 398
450 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); 399 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
400 /* Save the context */
401 timer->context.tclr = l;
402 timer->context.tldr = load;
403 omap_dm_timer_disable(timer);
404 return 0;
451} 405}
452EXPORT_SYMBOL_GPL(omap_dm_timer_set_load); 406EXPORT_SYMBOL_GPL(omap_dm_timer_set_load);
453 407
454/* Optimized set_load which removes costly spin wait in timer_start */ 408/* Optimized set_load which removes costly spin wait in timer_start */
455void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, 409int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
456 unsigned int load) 410 unsigned int load)
457{ 411{
458 u32 l; 412 u32 l;
459 413
414 if (unlikely(!timer))
415 return -EINVAL;
416
417 omap_dm_timer_enable(timer);
418
419 if (timer->loses_context) {
420 u32 ctx_loss_cnt_after =
421 timer->get_context_loss_count(&timer->pdev->dev);
422 if (ctx_loss_cnt_after != timer->ctx_loss_count)
423 omap_timer_restore_context(timer);
424 }
425
460 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); 426 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
461 if (autoreload) { 427 if (autoreload) {
462 l |= OMAP_TIMER_CTRL_AR; 428 l |= OMAP_TIMER_CTRL_AR;
@@ -466,15 +432,25 @@ void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
466 } 432 }
467 l |= OMAP_TIMER_CTRL_ST; 433 l |= OMAP_TIMER_CTRL_ST;
468 434
469 __omap_dm_timer_load_start(timer->io_base, l, load, timer->posted); 435 __omap_dm_timer_load_start(timer, l, load, timer->posted);
436
437 /* Save the context */
438 timer->context.tclr = l;
439 timer->context.tldr = load;
440 timer->context.tcrr = load;
441 return 0;
470} 442}
471EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start); 443EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start);
472 444
473void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, 445int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
474 unsigned int match) 446 unsigned int match)
475{ 447{
476 u32 l; 448 u32 l;
477 449
450 if (unlikely(!timer))
451 return -EINVAL;
452
453 omap_dm_timer_enable(timer);
478 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); 454 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
479 if (enable) 455 if (enable)
480 l |= OMAP_TIMER_CTRL_CE; 456 l |= OMAP_TIMER_CTRL_CE;
@@ -482,14 +458,24 @@ void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
482 l &= ~OMAP_TIMER_CTRL_CE; 458 l &= ~OMAP_TIMER_CTRL_CE;
483 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); 459 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
484 omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); 460 omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
461
462 /* Save the context */
463 timer->context.tclr = l;
464 timer->context.tmar = match;
465 omap_dm_timer_disable(timer);
466 return 0;
485} 467}
486EXPORT_SYMBOL_GPL(omap_dm_timer_set_match); 468EXPORT_SYMBOL_GPL(omap_dm_timer_set_match);
487 469
488void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, 470int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
489 int toggle, int trigger) 471 int toggle, int trigger)
490{ 472{
491 u32 l; 473 u32 l;
492 474
475 if (unlikely(!timer))
476 return -EINVAL;
477
478 omap_dm_timer_enable(timer);
493 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); 479 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
494 l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM | 480 l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |
495 OMAP_TIMER_CTRL_PT | (0x03 << 10)); 481 OMAP_TIMER_CTRL_PT | (0x03 << 10));
@@ -499,13 +485,22 @@ void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
499 l |= OMAP_TIMER_CTRL_PT; 485 l |= OMAP_TIMER_CTRL_PT;
500 l |= trigger << 10; 486 l |= trigger << 10;
501 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); 487 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
488
489 /* Save the context */
490 timer->context.tclr = l;
491 omap_dm_timer_disable(timer);
492 return 0;
502} 493}
503EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm); 494EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm);
504 495
505void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler) 496int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
506{ 497{
507 u32 l; 498 u32 l;
508 499
500 if (unlikely(!timer))
501 return -EINVAL;
502
503 omap_dm_timer_enable(timer);
509 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); 504 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
510 l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2)); 505 l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));
511 if (prescaler >= 0x00 && prescaler <= 0x07) { 506 if (prescaler >= 0x00 && prescaler <= 0x07) {
@@ -513,13 +508,28 @@ void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
513 l |= prescaler << 2; 508 l |= prescaler << 2;
514 } 509 }
515 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); 510 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
511
512 /* Save the context */
513 timer->context.tclr = l;
514 omap_dm_timer_disable(timer);
515 return 0;
516} 516}
517EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler); 517EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler);
518 518
519void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, 519int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
520 unsigned int value) 520 unsigned int value)
521{ 521{
522 __omap_dm_timer_int_enable(timer->io_base, value); 522 if (unlikely(!timer))
523 return -EINVAL;
524
525 omap_dm_timer_enable(timer);
526 __omap_dm_timer_int_enable(timer, value);
527
528 /* Save the context */
529 timer->context.tier = value;
530 timer->context.twer = value;
531 omap_dm_timer_disable(timer);
532 return 0;
523} 533}
524EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable); 534EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable);
525 535
@@ -527,40 +537,61 @@ unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
527{ 537{
528 unsigned int l; 538 unsigned int l;
529 539
530 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG); 540 if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
541 pr_err("%s: timer not available or enabled.\n", __func__);
542 return 0;
543 }
544
545 l = __raw_readl(timer->irq_stat);
531 546
532 return l; 547 return l;
533} 548}
534EXPORT_SYMBOL_GPL(omap_dm_timer_read_status); 549EXPORT_SYMBOL_GPL(omap_dm_timer_read_status);
535 550
536void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) 551int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
537{ 552{
538 __omap_dm_timer_write_status(timer->io_base, value); 553 if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev)))
554 return -EINVAL;
555
556 __omap_dm_timer_write_status(timer, value);
557 /* Save the context */
558 timer->context.tisr = value;
559 return 0;
539} 560}
540EXPORT_SYMBOL_GPL(omap_dm_timer_write_status); 561EXPORT_SYMBOL_GPL(omap_dm_timer_write_status);
541 562
542unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) 563unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
543{ 564{
544 return __omap_dm_timer_read_counter(timer->io_base, timer->posted); 565 if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
566 pr_err("%s: timer not iavailable or enabled.\n", __func__);
567 return 0;
568 }
569
570 return __omap_dm_timer_read_counter(timer, timer->posted);
545} 571}
546EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter); 572EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter);
547 573
548void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value) 574int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
549{ 575{
576 if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
577 pr_err("%s: timer not available or enabled.\n", __func__);
578 return -EINVAL;
579 }
580
550 omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value); 581 omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
582
583 /* Save the context */
584 timer->context.tcrr = value;
585 return 0;
551} 586}
552EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter); 587EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter);
553 588
554int omap_dm_timers_active(void) 589int omap_dm_timers_active(void)
555{ 590{
556 int i; 591 struct omap_dm_timer *timer;
557
558 for (i = 0; i < dm_timer_count; i++) {
559 struct omap_dm_timer *timer;
560
561 timer = &dm_timers[i];
562 592
563 if (!timer->enabled) 593 list_for_each_entry(timer, &omap_timer_list, node) {
594 if (!timer->reserved)
564 continue; 595 continue;
565 596
566 if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) & 597 if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
@@ -572,69 +603,147 @@ int omap_dm_timers_active(void)
572} 603}
573EXPORT_SYMBOL_GPL(omap_dm_timers_active); 604EXPORT_SYMBOL_GPL(omap_dm_timers_active);
574 605
575static int __init omap_dm_timer_init(void) 606/**
607 * omap_dm_timer_probe - probe function called for every registered device
608 * @pdev: pointer to current timer platform device
609 *
610 * Called by driver framework at the end of device registration for all
611 * timer devices.
612 */
613static int __devinit omap_dm_timer_probe(struct platform_device *pdev)
576{ 614{
615 int ret;
616 unsigned long flags;
577 struct omap_dm_timer *timer; 617 struct omap_dm_timer *timer;
578 int i, map_size = SZ_8K; /* Module 4KB + L4 4KB except on omap1 */ 618 struct resource *mem, *irq, *ioarea;
619 struct dmtimer_platform_data *pdata = pdev->dev.platform_data;
579 620
580 if (!(cpu_is_omap16xx() || cpu_class_is_omap2())) 621 if (!pdata) {
622 dev_err(&pdev->dev, "%s: no platform data.\n", __func__);
581 return -ENODEV; 623 return -ENODEV;
624 }
582 625
583 spin_lock_init(&dm_timer_lock); 626 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
584 627 if (unlikely(!irq)) {
585 if (cpu_class_is_omap1()) { 628 dev_err(&pdev->dev, "%s: no IRQ resource.\n", __func__);
586 dm_timers = omap1_dm_timers; 629 return -ENODEV;
587 dm_timer_count = omap1_dm_timer_count;
588 map_size = SZ_2K;
589 } else if (cpu_is_omap24xx()) {
590 dm_timers = omap2_dm_timers;
591 dm_timer_count = omap2_dm_timer_count;
592 dm_source_names = omap2_dm_source_names;
593 dm_source_clocks = omap2_dm_source_clocks;
594 } else if (cpu_is_omap34xx()) {
595 dm_timers = omap3_dm_timers;
596 dm_timer_count = omap3_dm_timer_count;
597 dm_source_names = omap3_dm_source_names;
598 dm_source_clocks = omap3_dm_source_clocks;
599 } else if (cpu_is_omap44xx()) {
600 dm_timers = omap4_dm_timers;
601 dm_timer_count = omap4_dm_timer_count;
602 dm_source_names = omap4_dm_source_names;
603 dm_source_clocks = omap4_dm_source_clocks;
604 } 630 }
605 631
606 if (cpu_class_is_omap2()) 632 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
607 for (i = 0; dm_source_names[i] != NULL; i++) 633 if (unlikely(!mem)) {
608 dm_source_clocks[i] = clk_get(NULL, dm_source_names[i]); 634 dev_err(&pdev->dev, "%s: no memory resource.\n", __func__);
635 return -ENODEV;
636 }
609 637
610 if (cpu_is_omap243x()) 638 ioarea = request_mem_region(mem->start, resource_size(mem),
611 dm_timers[0].phys_base = 0x49018000; 639 pdev->name);
640 if (!ioarea) {
641 dev_err(&pdev->dev, "%s: region already claimed.\n", __func__);
642 return -EBUSY;
643 }
612 644
613 for (i = 0; i < dm_timer_count; i++) { 645 timer = kzalloc(sizeof(struct omap_dm_timer), GFP_KERNEL);
614 timer = &dm_timers[i]; 646 if (!timer) {
647 dev_err(&pdev->dev, "%s: no memory for omap_dm_timer.\n",
648 __func__);
649 ret = -ENOMEM;
650 goto err_free_ioregion;
651 }
615 652
616 /* Static mapping, never released */ 653 timer->io_base = ioremap(mem->start, resource_size(mem));
617 timer->io_base = ioremap(timer->phys_base, map_size); 654 if (!timer->io_base) {
618 BUG_ON(!timer->io_base); 655 dev_err(&pdev->dev, "%s: ioremap failed.\n", __func__);
656 ret = -ENOMEM;
657 goto err_free_mem;
658 }
619 659
620#ifdef CONFIG_ARCH_OMAP2PLUS 660 timer->id = pdev->id;
621 if (cpu_class_is_omap2()) { 661 timer->irq = irq->start;
622 char clk_name[16]; 662 timer->reserved = pdata->reserved;
623 sprintf(clk_name, "gpt%d_ick", i + 1); 663 timer->pdev = pdev;
624 timer->iclk = clk_get(NULL, clk_name); 664 timer->loses_context = pdata->loses_context;
625 sprintf(clk_name, "gpt%d_fck", i + 1); 665 timer->get_context_loss_count = pdata->get_context_loss_count;
626 timer->fclk = clk_get(NULL, clk_name); 666
627 } 667 /* Skip pm_runtime_enable for OMAP1 */
668 if (!pdata->needs_manual_reset) {
669 pm_runtime_enable(&pdev->dev);
670 pm_runtime_irq_safe(&pdev->dev);
671 }
628 672
629 /* One or two timers may be set up early for sys_timer */ 673 if (!timer->reserved) {
630 if (sys_timer_reserved & (1 << i)) { 674 pm_runtime_get_sync(&pdev->dev);
631 timer->reserved = 1; 675 __omap_dm_timer_init_regs(timer);
632 timer->posted = 1; 676 pm_runtime_put(&pdev->dev);
633 }
634#endif
635 } 677 }
636 678
679 /* add the timer element to the list */
680 spin_lock_irqsave(&dm_timer_lock, flags);
681 list_add_tail(&timer->node, &omap_timer_list);
682 spin_unlock_irqrestore(&dm_timer_lock, flags);
683
684 dev_dbg(&pdev->dev, "Device Probed.\n");
685
637 return 0; 686 return 0;
687
688err_free_mem:
689 kfree(timer);
690
691err_free_ioregion:
692 release_mem_region(mem->start, resource_size(mem));
693
694 return ret;
638} 695}
639 696
640arch_initcall(omap_dm_timer_init); 697/**
698 * omap_dm_timer_remove - cleanup a registered timer device
699 * @pdev: pointer to current timer platform device
700 *
701 * Called by driver framework whenever a timer device is unregistered.
702 * In addition to freeing platform resources it also deletes the timer
703 * entry from the local list.
704 */
705static int __devexit omap_dm_timer_remove(struct platform_device *pdev)
706{
707 struct omap_dm_timer *timer;
708 unsigned long flags;
709 int ret = -EINVAL;
710
711 spin_lock_irqsave(&dm_timer_lock, flags);
712 list_for_each_entry(timer, &omap_timer_list, node)
713 if (timer->pdev->id == pdev->id) {
714 list_del(&timer->node);
715 kfree(timer);
716 ret = 0;
717 break;
718 }
719 spin_unlock_irqrestore(&dm_timer_lock, flags);
720
721 return ret;
722}
723
724static struct platform_driver omap_dm_timer_driver = {
725 .probe = omap_dm_timer_probe,
726 .remove = __devexit_p(omap_dm_timer_remove),
727 .driver = {
728 .name = "omap_timer",
729 },
730};
731
732static int __init omap_dm_timer_driver_init(void)
733{
734 return platform_driver_register(&omap_dm_timer_driver);
735}
736
737static void __exit omap_dm_timer_driver_exit(void)
738{
739 platform_driver_unregister(&omap_dm_timer_driver);
740}
741
742early_platform_init("earlytimer", &omap_dm_timer_driver);
743module_init(omap_dm_timer_driver_init);
744module_exit(omap_dm_timer_driver_exit);
745
746MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver");
747MODULE_LICENSE("GPL");
748MODULE_ALIAS("platform:" DRIVER_NAME);
749MODULE_AUTHOR("Texas Instruments Inc");