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 | ||
b1538832 | 38 | #include <linux/clk.h> |
869dec15 | 39 | #include <linux/module.h> |
fced80c7 | 40 | #include <linux/io.h> |
74dd9ec6 | 41 | #include <linux/device.h> |
3392cdd3 | 42 | #include <linux/err.h> |
ffe07cea | 43 | #include <linux/pm_runtime.h> |
9725f445 JH |
44 | #include <linux/of.h> |
45 | #include <linux/of_device.h> | |
40fc3bb5 JH |
46 | #include <linux/platform_device.h> |
47 | #include <linux/platform_data/dmtimer-omap.h> | |
44169075 | 48 | |
3392cdd3 | 49 | #include <plat/dmtimer.h> |
2c799cef | 50 | |
b7b4ff76 | 51 | static u32 omap_reserved_systimers; |
df28472a | 52 | static LIST_HEAD(omap_timer_list); |
3392cdd3 | 53 | static DEFINE_SPINLOCK(dm_timer_lock); |
92105bb7 | 54 | |
8fc7fcb5 JH |
55 | enum { |
56 | REQUEST_ANY = 0, | |
57 | REQUEST_BY_ID, | |
58 | REQUEST_BY_CAP, | |
59 | REQUEST_BY_NODE, | |
60 | }; | |
61 | ||
3392cdd3 TKD |
62 | /** |
63 | * omap_dm_timer_read_reg - read timer registers in posted and non-posted mode | |
64 | * @timer: timer pointer over which read operation to perform | |
65 | * @reg: lowest byte holds the register offset | |
66 | * | |
67 | * The posted mode bit is encoded in reg. Note that in posted mode write | |
68 | * pending bit must be checked. Otherwise a read of a non completed write | |
69 | * will produce an error. | |
0f0d0807 RW |
70 | */ |
71 | static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg) | |
77900a2f | 72 | { |
ee17f114 TL |
73 | WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET); |
74 | return __omap_dm_timer_read(timer, reg, timer->posted); | |
77900a2f | 75 | } |
92105bb7 | 76 | |
3392cdd3 TKD |
77 | /** |
78 | * omap_dm_timer_write_reg - write timer registers in posted and non-posted mode | |
79 | * @timer: timer pointer over which write operation is to perform | |
80 | * @reg: lowest byte holds the register offset | |
81 | * @value: data to write into the register | |
82 | * | |
83 | * The posted mode bit is encoded in reg. Note that in posted mode the write | |
84 | * pending bit must be checked. Otherwise a write on a register which has a | |
85 | * pending write will be lost. | |
0f0d0807 RW |
86 | */ |
87 | static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg, | |
88 | u32 value) | |
92105bb7 | 89 | { |
ee17f114 TL |
90 | WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET); |
91 | __omap_dm_timer_write(timer, reg, value, timer->posted); | |
92105bb7 TL |
92 | } |
93 | ||
b481113a TKD |
94 | static void omap_timer_restore_context(struct omap_dm_timer *timer) |
95 | { | |
b481113a TKD |
96 | omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, |
97 | timer->context.twer); | |
98 | omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, | |
99 | timer->context.tcrr); | |
100 | omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, | |
101 | timer->context.tldr); | |
102 | omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, | |
103 | timer->context.tmar); | |
104 | omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, | |
105 | timer->context.tsicr); | |
106 | __raw_writel(timer->context.tier, timer->irq_ena); | |
107 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, | |
108 | timer->context.tclr); | |
109 | } | |
110 | ||
ae6672cb | 111 | static int omap_dm_timer_reset(struct omap_dm_timer *timer) |
92105bb7 | 112 | { |
ae6672cb | 113 | u32 l, timeout = 100000; |
77900a2f | 114 | |
ae6672cb JH |
115 | if (timer->revision != 1) |
116 | return -EINVAL; | |
ee17f114 | 117 | |
ae6672cb JH |
118 | omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); |
119 | ||
120 | do { | |
121 | l = __omap_dm_timer_read(timer, | |
122 | OMAP_TIMER_V1_SYS_STAT_OFFSET, 0); | |
123 | } while (!l && timeout--); | |
124 | ||
125 | if (!timeout) { | |
126 | dev_err(&timer->pdev->dev, "Timer failed to reset\n"); | |
127 | return -ETIMEDOUT; | |
77900a2f | 128 | } |
92105bb7 | 129 | |
ae6672cb JH |
130 | /* Configure timer for smart-idle mode */ |
131 | l = __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0); | |
132 | l |= 0x2 << 0x3; | |
133 | __omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l, 0); | |
134 | ||
135 | timer->posted = 0; | |
136 | ||
137 | return 0; | |
77900a2f TT |
138 | } |
139 | ||
b0cadb3c | 140 | static int omap_dm_timer_prepare(struct omap_dm_timer *timer) |
77900a2f | 141 | { |
ae6672cb JH |
142 | int rc; |
143 | ||
bca45808 JH |
144 | /* |
145 | * FIXME: OMAP1 devices do not use the clock framework for dmtimers so | |
146 | * do not call clk_get() for these devices. | |
147 | */ | |
148 | if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) { | |
149 | timer->fclk = clk_get(&timer->pdev->dev, "fck"); | |
150 | if (WARN_ON_ONCE(IS_ERR_OR_NULL(timer->fclk))) { | |
151 | timer->fclk = NULL; | |
152 | dev_err(&timer->pdev->dev, ": No fclk handle.\n"); | |
153 | return -EINVAL; | |
154 | } | |
3392cdd3 TKD |
155 | } |
156 | ||
7b44cf2c JH |
157 | omap_dm_timer_enable(timer); |
158 | ||
ae6672cb JH |
159 | if (timer->capability & OMAP_TIMER_NEEDS_RESET) { |
160 | rc = omap_dm_timer_reset(timer); | |
161 | if (rc) { | |
162 | omap_dm_timer_disable(timer); | |
163 | return rc; | |
164 | } | |
165 | } | |
3392cdd3 | 166 | |
7b44cf2c JH |
167 | __omap_dm_timer_enable_posted(timer); |
168 | omap_dm_timer_disable(timer); | |
3392cdd3 | 169 | |
7b44cf2c | 170 | return omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); |
77900a2f TT |
171 | } |
172 | ||
b7b4ff76 JH |
173 | static inline u32 omap_dm_timer_reserved_systimer(int id) |
174 | { | |
175 | return (omap_reserved_systimers & (1 << (id - 1))) ? 1 : 0; | |
176 | } | |
177 | ||
178 | int omap_dm_timer_reserve_systimer(int id) | |
179 | { | |
180 | if (omap_dm_timer_reserved_systimer(id)) | |
181 | return -ENODEV; | |
182 | ||
183 | omap_reserved_systimers |= (1 << (id - 1)); | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
8fc7fcb5 | 188 | static struct omap_dm_timer *_omap_dm_timer_request(int req_type, void *data) |
77900a2f | 189 | { |
3392cdd3 | 190 | struct omap_dm_timer *timer = NULL, *t; |
8fc7fcb5 | 191 | struct device_node *np = NULL; |
77900a2f | 192 | unsigned long flags; |
8fc7fcb5 JH |
193 | u32 cap = 0; |
194 | int id = 0; | |
195 | ||
196 | switch (req_type) { | |
197 | case REQUEST_BY_ID: | |
198 | id = *(int *)data; | |
199 | break; | |
200 | case REQUEST_BY_CAP: | |
201 | cap = *(u32 *)data; | |
202 | break; | |
203 | case REQUEST_BY_NODE: | |
204 | np = (struct device_node *)data; | |
205 | break; | |
206 | default: | |
207 | /* REQUEST_ANY */ | |
208 | break; | |
209 | } | |
77900a2f TT |
210 | |
211 | spin_lock_irqsave(&dm_timer_lock, flags); | |
3392cdd3 TKD |
212 | list_for_each_entry(t, &omap_timer_list, node) { |
213 | if (t->reserved) | |
77900a2f TT |
214 | continue; |
215 | ||
8fc7fcb5 JH |
216 | switch (req_type) { |
217 | case REQUEST_BY_ID: | |
218 | if (id == t->pdev->id) { | |
219 | timer = t; | |
220 | timer->reserved = 1; | |
221 | goto found; | |
222 | } | |
223 | break; | |
224 | case REQUEST_BY_CAP: | |
225 | if (cap == (t->capability & cap)) { | |
226 | /* | |
227 | * If timer is not NULL, we have already found | |
228 | * one timer but it was not an exact match | |
229 | * because it had more capabilites that what | |
230 | * was required. Therefore, unreserve the last | |
231 | * timer found and see if this one is a better | |
232 | * match. | |
233 | */ | |
234 | if (timer) | |
235 | timer->reserved = 0; | |
236 | timer = t; | |
237 | timer->reserved = 1; | |
238 | ||
239 | /* Exit loop early if we find an exact match */ | |
240 | if (t->capability == cap) | |
241 | goto found; | |
242 | } | |
243 | break; | |
244 | case REQUEST_BY_NODE: | |
245 | if (np == t->pdev->dev.of_node) { | |
246 | timer = t; | |
247 | timer->reserved = 1; | |
248 | goto found; | |
249 | } | |
250 | break; | |
251 | default: | |
252 | /* REQUEST_ANY */ | |
253 | timer = t; | |
254 | timer->reserved = 1; | |
255 | goto found; | |
256 | } | |
77900a2f | 257 | } |
8fc7fcb5 | 258 | found: |
c5491d1a | 259 | spin_unlock_irqrestore(&dm_timer_lock, flags); |
3392cdd3 | 260 | |
8fc7fcb5 JH |
261 | if (timer && omap_dm_timer_prepare(timer)) { |
262 | timer->reserved = 0; | |
263 | timer = NULL; | |
3392cdd3 | 264 | } |
77900a2f | 265 | |
3392cdd3 TKD |
266 | if (!timer) |
267 | pr_debug("%s: timer request failed!\n", __func__); | |
83379c81 | 268 | |
77900a2f TT |
269 | return timer; |
270 | } | |
8fc7fcb5 JH |
271 | |
272 | struct omap_dm_timer *omap_dm_timer_request(void) | |
273 | { | |
274 | return _omap_dm_timer_request(REQUEST_ANY, NULL); | |
275 | } | |
6c366e32 | 276 | EXPORT_SYMBOL_GPL(omap_dm_timer_request); |
77900a2f TT |
277 | |
278 | struct omap_dm_timer *omap_dm_timer_request_specific(int id) | |
92105bb7 | 279 | { |
9725f445 JH |
280 | /* Requesting timer by ID is not supported when device tree is used */ |
281 | if (of_have_populated_dt()) { | |
8fc7fcb5 | 282 | pr_warn("%s: Please use omap_dm_timer_request_by_cap/node()\n", |
9725f445 JH |
283 | __func__); |
284 | return NULL; | |
285 | } | |
286 | ||
8fc7fcb5 | 287 | return _omap_dm_timer_request(REQUEST_BY_ID, &id); |
92105bb7 | 288 | } |
6c366e32 | 289 | EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific); |
92105bb7 | 290 | |
373fe0bd JH |
291 | /** |
292 | * omap_dm_timer_request_by_cap - Request a timer by capability | |
293 | * @cap: Bit mask of capabilities to match | |
294 | * | |
295 | * Find a timer based upon capabilities bit mask. Callers of this function | |
296 | * should use the definitions found in the plat/dmtimer.h file under the | |
297 | * comment "timer capabilities used in hwmod database". Returns pointer to | |
298 | * timer handle on success and a NULL pointer on failure. | |
299 | */ | |
300 | struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap) | |
301 | { | |
8fc7fcb5 JH |
302 | return _omap_dm_timer_request(REQUEST_BY_CAP, &cap); |
303 | } | |
304 | EXPORT_SYMBOL_GPL(omap_dm_timer_request_by_cap); | |
373fe0bd | 305 | |
8fc7fcb5 JH |
306 | /** |
307 | * omap_dm_timer_request_by_node - Request a timer by device-tree node | |
308 | * @np: Pointer to device-tree timer node | |
309 | * | |
310 | * Request a timer based upon a device node pointer. Returns pointer to | |
311 | * timer handle on success and a NULL pointer on failure. | |
312 | */ | |
313 | struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np) | |
314 | { | |
315 | if (!np) | |
373fe0bd JH |
316 | return NULL; |
317 | ||
8fc7fcb5 | 318 | return _omap_dm_timer_request(REQUEST_BY_NODE, np); |
373fe0bd | 319 | } |
8fc7fcb5 | 320 | EXPORT_SYMBOL_GPL(omap_dm_timer_request_by_node); |
373fe0bd | 321 | |
ab4eb8b0 | 322 | int omap_dm_timer_free(struct omap_dm_timer *timer) |
77900a2f | 323 | { |
ab4eb8b0 TKD |
324 | if (unlikely(!timer)) |
325 | return -EINVAL; | |
326 | ||
3392cdd3 | 327 | clk_put(timer->fclk); |
fa4bb626 | 328 | |
77900a2f TT |
329 | WARN_ON(!timer->reserved); |
330 | timer->reserved = 0; | |
ab4eb8b0 | 331 | return 0; |
77900a2f | 332 | } |
6c366e32 | 333 | EXPORT_SYMBOL_GPL(omap_dm_timer_free); |
77900a2f | 334 | |
12583a70 TT |
335 | void omap_dm_timer_enable(struct omap_dm_timer *timer) |
336 | { | |
9cc268d5 N |
337 | int c; |
338 | ||
ffe07cea | 339 | pm_runtime_get_sync(&timer->pdev->dev); |
9cc268d5 N |
340 | |
341 | if (!(timer->capability & OMAP_TIMER_ALWON)) { | |
342 | if (timer->get_context_loss_count) { | |
343 | c = timer->get_context_loss_count(&timer->pdev->dev); | |
344 | if (c != timer->ctx_loss_count) { | |
345 | omap_timer_restore_context(timer); | |
346 | timer->ctx_loss_count = c; | |
347 | } | |
385c4c7b JH |
348 | } else { |
349 | omap_timer_restore_context(timer); | |
9cc268d5 N |
350 | } |
351 | } | |
12583a70 | 352 | } |
6c366e32 | 353 | EXPORT_SYMBOL_GPL(omap_dm_timer_enable); |
12583a70 TT |
354 | |
355 | void omap_dm_timer_disable(struct omap_dm_timer *timer) | |
356 | { | |
54f32a35 | 357 | pm_runtime_put_sync(&timer->pdev->dev); |
12583a70 | 358 | } |
6c366e32 | 359 | EXPORT_SYMBOL_GPL(omap_dm_timer_disable); |
12583a70 | 360 | |
77900a2f TT |
361 | int omap_dm_timer_get_irq(struct omap_dm_timer *timer) |
362 | { | |
ab4eb8b0 TKD |
363 | if (timer) |
364 | return timer->irq; | |
365 | return -EINVAL; | |
77900a2f | 366 | } |
6c366e32 | 367 | EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq); |
77900a2f TT |
368 | |
369 | #if defined(CONFIG_ARCH_OMAP1) | |
7136f8d8 | 370 | #include <mach/hardware.h> |
a569c6ec TL |
371 | /** |
372 | * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR | |
373 | * @inputmask: current value of idlect mask | |
374 | */ | |
375 | __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) | |
376 | { | |
3392cdd3 TKD |
377 | int i = 0; |
378 | struct omap_dm_timer *timer = NULL; | |
379 | unsigned long flags; | |
a569c6ec TL |
380 | |
381 | /* If ARMXOR cannot be idled this function call is unnecessary */ | |
382 | if (!(inputmask & (1 << 1))) | |
383 | return inputmask; | |
384 | ||
385 | /* If any active timer is using ARMXOR return modified mask */ | |
3392cdd3 TKD |
386 | spin_lock_irqsave(&dm_timer_lock, flags); |
387 | list_for_each_entry(timer, &omap_timer_list, node) { | |
77900a2f TT |
388 | u32 l; |
389 | ||
3392cdd3 | 390 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
77900a2f TT |
391 | if (l & OMAP_TIMER_CTRL_ST) { |
392 | if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0) | |
a569c6ec TL |
393 | inputmask &= ~(1 << 1); |
394 | else | |
395 | inputmask &= ~(1 << 2); | |
396 | } | |
3392cdd3 | 397 | i++; |
77900a2f | 398 | } |
3392cdd3 | 399 | spin_unlock_irqrestore(&dm_timer_lock, flags); |
a569c6ec TL |
400 | |
401 | return inputmask; | |
402 | } | |
6c366e32 | 403 | EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask); |
a569c6ec | 404 | |
140455fa | 405 | #else |
a569c6ec | 406 | |
77900a2f | 407 | struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) |
92105bb7 | 408 | { |
ab4eb8b0 TKD |
409 | if (timer) |
410 | return timer->fclk; | |
411 | return NULL; | |
77900a2f | 412 | } |
6c366e32 | 413 | EXPORT_SYMBOL_GPL(omap_dm_timer_get_fclk); |
92105bb7 | 414 | |
77900a2f TT |
415 | __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) |
416 | { | |
417 | BUG(); | |
2121880e DB |
418 | |
419 | return 0; | |
92105bb7 | 420 | } |
6c366e32 | 421 | EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask); |
92105bb7 | 422 | |
77900a2f | 423 | #endif |
92105bb7 | 424 | |
ab4eb8b0 | 425 | int omap_dm_timer_trigger(struct omap_dm_timer *timer) |
92105bb7 | 426 | { |
ab4eb8b0 TKD |
427 | if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { |
428 | pr_err("%s: timer not available or enabled.\n", __func__); | |
429 | return -EINVAL; | |
b481113a TKD |
430 | } |
431 | ||
77900a2f | 432 | omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); |
ab4eb8b0 | 433 | return 0; |
92105bb7 | 434 | } |
6c366e32 | 435 | EXPORT_SYMBOL_GPL(omap_dm_timer_trigger); |
92105bb7 | 436 | |
ab4eb8b0 | 437 | int omap_dm_timer_start(struct omap_dm_timer *timer) |
77900a2f TT |
438 | { |
439 | u32 l; | |
92105bb7 | 440 | |
ab4eb8b0 TKD |
441 | if (unlikely(!timer)) |
442 | return -EINVAL; | |
443 | ||
b481113a TKD |
444 | omap_dm_timer_enable(timer); |
445 | ||
77900a2f TT |
446 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
447 | if (!(l & OMAP_TIMER_CTRL_ST)) { | |
448 | l |= OMAP_TIMER_CTRL_ST; | |
449 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); | |
450 | } | |
b481113a TKD |
451 | |
452 | /* Save the context */ | |
453 | timer->context.tclr = l; | |
ab4eb8b0 | 454 | return 0; |
77900a2f | 455 | } |
6c366e32 | 456 | EXPORT_SYMBOL_GPL(omap_dm_timer_start); |
92105bb7 | 457 | |
ab4eb8b0 | 458 | int omap_dm_timer_stop(struct omap_dm_timer *timer) |
92105bb7 | 459 | { |
caf64f2f | 460 | unsigned long rate = 0; |
92105bb7 | 461 | |
ab4eb8b0 TKD |
462 | if (unlikely(!timer)) |
463 | return -EINVAL; | |
464 | ||
6615975b | 465 | if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) |
3392cdd3 | 466 | rate = clk_get_rate(timer->fclk); |
caf64f2f | 467 | |
ee17f114 | 468 | __omap_dm_timer_stop(timer, timer->posted, rate); |
ab4eb8b0 | 469 | |
dffc9dae TKD |
470 | /* |
471 | * Since the register values are computed and written within | |
472 | * __omap_dm_timer_stop, we need to use read to retrieve the | |
473 | * context. | |
474 | */ | |
475 | timer->context.tclr = | |
476 | omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); | |
dffc9dae | 477 | omap_dm_timer_disable(timer); |
ab4eb8b0 | 478 | return 0; |
92105bb7 | 479 | } |
6c366e32 | 480 | EXPORT_SYMBOL_GPL(omap_dm_timer_stop); |
92105bb7 | 481 | |
f248076c | 482 | int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) |
92105bb7 | 483 | { |
3392cdd3 | 484 | int ret; |
2b2d3523 | 485 | char *parent_name = NULL; |
d7aba554 | 486 | struct clk *parent; |
ab4eb8b0 TKD |
487 | struct dmtimer_platform_data *pdata; |
488 | ||
489 | if (unlikely(!timer)) | |
490 | return -EINVAL; | |
491 | ||
492 | pdata = timer->pdev->dev.platform_data; | |
3392cdd3 | 493 | |
77900a2f | 494 | if (source < 0 || source >= 3) |
f248076c | 495 | return -EINVAL; |
77900a2f | 496 | |
2b2d3523 JH |
497 | /* |
498 | * FIXME: Used for OMAP1 devices only because they do not currently | |
499 | * use the clock framework to set the parent clock. To be removed | |
500 | * once OMAP1 migrated to using clock framework for dmtimers | |
501 | */ | |
9725f445 | 502 | if (pdata && pdata->set_timer_src) |
2b2d3523 JH |
503 | return pdata->set_timer_src(timer->pdev, source); |
504 | ||
d7aba554 | 505 | if (!timer->fclk) |
2b2d3523 | 506 | return -EINVAL; |
2b2d3523 JH |
507 | |
508 | switch (source) { | |
509 | case OMAP_TIMER_SRC_SYS_CLK: | |
c59b537d | 510 | parent_name = "timer_sys_ck"; |
2b2d3523 JH |
511 | break; |
512 | ||
513 | case OMAP_TIMER_SRC_32_KHZ: | |
c59b537d | 514 | parent_name = "timer_32k_ck"; |
2b2d3523 JH |
515 | break; |
516 | ||
517 | case OMAP_TIMER_SRC_EXT_CLK: | |
c59b537d | 518 | parent_name = "timer_ext_ck"; |
2b2d3523 JH |
519 | break; |
520 | } | |
521 | ||
522 | parent = clk_get(&timer->pdev->dev, parent_name); | |
523 | if (IS_ERR_OR_NULL(parent)) { | |
524 | pr_err("%s: %s not found\n", __func__, parent_name); | |
d7aba554 | 525 | return -EINVAL; |
2b2d3523 JH |
526 | } |
527 | ||
d7aba554 | 528 | ret = clk_set_parent(timer->fclk, parent); |
2b2d3523 JH |
529 | if (IS_ERR_VALUE(ret)) |
530 | pr_err("%s: failed to set %s as parent\n", __func__, | |
531 | parent_name); | |
532 | ||
533 | clk_put(parent); | |
3392cdd3 TKD |
534 | |
535 | return ret; | |
92105bb7 | 536 | } |
6c366e32 | 537 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_source); |
92105bb7 | 538 | |
ab4eb8b0 | 539 | int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, |
77900a2f | 540 | unsigned int load) |
92105bb7 TL |
541 | { |
542 | u32 l; | |
77900a2f | 543 | |
ab4eb8b0 TKD |
544 | if (unlikely(!timer)) |
545 | return -EINVAL; | |
546 | ||
b481113a | 547 | omap_dm_timer_enable(timer); |
92105bb7 | 548 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
77900a2f TT |
549 | if (autoreload) |
550 | l |= OMAP_TIMER_CTRL_AR; | |
551 | else | |
552 | l &= ~OMAP_TIMER_CTRL_AR; | |
92105bb7 | 553 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
77900a2f | 554 | omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); |
0f0d0807 | 555 | |
77900a2f | 556 | omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); |
b481113a TKD |
557 | /* Save the context */ |
558 | timer->context.tclr = l; | |
559 | timer->context.tldr = load; | |
560 | omap_dm_timer_disable(timer); | |
ab4eb8b0 | 561 | return 0; |
92105bb7 | 562 | } |
6c366e32 | 563 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_load); |
92105bb7 | 564 | |
3fddd09e | 565 | /* Optimized set_load which removes costly spin wait in timer_start */ |
ab4eb8b0 | 566 | int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, |
3fddd09e RW |
567 | unsigned int load) |
568 | { | |
569 | u32 l; | |
570 | ||
ab4eb8b0 TKD |
571 | if (unlikely(!timer)) |
572 | return -EINVAL; | |
573 | ||
b481113a TKD |
574 | omap_dm_timer_enable(timer); |
575 | ||
3fddd09e | 576 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
64ce2907 | 577 | if (autoreload) { |
3fddd09e | 578 | l |= OMAP_TIMER_CTRL_AR; |
64ce2907 PW |
579 | omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); |
580 | } else { | |
3fddd09e | 581 | l &= ~OMAP_TIMER_CTRL_AR; |
64ce2907 | 582 | } |
3fddd09e RW |
583 | l |= OMAP_TIMER_CTRL_ST; |
584 | ||
ee17f114 | 585 | __omap_dm_timer_load_start(timer, l, load, timer->posted); |
b481113a TKD |
586 | |
587 | /* Save the context */ | |
588 | timer->context.tclr = l; | |
589 | timer->context.tldr = load; | |
590 | timer->context.tcrr = load; | |
ab4eb8b0 | 591 | return 0; |
3fddd09e | 592 | } |
6c366e32 | 593 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start); |
3fddd09e | 594 | |
ab4eb8b0 | 595 | int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, |
77900a2f | 596 | unsigned int match) |
92105bb7 TL |
597 | { |
598 | u32 l; | |
599 | ||
ab4eb8b0 TKD |
600 | if (unlikely(!timer)) |
601 | return -EINVAL; | |
602 | ||
b481113a | 603 | omap_dm_timer_enable(timer); |
92105bb7 | 604 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
83379c81 | 605 | if (enable) |
77900a2f TT |
606 | l |= OMAP_TIMER_CTRL_CE; |
607 | else | |
608 | l &= ~OMAP_TIMER_CTRL_CE; | |
77900a2f | 609 | omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); |
991ad16a | 610 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
b481113a TKD |
611 | |
612 | /* Save the context */ | |
613 | timer->context.tclr = l; | |
614 | timer->context.tmar = match; | |
615 | omap_dm_timer_disable(timer); | |
ab4eb8b0 | 616 | return 0; |
92105bb7 | 617 | } |
6c366e32 | 618 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_match); |
92105bb7 | 619 | |
ab4eb8b0 | 620 | int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, |
77900a2f | 621 | int toggle, int trigger) |
92105bb7 TL |
622 | { |
623 | u32 l; | |
624 | ||
ab4eb8b0 TKD |
625 | if (unlikely(!timer)) |
626 | return -EINVAL; | |
627 | ||
b481113a | 628 | omap_dm_timer_enable(timer); |
92105bb7 | 629 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
77900a2f TT |
630 | l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM | |
631 | OMAP_TIMER_CTRL_PT | (0x03 << 10)); | |
632 | if (def_on) | |
633 | l |= OMAP_TIMER_CTRL_SCPWM; | |
634 | if (toggle) | |
635 | l |= OMAP_TIMER_CTRL_PT; | |
636 | l |= trigger << 10; | |
92105bb7 | 637 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
b481113a TKD |
638 | |
639 | /* Save the context */ | |
640 | timer->context.tclr = l; | |
641 | omap_dm_timer_disable(timer); | |
ab4eb8b0 | 642 | return 0; |
92105bb7 | 643 | } |
6c366e32 | 644 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm); |
92105bb7 | 645 | |
ab4eb8b0 | 646 | int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler) |
92105bb7 TL |
647 | { |
648 | u32 l; | |
649 | ||
ab4eb8b0 TKD |
650 | if (unlikely(!timer)) |
651 | return -EINVAL; | |
652 | ||
b481113a | 653 | omap_dm_timer_enable(timer); |
92105bb7 | 654 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); |
77900a2f TT |
655 | l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2)); |
656 | if (prescaler >= 0x00 && prescaler <= 0x07) { | |
657 | l |= OMAP_TIMER_CTRL_PRE; | |
658 | l |= prescaler << 2; | |
659 | } | |
92105bb7 | 660 | omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); |
b481113a TKD |
661 | |
662 | /* Save the context */ | |
663 | timer->context.tclr = l; | |
664 | omap_dm_timer_disable(timer); | |
ab4eb8b0 | 665 | return 0; |
92105bb7 | 666 | } |
6c366e32 | 667 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler); |
92105bb7 | 668 | |
ab4eb8b0 | 669 | int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, |
77900a2f | 670 | unsigned int value) |
92105bb7 | 671 | { |
ab4eb8b0 TKD |
672 | if (unlikely(!timer)) |
673 | return -EINVAL; | |
674 | ||
b481113a | 675 | omap_dm_timer_enable(timer); |
ee17f114 | 676 | __omap_dm_timer_int_enable(timer, value); |
b481113a TKD |
677 | |
678 | /* Save the context */ | |
679 | timer->context.tier = value; | |
680 | timer->context.twer = value; | |
681 | omap_dm_timer_disable(timer); | |
ab4eb8b0 | 682 | return 0; |
92105bb7 | 683 | } |
6c366e32 | 684 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable); |
92105bb7 | 685 | |
4249d96c JH |
686 | /** |
687 | * omap_dm_timer_set_int_disable - disable timer interrupts | |
688 | * @timer: pointer to timer handle | |
689 | * @mask: bit mask of interrupts to be disabled | |
690 | * | |
691 | * Disables the specified timer interrupts for a timer. | |
692 | */ | |
693 | int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask) | |
694 | { | |
695 | u32 l = mask; | |
696 | ||
697 | if (unlikely(!timer)) | |
698 | return -EINVAL; | |
699 | ||
700 | omap_dm_timer_enable(timer); | |
701 | ||
702 | if (timer->revision == 1) | |
703 | l = __raw_readl(timer->irq_ena) & ~mask; | |
704 | ||
705 | __raw_writel(l, timer->irq_dis); | |
706 | l = omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask; | |
707 | omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, l); | |
708 | ||
709 | /* Save the context */ | |
710 | timer->context.tier &= ~mask; | |
711 | timer->context.twer &= ~mask; | |
712 | omap_dm_timer_disable(timer); | |
713 | return 0; | |
714 | } | |
715 | EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_disable); | |
716 | ||
77900a2f | 717 | unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer) |
92105bb7 | 718 | { |
fa4bb626 TT |
719 | unsigned int l; |
720 | ||
ab4eb8b0 TKD |
721 | if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { |
722 | pr_err("%s: timer not available or enabled.\n", __func__); | |
b481113a TKD |
723 | return 0; |
724 | } | |
725 | ||
ee17f114 | 726 | l = __raw_readl(timer->irq_stat); |
fa4bb626 TT |
727 | |
728 | return l; | |
92105bb7 | 729 | } |
6c366e32 | 730 | EXPORT_SYMBOL_GPL(omap_dm_timer_read_status); |
92105bb7 | 731 | |
ab4eb8b0 | 732 | int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) |
92105bb7 | 733 | { |
ab4eb8b0 TKD |
734 | if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) |
735 | return -EINVAL; | |
736 | ||
ee17f114 | 737 | __omap_dm_timer_write_status(timer, value); |
1eaff710 | 738 | |
ab4eb8b0 | 739 | return 0; |
92105bb7 | 740 | } |
6c366e32 | 741 | EXPORT_SYMBOL_GPL(omap_dm_timer_write_status); |
92105bb7 | 742 | |
77900a2f | 743 | unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) |
92105bb7 | 744 | { |
ab4eb8b0 TKD |
745 | if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { |
746 | pr_err("%s: timer not iavailable or enabled.\n", __func__); | |
b481113a TKD |
747 | return 0; |
748 | } | |
749 | ||
ee17f114 | 750 | return __omap_dm_timer_read_counter(timer, timer->posted); |
92105bb7 | 751 | } |
6c366e32 | 752 | EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter); |
92105bb7 | 753 | |
ab4eb8b0 | 754 | int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value) |
83379c81 | 755 | { |
ab4eb8b0 TKD |
756 | if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { |
757 | pr_err("%s: timer not available or enabled.\n", __func__); | |
758 | return -EINVAL; | |
b481113a TKD |
759 | } |
760 | ||
fa4bb626 | 761 | omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value); |
b481113a TKD |
762 | |
763 | /* Save the context */ | |
764 | timer->context.tcrr = value; | |
ab4eb8b0 | 765 | return 0; |
83379c81 | 766 | } |
6c366e32 | 767 | EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter); |
83379c81 | 768 | |
77900a2f | 769 | int omap_dm_timers_active(void) |
92105bb7 | 770 | { |
3392cdd3 | 771 | struct omap_dm_timer *timer; |
12583a70 | 772 | |
3392cdd3 | 773 | list_for_each_entry(timer, &omap_timer_list, node) { |
ffe07cea | 774 | if (!timer->reserved) |
12583a70 TT |
775 | continue; |
776 | ||
77900a2f | 777 | if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) & |
fa4bb626 | 778 | OMAP_TIMER_CTRL_ST) { |
77900a2f | 779 | return 1; |
fa4bb626 | 780 | } |
77900a2f TT |
781 | } |
782 | return 0; | |
783 | } | |
6c366e32 | 784 | EXPORT_SYMBOL_GPL(omap_dm_timers_active); |
92105bb7 | 785 | |
df28472a TKD |
786 | /** |
787 | * omap_dm_timer_probe - probe function called for every registered device | |
788 | * @pdev: pointer to current timer platform device | |
789 | * | |
790 | * Called by driver framework at the end of device registration for all | |
791 | * timer devices. | |
792 | */ | |
351a102d | 793 | static int omap_dm_timer_probe(struct platform_device *pdev) |
df28472a | 794 | { |
df28472a TKD |
795 | unsigned long flags; |
796 | struct omap_dm_timer *timer; | |
74dd9ec6 TKD |
797 | struct resource *mem, *irq; |
798 | struct device *dev = &pdev->dev; | |
df28472a TKD |
799 | struct dmtimer_platform_data *pdata = pdev->dev.platform_data; |
800 | ||
9725f445 | 801 | if (!pdata && !dev->of_node) { |
74dd9ec6 | 802 | dev_err(dev, "%s: no platform data.\n", __func__); |
df28472a TKD |
803 | return -ENODEV; |
804 | } | |
805 | ||
806 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | |
807 | if (unlikely(!irq)) { | |
74dd9ec6 | 808 | dev_err(dev, "%s: no IRQ resource.\n", __func__); |
df28472a TKD |
809 | return -ENODEV; |
810 | } | |
811 | ||
812 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
813 | if (unlikely(!mem)) { | |
74dd9ec6 | 814 | dev_err(dev, "%s: no memory resource.\n", __func__); |
df28472a TKD |
815 | return -ENODEV; |
816 | } | |
817 | ||
74dd9ec6 | 818 | timer = devm_kzalloc(dev, sizeof(struct omap_dm_timer), GFP_KERNEL); |
df28472a | 819 | if (!timer) { |
74dd9ec6 TKD |
820 | dev_err(dev, "%s: memory alloc failed!\n", __func__); |
821 | return -ENOMEM; | |
df28472a TKD |
822 | } |
823 | ||
5857bd98 TR |
824 | timer->io_base = devm_ioremap_resource(dev, mem); |
825 | if (IS_ERR(timer->io_base)) | |
826 | return PTR_ERR(timer->io_base); | |
df28472a | 827 | |
9725f445 JH |
828 | if (dev->of_node) { |
829 | if (of_find_property(dev->of_node, "ti,timer-alwon", NULL)) | |
830 | timer->capability |= OMAP_TIMER_ALWON; | |
831 | if (of_find_property(dev->of_node, "ti,timer-dsp", NULL)) | |
832 | timer->capability |= OMAP_TIMER_HAS_DSP_IRQ; | |
833 | if (of_find_property(dev->of_node, "ti,timer-pwm", NULL)) | |
834 | timer->capability |= OMAP_TIMER_HAS_PWM; | |
835 | if (of_find_property(dev->of_node, "ti,timer-secure", NULL)) | |
836 | timer->capability |= OMAP_TIMER_SECURE; | |
837 | } else { | |
838 | timer->id = pdev->id; | |
bfd6d021 | 839 | timer->errata = pdata->timer_errata; |
9725f445 JH |
840 | timer->capability = pdata->timer_capability; |
841 | timer->reserved = omap_dm_timer_reserved_systimer(timer->id); | |
f56f52e0 | 842 | timer->get_context_loss_count = pdata->get_context_loss_count; |
9725f445 JH |
843 | } |
844 | ||
df28472a TKD |
845 | timer->irq = irq->start; |
846 | timer->pdev = pdev; | |
df28472a | 847 | |
ffe07cea | 848 | /* Skip pm_runtime_enable for OMAP1 */ |
6615975b | 849 | if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) { |
74dd9ec6 TKD |
850 | pm_runtime_enable(dev); |
851 | pm_runtime_irq_safe(dev); | |
ffe07cea TKD |
852 | } |
853 | ||
0dad9fae | 854 | if (!timer->reserved) { |
74dd9ec6 | 855 | pm_runtime_get_sync(dev); |
0dad9fae | 856 | __omap_dm_timer_init_regs(timer); |
74dd9ec6 | 857 | pm_runtime_put(dev); |
0dad9fae TL |
858 | } |
859 | ||
df28472a TKD |
860 | /* add the timer element to the list */ |
861 | spin_lock_irqsave(&dm_timer_lock, flags); | |
862 | list_add_tail(&timer->node, &omap_timer_list); | |
863 | spin_unlock_irqrestore(&dm_timer_lock, flags); | |
864 | ||
74dd9ec6 | 865 | dev_dbg(dev, "Device Probed.\n"); |
df28472a TKD |
866 | |
867 | return 0; | |
df28472a TKD |
868 | } |
869 | ||
870 | /** | |
871 | * omap_dm_timer_remove - cleanup a registered timer device | |
872 | * @pdev: pointer to current timer platform device | |
873 | * | |
874 | * Called by driver framework whenever a timer device is unregistered. | |
875 | * In addition to freeing platform resources it also deletes the timer | |
876 | * entry from the local list. | |
877 | */ | |
351a102d | 878 | static int omap_dm_timer_remove(struct platform_device *pdev) |
df28472a TKD |
879 | { |
880 | struct omap_dm_timer *timer; | |
881 | unsigned long flags; | |
882 | int ret = -EINVAL; | |
883 | ||
884 | spin_lock_irqsave(&dm_timer_lock, flags); | |
885 | list_for_each_entry(timer, &omap_timer_list, node) | |
9725f445 JH |
886 | if (!strcmp(dev_name(&timer->pdev->dev), |
887 | dev_name(&pdev->dev))) { | |
df28472a | 888 | list_del(&timer->node); |
df28472a TKD |
889 | ret = 0; |
890 | break; | |
891 | } | |
892 | spin_unlock_irqrestore(&dm_timer_lock, flags); | |
893 | ||
894 | return ret; | |
895 | } | |
896 | ||
9725f445 | 897 | static const struct of_device_id omap_timer_match[] = { |
002e1ec5 JH |
898 | { .compatible = "ti,omap2420-timer", }, |
899 | { .compatible = "ti,omap3430-timer", }, | |
900 | { .compatible = "ti,omap4430-timer", }, | |
901 | { .compatible = "ti,omap5430-timer", }, | |
902 | { .compatible = "ti,am335x-timer", }, | |
903 | { .compatible = "ti,am335x-timer-1ms", }, | |
9725f445 JH |
904 | {}, |
905 | }; | |
906 | MODULE_DEVICE_TABLE(of, omap_timer_match); | |
907 | ||
df28472a TKD |
908 | static struct platform_driver omap_dm_timer_driver = { |
909 | .probe = omap_dm_timer_probe, | |
351a102d | 910 | .remove = omap_dm_timer_remove, |
df28472a TKD |
911 | .driver = { |
912 | .name = "omap_timer", | |
9725f445 | 913 | .of_match_table = of_match_ptr(omap_timer_match), |
df28472a TKD |
914 | }, |
915 | }; | |
916 | ||
df28472a | 917 | early_platform_init("earlytimer", &omap_dm_timer_driver); |
e4e9f7ea | 918 | module_platform_driver(omap_dm_timer_driver); |
df28472a TKD |
919 | |
920 | MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver"); | |
921 | MODULE_LICENSE("GPL"); | |
922 | MODULE_ALIAS("platform:" DRIVER_NAME); | |
923 | MODULE_AUTHOR("Texas Instruments Inc"); |