blob: 43eb75038ba2ecd50642ce86c635f379d5e1df34 [file] [log] [blame]
Tony Lindgren92105bb2005-09-07 17:20:26 +01001/*
2 * linux/arch/arm/plat-omap/dmtimer.c
3 *
4 * OMAP Dual-Mode Timers
5 *
Tarun Kanti DebBarma97933d62011-09-20 17:00:17 +05306 * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
7 * Tarun Kanti DebBarma <tarun.kanti@ti.com>
8 * Thara Gopinath <thara@ti.com>
9 *
10 * dmtimer adaptation to platform_driver.
11 *
Tony Lindgren92105bb2005-09-07 17:20:26 +010012 * Copyright (C) 2005 Nokia Corporation
Timo Teras77900a22006-06-26 16:16:12 -070013 * OMAP2 support by Juha Yrjola
14 * API improvements and OMAP2 clock framework support by Timo Teras
Tony Lindgren92105bb2005-09-07 17:20:26 +010015 *
Santosh Shilimkar44169072009-05-28 14:16:04 -070016 * Copyright (C) 2009 Texas Instruments
17 * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
18 *
Tony Lindgren92105bb2005-09-07 17:20:26 +010019 * This program is free software; you can redistribute it and/or modify it
20 * under the terms of the GNU General Public License as published by the
21 * Free Software Foundation; either version 2 of the License, or (at your
22 * option) any later version.
23 *
24 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
27 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * You should have received a copy of the GNU General Public License along
34 * with this program; if not, write to the Free Software Foundation, Inc.,
35 * 675 Mass Ave, Cambridge, MA 02139, USA.
36 */
37
Russell Kingfced80c2008-09-06 12:10:45 +010038#include <linux/io.h>
Tarun Kanti DebBarmadf284722011-09-20 17:00:19 +053039#include <linux/slab.h>
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +053040#include <linux/err.h>
Tarun Kanti DebBarmaffe07ce2011-09-20 17:00:21 +053041#include <linux/pm_runtime.h>
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +053042
Tony Lindgrence491cf2009-10-20 09:40:47 -070043#include <plat/dmtimer.h>
Tony Lindgren92105bb2005-09-07 17:20:26 +010044
Tarun Kanti DebBarmadf284722011-09-20 17:00:19 +053045static LIST_HEAD(omap_timer_list);
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +053046static DEFINE_SPINLOCK(dm_timer_lock);
Tony Lindgren92105bb2005-09-07 17:20:26 +010047
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +053048/**
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.
Richard Woodruff0f0d0802008-07-03 12:24:30 +030056 */
57static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
Tony Lindgren92105bb2005-09-07 17:20:26 +010058{
Tony Lindgrenee17f112011-09-16 15:44:20 -070059 WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
60 return __omap_dm_timer_read(timer, reg, timer->posted);
Timo Teras77900a22006-06-26 16:16:12 -070061}
62
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +053063/**
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.
Richard Woodruff0f0d0802008-07-03 12:24:30 +030072 */
73static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
74 u32 value)
Timo Teras77900a22006-06-26 16:16:12 -070075{
Tony Lindgrenee17f112011-09-16 15:44:20 -070076 WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
77 __omap_dm_timer_write(timer, reg, value, timer->posted);
Tony Lindgren92105bb2005-09-07 17:20:26 +010078}
79
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +053080static 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);
101}
102
Timo Teras77900a22006-06-26 16:16:12 -0700103static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100104{
Timo Teras77900a22006-06-26 16:16:12 -0700105 int c;
106
Tony Lindgrenee17f112011-09-16 15:44:20 -0700107 if (!timer->sys_stat)
108 return;
109
Timo Teras77900a22006-06-26 16:16:12 -0700110 c = 0;
Tony Lindgrenee17f112011-09-16 15:44:20 -0700111 while (!(__raw_readl(timer->sys_stat) & 1)) {
Timo Teras77900a22006-06-26 16:16:12 -0700112 c++;
113 if (c > 100000) {
114 printk(KERN_ERR "Timer failed to reset\n");
115 return;
116 }
117 }
Tony Lindgren92105bb2005-09-07 17:20:26 +0100118}
119
Timo Teras77900a22006-06-26 16:16:12 -0700120static void omap_dm_timer_reset(struct omap_dm_timer *timer)
121{
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530122 omap_dm_timer_enable(timer);
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530123 if (timer->pdev->id != 1) {
Timo Terase32f7ec2006-06-26 16:16:13 -0700124 omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
125 omap_dm_timer_wait_for_reset(timer);
126 }
Timo Teras77900a22006-06-26 16:16:12 -0700127
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530128 __omap_dm_timer_reset(timer, 0, 0);
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530129 omap_dm_timer_disable(timer);
Richard Woodruff0f0d0802008-07-03 12:24:30 +0300130 timer->posted = 1;
Timo Teras77900a22006-06-26 16:16:12 -0700131}
132
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530133int omap_dm_timer_prepare(struct omap_dm_timer *timer)
Timo Teras77900a22006-06-26 16:16:12 -0700134{
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530135 struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
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
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530145 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;
Timo Teras77900a22006-06-26 16:16:12 -0700152}
153
154struct omap_dm_timer *omap_dm_timer_request(void)
155{
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530156 struct omap_dm_timer *timer = NULL, *t;
Timo Teras77900a22006-06-26 16:16:12 -0700157 unsigned long flags;
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530158 int ret = 0;
Timo Teras77900a22006-06-26 16:16:12 -0700159
160 spin_lock_irqsave(&dm_timer_lock, flags);
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530161 list_for_each_entry(t, &omap_timer_list, node) {
162 if (t->reserved)
Timo Teras77900a22006-06-26 16:16:12 -0700163 continue;
164
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530165 timer = t;
Timo Teras83379c82006-06-26 16:16:23 -0700166 timer->reserved = 1;
Timo Teras77900a22006-06-26 16:16:12 -0700167 break;
168 }
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530169
170 if (timer) {
171 ret = omap_dm_timer_prepare(timer);
172 if (ret) {
173 timer->reserved = 0;
174 timer = NULL;
175 }
176 }
Timo Teras77900a22006-06-26 16:16:12 -0700177 spin_unlock_irqrestore(&dm_timer_lock, flags);
178
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530179 if (!timer)
180 pr_debug("%s: timer request failed!\n", __func__);
Timo Teras83379c82006-06-26 16:16:23 -0700181
Timo Teras77900a22006-06-26 16:16:12 -0700182 return timer;
183}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700184EXPORT_SYMBOL_GPL(omap_dm_timer_request);
Timo Teras77900a22006-06-26 16:16:12 -0700185
186struct omap_dm_timer *omap_dm_timer_request_specific(int id)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100187{
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530188 struct omap_dm_timer *timer = NULL, *t;
Timo Teras77900a22006-06-26 16:16:12 -0700189 unsigned long flags;
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530190 int ret = 0;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100191
Timo Teras77900a22006-06-26 16:16:12 -0700192 spin_lock_irqsave(&dm_timer_lock, flags);
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530193 list_for_each_entry(t, &omap_timer_list, node) {
194 if (t->pdev->id == id && !t->reserved) {
195 timer = t;
196 timer->reserved = 1;
197 break;
198 }
Timo Teras77900a22006-06-26 16:16:12 -0700199 }
Tony Lindgren92105bb2005-09-07 17:20:26 +0100200
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530201 if (timer) {
202 ret = omap_dm_timer_prepare(timer);
203 if (ret) {
204 timer->reserved = 0;
205 timer = NULL;
206 }
207 }
Timo Teras77900a22006-06-26 16:16:12 -0700208 spin_unlock_irqrestore(&dm_timer_lock, flags);
209
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530210 if (!timer)
211 pr_debug("%s: timer%d request failed!\n", __func__, id);
Timo Teras83379c82006-06-26 16:16:23 -0700212
Timo Teras77900a22006-06-26 16:16:12 -0700213 return timer;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100214}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700215EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100216
Timo Teras77900a22006-06-26 16:16:12 -0700217void omap_dm_timer_free(struct omap_dm_timer *timer)
218{
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530219 clk_put(timer->fclk);
Timo Terasfa4bb622006-09-25 12:41:35 +0300220
Timo Teras77900a22006-06-26 16:16:12 -0700221 WARN_ON(!timer->reserved);
222 timer->reserved = 0;
223}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700224EXPORT_SYMBOL_GPL(omap_dm_timer_free);
Timo Teras77900a22006-06-26 16:16:12 -0700225
Timo Teras12583a72006-09-25 12:41:42 +0300226void omap_dm_timer_enable(struct omap_dm_timer *timer)
227{
Tarun Kanti DebBarmaffe07ce2011-09-20 17:00:21 +0530228 pm_runtime_get_sync(&timer->pdev->dev);
Timo Teras12583a72006-09-25 12:41:42 +0300229}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700230EXPORT_SYMBOL_GPL(omap_dm_timer_enable);
Timo Teras12583a72006-09-25 12:41:42 +0300231
232void omap_dm_timer_disable(struct omap_dm_timer *timer)
233{
Tarun Kanti DebBarmaffe07ce2011-09-20 17:00:21 +0530234 pm_runtime_put(&timer->pdev->dev);
Timo Teras12583a72006-09-25 12:41:42 +0300235}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700236EXPORT_SYMBOL_GPL(omap_dm_timer_disable);
Timo Teras12583a72006-09-25 12:41:42 +0300237
Timo Teras77900a22006-06-26 16:16:12 -0700238int omap_dm_timer_get_irq(struct omap_dm_timer *timer)
239{
240 return timer->irq;
241}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700242EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq);
Timo Teras77900a22006-06-26 16:16:12 -0700243
244#if defined(CONFIG_ARCH_OMAP1)
245
Tony Lindgrena569c6e2006-04-02 17:46:21 +0100246/**
247 * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR
248 * @inputmask: current value of idlect mask
249 */
250__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
251{
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530252 int i = 0;
253 struct omap_dm_timer *timer = NULL;
254 unsigned long flags;
Tony Lindgrena569c6e2006-04-02 17:46:21 +0100255
256 /* If ARMXOR cannot be idled this function call is unnecessary */
257 if (!(inputmask & (1 << 1)))
258 return inputmask;
259
260 /* If any active timer is using ARMXOR return modified mask */
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530261 spin_lock_irqsave(&dm_timer_lock, flags);
262 list_for_each_entry(timer, &omap_timer_list, node) {
Timo Teras77900a22006-06-26 16:16:12 -0700263 u32 l;
264
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530265 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
Timo Teras77900a22006-06-26 16:16:12 -0700266 if (l & OMAP_TIMER_CTRL_ST) {
267 if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0)
Tony Lindgrena569c6e2006-04-02 17:46:21 +0100268 inputmask &= ~(1 << 1);
269 else
270 inputmask &= ~(1 << 2);
271 }
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530272 i++;
Timo Teras77900a22006-06-26 16:16:12 -0700273 }
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530274 spin_unlock_irqrestore(&dm_timer_lock, flags);
Tony Lindgrena569c6e2006-04-02 17:46:21 +0100275
276 return inputmask;
277}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700278EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
Tony Lindgrena569c6e2006-04-02 17:46:21 +0100279
Tony Lindgren140455f2010-02-12 12:26:48 -0800280#else
Timo Teras77900a22006-06-26 16:16:12 -0700281
282struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
283{
Timo Terasfa4bb622006-09-25 12:41:35 +0300284 return timer->fclk;
Timo Teras77900a22006-06-26 16:16:12 -0700285}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700286EXPORT_SYMBOL_GPL(omap_dm_timer_get_fclk);
Timo Teras77900a22006-06-26 16:16:12 -0700287
288__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
289{
290 BUG();
Dirk Behme21218802006-12-06 17:14:00 -0800291
292 return 0;
Timo Teras77900a22006-06-26 16:16:12 -0700293}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700294EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
Timo Teras77900a22006-06-26 16:16:12 -0700295
296#endif
297
298void omap_dm_timer_trigger(struct omap_dm_timer *timer)
299{
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530300 if (unlikely(pm_runtime_suspended(&timer->pdev->dev))) {
301 pr_err("%s: timer%d not enabled.\n", __func__, timer->id);
302 return;
303 }
304
Timo Teras77900a22006-06-26 16:16:12 -0700305 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
306}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700307EXPORT_SYMBOL_GPL(omap_dm_timer_trigger);
Timo Teras77900a22006-06-26 16:16:12 -0700308
309void omap_dm_timer_start(struct omap_dm_timer *timer)
310{
311 u32 l;
312
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530313 omap_dm_timer_enable(timer);
314
315 if (timer->loses_context) {
316 u32 ctx_loss_cnt_after =
317 timer->get_context_loss_count(&timer->pdev->dev);
318 if (ctx_loss_cnt_after != timer->ctx_loss_count)
319 omap_timer_restore_context(timer);
320 }
321
Timo Teras77900a22006-06-26 16:16:12 -0700322 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
323 if (!(l & OMAP_TIMER_CTRL_ST)) {
324 l |= OMAP_TIMER_CTRL_ST;
325 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
326 }
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530327
328 /* Save the context */
329 timer->context.tclr = l;
Timo Teras77900a22006-06-26 16:16:12 -0700330}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700331EXPORT_SYMBOL_GPL(omap_dm_timer_start);
Timo Teras77900a22006-06-26 16:16:12 -0700332
333void omap_dm_timer_stop(struct omap_dm_timer *timer)
334{
Tony Lindgrencaf64f22011-03-29 15:54:48 -0700335 unsigned long rate = 0;
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530336 struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
Timo Teras77900a22006-06-26 16:16:12 -0700337
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530338 if (!pdata->needs_manual_reset)
339 rate = clk_get_rate(timer->fclk);
Tony Lindgrencaf64f22011-03-29 15:54:48 -0700340
Tony Lindgrenee17f112011-09-16 15:44:20 -0700341 __omap_dm_timer_stop(timer, timer->posted, rate);
Timo Teras77900a22006-06-26 16:16:12 -0700342}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700343EXPORT_SYMBOL_GPL(omap_dm_timer_stop);
Timo Teras77900a22006-06-26 16:16:12 -0700344
Paul Walmsleyf2480762009-04-23 21:11:10 -0600345int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100346{
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530347 int ret;
348 struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
349
Timo Teras77900a22006-06-26 16:16:12 -0700350 if (source < 0 || source >= 3)
Paul Walmsleyf2480762009-04-23 21:11:10 -0600351 return -EINVAL;
Timo Teras77900a22006-06-26 16:16:12 -0700352
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530353 ret = pdata->set_timer_src(timer->pdev, source);
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530354
355 return ret;
Timo Teras77900a22006-06-26 16:16:12 -0700356}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700357EXPORT_SYMBOL_GPL(omap_dm_timer_set_source);
Timo Teras77900a22006-06-26 16:16:12 -0700358
Timo Teras77900a22006-06-26 16:16:12 -0700359void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
360 unsigned int load)
361{
362 u32 l;
363
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530364 omap_dm_timer_enable(timer);
Timo Teras77900a22006-06-26 16:16:12 -0700365 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
366 if (autoreload)
367 l |= OMAP_TIMER_CTRL_AR;
368 else
369 l &= ~OMAP_TIMER_CTRL_AR;
370 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
371 omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
Richard Woodruff0f0d0802008-07-03 12:24:30 +0300372
Timo Teras77900a22006-06-26 16:16:12 -0700373 omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530374 /* Save the context */
375 timer->context.tclr = l;
376 timer->context.tldr = load;
377 omap_dm_timer_disable(timer);
Timo Teras77900a22006-06-26 16:16:12 -0700378}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700379EXPORT_SYMBOL_GPL(omap_dm_timer_set_load);
Timo Teras77900a22006-06-26 16:16:12 -0700380
Richard Woodruff3fddd092008-07-03 12:24:30 +0300381/* Optimized set_load which removes costly spin wait in timer_start */
382void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
383 unsigned int load)
384{
385 u32 l;
386
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530387 omap_dm_timer_enable(timer);
388
389 if (timer->loses_context) {
390 u32 ctx_loss_cnt_after =
391 timer->get_context_loss_count(&timer->pdev->dev);
392 if (ctx_loss_cnt_after != timer->ctx_loss_count)
393 omap_timer_restore_context(timer);
394 }
395
Richard Woodruff3fddd092008-07-03 12:24:30 +0300396 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
Paul Walmsley64ce2902008-12-10 17:36:34 -0800397 if (autoreload) {
Richard Woodruff3fddd092008-07-03 12:24:30 +0300398 l |= OMAP_TIMER_CTRL_AR;
Paul Walmsley64ce2902008-12-10 17:36:34 -0800399 omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
400 } else {
Richard Woodruff3fddd092008-07-03 12:24:30 +0300401 l &= ~OMAP_TIMER_CTRL_AR;
Paul Walmsley64ce2902008-12-10 17:36:34 -0800402 }
Richard Woodruff3fddd092008-07-03 12:24:30 +0300403 l |= OMAP_TIMER_CTRL_ST;
404
Tony Lindgrenee17f112011-09-16 15:44:20 -0700405 __omap_dm_timer_load_start(timer, l, load, timer->posted);
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530406
407 /* Save the context */
408 timer->context.tclr = l;
409 timer->context.tldr = load;
410 timer->context.tcrr = load;
Richard Woodruff3fddd092008-07-03 12:24:30 +0300411}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700412EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start);
Richard Woodruff3fddd092008-07-03 12:24:30 +0300413
Timo Teras77900a22006-06-26 16:16:12 -0700414void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
415 unsigned int match)
416{
417 u32 l;
418
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530419 omap_dm_timer_enable(timer);
Timo Teras77900a22006-06-26 16:16:12 -0700420 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
Timo Teras83379c82006-06-26 16:16:23 -0700421 if (enable)
Timo Teras77900a22006-06-26 16:16:12 -0700422 l |= OMAP_TIMER_CTRL_CE;
423 else
424 l &= ~OMAP_TIMER_CTRL_CE;
425 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
426 omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530427
428 /* Save the context */
429 timer->context.tclr = l;
430 timer->context.tmar = match;
431 omap_dm_timer_disable(timer);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100432}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700433EXPORT_SYMBOL_GPL(omap_dm_timer_set_match);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100434
Timo Teras77900a22006-06-26 16:16:12 -0700435void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
436 int toggle, int trigger)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100437{
Timo Teras77900a22006-06-26 16:16:12 -0700438 u32 l;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100439
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530440 omap_dm_timer_enable(timer);
Timo Teras77900a22006-06-26 16:16:12 -0700441 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
442 l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |
443 OMAP_TIMER_CTRL_PT | (0x03 << 10));
444 if (def_on)
445 l |= OMAP_TIMER_CTRL_SCPWM;
446 if (toggle)
447 l |= OMAP_TIMER_CTRL_PT;
448 l |= trigger << 10;
449 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530450
451 /* Save the context */
452 timer->context.tclr = l;
453 omap_dm_timer_disable(timer);
Timo Teras77900a22006-06-26 16:16:12 -0700454}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700455EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm);
Timo Teras77900a22006-06-26 16:16:12 -0700456
457void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
458{
459 u32 l;
460
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530461 omap_dm_timer_enable(timer);
Timo Teras77900a22006-06-26 16:16:12 -0700462 l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
463 l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));
464 if (prescaler >= 0x00 && prescaler <= 0x07) {
465 l |= OMAP_TIMER_CTRL_PRE;
466 l |= prescaler << 2;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100467 }
Timo Teras77900a22006-06-26 16:16:12 -0700468 omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530469
470 /* Save the context */
471 timer->context.tclr = l;
472 omap_dm_timer_disable(timer);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100473}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700474EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100475
476void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
Timo Teras77900a22006-06-26 16:16:12 -0700477 unsigned int value)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100478{
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530479 omap_dm_timer_enable(timer);
Tony Lindgrenee17f112011-09-16 15:44:20 -0700480 __omap_dm_timer_int_enable(timer, value);
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530481
482 /* Save the context */
483 timer->context.tier = value;
484 timer->context.twer = value;
485 omap_dm_timer_disable(timer);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100486}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700487EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100488
489unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
490{
Timo Terasfa4bb622006-09-25 12:41:35 +0300491 unsigned int l;
492
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530493 if (unlikely(pm_runtime_suspended(&timer->pdev->dev))) {
494 pr_err("%s: timer%d not enabled.\n", __func__, timer->id);
495 return 0;
496 }
497
Tony Lindgrenee17f112011-09-16 15:44:20 -0700498 l = __raw_readl(timer->irq_stat);
Timo Terasfa4bb622006-09-25 12:41:35 +0300499
500 return l;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100501}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700502EXPORT_SYMBOL_GPL(omap_dm_timer_read_status);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100503
504void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
505{
Tony Lindgrenee17f112011-09-16 15:44:20 -0700506 __omap_dm_timer_write_status(timer, value);
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530507 /* Save the context */
508 timer->context.tisr = value;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100509}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700510EXPORT_SYMBOL_GPL(omap_dm_timer_write_status);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100511
Tony Lindgren92105bb2005-09-07 17:20:26 +0100512unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
513{
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530514 if (unlikely(pm_runtime_suspended(&timer->pdev->dev))) {
515 pr_err("%s: timer%d not enabled.\n", __func__, timer->id);
516 return 0;
517 }
518
Tony Lindgrenee17f112011-09-16 15:44:20 -0700519 return __omap_dm_timer_read_counter(timer, timer->posted);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100520}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700521EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100522
Timo Teras83379c82006-06-26 16:16:23 -0700523void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
524{
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530525 if (unlikely(pm_runtime_suspended(&timer->pdev->dev))) {
526 pr_err("%s: timer%d not enabled.\n", __func__, timer->id);
527 return;
528 }
529
Timo Terasfa4bb622006-09-25 12:41:35 +0300530 omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530531
532 /* Save the context */
533 timer->context.tcrr = value;
Timo Teras83379c82006-06-26 16:16:23 -0700534}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700535EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter);
Timo Teras83379c82006-06-26 16:16:23 -0700536
Timo Teras77900a22006-06-26 16:16:12 -0700537int omap_dm_timers_active(void)
Tony Lindgren92105bb2005-09-07 17:20:26 +0100538{
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530539 struct omap_dm_timer *timer;
Tony Lindgren92105bb2005-09-07 17:20:26 +0100540
Tarun Kanti DebBarma3392cdd2011-09-20 17:00:20 +0530541 list_for_each_entry(timer, &omap_timer_list, node) {
Tarun Kanti DebBarmaffe07ce2011-09-20 17:00:21 +0530542 if (!timer->reserved)
Timo Teras12583a72006-09-25 12:41:42 +0300543 continue;
544
Timo Teras77900a22006-06-26 16:16:12 -0700545 if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
Timo Terasfa4bb622006-09-25 12:41:35 +0300546 OMAP_TIMER_CTRL_ST) {
Timo Teras77900a22006-06-26 16:16:12 -0700547 return 1;
Timo Terasfa4bb622006-09-25 12:41:35 +0300548 }
Tony Lindgren92105bb2005-09-07 17:20:26 +0100549 }
Tony Lindgren92105bb2005-09-07 17:20:26 +0100550 return 0;
551}
Timo Kokkonen6c366e32009-03-23 18:07:46 -0700552EXPORT_SYMBOL_GPL(omap_dm_timers_active);
Tony Lindgren92105bb2005-09-07 17:20:26 +0100553
Tarun Kanti DebBarmadf284722011-09-20 17:00:19 +0530554/**
555 * omap_dm_timer_probe - probe function called for every registered device
556 * @pdev: pointer to current timer platform device
557 *
558 * Called by driver framework at the end of device registration for all
559 * timer devices.
560 */
561static int __devinit omap_dm_timer_probe(struct platform_device *pdev)
562{
563 int ret;
564 unsigned long flags;
565 struct omap_dm_timer *timer;
566 struct resource *mem, *irq, *ioarea;
567 struct dmtimer_platform_data *pdata = pdev->dev.platform_data;
568
569 if (!pdata) {
570 dev_err(&pdev->dev, "%s: no platform data.\n", __func__);
571 return -ENODEV;
572 }
573
574 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
575 if (unlikely(!irq)) {
576 dev_err(&pdev->dev, "%s: no IRQ resource.\n", __func__);
577 return -ENODEV;
578 }
579
580 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
581 if (unlikely(!mem)) {
582 dev_err(&pdev->dev, "%s: no memory resource.\n", __func__);
583 return -ENODEV;
584 }
585
586 ioarea = request_mem_region(mem->start, resource_size(mem),
587 pdev->name);
588 if (!ioarea) {
589 dev_err(&pdev->dev, "%s: region already claimed.\n", __func__);
590 return -EBUSY;
591 }
592
593 timer = kzalloc(sizeof(struct omap_dm_timer), GFP_KERNEL);
594 if (!timer) {
595 dev_err(&pdev->dev, "%s: no memory for omap_dm_timer.\n",
596 __func__);
597 ret = -ENOMEM;
598 goto err_free_ioregion;
599 }
600
601 timer->io_base = ioremap(mem->start, resource_size(mem));
602 if (!timer->io_base) {
603 dev_err(&pdev->dev, "%s: ioremap failed.\n", __func__);
604 ret = -ENOMEM;
605 goto err_free_mem;
606 }
607
608 timer->id = pdev->id;
609 timer->irq = irq->start;
Tony Lindgren0dad9fa2011-09-21 16:38:51 -0700610 timer->reserved = pdata->reserved;
Tarun Kanti DebBarmadf284722011-09-20 17:00:19 +0530611 timer->pdev = pdev;
Tarun Kanti DebBarmab4811132011-09-20 17:00:24 +0530612 timer->loses_context = pdata->loses_context;
613 timer->get_context_loss_count = pdata->get_context_loss_count;
Tarun Kanti DebBarmadf284722011-09-20 17:00:19 +0530614
Tarun Kanti DebBarmaffe07ce2011-09-20 17:00:21 +0530615 /* Skip pm_runtime_enable for OMAP1 */
616 if (!pdata->needs_manual_reset) {
617 pm_runtime_enable(&pdev->dev);
618 pm_runtime_irq_safe(&pdev->dev);
619 }
620
Tony Lindgren0dad9fa2011-09-21 16:38:51 -0700621 if (!timer->reserved) {
622 pm_runtime_get_sync(&pdev->dev);
623 __omap_dm_timer_init_regs(timer);
624 pm_runtime_put(&pdev->dev);
625 }
626
Tarun Kanti DebBarmadf284722011-09-20 17:00:19 +0530627 /* add the timer element to the list */
628 spin_lock_irqsave(&dm_timer_lock, flags);
629 list_add_tail(&timer->node, &omap_timer_list);
630 spin_unlock_irqrestore(&dm_timer_lock, flags);
631
632 dev_dbg(&pdev->dev, "Device Probed.\n");
633
634 return 0;
635
636err_free_mem:
637 kfree(timer);
638
639err_free_ioregion:
640 release_mem_region(mem->start, resource_size(mem));
641
642 return ret;
643}
644
645/**
646 * omap_dm_timer_remove - cleanup a registered timer device
647 * @pdev: pointer to current timer platform device
648 *
649 * Called by driver framework whenever a timer device is unregistered.
650 * In addition to freeing platform resources it also deletes the timer
651 * entry from the local list.
652 */
653static int __devexit omap_dm_timer_remove(struct platform_device *pdev)
654{
655 struct omap_dm_timer *timer;
656 unsigned long flags;
657 int ret = -EINVAL;
658
659 spin_lock_irqsave(&dm_timer_lock, flags);
660 list_for_each_entry(timer, &omap_timer_list, node)
661 if (timer->pdev->id == pdev->id) {
662 list_del(&timer->node);
663 kfree(timer);
664 ret = 0;
665 break;
666 }
667 spin_unlock_irqrestore(&dm_timer_lock, flags);
668
669 return ret;
670}
671
672static struct platform_driver omap_dm_timer_driver = {
673 .probe = omap_dm_timer_probe,
674 .remove = omap_dm_timer_remove,
675 .driver = {
676 .name = "omap_timer",
677 },
678};
679
680static int __init omap_dm_timer_driver_init(void)
681{
682 return platform_driver_register(&omap_dm_timer_driver);
683}
684
685static void __exit omap_dm_timer_driver_exit(void)
686{
687 platform_driver_unregister(&omap_dm_timer_driver);
688}
689
690early_platform_init("earlytimer", &omap_dm_timer_driver);
691module_init(omap_dm_timer_driver_init);
692module_exit(omap_dm_timer_driver_exit);
693
694MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver");
695MODULE_LICENSE("GPL");
696MODULE_ALIAS("platform:" DRIVER_NAME);
697MODULE_AUTHOR("Texas Instruments Inc");