xref: /openbmc/linux/arch/arm/mach-omap1/time.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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