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/smp.h>
2013-04-26 00:31:49 +04:00
# include <linux/device.h>
2007-02-16 12:28:00 +03:00
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 ) ;
/* Protection for the above */
2009-12-08 14:40:31 +03:00
static DEFINE_RAW_SPINLOCK ( clockevents_lock ) ;
2013-04-26 00:31:50 +04:00
/* Protection for unbind operations */
static DEFINE_MUTEX ( clockevents_mutex ) ;
struct ce_unbind {
struct clock_event_device * ce ;
int res ;
} ;
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
}
/*
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 ) ;
2013-04-26 00:31:47 +04:00
tick_check_new_device ( dev ) ;
2007-02-16 12:28:00 +03:00
}
}
2013-04-26 00:31:50 +04:00
/*
* Try to install a replacement clock event device
*/
static int clockevents_replace ( struct clock_event_device * ced )
{
struct clock_event_device * dev , * newdev = NULL ;
list_for_each_entry ( dev , & clockevent_devices , list ) {
if ( dev = = ced | | dev - > mode ! = CLOCK_EVT_MODE_UNUSED )
continue ;
if ( ! tick_check_replacement ( newdev , dev ) )
continue ;
if ( ! try_module_get ( dev - > owner ) )
continue ;
if ( newdev )
module_put ( newdev - > owner ) ;
newdev = dev ;
}
if ( newdev ) {
tick_install_replacement ( newdev ) ;
list_del_init ( & ced - > list ) ;
}
return newdev ? 0 : - EBUSY ;
}
/*
* Called with clockevents_mutex and clockevents_lock held
*/
static int __clockevents_try_unbind ( struct clock_event_device * ced , int cpu )
{
/* Fast track. Device is unused */
if ( ced - > mode = = CLOCK_EVT_MODE_UNUSED ) {
list_del_init ( & ced - > list ) ;
return 0 ;
}
return ced = = per_cpu ( tick_cpu_device , cpu ) . evtdev ? - EAGAIN : - EBUSY ;
}
/*
* SMP function call to unbind a device
*/
static void __clockevents_unbind ( void * arg )
{
struct ce_unbind * cu = arg ;
int res ;
raw_spin_lock ( & clockevents_lock ) ;
res = __clockevents_try_unbind ( cu - > ce , smp_processor_id ( ) ) ;
if ( res = = - EAGAIN )
res = clockevents_replace ( cu - > ce ) ;
cu - > res = res ;
raw_spin_unlock ( & clockevents_lock ) ;
}
/*
* Issues smp function call to unbind a per cpu device . Called with
* clockevents_mutex held .
*/
static int clockevents_unbind ( struct clock_event_device * ced , int cpu )
{
struct ce_unbind cu = { . ce = ced , . res = - ENODEV } ;
smp_call_function_single ( cpu , __clockevents_unbind , & cu , 1 ) ;
return cu . res ;
}
/*
* Unbind a clockevents device .
*/
int clockevents_unbind_device ( struct clock_event_device * ced , int cpu )
{
int ret ;
mutex_lock ( & clockevents_mutex ) ;
ret = clockevents_unbind ( ced , cpu ) ;
mutex_unlock ( & clockevents_mutex ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( clockevents_unbind ) ;
2007-02-16 12:28:00 +03:00
/**
* 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 ) ;
2013-04-26 00:31:47 +04:00
tick_check_new_device ( dev ) ;
2007-02-16 12:28:00 +03:00
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 ) ;
}
2013-01-12 15:50:04 +04:00
EXPORT_SYMBOL_GPL ( clockevents_config_and_register ) ;
2011-05-19 01:33:41 +04:00
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 ) {
2013-04-26 00:31:49 +04:00
module_put ( old - > owner ) ;
2007-02-16 12:28:00 +03:00
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
switch ( reason ) {
2013-04-26 00:31:48 +04:00
case CLOCK_EVT_NOTIFY_BROADCAST_ON :
case CLOCK_EVT_NOTIFY_BROADCAST_OFF :
case CLOCK_EVT_NOTIFY_BROADCAST_FORCE :
tick_broadcast_on_off ( reason , arg ) ;
break ;
case CLOCK_EVT_NOTIFY_BROADCAST_ENTER :
case CLOCK_EVT_NOTIFY_BROADCAST_EXIT :
tick_broadcast_oneshot_control ( reason ) ;
break ;
case CLOCK_EVT_NOTIFY_CPU_DYING :
tick_handover_do_timer ( arg ) ;
break ;
case CLOCK_EVT_NOTIFY_SUSPEND :
tick_suspend ( ) ;
tick_suspend_broadcast ( ) ;
break ;
case CLOCK_EVT_NOTIFY_RESUME :
tick_resume ( ) ;
break ;
2007-02-16 12:28:00 +03:00
case CLOCK_EVT_NOTIFY_CPU_DEAD :
2013-04-26 00:31:48 +04:00
tick_shutdown_broadcast_oneshot ( arg ) ;
tick_shutdown_broadcast ( arg ) ;
tick_shutdown ( arg ) ;
2007-02-16 12:28:00 +03:00
/*
* 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 ) ;
2013-04-26 00:31:49 +04:00
# ifdef CONFIG_SYSFS
struct bus_type clockevents_subsys = {
. name = " clockevents " ,
. dev_name = " clockevent " ,
} ;
static DEFINE_PER_CPU ( struct device , tick_percpu_dev ) ;
static struct tick_device * tick_get_tick_dev ( struct device * dev ) ;
static ssize_t sysfs_show_current_tick_dev ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct tick_device * td ;
ssize_t count = 0 ;
raw_spin_lock_irq ( & clockevents_lock ) ;
td = tick_get_tick_dev ( dev ) ;
if ( td & & td - > evtdev )
count = snprintf ( buf , PAGE_SIZE , " %s \n " , td - > evtdev - > name ) ;
raw_spin_unlock_irq ( & clockevents_lock ) ;
return count ;
}
static DEVICE_ATTR ( current_device , 0444 , sysfs_show_current_tick_dev , NULL ) ;
2013-04-26 00:31:50 +04:00
/* We don't support the abomination of removable broadcast devices */
static ssize_t sysfs_unbind_tick_dev ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
char name [ CS_NAME_LEN ] ;
size_t ret = sysfs_get_uname ( buf , name , count ) ;
struct clock_event_device * ce ;
if ( ret < 0 )
return ret ;
ret = - ENODEV ;
mutex_lock ( & clockevents_mutex ) ;
raw_spin_lock_irq ( & clockevents_lock ) ;
list_for_each_entry ( ce , & clockevent_devices , list ) {
if ( ! strcmp ( ce - > name , name ) ) {
ret = __clockevents_try_unbind ( ce , dev - > id ) ;
break ;
}
}
raw_spin_unlock_irq ( & clockevents_lock ) ;
/*
* We hold clockevents_mutex , so ce can ' t go away
*/
if ( ret = = - EAGAIN )
ret = clockevents_unbind ( ce , dev - > id ) ;
mutex_unlock ( & clockevents_mutex ) ;
return ret ? ret : count ;
}
static DEVICE_ATTR ( unbind_device , 0200 , NULL , sysfs_unbind_tick_dev ) ;
2013-04-26 00:31:49 +04:00
# ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
static struct device tick_bc_dev = {
. init_name = " broadcast " ,
. id = 0 ,
. bus = & clockevents_subsys ,
} ;
static struct tick_device * tick_get_tick_dev ( struct device * dev )
{
return dev = = & tick_bc_dev ? tick_get_broadcast_device ( ) :
& per_cpu ( tick_cpu_device , dev - > id ) ;
}
static __init int tick_broadcast_init_sysfs ( void )
{
int err = device_register ( & tick_bc_dev ) ;
if ( ! err )
err = device_create_file ( & tick_bc_dev , & dev_attr_current_device ) ;
return err ;
}
# else
static struct tick_device * tick_get_tick_dev ( struct device * dev )
{
return & per_cpu ( tick_cpu_device , dev - > id ) ;
}
static inline int tick_broadcast_init_sysfs ( void ) { return 0 ; }
2007-10-13 01:04:05 +04:00
# endif
2013-04-26 00:31:49 +04:00
static int __init tick_init_sysfs ( void )
{
int cpu ;
for_each_possible_cpu ( cpu ) {
struct device * dev = & per_cpu ( tick_percpu_dev , cpu ) ;
int err ;
dev - > id = cpu ;
dev - > bus = & clockevents_subsys ;
err = device_register ( dev ) ;
if ( ! err )
err = device_create_file ( dev , & dev_attr_current_device ) ;
2013-04-26 00:31:50 +04:00
if ( ! err )
err = device_create_file ( dev , & dev_attr_unbind_device ) ;
2013-04-26 00:31:49 +04:00
if ( err )
return err ;
}
return tick_broadcast_init_sysfs ( ) ;
}
static int __init clockevents_init_sysfs ( void )
{
int err = subsys_system_register ( & clockevents_subsys , NULL ) ;
if ( ! err )
err = tick_init_sysfs ( ) ;
return err ;
}
device_initcall ( clockevents_init_sysfs ) ;
# endif /* SYSFS */
# endif /* GENERIC_CLOCK_EVENTS */