2007-02-16 12:28:00 +03:00
/*
* linux / kernel / time / clockevents . c
*
* This file contains functions which manage clock event devices .
*
* Copyright ( C ) 2005 - 2006 , Thomas Gleixner < tglx @ linutronix . de >
* Copyright ( C ) 2005 - 2007 , Red Hat , Inc . , Ingo Molnar
* Copyright ( C ) 2006 - 2007 , Timesys Corp . , Thomas Gleixner
*
* This code is licenced under the GPL version 2. For details see
* kernel - base / COPYING .
*/
# include <linux/clockchips.h>
# include <linux/hrtimer.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/notifier.h>
# include <linux/smp.h>
2009-10-17 02:19:01 +04:00
# include "tick-internal.h"
2007-02-16 12:28:00 +03:00
/* The registered clock event devices */
static LIST_HEAD ( clockevent_devices ) ;
static LIST_HEAD ( clockevents_released ) ;
/* Notification for clock events */
static RAW_NOTIFIER_HEAD ( clockevents_chain ) ;
/* Protection for the above */
2009-12-08 14:40:31 +03:00
static DEFINE_RAW_SPINLOCK ( clockevents_lock ) ;
2007-02-16 12:28:00 +03:00
/**
* clockevents_delta2ns - Convert a latch value ( device ticks ) to nanoseconds
* @ latch : value to convert
* @ evt : pointer to clock event device descriptor
*
* Math helper , returns latch value converted to nanoseconds ( bound checked )
*/
2009-08-18 21:45:11 +04:00
u64 clockevent_delta2ns ( unsigned long latch , struct clock_event_device * evt )
2007-02-16 12:28:00 +03:00
{
2009-08-18 21:45:11 +04:00
u64 clc = ( u64 ) latch < < evt - > shift ;
2007-02-16 12:28:00 +03:00
2008-01-30 15:30:03 +03:00
if ( unlikely ( ! evt - > mult ) ) {
evt - > mult = 1 ;
WARN_ON ( 1 ) ;
}
2007-02-16 12:28:00 +03:00
do_div ( clc , evt - > mult ) ;
if ( clc < 1000 )
clc = 1000 ;
2009-08-18 21:45:11 +04:00
if ( clc > KTIME_MAX )
clc = KTIME_MAX ;
2007-02-16 12:28:00 +03:00
2009-08-18 21:45:11 +04:00
return clc ;
2007-02-16 12:28:00 +03:00
}
2009-05-01 09:52:47 +04:00
EXPORT_SYMBOL_GPL ( clockevent_delta2ns ) ;
2007-02-16 12:28:00 +03:00
/**
* clockevents_set_mode - set the operating mode of a clock event device
* @ dev : device to modify
* @ mode : new mode
*
* Must be called with interrupts disabled !
*/
void clockevents_set_mode ( struct clock_event_device * dev ,
enum clock_event_mode mode )
{
if ( dev - > mode ! = mode ) {
dev - > set_mode ( mode , dev ) ;
dev - > mode = mode ;
2009-01-16 11:14:38 +03:00
/*
* A nsec2cyc multiplicator of 0 is invalid and we ' d crash
* on it , so fix it up and emit a warning :
*/
if ( mode = = CLOCK_EVT_MODE_ONESHOT ) {
if ( unlikely ( ! dev - > mult ) ) {
dev - > mult = 1 ;
WARN_ON ( 1 ) ;
}
}
2007-02-16 12:28:00 +03:00
}
}
2008-09-16 22:32:50 +04:00
/**
* clockevents_shutdown - shutdown the device and clear next_event
* @ dev : device to shutdown
*/
void clockevents_shutdown ( struct clock_event_device * dev )
{
clockevents_set_mode ( dev , CLOCK_EVT_MODE_SHUTDOWN ) ;
dev - > next_event . tv64 = KTIME_MAX ;
}
2011-08-23 17:29:42 +04:00
# ifdef CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST
/* Limit min_delta to a jiffie */
# define MIN_DELTA_LIMIT (NSEC_PER_SEC / HZ)
/**
* clockevents_increase_min_delta - raise minimum delta of a clock event device
* @ dev : device to increase the minimum delta
*
* Returns 0 on success , - ETIME when the minimum delta reached the limit .
*/
static int clockevents_increase_min_delta ( struct clock_event_device * dev )
{
/* Nothing to do if we already reached the limit */
if ( dev - > min_delta_ns > = MIN_DELTA_LIMIT ) {
printk ( KERN_WARNING " CE: Reprogramming failure. Giving up \n " ) ;
dev - > next_event . tv64 = KTIME_MAX ;
return - ETIME ;
}
if ( dev - > min_delta_ns < 5000 )
dev - > min_delta_ns = 5000 ;
else
dev - > min_delta_ns + = dev - > min_delta_ns > > 1 ;
if ( dev - > min_delta_ns > MIN_DELTA_LIMIT )
dev - > min_delta_ns = MIN_DELTA_LIMIT ;
printk ( KERN_WARNING " CE: %s increased min_delta_ns to %llu nsec \n " ,
dev - > name ? dev - > name : " ? " ,
( unsigned long long ) dev - > min_delta_ns ) ;
return 0 ;
}
/**
* clockevents_program_min_delta - Set clock event device to the minimum delay .
* @ dev : device to program
*
* Returns 0 on success , - ETIME when the retry loop failed .
*/
static int clockevents_program_min_delta ( struct clock_event_device * dev )
{
unsigned long long clc ;
int64_t delta ;
int i ;
for ( i = 0 ; ; ) {
delta = dev - > min_delta_ns ;
dev - > next_event = ktime_add_ns ( ktime_get ( ) , delta ) ;
if ( dev - > mode = = CLOCK_EVT_MODE_SHUTDOWN )
return 0 ;
dev - > retries + + ;
clc = ( ( unsigned long long ) delta * dev - > mult ) > > dev - > shift ;
if ( dev - > set_next_event ( ( unsigned long ) clc , dev ) = = 0 )
return 0 ;
if ( + + i > 2 ) {
/*
* We tried 3 times to program the device with the
* given min_delta_ns . Try to increase the minimum
* delta , if that fails as well get out of here .
*/
if ( clockevents_increase_min_delta ( dev ) )
return - ETIME ;
i = 0 ;
}
}
}
# else /* CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST */
/**
* clockevents_program_min_delta - Set clock event device to the minimum delay .
* @ dev : device to program
*
* Returns 0 on success , - ETIME when the retry loop failed .
*/
static int clockevents_program_min_delta ( struct clock_event_device * dev )
{
unsigned long long clc ;
int64_t delta ;
delta = dev - > min_delta_ns ;
dev - > next_event = ktime_add_ns ( ktime_get ( ) , delta ) ;
if ( dev - > mode = = CLOCK_EVT_MODE_SHUTDOWN )
return 0 ;
dev - > retries + + ;
clc = ( ( unsigned long long ) delta * dev - > mult ) > > dev - > shift ;
return dev - > set_next_event ( ( unsigned long ) clc , dev ) ;
}
# endif /* CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST */
2007-02-16 12:28:00 +03:00
/**
* clockevents_program_event - Reprogram the clock event device .
2011-08-23 17:29:42 +04:00
* @ dev : device to program
2007-02-16 12:28:00 +03:00
* @ expires : absolute expiry time ( monotonic clock )
2011-08-23 17:29:42 +04:00
* @ force : program minimum delay if expires can not be set
2007-02-16 12:28:00 +03:00
*
* Returns 0 on success , - ETIME when the event is in the past .
*/
int clockevents_program_event ( struct clock_event_device * dev , ktime_t expires ,
2011-08-23 17:29:42 +04:00
bool force )
2007-02-16 12:28:00 +03:00
{
unsigned long long clc ;
int64_t delta ;
2011-08-23 17:29:42 +04:00
int rc ;
2007-02-16 12:28:00 +03:00
2007-12-07 21:16:17 +03:00
if ( unlikely ( expires . tv64 < 0 ) ) {
WARN_ON_ONCE ( 1 ) ;
return - ETIME ;
}
2007-02-16 12:28:00 +03:00
dev - > next_event = expires ;
if ( dev - > mode = = CLOCK_EVT_MODE_SHUTDOWN )
return 0 ;
2011-08-23 17:29:43 +04:00
/* Shortcut for clockevent devices that can deal with ktime. */
if ( dev - > features & CLOCK_EVT_FEAT_KTIME )
return dev - > set_next_ktime ( expires , dev ) ;
2011-08-23 17:29:42 +04:00
delta = ktime_to_ns ( ktime_sub ( expires , ktime_get ( ) ) ) ;
if ( delta < = 0 )
return force ? clockevents_program_min_delta ( dev ) : - ETIME ;
2007-02-16 12:28:00 +03:00
2011-08-23 17:29:42 +04:00
delta = min ( delta , ( int64_t ) dev - > max_delta_ns ) ;
delta = max ( delta , ( int64_t ) dev - > min_delta_ns ) ;
2007-02-16 12:28:00 +03:00
2011-08-23 17:29:42 +04:00
clc = ( ( unsigned long long ) delta * dev - > mult ) > > dev - > shift ;
rc = dev - > set_next_event ( ( unsigned long ) clc , dev ) ;
return ( rc & & force ) ? clockevents_program_min_delta ( dev ) : rc ;
2007-02-16 12:28:00 +03:00
}
/**
* clockevents_register_notifier - register a clock events change listener
*/
int clockevents_register_notifier ( struct notifier_block * nb )
{
2009-08-18 01:34:59 +04:00
unsigned long flags ;
2007-02-16 12:28:00 +03:00
int ret ;
2009-12-08 14:40:31 +03:00
raw_spin_lock_irqsave ( & clockevents_lock , flags ) ;
2007-02-16 12:28:00 +03:00
ret = raw_notifier_chain_register ( & clockevents_chain , nb ) ;
2009-12-08 14:40:31 +03:00
raw_spin_unlock_irqrestore ( & clockevents_lock , flags ) ;
2007-02-16 12:28:00 +03:00
return ret ;
}
/*
* Notify about a clock event change . Called with clockevents_lock
* held .
*/
static void clockevents_do_notify ( unsigned long reason , void * dev )
{
raw_notifier_call_chain ( & clockevents_chain , reason , dev ) ;
}
/*
2008-02-08 15:19:25 +03:00
* Called after a notify add to make devices available which were
2007-02-16 12:28:00 +03:00
* released from the notifier call .
*/
static void clockevents_notify_released ( void )
{
struct clock_event_device * dev ;
while ( ! list_empty ( & clockevents_released ) ) {
dev = list_entry ( clockevents_released . next ,
struct clock_event_device , list ) ;
list_del ( & dev - > list ) ;
list_add ( & dev - > list , & clockevent_devices ) ;
clockevents_do_notify ( CLOCK_EVT_NOTIFY_ADD , dev ) ;
}
}
/**
* clockevents_register_device - register a clock event device
* @ dev : device to register
*/
void clockevents_register_device ( struct clock_event_device * dev )
{
2009-08-18 01:34:59 +04:00
unsigned long flags ;
2007-02-16 12:28:00 +03:00
BUG_ON ( dev - > mode ! = CLOCK_EVT_MODE_UNUSED ) ;
2011-06-03 13:13:33 +04:00
if ( ! dev - > cpumask ) {
WARN_ON ( num_possible_cpus ( ) > 1 ) ;
dev - > cpumask = cpumask_of ( smp_processor_id ( ) ) ;
}
2008-12-13 13:50:26 +03:00
2009-12-08 14:40:31 +03:00
raw_spin_lock_irqsave ( & clockevents_lock , flags ) ;
2007-02-16 12:28:00 +03:00
list_add ( & dev - > list , & clockevent_devices ) ;
clockevents_do_notify ( CLOCK_EVT_NOTIFY_ADD , dev ) ;
clockevents_notify_released ( ) ;
2009-12-08 14:40:31 +03:00
raw_spin_unlock_irqrestore ( & clockevents_lock , flags ) ;
2007-02-16 12:28:00 +03:00
}
2009-05-01 09:52:47 +04:00
EXPORT_SYMBOL_GPL ( clockevents_register_device ) ;
2007-02-16 12:28:00 +03:00
2012-05-09 18:39:34 +04:00
void clockevents_config ( struct clock_event_device * dev , u32 freq )
2011-05-19 01:33:41 +04:00
{
2011-05-20 12:50:52 +04:00
u64 sec ;
2011-05-19 01:33:41 +04:00
if ( ! ( dev - > features & CLOCK_EVT_FEAT_ONESHOT ) )
return ;
/*
* Calculate the maximum number of seconds we can sleep . Limit
* to 10 minutes for hardware which can program more than
* 32 bit ticks so we still get reasonable conversion values .
*/
sec = dev - > max_delta_ticks ;
do_div ( sec , freq ) ;
if ( ! sec )
sec = 1 ;
else if ( sec > 600 & & dev - > max_delta_ticks > UINT_MAX )
sec = 600 ;
clockevents_calc_mult_shift ( dev , freq , sec ) ;
dev - > min_delta_ns = clockevent_delta2ns ( dev - > min_delta_ticks , dev ) ;
dev - > max_delta_ns = clockevent_delta2ns ( dev - > max_delta_ticks , dev ) ;
}
/**
* clockevents_config_and_register - Configure and register a clock event device
* @ dev : device to register
* @ freq : The clock frequency
* @ min_delta : The minimum clock ticks to program in oneshot mode
* @ max_delta : The maximum clock ticks to program in oneshot mode
*
* min / max_delta can be 0 for devices which do not support oneshot mode .
*/
void clockevents_config_and_register ( struct clock_event_device * dev ,
u32 freq , unsigned long min_delta ,
unsigned long max_delta )
{
dev - > min_delta_ticks = min_delta ;
dev - > max_delta_ticks = max_delta ;
clockevents_config ( dev , freq ) ;
clockevents_register_device ( dev ) ;
}
2011-05-19 01:33:42 +04:00
/**
* clockevents_update_freq - Update frequency and reprogram a clock event device .
* @ dev : device to modify
* @ freq : new device frequency
*
* Reconfigure and reprogram a clock event device in oneshot
* mode . Must be called on the cpu for which the device delivers per
* cpu timer events with interrupts disabled ! Returns 0 on success ,
* - ETIME when the event is in the past .
*/
int clockevents_update_freq ( struct clock_event_device * dev , u32 freq )
{
clockevents_config ( dev , freq ) ;
if ( dev - > mode ! = CLOCK_EVT_MODE_ONESHOT )
return 0 ;
2011-08-23 17:29:42 +04:00
return clockevents_program_event ( dev , dev - > next_event , false ) ;
2011-05-19 01:33:42 +04:00
}
2007-02-16 12:28:00 +03:00
/*
* Noop handler when we shut down an event device
*/
2008-09-04 01:36:50 +04:00
void clockevents_handle_noop ( struct clock_event_device * dev )
2007-02-16 12:28:00 +03:00
{
}
/**
* clockevents_exchange_device - release and request clock devices
* @ old : device to release ( can be NULL )
* @ new : device to request ( can be NULL )
*
* Called from the notifier chain . clockevents_lock is held already
*/
void clockevents_exchange_device ( struct clock_event_device * old ,
struct clock_event_device * new )
{
unsigned long flags ;
local_irq_save ( flags ) ;
/*
* Caller releases a clock event device . We queue it into the
* released list and do a notify add later .
*/
if ( old ) {
clockevents_set_mode ( old , CLOCK_EVT_MODE_UNUSED ) ;
list_del ( & old - > list ) ;
list_add ( & old - > list , & clockevents_released ) ;
}
if ( new ) {
BUG_ON ( new - > mode ! = CLOCK_EVT_MODE_UNUSED ) ;
2008-09-16 22:32:50 +04:00
clockevents_shutdown ( new ) ;
2007-02-16 12:28:00 +03:00
}
local_irq_restore ( flags ) ;
}
2012-08-06 03:40:41 +04:00
/**
* clockevents_suspend - suspend clock devices
*/
void clockevents_suspend ( void )
{
struct clock_event_device * dev ;
list_for_each_entry_reverse ( dev , & clockevent_devices , list )
if ( dev - > suspend )
dev - > suspend ( dev ) ;
}
/**
* clockevents_resume - resume clock devices
*/
void clockevents_resume ( void )
{
struct clock_event_device * dev ;
list_for_each_entry ( dev , & clockevent_devices , list )
if ( dev - > resume )
dev - > resume ( dev ) ;
}
2007-10-13 01:04:05 +04:00
# ifdef CONFIG_GENERIC_CLOCKEVENTS
2007-02-16 12:28:00 +03:00
/**
* clockevents_notify - notification about relevant events
*/
void clockevents_notify ( unsigned long reason , void * arg )
{
2009-12-10 17:35:10 +03:00
struct clock_event_device * dev , * tmp ;
2009-08-18 01:34:59 +04:00
unsigned long flags ;
2009-12-10 17:35:10 +03:00
int cpu ;
2008-02-08 15:19:24 +03:00
2009-12-08 14:40:31 +03:00
raw_spin_lock_irqsave ( & clockevents_lock , flags ) ;
2007-02-16 12:28:00 +03:00
clockevents_do_notify ( reason , arg ) ;
switch ( reason ) {
case CLOCK_EVT_NOTIFY_CPU_DEAD :
/*
* Unregister the clock event devices which were
* released from the users in the notify chain .
*/
2009-12-10 17:35:10 +03:00
list_for_each_entry_safe ( dev , tmp , & clockevents_released , list )
list_del ( & dev - > list ) ;
/*
* Now check whether the CPU has left unused per cpu devices
*/
cpu = * ( ( int * ) arg ) ;
list_for_each_entry_safe ( dev , tmp , & clockevent_devices , list ) {
if ( cpumask_test_cpu ( cpu , dev - > cpumask ) & &
2010-01-07 06:22:44 +03:00
cpumask_weight ( dev - > cpumask ) = = 1 & &
! tick_is_broadcast_device ( dev ) ) {
2009-12-10 17:35:10 +03:00
BUG_ON ( dev - > mode ! = CLOCK_EVT_MODE_UNUSED ) ;
list_del ( & dev - > list ) ;
}
}
2007-02-16 12:28:00 +03:00
break ;
default :
break ;
}
2009-12-08 14:40:31 +03:00
raw_spin_unlock_irqrestore ( & clockevents_lock , flags ) ;
2007-02-16 12:28:00 +03:00
}
EXPORT_SYMBOL_GPL ( clockevents_notify ) ;
2007-10-13 01:04:05 +04:00
# endif