2018-09-19 04:16:32 +03:00
// SPDX-License-Identifier: GPL-2.0
2006-03-27 13:16:42 +04:00
/*
* An RTC test device / driver
* Copyright ( C ) 2005 Tower Technologies
* Author : Alessandro Zummo < a . zummo @ towertech . it >
*/
# include <linux/module.h>
# include <linux/err.h>
# include <linux/rtc.h>
# include <linux/platform_device.h>
2018-06-01 00:09:56 +03:00
# define MAX_RTC_TEST 3
2018-06-01 00:09:57 +03:00
struct rtc_test_data {
struct rtc_device * rtc ;
time64_t offset ;
2018-06-01 00:09:58 +03:00
struct timer_list alarm ;
bool alarm_en ;
2018-06-01 00:09:57 +03:00
} ;
2018-06-24 13:38:03 +03:00
static struct platform_device * pdev [ MAX_RTC_TEST ] ;
2006-03-27 13:16:42 +04:00
2018-06-01 00:09:58 +03:00
static int test_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
2006-03-27 13:16:42 +04:00
{
2018-06-01 00:09:58 +03:00
struct rtc_test_data * rtd = dev_get_drvdata ( dev ) ;
time64_t alarm ;
alarm = ( rtd - > alarm . expires - jiffies ) / HZ ;
alarm + = ktime_get_real_seconds ( ) + rtd - > offset ;
rtc_time64_to_tm ( alarm , & alrm - > time ) ;
alrm - > enabled = rtd - > alarm_en ;
2006-03-27 13:16:42 +04:00
return 0 ;
}
2018-06-01 00:09:58 +03:00
static int test_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
2006-03-27 13:16:42 +04:00
{
2018-06-01 00:09:58 +03:00
struct rtc_test_data * rtd = dev_get_drvdata ( dev ) ;
ktime_t timeout ;
u64 expires ;
timeout = rtc_tm_to_time64 ( & alrm - > time ) - ktime_get_real_seconds ( ) ;
timeout - = rtd - > offset ;
del_timer ( & rtd - > alarm ) ;
expires = jiffies + timeout * HZ ;
if ( expires > U32_MAX )
expires = U32_MAX ;
pr_err ( " ABE: %s +%d %s \n " , __FILE__ , __LINE__ , __func__ ) ;
rtd - > alarm . expires = expires ;
if ( alrm - > enabled )
add_timer ( & rtd - > alarm ) ;
rtd - > alarm_en = alrm - > enabled ;
2006-03-27 13:16:42 +04:00
return 0 ;
}
2018-06-01 00:09:57 +03:00
static int test_rtc_read_time ( struct device * dev , struct rtc_time * tm )
2006-03-27 13:16:42 +04:00
{
2018-06-01 00:09:57 +03:00
struct rtc_test_data * rtd = dev_get_drvdata ( dev ) ;
rtc_time64_to_tm ( ktime_get_real_seconds ( ) + rtd - > offset , tm ) ;
2015-04-02 06:34:28 +03:00
return 0 ;
}
2019-04-08 00:30:12 +03:00
static int test_rtc_set_time ( struct device * dev , struct rtc_time * tm )
2015-04-02 06:34:28 +03:00
{
2018-06-01 00:09:57 +03:00
struct rtc_test_data * rtd = dev_get_drvdata ( dev ) ;
2019-04-08 00:30:12 +03:00
rtd - > offset = rtc_tm_to_time64 ( tm ) - ktime_get_real_seconds ( ) ;
2018-06-01 00:09:57 +03:00
2006-03-27 13:16:42 +04:00
return 0 ;
}
2011-02-03 04:02:41 +03:00
static int test_rtc_alarm_irq_enable ( struct device * dev , unsigned int enable )
2006-03-27 13:16:42 +04:00
{
2018-06-01 00:09:58 +03:00
struct rtc_test_data * rtd = dev_get_drvdata ( dev ) ;
rtd - > alarm_en = enable ;
if ( enable )
add_timer ( & rtd - > alarm ) ;
else
del_timer ( & rtd - > alarm ) ;
2011-02-03 04:02:41 +03:00
return 0 ;
2006-03-27 13:16:42 +04:00
}
2018-06-06 00:09:13 +03:00
static const struct rtc_class_ops test_rtc_ops_noalm = {
. read_time = test_rtc_read_time ,
2019-04-08 00:30:12 +03:00
. set_time = test_rtc_set_time ,
2018-06-06 00:09:13 +03:00
. alarm_irq_enable = test_rtc_alarm_irq_enable ,
} ;
2018-05-26 04:57:29 +03:00
static const struct rtc_class_ops test_rtc_ops = {
2006-03-27 13:16:42 +04:00
. read_time = test_rtc_read_time ,
2019-04-08 00:30:12 +03:00
. set_time = test_rtc_set_time ,
2006-03-27 13:16:42 +04:00
. read_alarm = test_rtc_read_alarm ,
. set_alarm = test_rtc_set_alarm ,
2011-02-03 04:02:41 +03:00
. alarm_irq_enable = test_rtc_alarm_irq_enable ,
2006-03-27 13:16:42 +04:00
} ;
2018-06-01 00:09:58 +03:00
static void test_rtc_alarm_handler ( struct timer_list * t )
{
struct rtc_test_data * rtd = from_timer ( rtd , t , alarm ) ;
rtc_update_irq ( rtd - > rtc , 1 , RTC_AF | RTC_IRQF ) ;
}
2006-03-27 13:16:42 +04:00
static int test_probe ( struct platform_device * plat_dev )
{
2018-06-01 00:09:57 +03:00
struct rtc_test_data * rtd ;
2013-04-30 03:19:52 +04:00
2018-06-01 00:09:57 +03:00
rtd = devm_kzalloc ( & plat_dev - > dev , sizeof ( * rtd ) , GFP_KERNEL ) ;
if ( ! rtd )
return - ENOMEM ;
2006-12-07 07:35:34 +03:00
2018-06-01 00:09:57 +03:00
platform_set_drvdata ( plat_dev , rtd ) ;
2006-03-27 13:16:42 +04:00
2018-06-06 00:09:12 +03:00
rtd - > rtc = devm_rtc_allocate_device ( & plat_dev - > dev ) ;
2018-06-01 00:09:57 +03:00
if ( IS_ERR ( rtd - > rtc ) )
return PTR_ERR ( rtd - > rtc ) ;
2006-03-27 13:16:42 +04:00
2018-06-06 00:09:13 +03:00
switch ( plat_dev - > id ) {
case 0 :
rtd - > rtc - > ops = & test_rtc_ops_noalm ;
break ;
default :
rtd - > rtc - > ops = & test_rtc_ops ;
2019-05-15 17:20:22 +03:00
device_init_wakeup ( & plat_dev - > dev , 1 ) ;
2018-06-06 00:09:13 +03:00
}
2018-06-06 00:09:12 +03:00
2018-06-01 00:09:58 +03:00
timer_setup ( & rtd - > alarm , test_rtc_alarm_handler , 0 ) ;
rtd - > alarm . expires = 0 ;
2018-06-06 00:09:12 +03:00
return rtc_register_device ( rtd - > rtc ) ;
2006-03-27 13:16:42 +04:00
}
2008-04-28 13:11:55 +04:00
static struct platform_driver test_driver = {
2006-03-27 13:16:42 +04:00
. probe = test_probe ,
. driver = {
. name = " rtc-test " ,
} ,
} ;
static int __init test_init ( void )
{
2018-06-01 00:09:56 +03:00
int i , err ;
2006-03-27 13:16:42 +04:00
2019-03-20 15:03:12 +03:00
err = platform_driver_register ( & test_driver ) ;
if ( err )
2006-03-27 13:16:42 +04:00
return err ;
2018-06-01 00:09:56 +03:00
err = - ENOMEM ;
for ( i = 0 ; i < MAX_RTC_TEST ; i + + ) {
pdev [ i ] = platform_device_alloc ( " rtc-test " , i ) ;
if ( ! pdev [ i ] )
goto exit_free_mem ;
2006-03-27 13:16:42 +04:00
}
2018-06-01 00:09:56 +03:00
for ( i = 0 ; i < MAX_RTC_TEST ; i + + ) {
err = platform_device_add ( pdev [ i ] ) ;
if ( err )
goto exit_device_del ;
2006-03-27 13:16:42 +04:00
}
return 0 ;
2018-06-01 00:09:56 +03:00
exit_device_del :
for ( ; i > 0 ; i - - )
platform_device_del ( pdev [ i - 1 ] ) ;
2006-03-27 13:16:42 +04:00
2018-06-01 00:09:56 +03:00
exit_free_mem :
for ( i = 0 ; i < MAX_RTC_TEST ; i + + )
platform_device_put ( pdev [ i ] ) ;
2006-03-27 13:16:42 +04:00
2008-04-28 13:11:55 +04:00
platform_driver_unregister ( & test_driver ) ;
2006-03-27 13:16:42 +04:00
return err ;
}
static void __exit test_exit ( void )
{
2018-06-01 00:09:56 +03:00
int i ;
for ( i = 0 ; i < MAX_RTC_TEST ; i + + )
platform_device_unregister ( pdev [ i ] ) ;
2008-04-28 13:11:55 +04:00
platform_driver_unregister ( & test_driver ) ;
2006-03-27 13:16:42 +04:00
}
MODULE_AUTHOR ( " Alessandro Zummo <a.zummo@towertech.it> " ) ;
MODULE_DESCRIPTION ( " RTC test driver/device " ) ;
2018-09-19 04:16:31 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;
2006-03-27 13:16:42 +04:00
module_init ( test_init ) ;
module_exit ( test_exit ) ;