2006-03-27 13:16:37 +04:00
/*
* RTC subsystem , interface functions
*
* Copyright ( C ) 2005 Tower Technologies
* Author : Alessandro Zummo < a . zummo @ towertech . it >
*
* based on arch / arm / common / rtctime . c
*
* 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 .
*/
# include <linux/rtc.h>
2007-05-08 11:33:30 +04:00
int rtc_read_time ( struct rtc_device * rtc , struct rtc_time * tm )
2006-03-27 13:16:37 +04:00
{
int err ;
err = mutex_lock_interruptible ( & rtc - > ops_lock ) ;
if ( err )
return - EBUSY ;
if ( ! rtc - > ops )
err = - ENODEV ;
else if ( ! rtc - > ops - > read_time )
err = - EINVAL ;
else {
memset ( tm , 0 , sizeof ( struct rtc_time ) ) ;
2007-05-08 11:33:40 +04:00
err = rtc - > ops - > read_time ( rtc - > dev . parent , tm ) ;
2006-03-27 13:16:37 +04:00
}
mutex_unlock ( & rtc - > ops_lock ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( rtc_read_time ) ;
2007-05-08 11:33:30 +04:00
int rtc_set_time ( struct rtc_device * rtc , struct rtc_time * tm )
2006-03-27 13:16:37 +04:00
{
int err ;
err = rtc_valid_tm ( tm ) ;
if ( err ! = 0 )
return err ;
err = mutex_lock_interruptible ( & rtc - > ops_lock ) ;
if ( err )
return - EBUSY ;
if ( ! rtc - > ops )
err = - ENODEV ;
else if ( ! rtc - > ops - > set_time )
err = - EINVAL ;
else
2007-05-08 11:33:40 +04:00
err = rtc - > ops - > set_time ( rtc - > dev . parent , tm ) ;
2006-03-27 13:16:37 +04:00
mutex_unlock ( & rtc - > ops_lock ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( rtc_set_time ) ;
2007-05-08 11:33:30 +04:00
int rtc_set_mmss ( struct rtc_device * rtc , unsigned long secs )
2006-03-27 13:16:37 +04:00
{
int err ;
err = mutex_lock_interruptible ( & rtc - > ops_lock ) ;
if ( err )
return - EBUSY ;
if ( ! rtc - > ops )
err = - ENODEV ;
else if ( rtc - > ops - > set_mmss )
2007-05-08 11:33:40 +04:00
err = rtc - > ops - > set_mmss ( rtc - > dev . parent , secs ) ;
2006-03-27 13:16:37 +04:00
else if ( rtc - > ops - > read_time & & rtc - > ops - > set_time ) {
struct rtc_time new , old ;
2007-05-08 11:33:40 +04:00
err = rtc - > ops - > read_time ( rtc - > dev . parent , & old ) ;
2006-03-27 13:16:37 +04:00
if ( err = = 0 ) {
rtc_time_to_tm ( secs , & new ) ;
/*
* avoid writing when we ' re going to change the day of
* the month . We will retry in the next minute . This
* basically means that if the RTC must not drift
* by more than 1 minute in 11 minutes .
*/
if ( ! ( ( old . tm_hour = = 23 & & old . tm_min = = 59 ) | |
( new . tm_hour = = 23 & & new . tm_min = = 59 ) ) )
2007-05-08 11:33:40 +04:00
err = rtc - > ops - > set_time ( rtc - > dev . parent ,
2007-05-08 11:33:30 +04:00
& new ) ;
2006-03-27 13:16:37 +04:00
}
}
else
err = - EINVAL ;
mutex_unlock ( & rtc - > ops_lock ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( rtc_set_mmss ) ;
2007-05-08 11:33:30 +04:00
int rtc_read_alarm ( struct rtc_device * rtc , struct rtc_wkalrm * alarm )
2006-03-27 13:16:37 +04:00
{
int err ;
err = mutex_lock_interruptible ( & rtc - > ops_lock ) ;
if ( err )
return - EBUSY ;
if ( rtc - > ops = = NULL )
err = - ENODEV ;
else if ( ! rtc - > ops - > read_alarm )
err = - EINVAL ;
else {
memset ( alarm , 0 , sizeof ( struct rtc_wkalrm ) ) ;
2007-05-08 11:33:40 +04:00
err = rtc - > ops - > read_alarm ( rtc - > dev . parent , alarm ) ;
2006-03-27 13:16:37 +04:00
}
mutex_unlock ( & rtc - > ops_lock ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( rtc_read_alarm ) ;
2007-05-08 11:33:30 +04:00
int rtc_set_alarm ( struct rtc_device * rtc , struct rtc_wkalrm * alarm )
2006-03-27 13:16:37 +04:00
{
int err ;
2007-05-08 11:34:07 +04:00
err = rtc_valid_tm ( & alarm - > time ) ;
if ( err ! = 0 )
return err ;
2006-03-27 13:16:37 +04:00
err = mutex_lock_interruptible ( & rtc - > ops_lock ) ;
if ( err )
return - EBUSY ;
if ( ! rtc - > ops )
err = - ENODEV ;
else if ( ! rtc - > ops - > set_alarm )
err = - EINVAL ;
else
2007-05-08 11:33:40 +04:00
err = rtc - > ops - > set_alarm ( rtc - > dev . parent , alarm ) ;
2006-03-27 13:16:37 +04:00
mutex_unlock ( & rtc - > ops_lock ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( rtc_set_alarm ) ;
2006-11-25 22:09:28 +03:00
/**
* rtc_update_irq - report RTC periodic , alarm , and / or update irqs
2007-05-08 11:33:30 +04:00
* @ rtc : the rtc device
2006-11-25 22:09:28 +03:00
* @ num : how many irqs are being reported ( usually one )
* @ events : mask of RTC_IRQF with one or more of RTC_PF , RTC_AF , RTC_UF
* Context : in_interrupt ( ) , irqs blocked
*/
2007-05-08 11:33:30 +04:00
void rtc_update_irq ( struct rtc_device * rtc ,
2006-03-27 13:16:37 +04:00
unsigned long num , unsigned long events )
{
spin_lock ( & rtc - > irq_lock ) ;
rtc - > irq_data = ( rtc - > irq_data + ( num < < 8 ) ) | events ;
spin_unlock ( & rtc - > irq_lock ) ;
spin_lock ( & rtc - > irq_task_lock ) ;
if ( rtc - > irq_task )
rtc - > irq_task - > func ( rtc - > irq_task - > private_data ) ;
spin_unlock ( & rtc - > irq_task_lock ) ;
wake_up_interruptible ( & rtc - > irq_queue ) ;
kill_fasync ( & rtc - > async_queue , SIGIO , POLL_IN ) ;
}
EXPORT_SYMBOL_GPL ( rtc_update_irq ) ;
2007-05-08 11:33:30 +04:00
struct rtc_device * rtc_class_open ( char * name )
2006-03-27 13:16:37 +04:00
{
2007-05-08 11:33:40 +04:00
struct device * dev ;
2007-05-08 11:33:30 +04:00
struct rtc_device * rtc = NULL ;
2006-03-27 13:16:37 +04:00
down ( & rtc_class - > sem ) ;
2007-05-08 11:33:40 +04:00
list_for_each_entry ( dev , & rtc_class - > devices , node ) {
if ( strncmp ( dev - > bus_id , name , BUS_ID_SIZE ) = = 0 ) {
dev = get_device ( dev ) ;
if ( dev )
rtc = to_rtc_device ( dev ) ;
2006-03-27 13:16:37 +04:00
break ;
}
}
2007-05-08 11:33:30 +04:00
if ( rtc ) {
if ( ! try_module_get ( rtc - > owner ) ) {
2007-05-08 11:33:40 +04:00
put_device ( dev ) ;
2007-05-08 11:33:30 +04:00
rtc = NULL ;
}
2006-03-27 13:16:37 +04:00
}
up ( & rtc_class - > sem ) ;
2007-05-08 11:33:30 +04:00
return rtc ;
2006-03-27 13:16:37 +04:00
}
EXPORT_SYMBOL_GPL ( rtc_class_open ) ;
2007-05-08 11:33:30 +04:00
void rtc_class_close ( struct rtc_device * rtc )
2006-03-27 13:16:37 +04:00
{
2007-05-08 11:33:30 +04:00
module_put ( rtc - > owner ) ;
2007-05-08 11:33:40 +04:00
put_device ( & rtc - > dev ) ;
2006-03-27 13:16:37 +04:00
}
EXPORT_SYMBOL_GPL ( rtc_class_close ) ;
2007-05-08 11:33:30 +04:00
int rtc_irq_register ( struct rtc_device * rtc , struct rtc_task * task )
2006-03-27 13:16:37 +04:00
{
int retval = - EBUSY ;
if ( task = = NULL | | task - > func = = NULL )
return - EINVAL ;
2007-10-16 12:28:15 +04:00
/* Cannot register while the char dev is in use */
if ( ! ( mutex_trylock ( & rtc - > char_lock ) ) )
return - EBUSY ;
2006-11-25 22:09:28 +03:00
spin_lock_irq ( & rtc - > irq_task_lock ) ;
2006-03-27 13:16:37 +04:00
if ( rtc - > irq_task = = NULL ) {
rtc - > irq_task = task ;
retval = 0 ;
}
2006-11-25 22:09:28 +03:00
spin_unlock_irq ( & rtc - > irq_task_lock ) ;
2006-03-27 13:16:37 +04:00
2007-10-16 12:28:15 +04:00
mutex_unlock ( & rtc - > char_lock ) ;
2006-03-27 13:16:37 +04:00
return retval ;
}
EXPORT_SYMBOL_GPL ( rtc_irq_register ) ;
2007-05-08 11:33:30 +04:00
void rtc_irq_unregister ( struct rtc_device * rtc , struct rtc_task * task )
2006-03-27 13:16:37 +04:00
{
2006-11-25 22:09:28 +03:00
spin_lock_irq ( & rtc - > irq_task_lock ) ;
2006-03-27 13:16:37 +04:00
if ( rtc - > irq_task = = task )
rtc - > irq_task = NULL ;
2006-11-25 22:09:28 +03:00
spin_unlock_irq ( & rtc - > irq_task_lock ) ;
2006-03-27 13:16:37 +04:00
}
EXPORT_SYMBOL_GPL ( rtc_irq_unregister ) ;
2007-05-08 11:33:30 +04:00
int rtc_irq_set_state ( struct rtc_device * rtc , struct rtc_task * task , int enabled )
2006-03-27 13:16:37 +04:00
{
int err = 0 ;
unsigned long flags ;
2006-06-25 16:48:20 +04:00
if ( rtc - > ops - > irq_set_state = = NULL )
return - ENXIO ;
2006-03-27 13:16:37 +04:00
spin_lock_irqsave ( & rtc - > irq_task_lock , flags ) ;
2007-10-16 12:28:15 +04:00
if ( rtc - > irq_task ! = NULL & & task = = NULL )
err = - EBUSY ;
2006-03-27 13:16:37 +04:00
if ( rtc - > irq_task ! = task )
2007-10-16 12:28:15 +04:00
err = - EACCES ;
2006-03-27 13:16:37 +04:00
spin_unlock_irqrestore ( & rtc - > irq_task_lock , flags ) ;
if ( err = = 0 )
2007-05-08 11:33:40 +04:00
err = rtc - > ops - > irq_set_state ( rtc - > dev . parent , enabled ) ;
2006-03-27 13:16:37 +04:00
return err ;
}
EXPORT_SYMBOL_GPL ( rtc_irq_set_state ) ;
2007-05-08 11:33:30 +04:00
int rtc_irq_set_freq ( struct rtc_device * rtc , struct rtc_task * task , int freq )
2006-03-27 13:16:37 +04:00
{
2006-06-25 16:48:20 +04:00
int err = 0 ;
2006-03-27 13:16:37 +04:00
unsigned long flags ;
2006-06-25 16:48:20 +04:00
if ( rtc - > ops - > irq_set_freq = = NULL )
return - ENXIO ;
2006-03-27 13:16:37 +04:00
spin_lock_irqsave ( & rtc - > irq_task_lock , flags ) ;
2007-10-16 12:28:15 +04:00
if ( rtc - > irq_task ! = NULL & & task = = NULL )
err = - EBUSY ;
2006-03-27 13:16:37 +04:00
if ( rtc - > irq_task ! = task )
2007-10-16 12:28:15 +04:00
err = - EACCES ;
2006-03-27 13:16:37 +04:00
spin_unlock_irqrestore ( & rtc - > irq_task_lock , flags ) ;
if ( err = = 0 ) {
2007-05-08 11:33:40 +04:00
err = rtc - > ops - > irq_set_freq ( rtc - > dev . parent , freq ) ;
2006-03-27 13:16:37 +04:00
if ( err = = 0 )
rtc - > irq_freq = freq ;
}
return err ;
}
2006-11-25 22:09:27 +03:00
EXPORT_SYMBOL_GPL ( rtc_irq_set_freq ) ;