2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / mach - integrator / time . c
*
* Copyright ( C ) 2000 - 2001 Deep Blue Solutions Ltd .
*
* 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/module.h>
# include <linux/kernel.h>
# include <linux/time.h>
# include <linux/mc146818rtc.h>
# include <linux/interrupt.h>
# include <linux/init.h>
# include <linux/device.h>
# include <asm/hardware/amba.h>
# include <asm/hardware.h>
# include <asm/io.h>
# include <asm/uaccess.h>
# include <asm/rtc.h>
# include <asm/mach/time.h>
# define RTC_DR (0)
# define RTC_MR (4)
# define RTC_STAT (8)
# define RTC_EOI (8)
# define RTC_LR (12)
# define RTC_CR (16)
# define RTC_CR_MIE (1 << 0)
extern int ( * set_rtc ) ( void ) ;
static void __iomem * rtc_base ;
static int integrator_set_rtc ( void )
{
__raw_writel ( xtime . tv_sec , rtc_base + RTC_LR ) ;
return 1 ;
}
2005-04-30 15:19:28 +04:00
static int rtc_read_alarm ( struct rtc_wkalrm * alrm )
2005-04-17 02:20:36 +04:00
{
rtc_time_to_tm ( readl ( rtc_base + RTC_MR ) , & alrm - > time ) ;
2005-04-30 15:19:28 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-04-30 15:19:28 +04:00
static inline int rtc_set_alarm ( struct rtc_wkalrm * alrm )
2005-04-17 02:20:36 +04:00
{
unsigned long time ;
int ret ;
2005-04-30 15:19:28 +04:00
/*
* At the moment , we can only deal with non - wildcarded alarm times .
*/
ret = rtc_valid_tm ( & alrm - > time ) ;
if ( ret = = 0 )
ret = rtc_tm_to_time ( & alrm - > time , & time ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 )
writel ( time , rtc_base + RTC_MR ) ;
return ret ;
}
2005-04-30 15:19:28 +04:00
static int rtc_read_time ( struct rtc_time * tm )
2005-04-17 02:20:36 +04:00
{
rtc_time_to_tm ( readl ( rtc_base + RTC_DR ) , tm ) ;
2005-04-30 15:19:28 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Set the RTC time . Unfortunately , we can ' t accurately set
* the point at which the counter updates .
*
* Also , since RTC_LR is transferred to RTC_CR on next rising
* edge of the 1 Hz clock , we must write the time one second
* in advance .
*/
2005-04-30 15:19:28 +04:00
static inline int rtc_set_time ( struct rtc_time * tm )
2005-04-17 02:20:36 +04:00
{
unsigned long time ;
int ret ;
ret = rtc_tm_to_time ( tm , & time ) ;
if ( ret = = 0 )
writel ( time + 1 , rtc_base + RTC_LR ) ;
return ret ;
}
static struct rtc_ops rtc_ops = {
. owner = THIS_MODULE ,
. read_time = rtc_read_time ,
. set_time = rtc_set_time ,
. read_alarm = rtc_read_alarm ,
. set_alarm = rtc_set_alarm ,
} ;
static irqreturn_t rtc_interrupt ( int irq , void * dev_id , struct pt_regs * regs )
{
writel ( 0 , rtc_base + RTC_EOI ) ;
return IRQ_HANDLED ;
}
static int rtc_probe ( struct amba_device * dev , void * id )
{
int ret ;
if ( rtc_base )
return - EBUSY ;
ret = amba_request_regions ( dev , NULL ) ;
if ( ret )
goto out ;
rtc_base = ioremap ( dev - > res . start , SZ_4K ) ;
if ( ! rtc_base ) {
ret = - ENOMEM ;
goto res_out ;
}
__raw_writel ( 0 , rtc_base + RTC_CR ) ;
__raw_writel ( 0 , rtc_base + RTC_EOI ) ;
xtime . tv_sec = __raw_readl ( rtc_base + RTC_DR ) ;
ret = request_irq ( dev - > irq [ 0 ] , rtc_interrupt , SA_INTERRUPT ,
" rtc-pl030 " , dev ) ;
if ( ret )
goto map_out ;
ret = register_rtc ( & rtc_ops ) ;
if ( ret )
goto irq_out ;
set_rtc = integrator_set_rtc ;
return 0 ;
irq_out :
free_irq ( dev - > irq [ 0 ] , dev ) ;
map_out :
iounmap ( rtc_base ) ;
rtc_base = NULL ;
res_out :
amba_release_regions ( dev ) ;
out :
return ret ;
}
static int rtc_remove ( struct amba_device * dev )
{
set_rtc = NULL ;
writel ( 0 , rtc_base + RTC_CR ) ;
free_irq ( dev - > irq [ 0 ] , dev ) ;
unregister_rtc ( & rtc_ops ) ;
iounmap ( rtc_base ) ;
rtc_base = NULL ;
amba_release_regions ( dev ) ;
return 0 ;
}
static struct timespec rtc_delta ;
static int rtc_suspend ( struct amba_device * dev , pm_message_t state )
{
struct timespec rtc ;
rtc . tv_sec = readl ( rtc_base + RTC_DR ) ;
rtc . tv_nsec = 0 ;
save_time_delta ( & rtc_delta , & rtc ) ;
return 0 ;
}
static int rtc_resume ( struct amba_device * dev )
{
struct timespec rtc ;
rtc . tv_sec = readl ( rtc_base + RTC_DR ) ;
rtc . tv_nsec = 0 ;
restore_time_delta ( & rtc_delta , & rtc ) ;
return 0 ;
}
static struct amba_id rtc_ids [ ] = {
{
. id = 0x00041030 ,
. mask = 0x000fffff ,
} ,
{ 0 , 0 } ,
} ;
static struct amba_driver rtc_driver = {
. drv = {
. name = " rtc-pl030 " ,
} ,
. probe = rtc_probe ,
. remove = rtc_remove ,
. suspend = rtc_suspend ,
. resume = rtc_resume ,
. id_table = rtc_ids ,
} ;
static int __init integrator_rtc_init ( void )
{
return amba_driver_register ( & rtc_driver ) ;
}
static void __exit integrator_rtc_exit ( void )
{
amba_driver_unregister ( & rtc_driver ) ;
}
module_init ( integrator_rtc_init ) ;
module_exit ( integrator_rtc_exit ) ;