ARM: OMAP: dmtimer: skip reserved timers
[deliverable/linux.git] / arch / arm / plat-omap / dmtimer.c
CommitLineData
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
fced80c7 38#include <linux/io.h>
df28472a 39#include <linux/slab.h>
3392cdd3 40#include <linux/err.h>
ffe07cea 41#include <linux/pm_runtime.h>
44169075 42
3392cdd3 43#include <plat/dmtimer.h>
471b3aa7 44
df28472a 45static LIST_HEAD(omap_timer_list);
3392cdd3 46static DEFINE_SPINLOCK(dm_timer_lock);
92105bb7 47
3392cdd3
TKD
48/**
49 * omap_dm_timer_read_reg - read timer registers in posted and non-posted mode
50 * @timer: timer pointer over which read operation to perform
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.
0f0d0807
RW
56 */
57static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
77900a2f 58{
ee17f114
TL
59 WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
60 return __omap_dm_timer_read(timer, reg, timer->posted);
77900a2f 61}
92105bb7 62
3392cdd3
TKD
63/**
64 * omap_dm_timer_write_reg - write timer registers in posted and non-posted mode
65 * @timer: timer pointer over which write operation is to perform
66 * @reg: lowest byte holds the register offset
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.
0f0d0807
RW
72 */
73static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
74 u32 value)
92105bb7 75{
ee17f114
TL
76 WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
77 __omap_dm_timer_write(timer, reg, value, timer->posted);
92105bb7
TL
78}
79
77900a2f 80static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
92105bb7 81{
77900a2f
TT
82 int c;
83
ee17f114
TL
84 if (!timer->sys_stat)
85 return;
86
77900a2f 87 c = 0;
ee17f114 88 while (!(__raw_readl(timer->sys_stat) & 1)) {
77900a2f
TT
89 c++;
90 if (c > 100000) {
91 printk(KERN_ERR "Timer failed to reset\n");
92 return;
93 }
94 }
92105bb7
TL
95}
96
77900a2f
TT
97static void omap_dm_timer_reset(struct omap_dm_timer *timer)
98{
3392cdd3 99 if (timer->pdev->id != 1) {
e32f7ec2
TT
100 omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
101 omap_dm_timer_wait_for_reset(timer);
102 }
0f0d0807 103
3392cdd3 104 __omap_dm_timer_reset(timer, 0, 0);
0f0d0807 105 timer->posted = 1;
77900a2f
TT
106}
107
3392cdd3 108int omap_dm_timer_prepare(struct omap_dm_timer *timer)
77900a2f 109{
3392cdd3
TKD
110 struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
111 int ret;
112
113 timer->fclk = clk_get(&timer->pdev->dev, "fck");
114 if (WARN_ON_ONCE(IS_ERR_OR_NULL(timer->fclk))) {
115 timer->fclk = NULL;
116 dev_err(&timer->pdev->dev, ": No fclk handle.\n");
117 return -EINVAL;
118 }
119
12583a70 120 omap_dm_timer_enable(timer);
3392cdd3
TKD
121
122 if (pdata->needs_manual_reset)
123 omap_dm_timer_reset(timer);
124
125 ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
126
127 timer->posted = 1;
128 return ret;
77900a2f
TT
129}
130
131struct omap_dm_timer *omap_dm_timer_request(void)
132{
3392cdd3 133 struct omap_dm_timer *timer = NULL, *t;
77900a2f 134 unsigned long flags;
3392cdd3 135 int ret = 0;
77900a2f
TT
136
137 spin_lock_irqsave(&dm_timer_lock, flags);
3392cdd3
TKD
138 list_for_each_entry(t, &omap_timer_list, node) {
139 if (t->reserved)
77900a2f
TT
140 continue;
141
3392cdd3 142 timer = t;
83379c81 143 timer->reserved = 1;
77900a2f
TT
144 break;
145 }
3392cdd3
TKD
146
147 if (timer) {
148 ret = omap_dm_timer_prepare(timer);
149 if (ret) {
150 timer->reserved = 0;
151 timer = NULL;
152 }
153 }
77900a2f
TT
154 spin_unlock_irqrestore(&dm_timer_lock, flags);
155
3392cdd3
TKD
156 if (!timer)
157 pr_debug("%s: timer request failed!\n", __func__);
83379c81 158
77900a2f
TT
159 return timer;
160}
6c366e32 161EXPORT_SYMBOL_GPL(omap_dm_timer_request);
77900a2f
TT
162
163struct omap_dm_timer *omap_dm_timer_request_specific(int id)
92105bb7 164{
3392cdd3 165 struct omap_dm_timer *timer = NULL, *t;
77900a2f 166 unsigned long flags;
3392cdd3 167 int ret = 0;
92105bb7 168
77900a2f 169 spin_lock_irqsave(&dm_timer_lock, flags);
3392cdd3
TKD
170 list_for_each_entry(t, &omap_timer_list, node) {
171 if (t->pdev->id == id && !t->reserved) {
172 timer = t;
173 timer->reserved = 1;
174 break;
175 }
77900a2f 176 }
92105bb7 177
3392cdd3
TKD
178 if (timer) {
179 ret = omap_dm_timer_prepare(timer);
180 if (ret) {
181 timer->reserved = 0;
182 timer = NULL;
183 }
184 }
77900a2f
TT
185 spin_unlock_irqrestore(&dm_timer_lock, flags);
186
3392cdd3
TKD
187 if (!timer)
188 pr_debug("%s: timer%d request failed!\n", __func__, id);
83379c81 189
77900a2f 190 return timer;
92105bb7 191}
6c366e32 192EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific);
92105bb7 193
77900a2f
TT
194void omap_dm_timer_free(struct omap_dm_timer *timer)
195{
12583a70 196 omap_dm_timer_disable(timer);
3392cdd3 197 clk_put(timer->fclk);
fa4bb626 198
77900a2f
TT
199 WARN_ON(!timer->reserved);
200 timer->reserved = 0;
201}
6c366e32 202EXPORT_SYMBOL_GPL(omap_dm_timer_free);
77900a2f 203
12583a70
TT
204void omap_dm_timer_enable(struct omap_dm_timer *timer)
205{
ffe07cea 206 pm_runtime_get_sync(&timer->pdev->dev);
12583a70 207}
6c366e32 208EXPORT_SYMBOL_GPL(omap_dm_timer_enable);
12583a70
TT
209
210void omap_dm_timer_disable(struct omap_dm_timer *timer)
211{
ffe07cea 212 pm_runtime_put(&timer->pdev->dev);
12583a70 213}
6c366e32 214EXPORT_SYMBOL_GPL(omap_dm_timer_disable);
12583a70 215
77900a2f
TT
216int omap_dm_timer_get_irq(struct omap_dm_timer *timer)
217{
218 return timer->irq;
219}
6c366e32 220EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq);
77900a2f
TT
221
222#if defined(CONFIG_ARCH_OMAP1)
223
a569c6ec
TL
224/**
225 * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR
226 * @inputmask: current value of idlect mask
227 */
228__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
229{
3392cdd3
TKD
230 int i = 0;
231 struct omap_dm_timer *timer = NULL;
232 unsigned long flags;
a569c6ec
TL
233
234 /* If ARMXOR cannot be idled this function call is unnecessary */
235 if (!(inputmask & (1 << 1)))
236 return inputmask;
237
238 /* If any active timer is using ARMXOR return modified mask */
3392cdd3
TKD
239 spin_lock_irqsave(&dm_timer_lock, flags);
240 list_for_each_entry(timer, &omap_timer_list, node) {
77900a2f
TT
241 u32 l;
242
3392cdd3 243 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
77900a2f
TT
244 if (l & OMAP_TIMER_CTRL_ST) {
245 if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0)
a569c6ec
TL
246 inputmask &= ~(1 << 1);
247 else
248 inputmask &= ~(1 << 2);
249 }
3392cdd3 250 i++;
77900a2f 251 }
3392cdd3 252 spin_unlock_irqrestore(&dm_timer_lock, flags);
a569c6ec
TL
253
254 return inputmask;
255}
6c366e32 256EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
a569c6ec 257
140455fa 258#else
a569c6ec 259
77900a2f 260struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
92105bb7 261{
fa4bb626 262 return timer->fclk;
77900a2f 263}
6c366e32 264EXPORT_SYMBOL_GPL(omap_dm_timer_get_fclk);
92105bb7 265
77900a2f
TT
266__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
267{
268 BUG();
2121880e
DB
269
270 return 0;
92105bb7 271}
6c366e32 272EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
92105bb7 273
77900a2f 274#endif
92105bb7 275
77900a2f 276void omap_dm_timer_trigger(struct omap_dm_timer *timer)
92105bb7 277{
77900a2f 278 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
92105bb7 279}
6c366e32 280EXPORT_SYMBOL_GPL(omap_dm_timer_trigger);
92105bb7 281
77900a2f
TT
282void omap_dm_timer_start(struct omap_dm_timer *timer)
283{
284 u32 l;
92105bb7 285
77900a2f
TT
286 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
287 if (!(l & OMAP_TIMER_CTRL_ST)) {
288 l |= OMAP_TIMER_CTRL_ST;
289 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
290 }
291}
6c366e32 292EXPORT_SYMBOL_GPL(omap_dm_timer_start);
92105bb7 293
77900a2f 294void omap_dm_timer_stop(struct omap_dm_timer *timer)
92105bb7 295{
caf64f2f 296 unsigned long rate = 0;
3392cdd3 297 struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
92105bb7 298
3392cdd3
TKD
299 if (!pdata->needs_manual_reset)
300 rate = clk_get_rate(timer->fclk);
caf64f2f 301
ee17f114 302 __omap_dm_timer_stop(timer, timer->posted, rate);
92105bb7 303}
6c366e32 304EXPORT_SYMBOL_GPL(omap_dm_timer_stop);
92105bb7 305
f248076c 306int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
92105bb7 307{
3392cdd3
TKD
308 int ret;
309 struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
310
77900a2f 311 if (source < 0 || source >= 3)
f248076c 312 return -EINVAL;
77900a2f 313
3392cdd3
TKD
314 omap_dm_timer_disable(timer);
315 ret = pdata->set_timer_src(timer->pdev, source);
316 omap_dm_timer_enable(timer);
317
318 return ret;
92105bb7 319}
6c366e32 320EXPORT_SYMBOL_GPL(omap_dm_timer_set_source);
92105bb7 321
77900a2f
TT
322void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
323 unsigned int load)
92105bb7
TL
324{
325 u32 l;
77900a2f 326
92105bb7 327 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
77900a2f
TT
328 if (autoreload)
329 l |= OMAP_TIMER_CTRL_AR;
330 else
331 l &= ~OMAP_TIMER_CTRL_AR;
92105bb7 332 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
77900a2f 333 omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
0f0d0807 334
77900a2f 335 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
92105bb7 336}
6c366e32 337EXPORT_SYMBOL_GPL(omap_dm_timer_set_load);
92105bb7 338
3fddd09e
RW
339/* Optimized set_load which removes costly spin wait in timer_start */
340void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
341 unsigned int load)
342{
343 u32 l;
344
345 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
64ce2907 346 if (autoreload) {
3fddd09e 347 l |= OMAP_TIMER_CTRL_AR;
64ce2907
PW
348 omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
349 } else {
3fddd09e 350 l &= ~OMAP_TIMER_CTRL_AR;
64ce2907 351 }
3fddd09e
RW
352 l |= OMAP_TIMER_CTRL_ST;
353
ee17f114 354 __omap_dm_timer_load_start(timer, l, load, timer->posted);
3fddd09e 355}
6c366e32 356EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start);
3fddd09e 357
77900a2f
TT
358void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
359 unsigned int match)
92105bb7
TL
360{
361 u32 l;
362
363 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
83379c81 364 if (enable)
77900a2f
TT
365 l |= OMAP_TIMER_CTRL_CE;
366 else
367 l &= ~OMAP_TIMER_CTRL_CE;
92105bb7 368 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
77900a2f 369 omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
92105bb7 370}
6c366e32 371EXPORT_SYMBOL_GPL(omap_dm_timer_set_match);
92105bb7 372
77900a2f
TT
373void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
374 int toggle, int trigger)
92105bb7
TL
375{
376 u32 l;
377
378 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
77900a2f
TT
379 l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |
380 OMAP_TIMER_CTRL_PT | (0x03 << 10));
381 if (def_on)
382 l |= OMAP_TIMER_CTRL_SCPWM;
383 if (toggle)
384 l |= OMAP_TIMER_CTRL_PT;
385 l |= trigger << 10;
92105bb7
TL
386 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
387}
6c366e32 388EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm);
92105bb7 389
77900a2f 390void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
92105bb7
TL
391{
392 u32 l;
393
394 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
77900a2f
TT
395 l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));
396 if (prescaler >= 0x00 && prescaler <= 0x07) {
397 l |= OMAP_TIMER_CTRL_PRE;
398 l |= prescaler << 2;
399 }
92105bb7
TL
400 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
401}
6c366e32 402EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler);
92105bb7 403
77900a2f
TT
404void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
405 unsigned int value)
92105bb7 406{
ee17f114 407 __omap_dm_timer_int_enable(timer, value);
92105bb7 408}
6c366e32 409EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable);
92105bb7 410
77900a2f 411unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
92105bb7 412{
fa4bb626
TT
413 unsigned int l;
414
ee17f114 415 l = __raw_readl(timer->irq_stat);
fa4bb626
TT
416
417 return l;
92105bb7 418}
6c366e32 419EXPORT_SYMBOL_GPL(omap_dm_timer_read_status);
92105bb7 420
77900a2f 421void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
92105bb7 422{
ee17f114 423 __omap_dm_timer_write_status(timer, value);
92105bb7 424}
6c366e32 425EXPORT_SYMBOL_GPL(omap_dm_timer_write_status);
92105bb7 426
77900a2f 427unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
92105bb7 428{
ee17f114 429 return __omap_dm_timer_read_counter(timer, timer->posted);
92105bb7 430}
6c366e32 431EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter);
92105bb7 432
83379c81
TT
433void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
434{
fa4bb626 435 omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
83379c81 436}
6c366e32 437EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter);
83379c81 438
77900a2f 439int omap_dm_timers_active(void)
92105bb7 440{
3392cdd3 441 struct omap_dm_timer *timer;
12583a70 442
3392cdd3 443 list_for_each_entry(timer, &omap_timer_list, node) {
ffe07cea 444 if (!timer->reserved)
12583a70
TT
445 continue;
446
77900a2f 447 if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
fa4bb626 448 OMAP_TIMER_CTRL_ST) {
77900a2f 449 return 1;
fa4bb626 450 }
77900a2f
TT
451 }
452 return 0;
453}
6c366e32 454EXPORT_SYMBOL_GPL(omap_dm_timers_active);
92105bb7 455
df28472a
TKD
456/**
457 * omap_dm_timer_probe - probe function called for every registered device
458 * @pdev: pointer to current timer platform device
459 *
460 * Called by driver framework at the end of device registration for all
461 * timer devices.
462 */
463static int __devinit omap_dm_timer_probe(struct platform_device *pdev)
464{
465 int ret;
466 unsigned long flags;
467 struct omap_dm_timer *timer;
468 struct resource *mem, *irq, *ioarea;
469 struct dmtimer_platform_data *pdata = pdev->dev.platform_data;
470
471 if (!pdata) {
472 dev_err(&pdev->dev, "%s: no platform data.\n", __func__);
473 return -ENODEV;
474 }
475
476 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
477 if (unlikely(!irq)) {
478 dev_err(&pdev->dev, "%s: no IRQ resource.\n", __func__);
479 return -ENODEV;
480 }
481
482 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
483 if (unlikely(!mem)) {
484 dev_err(&pdev->dev, "%s: no memory resource.\n", __func__);
485 return -ENODEV;
486 }
487
488 ioarea = request_mem_region(mem->start, resource_size(mem),
489 pdev->name);
490 if (!ioarea) {
491 dev_err(&pdev->dev, "%s: region already claimed.\n", __func__);
492 return -EBUSY;
493 }
494
495 timer = kzalloc(sizeof(struct omap_dm_timer), GFP_KERNEL);
496 if (!timer) {
497 dev_err(&pdev->dev, "%s: no memory for omap_dm_timer.\n",
498 __func__);
499 ret = -ENOMEM;
500 goto err_free_ioregion;
501 }
502
503 timer->io_base = ioremap(mem->start, resource_size(mem));
504 if (!timer->io_base) {
505 dev_err(&pdev->dev, "%s: ioremap failed.\n", __func__);
506 ret = -ENOMEM;
507 goto err_free_mem;
508 }
509
510 timer->id = pdev->id;
511 timer->irq = irq->start;
0dad9fae 512 timer->reserved = pdata->reserved;
df28472a 513 timer->pdev = pdev;
df28472a 514
ffe07cea
TKD
515 /* Skip pm_runtime_enable for OMAP1 */
516 if (!pdata->needs_manual_reset) {
517 pm_runtime_enable(&pdev->dev);
518 pm_runtime_irq_safe(&pdev->dev);
519 }
520
0dad9fae
TL
521 if (!timer->reserved) {
522 pm_runtime_get_sync(&pdev->dev);
523 __omap_dm_timer_init_regs(timer);
524 pm_runtime_put(&pdev->dev);
525 }
526
df28472a
TKD
527 /* add the timer element to the list */
528 spin_lock_irqsave(&dm_timer_lock, flags);
529 list_add_tail(&timer->node, &omap_timer_list);
530 spin_unlock_irqrestore(&dm_timer_lock, flags);
531
532 dev_dbg(&pdev->dev, "Device Probed.\n");
533
534 return 0;
535
536err_free_mem:
537 kfree(timer);
538
539err_free_ioregion:
540 release_mem_region(mem->start, resource_size(mem));
541
542 return ret;
543}
544
545/**
546 * omap_dm_timer_remove - cleanup a registered timer device
547 * @pdev: pointer to current timer platform device
548 *
549 * Called by driver framework whenever a timer device is unregistered.
550 * In addition to freeing platform resources it also deletes the timer
551 * entry from the local list.
552 */
553static int __devexit omap_dm_timer_remove(struct platform_device *pdev)
554{
555 struct omap_dm_timer *timer;
556 unsigned long flags;
557 int ret = -EINVAL;
558
559 spin_lock_irqsave(&dm_timer_lock, flags);
560 list_for_each_entry(timer, &omap_timer_list, node)
561 if (timer->pdev->id == pdev->id) {
562 list_del(&timer->node);
563 kfree(timer);
564 ret = 0;
565 break;
566 }
567 spin_unlock_irqrestore(&dm_timer_lock, flags);
568
569 return ret;
570}
571
572static struct platform_driver omap_dm_timer_driver = {
573 .probe = omap_dm_timer_probe,
574 .remove = omap_dm_timer_remove,
575 .driver = {
576 .name = "omap_timer",
577 },
578};
579
580static int __init omap_dm_timer_driver_init(void)
581{
582 return platform_driver_register(&omap_dm_timer_driver);
583}
584
585static void __exit omap_dm_timer_driver_exit(void)
586{
587 platform_driver_unregister(&omap_dm_timer_driver);
588}
589
590early_platform_init("earlytimer", &omap_dm_timer_driver);
591module_init(omap_dm_timer_driver_init);
592module_exit(omap_dm_timer_driver_exit);
593
594MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver");
595MODULE_LICENSE("GPL");
596MODULE_ALIAS("platform:" DRIVER_NAME);
597MODULE_AUTHOR("Texas Instruments Inc");
This page took 0.459836 seconds and 5 git commands to generate.