2005-04-16 15:20:36 -07:00
/*
* linux / arch / arm / kernel / time . c
*
* Copyright ( C ) 1991 , 1992 , 1995 Linus Torvalds
* Modifications for ARM ( C ) 1994 - 2001 Russell King
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This file contains the ARM - specific time handling details :
* reading the RTC at bootup , etc . . .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/time.h>
# include <linux/init.h>
2009-10-07 17:09:06 +04:00
# include <linux/sched.h>
2005-04-16 15:20:36 -07:00
# include <linux/smp.h>
# include <linux/timex.h>
# include <linux/errno.h>
# include <linux/profile.h>
# include <linux/sysdev.h>
# include <linux/timer.h>
2006-10-06 18:58:24 +00:00
# include <linux/irq.h>
2005-04-16 15:20:36 -07:00
2006-12-24 01:36:35 +01:00
# include <linux/mc146818rtc.h>
2005-04-16 15:20:36 -07:00
# include <asm/leds.h>
# include <asm/thread_info.h>
2009-02-11 13:07:53 +01:00
# include <asm/stacktrace.h>
2005-04-16 15:20:36 -07:00
# include <asm/mach/time.h>
/*
* Our system timer .
*/
struct sys_timer * system_timer ;
2007-01-17 22:11:27 +01:00
# if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE)
2005-04-16 15:20:36 -07:00
/* this needs a better home */
DEFINE_SPINLOCK ( rtc_lock ) ;
2007-01-17 22:11:27 +01:00
# ifdef CONFIG_RTC_DRV_CMOS_MODULE
2005-04-16 15:20:36 -07:00
EXPORT_SYMBOL ( rtc_lock ) ;
# endif
2007-01-17 22:11:27 +01:00
# endif /* pc-style 'CMOS' RTC support */
2005-04-16 15:20:36 -07:00
/* change this if you have some constant time drift */
# define USECS_PER_JIFFY (1000000 / HZ)
# ifdef CONFIG_SMP
unsigned long profile_pc ( struct pt_regs * regs )
{
2009-02-11 13:07:53 +01:00
struct stackframe frame ;
2005-04-16 15:20:36 -07:00
2009-02-11 13:07:53 +01:00
if ( ! in_lock_functions ( regs - > ARM_pc ) )
return regs - > ARM_pc ;
2005-04-16 15:20:36 -07:00
2009-02-11 13:07:53 +01:00
frame . fp = regs - > ARM_fp ;
frame . sp = regs - > ARM_sp ;
frame . lr = regs - > ARM_lr ;
frame . pc = regs - > ARM_pc ;
do {
int ret = unwind_frame ( & frame ) ;
if ( ret < 0 )
return 0 ;
} while ( in_lock_functions ( frame . pc ) ) ;
return frame . pc ;
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL ( profile_pc ) ;
# endif
2010-03-24 00:22:36 +00:00
# ifdef CONFIG_ARCH_USES_GETTIMEOFFSET
u32 arch_gettimeoffset ( void )
2005-04-16 15:20:36 -07:00
{
2010-03-24 00:22:36 +00:00
if ( system_timer - > offset ! = NULL )
return system_timer - > offset ( ) * 1000 ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2010-03-24 00:22:36 +00:00
# endif /* CONFIG_ARCH_USES_GETTIMEOFFSET */
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_LEDS_TIMER
static inline void do_leds ( void )
{
2006-11-06 19:29:16 +01:00
static unsigned int count = HZ / 2 ;
2005-04-16 15:20:36 -07:00
if ( - - count = = 0 ) {
2006-11-06 19:29:16 +01:00
count = HZ / 2 ;
2005-04-16 15:20:36 -07:00
leds_event ( led_timer ) ;
}
}
# else
# define do_leds()
# endif
2007-03-14 17:33:24 +01:00
# ifndef CONFIG_GENERIC_CLOCKEVENTS
2005-04-16 15:20:36 -07:00
/*
* Kernel system timer support .
*/
2006-10-06 10:53:39 -07:00
void timer_tick ( void )
2005-04-16 15:20:36 -07:00
{
2006-10-06 18:58:24 +00:00
profile_tick ( CPU_PROFILING ) ;
2005-04-16 15:20:36 -07:00
do_leds ( ) ;
2008-01-28 10:16:37 +00:00
write_seqlock ( & xtime_lock ) ;
2006-09-29 02:00:32 -07:00
do_timer ( 1 ) ;
2008-01-28 10:16:37 +00:00
write_sequnlock ( & xtime_lock ) ;
2005-04-16 15:20:36 -07:00
# ifndef CONFIG_SMP
2006-10-25 13:59:16 +01:00
update_process_times ( user_mode ( get_irq_regs ( ) ) ) ;
2005-04-16 15:20:36 -07:00
# endif
}
2007-03-14 17:33:24 +01:00
# endif
2005-04-16 15:20:36 -07:00
2007-03-14 17:33:24 +01:00
# if defined(CONFIG_PM) && !defined(CONFIG_GENERIC_CLOCKEVENTS)
2005-04-16 15:20:36 -07:00
static int timer_suspend ( struct sys_device * dev , pm_message_t state )
{
struct sys_timer * timer = container_of ( dev , struct sys_timer , dev ) ;
if ( timer - > suspend ! = NULL )
timer - > suspend ( ) ;
return 0 ;
}
static int timer_resume ( struct sys_device * dev )
{
struct sys_timer * timer = container_of ( dev , struct sys_timer , dev ) ;
if ( timer - > resume ! = NULL )
timer - > resume ( ) ;
return 0 ;
}
# else
# define timer_suspend NULL
# define timer_resume NULL
# endif
static struct sysdev_class timer_sysclass = {
2007-12-20 02:09:39 +01:00
. name = " timer " ,
2005-04-16 15:20:36 -07:00
. suspend = timer_suspend ,
. resume = timer_resume ,
} ;
static int __init timer_init_sysfs ( void )
{
int ret = sysdev_class_register ( & timer_sysclass ) ;
if ( ret = = 0 ) {
system_timer - > dev . cls = & timer_sysclass ;
ret = sysdev_register ( & system_timer - > dev ) ;
}
2005-06-25 19:39:45 +01:00
2005-04-16 15:20:36 -07:00
return ret ;
}
device_initcall ( timer_init_sysfs ) ;
void __init time_init ( void )
{
system_timer - > init ( ) ;
}