2008-11-15 03:37:54 +03:00
/* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems.
2008-08-29 12:32:43 +04:00
*
* Copyright ( C ) 2008 David S . Miller < davem @ davemloft . net >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/rtc.h>
# include <linux/platform_device.h>
# include <asm/hypervisor.h>
static unsigned long hypervisor_get_time ( void )
{
unsigned long ret , time ;
int retries = 10000 ;
retry :
ret = sun4v_tod_get ( & time ) ;
if ( ret = = HV_EOK )
return time ;
if ( ret = = HV_EWOULDBLOCK ) {
if ( - - retries > 0 ) {
udelay ( 100 ) ;
goto retry ;
}
printk ( KERN_WARNING " SUN4V: tod_get() timed out. \n " ) ;
return 0 ;
}
printk ( KERN_WARNING " SUN4V: tod_get() not supported. \n " ) ;
return 0 ;
}
static int sun4v_read_time ( struct device * dev , struct rtc_time * tm )
{
2008-11-15 03:37:54 +03:00
rtc_time_to_tm ( hypervisor_get_time ( ) , tm ) ;
2008-08-29 12:32:43 +04:00
return 0 ;
}
static int hypervisor_set_time ( unsigned long secs )
{
unsigned long ret ;
int retries = 10000 ;
retry :
ret = sun4v_tod_set ( secs ) ;
if ( ret = = HV_EOK )
return 0 ;
if ( ret = = HV_EWOULDBLOCK ) {
if ( - - retries > 0 ) {
udelay ( 100 ) ;
goto retry ;
}
printk ( KERN_WARNING " SUN4V: tod_set() timed out. \n " ) ;
return - EAGAIN ;
}
printk ( KERN_WARNING " SUN4V: tod_set() not supported. \n " ) ;
return - EOPNOTSUPP ;
}
static int sun4v_set_time ( struct device * dev , struct rtc_time * tm )
{
2008-11-15 03:37:54 +03:00
unsigned long secs ;
2008-08-29 12:32:43 +04:00
int err ;
err = rtc_tm_to_time ( tm , & secs ) ;
if ( err )
return err ;
2008-11-15 03:37:54 +03:00
return hypervisor_set_time ( secs ) ;
2008-08-29 12:32:43 +04:00
}
static const struct rtc_class_ops sun4v_rtc_ops = {
. read_time = sun4v_read_time ,
. set_time = sun4v_set_time ,
} ;
2008-11-15 03:37:54 +03:00
static int __init sun4v_rtc_probe ( struct platform_device * pdev )
2008-08-29 12:32:43 +04:00
{
2008-11-15 03:37:54 +03:00
struct rtc_device * rtc = rtc_device_register ( " sun4v " , & pdev - > dev ,
2008-08-29 12:32:43 +04:00
& sun4v_rtc_ops , THIS_MODULE ) ;
2008-11-15 03:37:54 +03:00
if ( IS_ERR ( rtc ) )
return PTR_ERR ( rtc ) ;
platform_set_drvdata ( pdev , rtc ) ;
2008-08-29 12:32:43 +04:00
return 0 ;
}
2008-11-15 03:37:54 +03:00
static int __exit sun4v_rtc_remove ( struct platform_device * pdev )
2008-08-29 12:32:43 +04:00
{
2008-11-15 03:37:54 +03:00
struct rtc_device * rtc = platform_get_drvdata ( pdev ) ;
2008-08-29 12:32:43 +04:00
2008-11-15 03:37:54 +03:00
rtc_device_unregister ( rtc ) ;
2008-08-29 12:32:43 +04:00
return 0 ;
}
static struct platform_driver sun4v_rtc_driver = {
. driver = {
. name = " rtc-sun4v " ,
. owner = THIS_MODULE ,
} ,
2008-11-15 03:37:54 +03:00
. remove = __exit_p ( sun4v_rtc_remove ) ,
2008-08-29 12:32:43 +04:00
} ;
static int __init sun4v_rtc_init ( void )
{
2008-11-15 03:37:54 +03:00
return platform_driver_probe ( & sun4v_rtc_driver , sun4v_rtc_probe ) ;
2008-08-29 12:32:43 +04:00
}
static void __exit sun4v_rtc_exit ( void )
{
platform_driver_unregister ( & sun4v_rtc_driver ) ;
}
module_init ( sun4v_rtc_init ) ;
module_exit ( sun4v_rtc_exit ) ;
2008-11-15 03:37:54 +03:00
MODULE_AUTHOR ( " David S. Miller <davem@davemloft.net> " ) ;
MODULE_DESCRIPTION ( " SUN4V RTC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;