2005-06-23 22:01:16 -07:00
/*
* arch / xtensa / kernel / time . c
*
* Timer and clock support .
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 2005 Tensilica Inc .
*
* Chris Zankel < chris @ zankel . net >
*/
# include <linux/errno.h>
2009-10-07 17:09:06 +04:00
# include <linux/sched.h>
2005-06-23 22:01:16 -07:00
# include <linux/time.h>
2009-03-04 21:39:12 +01:00
# include <linux/clocksource.h>
2005-06-23 22:01:16 -07:00
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/irq.h>
# include <linux/profile.h>
# include <linux/delay.h>
# include <asm/timex.h>
# include <asm/platform.h>
# ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
unsigned long ccount_per_jiffy ; /* per 1/HZ */
2007-08-05 10:26:30 -07:00
unsigned long nsec_per_ccount ; /* nsec per ccount increment */
2005-06-23 22:01:16 -07:00
# endif
2009-03-04 21:39:12 +01:00
static cycle_t ccount_read ( void )
{
return ( cycle_t ) get_ccount ( ) ;
}
static struct clocksource ccount_clocksource = {
. name = " ccount " ,
. rating = 200 ,
. read = ccount_read ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
/*
* With a shift of 22 the lower limit of the cpu clock is
* 1 MHz , where NSEC_PER_CCOUNT is 1000 or a bit less than
* 2 ^ 10 : Since we have 32 bits and the multiplicator can
* already take up as much as 10 bits , this leaves us with
* remaining upper 22 bits .
*/
. shift = 22 ,
} ;
2006-12-10 02:18:47 -08:00
static irqreturn_t timer_interrupt ( int irq , void * dev_id ) ;
2005-06-23 22:01:16 -07:00
static struct irqaction timer_irqaction = {
. handler = timer_interrupt ,
2006-07-01 19:29:31 -07:00
. flags = IRQF_DISABLED ,
2005-06-23 22:01:16 -07:00
. name = " timer " ,
} ;
void __init time_init ( void )
{
2005-09-22 21:44:23 -07:00
# ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
2005-06-23 22:01:16 -07:00
printk ( " Calibrating CPU frequency " ) ;
platform_calibrate_ccount ( ) ;
printk ( " %d.%02d MHz \n " , ( int ) ccount_per_jiffy / ( 1000000 / HZ ) ,
( int ) ( ccount_per_jiffy / ( 10000 / HZ ) ) % 100 ) ;
# endif
2009-03-04 21:39:12 +01:00
ccount_clocksource . mult =
clocksource_hz2mult ( CCOUNT_PER_JIFFY * HZ ,
ccount_clocksource . shift ) ;
clocksource_register ( & ccount_clocksource ) ;
2005-06-23 22:01:16 -07:00
/* Initialize the linux timer interrupt. */
setup_irq ( LINUX_TIMER_INT , & timer_irqaction ) ;
set_linux_timer ( get_ccount ( ) + CCOUNT_PER_JIFFY ) ;
}
/*
* The timer interrupt is called HZ times per second .
*/
2006-12-10 02:18:47 -08:00
irqreturn_t timer_interrupt ( int irq , void * dev_id )
2005-06-23 22:01:16 -07:00
{
unsigned long next ;
next = get_linux_timer ( ) ;
again :
while ( ( signed long ) ( get_ccount ( ) - next ) > 0 ) {
2006-12-10 02:18:47 -08:00
profile_tick ( CPU_PROFILING ) ;
2005-06-23 22:01:16 -07:00
# ifndef CONFIG_SMP
2006-12-10 02:18:47 -08:00
update_process_times ( user_mode ( get_irq_regs ( ) ) ) ;
2005-06-23 22:01:16 -07:00
# endif
write_seqlock ( & xtime_lock ) ;
2007-08-05 10:26:30 -07:00
do_timer ( 1 ) ; /* Linux handler in kernel/timer.c */
/* Note that writing CCOMPARE clears the interrupt. */
2005-06-23 22:01:16 -07:00
next + = CCOUNT_PER_JIFFY ;
2007-08-05 10:26:30 -07:00
set_linux_timer ( next ) ;
2005-06-23 22:01:16 -07:00
write_sequnlock ( & xtime_lock ) ;
}
2007-08-05 10:26:30 -07:00
/* Allow platform to do something useful (Wdog). */
2005-06-23 22:01:16 -07:00
2007-08-05 10:26:30 -07:00
platform_heartbeat ( ) ;
2005-06-23 22:01:16 -07:00
/* Make sure we didn't miss any tick... */
if ( ( signed long ) ( get_ccount ( ) - next ) > 0 )
goto again ;
return IRQ_HANDLED ;
}
# ifndef CONFIG_GENERIC_CALIBRATE_DELAY
2008-02-06 01:37:51 -08:00
void __cpuinit calibrate_delay ( void )
2005-06-23 22:01:16 -07:00
{
loops_per_jiffy = CCOUNT_PER_JIFFY ;
printk ( " Calibrating delay loop (skipped)... "
" %lu.%02lu BogoMIPS preset \n " ,
loops_per_jiffy / ( 1000000 / HZ ) ,
( loops_per_jiffy / ( 10000 / HZ ) ) % 100 ) ;
}
# endif