2005-04-17 02:20:36 +04:00
/*
2005-07-10 22:58:09 +04:00
* linux / arch / arm / mach - omap1 / time . c
2005-04-17 02:20:36 +04:00
*
* OMAP Timers
*
* Copyright ( C ) 2004 Nokia Corporation
2005-06-29 22:59:48 +04:00
* Partial timer rewrite and additional dynamic tick timer support by
2005-04-17 02:20:36 +04:00
* Tony Lindgen < tony @ atomide . com > and
* Tuukka Tikkanen < tuukka . tikkanen @ elektrobit . com >
*
* MPU timer code based on the older MPU timer code for OMAP
* Copyright ( C ) 2000 RidgeRun , Inc .
* Author : Greg Lonnon < glonnon @ ridgerun . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
* THIS SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF
* USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/sched.h>
# include <linux/spinlock.h>
2007-03-08 22:32:19 +03:00
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/clocksource.h>
# include <linux/clockchips.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2011-01-19 00:25:39 +03:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2005-04-17 02:20:36 +04:00
# include <asm/leds.h>
# include <asm/irq.h>
2011-01-19 00:25:39 +03:00
# include <asm/sched_clock.h>
2005-04-17 02:20:36 +04:00
# include <asm/mach/irq.h>
# include <asm/mach/time.h>
2010-11-18 20:59:46 +03:00
# include <plat/common.h>
2005-04-17 02:20:36 +04:00
2011-01-18 23:42:23 +03:00
# ifdef CONFIG_OMAP_MPU_TIMER
2005-04-17 02:20:36 +04:00
# define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE
# define OMAP_MPU_TIMER_OFFSET 0x100
typedef struct {
u32 cntl ; /* CNTL_TIMER, R/W */
u32 load_tim ; /* LOAD_TIM, W */
u32 read_tim ; /* READ_TIM, R */
} omap_mpu_timer_regs_t ;
2009-08-28 21:50:33 +04:00
# define omap_mpu_timer_base(n) \
( ( volatile omap_mpu_timer_regs_t * ) OMAP1_IO_ADDRESS ( OMAP_MPU_TIMER_BASE + \
2005-04-17 02:20:36 +04:00
( n ) * OMAP_MPU_TIMER_OFFSET ) )
2011-01-19 00:25:39 +03:00
static inline unsigned long notrace omap_mpu_timer_read ( int nr )
2005-04-17 02:20:36 +04:00
{
volatile omap_mpu_timer_regs_t * timer = omap_mpu_timer_base ( nr ) ;
return timer - > read_tim ;
}
2007-03-08 22:32:19 +03:00
static inline void omap_mpu_set_autoreset ( int nr )
2005-04-17 02:20:36 +04:00
{
volatile omap_mpu_timer_regs_t * timer = omap_mpu_timer_base ( nr ) ;
2007-03-08 22:32:19 +03:00
timer - > cntl = timer - > cntl | MPU_TIMER_AR ;
2005-04-17 02:20:36 +04:00
}
2007-03-08 22:32:19 +03:00
static inline void omap_mpu_remove_autoreset ( int nr )
2005-04-17 02:20:36 +04:00
{
2007-03-08 22:32:19 +03:00
volatile omap_mpu_timer_regs_t * timer = omap_mpu_timer_base ( nr ) ;
2005-04-17 02:20:36 +04:00
2007-03-08 22:32:19 +03:00
timer - > cntl = timer - > cntl & ~ MPU_TIMER_AR ;
2005-04-17 02:20:36 +04:00
}
2007-03-08 22:32:19 +03:00
static inline void omap_mpu_timer_start ( int nr , unsigned long load_val ,
int autoreset )
{
volatile omap_mpu_timer_regs_t * timer = omap_mpu_timer_base ( nr ) ;
unsigned int timerflags = ( MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_ST ) ;
if ( autoreset ) timerflags | = MPU_TIMER_AR ;
timer - > cntl = MPU_TIMER_CLOCK_ENABLE ;
udelay ( 1 ) ;
timer - > load_tim = load_val ;
udelay ( 1 ) ;
timer - > cntl = timerflags ;
}
2005-04-17 02:20:36 +04:00
2007-10-19 10:04:43 +04:00
static inline void omap_mpu_timer_stop ( int nr )
{
volatile omap_mpu_timer_regs_t * timer = omap_mpu_timer_base ( nr ) ;
timer - > cntl & = ~ MPU_TIMER_ST ;
}
2005-04-17 02:20:36 +04:00
/*
2007-03-08 22:32:19 +03:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* MPU timer 1 . . . count down to zero , interrupt , reload
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2005-04-17 02:20:36 +04:00
*/
2007-03-08 22:32:19 +03:00
static int omap_mpu_set_next_event ( unsigned long cycles ,
2007-10-19 10:04:43 +04:00
struct clock_event_device * evt )
2005-04-17 02:20:36 +04:00
{
2007-03-08 22:32:19 +03:00
omap_mpu_timer_start ( 0 , cycles , 0 ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2007-03-08 22:32:19 +03:00
static void omap_mpu_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
omap_mpu_set_autoreset ( 0 ) ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
2007-10-19 10:04:43 +04:00
omap_mpu_timer_stop ( 0 ) ;
2007-03-08 22:32:19 +03:00
omap_mpu_remove_autoreset ( 0 ) ;
break ;
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
2007-07-21 15:37:34 +04:00
case CLOCK_EVT_MODE_RESUME :
2007-03-08 22:32:19 +03:00
break ;
}
2005-04-17 02:20:36 +04:00
}
2007-03-08 22:32:19 +03:00
static struct clock_event_device clockevent_mpu_timer1 = {
. name = " mpu_timer1 " ,
2008-03-11 12:47:43 +03:00
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
2007-03-08 22:32:19 +03:00
. shift = 32 ,
. set_next_event = omap_mpu_set_next_event ,
. set_mode = omap_mpu_set_mode ,
} ;
static irqreturn_t omap_mpu_timer1_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2007-03-08 22:32:19 +03:00
struct clock_event_device * evt = & clockevent_mpu_timer1 ;
2005-04-17 02:20:36 +04:00
2007-03-08 22:32:19 +03:00
evt - > event_handler ( evt ) ;
2005-04-17 02:20:36 +04:00
return IRQ_HANDLED ;
}
2007-03-08 22:32:19 +03:00
static struct irqaction omap_mpu_timer1_irq = {
. name = " mpu_timer1 " ,
2007-05-08 11:35:39 +04:00
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
2007-03-08 22:32:19 +03:00
. handler = omap_mpu_timer1_interrupt ,
2005-04-17 02:20:36 +04:00
} ;
2007-03-08 22:32:19 +03:00
static __init void omap_init_mpu_timer ( unsigned long rate )
{
setup_irq ( INT_TIMER1 , & omap_mpu_timer1_irq ) ;
omap_mpu_timer_start ( 0 , ( rate / HZ ) - 1 , 1 ) ;
clockevent_mpu_timer1 . mult = div_sc ( rate , NSEC_PER_SEC ,
clockevent_mpu_timer1 . shift ) ;
clockevent_mpu_timer1 . max_delta_ns =
clockevent_delta2ns ( - 1 , & clockevent_mpu_timer1 ) ;
clockevent_mpu_timer1 . min_delta_ns =
clockevent_delta2ns ( 1 , & clockevent_mpu_timer1 ) ;
2008-12-13 13:50:26 +03:00
clockevent_mpu_timer1 . cpumask = cpumask_of ( 0 ) ;
2007-03-08 22:32:19 +03:00
clockevents_register_device ( & clockevent_mpu_timer1 ) ;
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* MPU timer 2 . . . free running 32 - bit clock source and scheduler clock
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static unsigned long omap_mpu_timer2_overflows ;
static irqreturn_t omap_mpu_timer2_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2007-03-08 22:32:19 +03:00
omap_mpu_timer2_overflows + + ;
2005-04-17 02:20:36 +04:00
return IRQ_HANDLED ;
}
2007-03-08 22:32:19 +03:00
static struct irqaction omap_mpu_timer2_irq = {
. name = " mpu_timer2 " ,
2006-07-03 04:20:05 +04:00
. flags = IRQF_DISABLED ,
2007-03-08 22:32:19 +03:00
. handler = omap_mpu_timer2_interrupt ,
2005-04-17 02:20:36 +04:00
} ;
2009-04-21 23:24:00 +04:00
static cycle_t mpu_read ( struct clocksource * cs )
2005-04-17 02:20:36 +04:00
{
2007-03-08 22:32:19 +03:00
return ~ omap_mpu_timer_read ( 1 ) ;
}
static struct clocksource clocksource_mpu = {
. name = " mpu_timer2 " ,
. rating = 300 ,
. read = mpu_read ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
2011-01-19 00:25:39 +03:00
static DEFINE_CLOCK_DATA ( cd ) ;
2011-01-19 04:00:00 +03:00
static inline unsigned long long notrace _omap_mpu_sched_clock ( void )
{
u32 cyc = mpu_read ( & clocksource_mpu ) ;
return cyc_to_sched_clock ( & cd , cyc , ( u32 ) ~ 0 ) ;
}
# ifndef CONFIG_OMAP_32K_TIMER
unsigned long long notrace sched_clock ( void )
{
return _omap_mpu_sched_clock ( ) ;
}
# else
static unsigned long long notrace omap_mpu_sched_clock ( void )
{
return _omap_mpu_sched_clock ( ) ;
}
# endif
2011-01-19 00:25:39 +03:00
static void notrace mpu_update_sched_clock ( void )
{
u32 cyc = mpu_read ( & clocksource_mpu ) ;
update_sched_clock ( & cd , cyc , ( u32 ) ~ 0 ) ;
}
2007-03-08 22:32:19 +03:00
static void __init omap_init_clocksource ( unsigned long rate )
{
static char err [ ] __initdata = KERN_ERR
" %s: can't register clocksource! \n " ;
setup_irq ( INT_TIMER2 , & omap_mpu_timer2_irq ) ;
omap_mpu_timer_start ( 1 , ~ 0 , 1 ) ;
2011-01-19 00:25:39 +03:00
init_sched_clock ( & cd , mpu_update_sched_clock , 32 , rate ) ;
2007-03-08 22:32:19 +03:00
2010-12-13 16:18:44 +03:00
if ( clocksource_register_hz ( & clocksource_mpu , rate ) )
2007-03-08 22:32:19 +03:00
printk ( err , clocksource_mpu . name ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-18 23:42:23 +03:00
static void __init omap_mpu_timer_init ( void )
2005-04-17 02:20:36 +04:00
{
2007-03-08 22:32:19 +03:00
struct clk * ck_ref = clk_get ( NULL , " ck_ref " ) ;
unsigned long rate ;
BUG_ON ( IS_ERR ( ck_ref ) ) ;
rate = clk_get_rate ( ck_ref ) ;
clk_put ( ck_ref ) ;
/* PTV = 0 */
rate / = 2 ;
omap_init_mpu_timer ( rate ) ;
omap_init_clocksource ( rate ) ;
2011-01-18 23:42:23 +03:00
}
# else
static inline void omap_mpu_timer_init ( void )
{
pr_err ( " Bogus timer, should not happen \n " ) ;
}
# endif /* CONFIG_OMAP_MPU_TIMER */
2011-01-19 04:00:00 +03:00
# if defined(CONFIG_OMAP_MPU_TIMER) && defined(CONFIG_OMAP_32K_TIMER)
static unsigned long long ( * preferred_sched_clock ) ( void ) ;
unsigned long long notrace sched_clock ( void )
{
if ( ! preferred_sched_clock )
return 0 ;
return preferred_sched_clock ( ) ;
}
static inline void preferred_sched_clock_init ( bool use_32k_sched_clock )
{
if ( use_32k_sched_clock )
preferred_sched_clock = omap_32k_sched_clock ;
else
preferred_sched_clock = omap_mpu_sched_clock ;
}
# else
static inline void preferred_sched_clock_init ( bool use_32k_sched_clcok )
{
}
# endif
2011-01-18 23:42:23 +03:00
static inline int omap_32k_timer_usable ( void )
{
int res = false ;
if ( cpu_is_omap730 ( ) | | cpu_is_omap15xx ( ) )
return res ;
# ifdef CONFIG_OMAP_32K_TIMER
res = omap_32k_timer_init ( ) ;
# endif
return res ;
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Timer initialization
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static void __init omap_timer_init ( void )
{
2011-01-19 04:00:00 +03:00
if ( omap_32k_timer_usable ( ) ) {
preferred_sched_clock_init ( 1 ) ;
} else {
2011-01-18 23:42:23 +03:00
omap_mpu_timer_init ( ) ;
2011-01-19 04:00:00 +03:00
preferred_sched_clock_init ( 0 ) ;
}
2005-04-17 02:20:36 +04:00
}
struct sys_timer omap_timer = {
. init = omap_timer_init ,
} ;