13b59b6beSTony Lindgren /*
23b59b6beSTony Lindgren * linux/arch/arm/mach-omap1/time.c
33b59b6beSTony Lindgren *
43b59b6beSTony Lindgren * OMAP Timers
53b59b6beSTony Lindgren *
63b59b6beSTony Lindgren * Copyright (C) 2004 Nokia Corporation
73b59b6beSTony Lindgren * Partial timer rewrite and additional dynamic tick timer support by
83b59b6beSTony Lindgren * Tony Lindgen <tony@atomide.com> and
93b59b6beSTony Lindgren * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
103b59b6beSTony Lindgren *
113b59b6beSTony Lindgren * MPU timer code based on the older MPU timer code for OMAP
123b59b6beSTony Lindgren * Copyright (C) 2000 RidgeRun, Inc.
133b59b6beSTony Lindgren * Author: Greg Lonnon <glonnon@ridgerun.com>
143b59b6beSTony Lindgren *
153b59b6beSTony Lindgren * This program is free software; you can redistribute it and/or modify it
163b59b6beSTony Lindgren * under the terms of the GNU General Public License as published by the
173b59b6beSTony Lindgren * Free Software Foundation; either version 2 of the License, or (at your
183b59b6beSTony Lindgren * option) any later version.
193b59b6beSTony Lindgren *
203b59b6beSTony Lindgren * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
213b59b6beSTony Lindgren * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
223b59b6beSTony Lindgren * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
233b59b6beSTony Lindgren * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
243b59b6beSTony Lindgren * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
253b59b6beSTony Lindgren * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
263b59b6beSTony Lindgren * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
273b59b6beSTony Lindgren * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
283b59b6beSTony Lindgren * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
293b59b6beSTony Lindgren * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
303b59b6beSTony Lindgren *
313b59b6beSTony Lindgren * You should have received a copy of the GNU General Public License along
323b59b6beSTony Lindgren * with this program; if not, write to the Free Software Foundation, Inc.,
333b59b6beSTony Lindgren * 675 Mass Ave, Cambridge, MA 02139, USA.
343b59b6beSTony Lindgren */
353b59b6beSTony Lindgren
363b59b6beSTony Lindgren #include <linux/kernel.h>
373b59b6beSTony Lindgren #include <linux/init.h>
383b59b6beSTony Lindgren #include <linux/delay.h>
393b59b6beSTony Lindgren #include <linux/interrupt.h>
403b59b6beSTony Lindgren #include <linux/spinlock.h>
41075192aeSKevin Hilman #include <linux/clk.h>
42075192aeSKevin Hilman #include <linux/err.h>
43075192aeSKevin Hilman #include <linux/clocksource.h>
44075192aeSKevin Hilman #include <linux/clockchips.h>
45fced80c7SRussell King #include <linux/io.h>
4638ff87f7SStephen Boyd #include <linux/sched_clock.h>
473b59b6beSTony Lindgren
483b59b6beSTony Lindgren #include <asm/irq.h>
49f376ea17STony Lindgren
503b59b6beSTony Lindgren #include <asm/mach/irq.h>
513b59b6beSTony Lindgren #include <asm/mach/time.h>
523b59b6beSTony Lindgren
537e0a9e62SArnd Bergmann #include "hardware.h"
54*c73b9099SJanusz Krzysztofik #include "mux.h"
552e3ee9f4STony Lindgren #include "iomap.h"
564e65331cSTony Lindgren #include "common.h"
57*c73b9099SJanusz Krzysztofik #include "clock.h"
583b59b6beSTony Lindgren
5905b5ca9bSTony Lindgren #ifdef CONFIG_OMAP_MPU_TIMER
6005b5ca9bSTony Lindgren
613b59b6beSTony Lindgren #define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE
623b59b6beSTony Lindgren #define OMAP_MPU_TIMER_OFFSET 0x100
633b59b6beSTony Lindgren
643b59b6beSTony Lindgren typedef struct {
653b59b6beSTony Lindgren u32 cntl; /* CNTL_TIMER, R/W */
663b59b6beSTony Lindgren u32 load_tim; /* LOAD_TIM, W */
673b59b6beSTony Lindgren u32 read_tim; /* READ_TIM, R */
683b59b6beSTony Lindgren } omap_mpu_timer_regs_t;
693b59b6beSTony Lindgren
703b59b6beSTony Lindgren #define omap_mpu_timer_base(n) \
71111c7751SRussell King ((omap_mpu_timer_regs_t __iomem *)OMAP1_IO_ADDRESS(OMAP_MPU_TIMER_BASE + \
723b59b6beSTony Lindgren (n)*OMAP_MPU_TIMER_OFFSET))
733b59b6beSTony Lindgren
omap_mpu_timer_read(int nr)74f376ea17STony Lindgren static inline unsigned long notrace omap_mpu_timer_read(int nr)
753b59b6beSTony Lindgren {
76111c7751SRussell King omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr);
77111c7751SRussell King return readl(&timer->read_tim);
783b59b6beSTony Lindgren }
793b59b6beSTony Lindgren
omap_mpu_set_autoreset(int nr)80075192aeSKevin Hilman static inline void omap_mpu_set_autoreset(int nr)
813b59b6beSTony Lindgren {
82111c7751SRussell King omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr);
833b59b6beSTony Lindgren
84111c7751SRussell King writel(readl(&timer->cntl) | MPU_TIMER_AR, &timer->cntl);
85075192aeSKevin Hilman }
86075192aeSKevin Hilman
omap_mpu_remove_autoreset(int nr)87075192aeSKevin Hilman static inline void omap_mpu_remove_autoreset(int nr)
88075192aeSKevin Hilman {
89111c7751SRussell King omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr);
90075192aeSKevin Hilman
91111c7751SRussell King writel(readl(&timer->cntl) & ~MPU_TIMER_AR, &timer->cntl);
92075192aeSKevin Hilman }
93075192aeSKevin Hilman
omap_mpu_timer_start(int nr,unsigned long load_val,int autoreset)94075192aeSKevin Hilman static inline void omap_mpu_timer_start(int nr, unsigned long load_val,
95075192aeSKevin Hilman int autoreset)
96075192aeSKevin Hilman {
97111c7751SRussell King omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr);
98111c7751SRussell King unsigned int timerflags = MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_ST;
99075192aeSKevin Hilman
100111c7751SRussell King if (autoreset)
101111c7751SRussell King timerflags |= MPU_TIMER_AR;
102075192aeSKevin Hilman
103111c7751SRussell King writel(MPU_TIMER_CLOCK_ENABLE, &timer->cntl);
1043b59b6beSTony Lindgren udelay(1);
105111c7751SRussell King writel(load_val, &timer->load_tim);
1063b59b6beSTony Lindgren udelay(1);
107111c7751SRussell King writel(timerflags, &timer->cntl);
1083b59b6beSTony Lindgren }
1093b59b6beSTony Lindgren
omap_mpu_timer_stop(int nr)11006cad098SKevin Hilman static inline void omap_mpu_timer_stop(int nr)
11106cad098SKevin Hilman {
112111c7751SRussell King omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr);
11306cad098SKevin Hilman
114111c7751SRussell King writel(readl(&timer->cntl) & ~MPU_TIMER_ST, &timer->cntl);
11506cad098SKevin Hilman }
11606cad098SKevin Hilman
1173b59b6beSTony Lindgren /*
118075192aeSKevin Hilman * ---------------------------------------------------------------------------
119075192aeSKevin Hilman * MPU timer 1 ... count down to zero, interrupt, reload
120075192aeSKevin Hilman * ---------------------------------------------------------------------------
1213b59b6beSTony Lindgren */
omap_mpu_set_next_event(unsigned long cycles,struct clock_event_device * evt)122075192aeSKevin Hilman static int omap_mpu_set_next_event(unsigned long cycles,
123075192aeSKevin Hilman struct clock_event_device *evt)
1243b59b6beSTony Lindgren {
125075192aeSKevin Hilman omap_mpu_timer_start(0, cycles, 0);
126075192aeSKevin Hilman return 0;
1273b59b6beSTony Lindgren }
1283b59b6beSTony Lindgren
omap_mpu_set_oneshot(struct clock_event_device * evt)12929105e10SViresh Kumar static int omap_mpu_set_oneshot(struct clock_event_device *evt)
1303b59b6beSTony Lindgren {
13106cad098SKevin Hilman omap_mpu_timer_stop(0);
132075192aeSKevin Hilman omap_mpu_remove_autoreset(0);
13329105e10SViresh Kumar return 0;
134075192aeSKevin Hilman }
13529105e10SViresh Kumar
omap_mpu_set_periodic(struct clock_event_device * evt)13629105e10SViresh Kumar static int omap_mpu_set_periodic(struct clock_event_device *evt)
13729105e10SViresh Kumar {
13829105e10SViresh Kumar omap_mpu_set_autoreset(0);
13929105e10SViresh Kumar return 0;
1403b59b6beSTony Lindgren }
1413b59b6beSTony Lindgren
142075192aeSKevin Hilman static struct clock_event_device clockevent_mpu_timer1 = {
143075192aeSKevin Hilman .name = "mpu_timer1",
14429105e10SViresh Kumar .features = CLOCK_EVT_FEAT_PERIODIC |
14529105e10SViresh Kumar CLOCK_EVT_FEAT_ONESHOT,
146075192aeSKevin Hilman .set_next_event = omap_mpu_set_next_event,
14729105e10SViresh Kumar .set_state_periodic = omap_mpu_set_periodic,
14829105e10SViresh Kumar .set_state_oneshot = omap_mpu_set_oneshot,
1493b59b6beSTony Lindgren };
1503b59b6beSTony Lindgren
omap_mpu_timer1_interrupt(int irq,void * dev_id)1510cd61b68SLinus Torvalds static irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id)
1523b59b6beSTony Lindgren {
153075192aeSKevin Hilman struct clock_event_device *evt = &clockevent_mpu_timer1;
154075192aeSKevin Hilman
155075192aeSKevin Hilman evt->event_handler(evt);
156075192aeSKevin Hilman
1573b59b6beSTony Lindgren return IRQ_HANDLED;
1583b59b6beSTony Lindgren }
1593b59b6beSTony Lindgren
omap_init_mpu_timer(unsigned long rate)160075192aeSKevin Hilman static __init void omap_init_mpu_timer(unsigned long rate)
1613b59b6beSTony Lindgren {
162b75ca521Safzal mohammed if (request_irq(INT_TIMER1, omap_mpu_timer1_interrupt,
163b75ca521Safzal mohammed IRQF_TIMER | IRQF_IRQPOLL, "mpu_timer1", NULL))
164b75ca521Safzal mohammed pr_err("Failed to request irq %d (mpu_timer1)\n", INT_TIMER1);
165075192aeSKevin Hilman omap_mpu_timer_start(0, (rate / HZ) - 1, 1);
166075192aeSKevin Hilman
167320ab2b0SRusty Russell clockevent_mpu_timer1.cpumask = cpumask_of(0);
168838a2ae8SShawn Guo clockevents_config_and_register(&clockevent_mpu_timer1, rate,
169838a2ae8SShawn Guo 1, -1);
1703b59b6beSTony Lindgren }
1713b59b6beSTony Lindgren
172075192aeSKevin Hilman
173075192aeSKevin Hilman /*
174075192aeSKevin Hilman * ---------------------------------------------------------------------------
175075192aeSKevin Hilman * MPU timer 2 ... free running 32-bit clock source and scheduler clock
176075192aeSKevin Hilman * ---------------------------------------------------------------------------
177075192aeSKevin Hilman */
178075192aeSKevin Hilman
omap_mpu_read_sched_clock(void)17950f6dca6SStephen Boyd static u64 notrace omap_mpu_read_sched_clock(void)
1804912cf04STony Lindgren {
1812f0778afSMarc Zyngier return ~omap_mpu_timer_read(1);
182f376ea17STony Lindgren }
183f376ea17STony Lindgren
omap_init_clocksource(unsigned long rate)184075192aeSKevin Hilman static void __init omap_init_clocksource(unsigned long rate)
185075192aeSKevin Hilman {
186933e54a5SRussell King omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(1);
187075192aeSKevin Hilman static char err[] __initdata = KERN_ERR
188075192aeSKevin Hilman "%s: can't register clocksource!\n";
189075192aeSKevin Hilman
190075192aeSKevin Hilman omap_mpu_timer_start(1, ~0, 1);
19150f6dca6SStephen Boyd sched_clock_register(omap_mpu_read_sched_clock, 32, rate);
192075192aeSKevin Hilman
193933e54a5SRussell King if (clocksource_mmio_init(&timer->read_tim, "mpu_timer2", rate,
194933e54a5SRussell King 300, 32, clocksource_mmio_readl_down))
195933e54a5SRussell King printk(err, "mpu_timer2");
196075192aeSKevin Hilman }
197075192aeSKevin Hilman
omap_mpu_timer_init(void)19805b5ca9bSTony Lindgren static void __init omap_mpu_timer_init(void)
1993b59b6beSTony Lindgren {
200075192aeSKevin Hilman struct clk *ck_ref = clk_get(NULL, "ck_ref");
201075192aeSKevin Hilman unsigned long rate;
202075192aeSKevin Hilman
203075192aeSKevin Hilman BUG_ON(IS_ERR(ck_ref));
204075192aeSKevin Hilman
205075192aeSKevin Hilman rate = clk_get_rate(ck_ref);
206075192aeSKevin Hilman clk_put(ck_ref);
207075192aeSKevin Hilman
208075192aeSKevin Hilman /* PTV = 0 */
209075192aeSKevin Hilman rate /= 2;
210075192aeSKevin Hilman
211075192aeSKevin Hilman omap_init_mpu_timer(rate);
212075192aeSKevin Hilman omap_init_clocksource(rate);
21305b5ca9bSTony Lindgren }
21405b5ca9bSTony Lindgren
21505b5ca9bSTony Lindgren #else
omap_mpu_timer_init(void)21605b5ca9bSTony Lindgren static inline void omap_mpu_timer_init(void)
21705b5ca9bSTony Lindgren {
21805b5ca9bSTony Lindgren pr_err("Bogus timer, should not happen\n");
21905b5ca9bSTony Lindgren }
22005b5ca9bSTony Lindgren #endif /* CONFIG_OMAP_MPU_TIMER */
22105b5ca9bSTony Lindgren
222d8328f3bSPaul Walmsley /*
22305b5ca9bSTony Lindgren * ---------------------------------------------------------------------------
22405b5ca9bSTony Lindgren * Timer initialization
22505b5ca9bSTony Lindgren * ---------------------------------------------------------------------------
226d8328f3bSPaul Walmsley */
omap1_timer_init(void)2276bb27d73SStephen Warren void __init omap1_timer_init(void)
22805b5ca9bSTony Lindgren {
229*c73b9099SJanusz Krzysztofik omap1_clk_init();
230*c73b9099SJanusz Krzysztofik omap1_mux_init();
231*c73b9099SJanusz Krzysztofik
23218799911SVaibhav Hiremath if (omap_32k_timer_init() != 0)
23305b5ca9bSTony Lindgren omap_mpu_timer_init();
2343b59b6beSTony Lindgren }
235