2006-09-27 17:13:19 +09:00
/*
* SuperH On - Chip RTC Support
*
2007-08-03 14:19:58 +09:00
* Copyright ( C ) 2006 , 2007 Paul Mundt
2006-12-08 15:26:15 +09:00
* Copyright ( C ) 2006 Jamie Lenehan
2006-09-27 17:13:19 +09:00
*
* Based on the old arch / sh / kernel / cpu / rtc . c by :
*
* Copyright ( C ) 2000 Philipp Rumpf < prumpf @ tux . org >
* Copyright ( C ) 1999 Tetsuya Okada & Niibe Yutaka
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/bcd.h>
# include <linux/rtc.h>
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/seq_file.h>
# include <linux/interrupt.h>
# include <linux/spinlock.h>
2006-12-07 17:23:50 +09:00
# include <linux/io.h>
2007-08-03 14:19:58 +09:00
# include <asm/rtc.h>
2006-09-27 17:13:19 +09:00
2006-12-08 15:26:15 +09:00
# define DRV_NAME "sh-rtc"
2007-08-03 14:19:58 +09:00
# define DRV_VERSION "0.1.3"
2006-12-08 15:26:15 +09:00
2006-09-27 17:13:19 +09:00
# ifdef CONFIG_CPU_SH3
# define rtc_reg_size sizeof(u16)
# define RTC_BIT_INVERTED 0 /* No bug on SH7708, SH7709A */
2007-08-03 14:19:58 +09:00
# define RTC_DEF_CAPABILITIES 0UL
2006-09-27 17:13:19 +09:00
# elif defined(CONFIG_CPU_SH4)
# define rtc_reg_size sizeof(u32)
# define RTC_BIT_INVERTED 0x40 /* bug on SH7750, SH7750S */
2007-08-03 14:19:58 +09:00
# define RTC_DEF_CAPABILITIES RTC_CAP_4_DIGIT_YEAR
2006-09-27 17:13:19 +09:00
# endif
# define RTC_REG(r) ((r) * rtc_reg_size)
2006-12-07 17:23:50 +09:00
# define R64CNT RTC_REG(0)
2006-12-08 15:26:15 +09:00
# define RSECCNT RTC_REG(1) /* RTC sec */
# define RMINCNT RTC_REG(2) /* RTC min */
# define RHRCNT RTC_REG(3) /* RTC hour */
# define RWKCNT RTC_REG(4) /* RTC week */
# define RDAYCNT RTC_REG(5) /* RTC day */
# define RMONCNT RTC_REG(6) /* RTC month */
# define RYRCNT RTC_REG(7) /* RTC year */
# define RSECAR RTC_REG(8) /* ALARM sec */
# define RMINAR RTC_REG(9) /* ALARM min */
# define RHRAR RTC_REG(10) /* ALARM hour */
# define RWKAR RTC_REG(11) /* ALARM week */
# define RDAYAR RTC_REG(12) /* ALARM day */
# define RMONAR RTC_REG(13) /* ALARM month */
# define RCR1 RTC_REG(14) /* Control */
# define RCR2 RTC_REG(15) /* Control */
/* ALARM Bits - or with BCD encoded value */
# define AR_ENB 0x80 /* Enable for alarm cmp */
2006-09-27 17:13:19 +09:00
/* RCR1 Bits */
# define RCR1_CF 0x80 /* Carry Flag */
# define RCR1_CIE 0x10 /* Carry Interrupt Enable */
# define RCR1_AIE 0x08 /* Alarm Interrupt Enable */
# define RCR1_AF 0x01 /* Alarm Flag */
/* RCR2 Bits */
# define RCR2_PEF 0x80 /* PEriodic interrupt Flag */
# define RCR2_PESMASK 0x70 /* Periodic interrupt Set */
# define RCR2_RTCEN 0x08 /* ENable RTC */
# define RCR2_ADJ 0x04 /* ADJustment (30-second) */
# define RCR2_RESET 0x02 /* Reset bit */
# define RCR2_START 0x01 /* Start bit */
struct sh_rtc {
void __iomem * regbase ;
unsigned long regsize ;
struct resource * res ;
unsigned int alarm_irq , periodic_irq , carry_irq ;
struct rtc_device * rtc_dev ;
spinlock_t lock ;
2006-12-08 15:26:15 +09:00
int rearm_aie ;
2007-08-03 14:19:58 +09:00
unsigned long capabilities ; /* See asm-sh/rtc.h for cap bits */
2006-09-27 17:13:19 +09:00
} ;
2006-12-07 17:23:50 +09:00
static irqreturn_t sh_rtc_interrupt ( int irq , void * dev_id )
2006-09-27 17:13:19 +09:00
{
2006-12-07 17:23:50 +09:00
struct platform_device * pdev = to_platform_device ( dev_id ) ;
2006-09-27 17:13:19 +09:00
struct sh_rtc * rtc = platform_get_drvdata ( pdev ) ;
unsigned int tmp , events = 0 ;
spin_lock ( & rtc - > lock ) ;
tmp = readb ( rtc - > regbase + RCR1 ) ;
2006-12-08 15:26:15 +09:00
tmp & = ~ RCR1_CF ;
2006-09-27 17:13:19 +09:00
2006-12-08 15:26:15 +09:00
if ( rtc - > rearm_aie ) {
if ( tmp & RCR1_AF )
tmp & = ~ RCR1_AF ; /* try to clear AF again */
else {
tmp | = RCR1_AIE ; /* AF has cleared, rearm IRQ */
rtc - > rearm_aie = 0 ;
}
}
2006-09-27 17:13:19 +09:00
writeb ( tmp , rtc - > regbase + RCR1 ) ;
2007-05-09 12:37:53 +09:00
rtc_update_irq ( rtc - > rtc_dev , 1 , events ) ;
2006-09-27 17:13:19 +09:00
spin_unlock ( & rtc - > lock ) ;
return IRQ_HANDLED ;
}
2006-12-08 15:26:15 +09:00
static irqreturn_t sh_rtc_alarm ( int irq , void * dev_id )
{
struct platform_device * pdev = to_platform_device ( dev_id ) ;
struct sh_rtc * rtc = platform_get_drvdata ( pdev ) ;
unsigned int tmp , events = 0 ;
spin_lock ( & rtc - > lock ) ;
tmp = readb ( rtc - > regbase + RCR1 ) ;
/*
* If AF is set then the alarm has triggered . If we clear AF while
* the alarm time still matches the RTC time then AF will
* immediately be set again , and if AIE is enabled then the alarm
* interrupt will immediately be retrigger . So we clear AIE here
* and use rtc - > rearm_aie so that the carry interrupt will keep
* trying to clear AF and once it stays cleared it ' ll re - enable
* AIE .
*/
if ( tmp & RCR1_AF ) {
events | = RTC_AF | RTC_IRQF ;
tmp & = ~ ( RCR1_AF | RCR1_AIE ) ;
writeb ( tmp , rtc - > regbase + RCR1 ) ;
rtc - > rearm_aie = 1 ;
2007-05-09 12:37:53 +09:00
rtc_update_irq ( rtc - > rtc_dev , 1 , events ) ;
2006-12-08 15:26:15 +09:00
}
spin_unlock ( & rtc - > lock ) ;
return IRQ_HANDLED ;
}
2006-12-07 17:23:50 +09:00
static irqreturn_t sh_rtc_periodic ( int irq , void * dev_id )
2006-09-27 17:13:19 +09:00
{
2006-12-07 17:23:50 +09:00
struct platform_device * pdev = to_platform_device ( dev_id ) ;
struct sh_rtc * rtc = platform_get_drvdata ( pdev ) ;
2006-09-27 17:13:19 +09:00
spin_lock ( & rtc - > lock ) ;
2007-05-09 12:37:53 +09:00
rtc_update_irq ( rtc - > rtc_dev , 1 , RTC_PF | RTC_IRQF ) ;
2006-09-27 17:13:19 +09:00
spin_unlock ( & rtc - > lock ) ;
return IRQ_HANDLED ;
}
static inline void sh_rtc_setpie ( struct device * dev , unsigned int enable )
{
struct sh_rtc * rtc = dev_get_drvdata ( dev ) ;
unsigned int tmp ;
spin_lock_irq ( & rtc - > lock ) ;
tmp = readb ( rtc - > regbase + RCR2 ) ;
if ( enable ) {
tmp & = ~ RCR2_PESMASK ;
tmp | = RCR2_PEF | ( 2 < < 4 ) ;
} else
tmp & = ~ ( RCR2_PESMASK | RCR2_PEF ) ;
writeb ( tmp , rtc - > regbase + RCR2 ) ;
spin_unlock_irq ( & rtc - > lock ) ;
}
static inline void sh_rtc_setaie ( struct device * dev , unsigned int enable )
{
struct sh_rtc * rtc = dev_get_drvdata ( dev ) ;
unsigned int tmp ;
spin_lock_irq ( & rtc - > lock ) ;
tmp = readb ( rtc - > regbase + RCR1 ) ;
2006-12-08 15:26:15 +09:00
if ( ! enable ) {
2006-09-27 17:13:19 +09:00
tmp & = ~ RCR1_AIE ;
2006-12-08 15:26:15 +09:00
rtc - > rearm_aie = 0 ;
} else if ( rtc - > rearm_aie = = 0 )
tmp | = RCR1_AIE ;
2006-09-27 17:13:19 +09:00
writeb ( tmp , rtc - > regbase + RCR1 ) ;
spin_unlock_irq ( & rtc - > lock ) ;
}
static int sh_rtc_open ( struct device * dev )
{
struct sh_rtc * rtc = dev_get_drvdata ( dev ) ;
unsigned int tmp ;
int ret ;
tmp = readb ( rtc - > regbase + RCR1 ) ;
tmp & = ~ RCR1_CF ;
tmp | = RCR1_CIE ;
writeb ( tmp , rtc - > regbase + RCR1 ) ;
2006-10-06 15:31:16 +09:00
ret = request_irq ( rtc - > periodic_irq , sh_rtc_periodic , IRQF_DISABLED ,
2006-09-27 17:13:19 +09:00
" sh-rtc period " , dev ) ;
if ( unlikely ( ret ) ) {
dev_err ( dev , " request period IRQ failed with %d, IRQ %d \n " ,
ret , rtc - > periodic_irq ) ;
return ret ;
}
2006-10-06 15:31:16 +09:00
ret = request_irq ( rtc - > carry_irq , sh_rtc_interrupt , IRQF_DISABLED ,
2006-09-27 17:13:19 +09:00
" sh-rtc carry " , dev ) ;
if ( unlikely ( ret ) ) {
dev_err ( dev , " request carry IRQ failed with %d, IRQ %d \n " ,
ret , rtc - > carry_irq ) ;
free_irq ( rtc - > periodic_irq , dev ) ;
goto err_bad_carry ;
}
2006-12-08 15:26:15 +09:00
ret = request_irq ( rtc - > alarm_irq , sh_rtc_alarm , IRQF_DISABLED ,
2006-09-27 17:13:19 +09:00
" sh-rtc alarm " , dev ) ;
if ( unlikely ( ret ) ) {
dev_err ( dev , " request alarm IRQ failed with %d, IRQ %d \n " ,
ret , rtc - > alarm_irq ) ;
goto err_bad_alarm ;
}
return 0 ;
err_bad_alarm :
free_irq ( rtc - > carry_irq , dev ) ;
err_bad_carry :
free_irq ( rtc - > periodic_irq , dev ) ;
return ret ;
}
static void sh_rtc_release ( struct device * dev )
{
struct sh_rtc * rtc = dev_get_drvdata ( dev ) ;
sh_rtc_setpie ( dev , 0 ) ;
2006-12-08 15:26:15 +09:00
sh_rtc_setaie ( dev , 0 ) ;
2006-09-27 17:13:19 +09:00
free_irq ( rtc - > periodic_irq , dev ) ;
free_irq ( rtc - > carry_irq , dev ) ;
free_irq ( rtc - > alarm_irq , dev ) ;
}
static int sh_rtc_proc ( struct device * dev , struct seq_file * seq )
{
struct sh_rtc * rtc = dev_get_drvdata ( dev ) ;
unsigned int tmp ;
tmp = readb ( rtc - > regbase + RCR1 ) ;
seq_printf ( seq , " carry_IRQ \t : %s \n " ,
( tmp & RCR1_CIE ) ? " yes " : " no " ) ;
tmp = readb ( rtc - > regbase + RCR2 ) ;
seq_printf ( seq , " periodic_IRQ \t : %s \n " ,
( tmp & RCR2_PEF ) ? " yes " : " no " ) ;
return 0 ;
}
static int sh_rtc_ioctl ( struct device * dev , unsigned int cmd , unsigned long arg )
{
unsigned int ret = - ENOIOCTLCMD ;
switch ( cmd ) {
case RTC_PIE_OFF :
case RTC_PIE_ON :
sh_rtc_setpie ( dev , cmd = = RTC_PIE_ON ) ;
ret = 0 ;
break ;
case RTC_AIE_OFF :
case RTC_AIE_ON :
sh_rtc_setaie ( dev , cmd = = RTC_AIE_ON ) ;
ret = 0 ;
break ;
}
return ret ;
}
static int sh_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct sh_rtc * rtc = platform_get_drvdata ( pdev ) ;
unsigned int sec128 , sec2 , yr , yr100 , cf_bit ;
do {
unsigned int tmp ;
spin_lock_irq ( & rtc - > lock ) ;
tmp = readb ( rtc - > regbase + RCR1 ) ;
tmp & = ~ RCR1_CF ; /* Clear CF-bit */
tmp | = RCR1_CIE ;
writeb ( tmp , rtc - > regbase + RCR1 ) ;
sec128 = readb ( rtc - > regbase + R64CNT ) ;
tm - > tm_sec = BCD2BIN ( readb ( rtc - > regbase + RSECCNT ) ) ;
tm - > tm_min = BCD2BIN ( readb ( rtc - > regbase + RMINCNT ) ) ;
tm - > tm_hour = BCD2BIN ( readb ( rtc - > regbase + RHRCNT ) ) ;
tm - > tm_wday = BCD2BIN ( readb ( rtc - > regbase + RWKCNT ) ) ;
tm - > tm_mday = BCD2BIN ( readb ( rtc - > regbase + RDAYCNT ) ) ;
2006-12-08 14:49:30 +09:00
tm - > tm_mon = BCD2BIN ( readb ( rtc - > regbase + RMONCNT ) ) - 1 ;
2006-09-27 17:13:19 +09:00
2007-08-03 14:19:58 +09:00
if ( rtc - > capabilities & RTC_CAP_4_DIGIT_YEAR ) {
yr = readw ( rtc - > regbase + RYRCNT ) ;
yr100 = BCD2BIN ( yr > > 8 ) ;
yr & = 0xff ;
} else {
yr = readb ( rtc - > regbase + RYRCNT ) ;
yr100 = BCD2BIN ( ( yr = = 0x99 ) ? 0x19 : 0x20 ) ;
}
2006-09-27 17:13:19 +09:00
tm - > tm_year = ( yr100 * 100 + BCD2BIN ( yr ) ) - 1900 ;
sec2 = readb ( rtc - > regbase + R64CNT ) ;
cf_bit = readb ( rtc - > regbase + RCR1 ) & RCR1_CF ;
spin_unlock_irq ( & rtc - > lock ) ;
} while ( cf_bit ! = 0 | | ( ( sec128 ^ sec2 ) & RTC_BIT_INVERTED ) ! = 0 ) ;
# if RTC_BIT_INVERTED != 0
if ( ( sec128 & RTC_BIT_INVERTED ) )
tm - > tm_sec - - ;
# endif
2007-05-08 11:56:27 +09:00
dev_dbg ( dev , " %s: tm is secs=%d, mins=%d, hours=%d, "
2006-09-27 17:13:19 +09:00
" mday=%d, mon=%d, year=%d, wday=%d \n " ,
__FUNCTION__ ,
tm - > tm_sec , tm - > tm_min , tm - > tm_hour ,
2006-12-08 14:49:30 +09:00
tm - > tm_mday , tm - > tm_mon + 1 , tm - > tm_year , tm - > tm_wday ) ;
2006-09-27 17:13:19 +09:00
if ( rtc_valid_tm ( tm ) < 0 )
dev_err ( dev , " invalid date \n " ) ;
return 0 ;
}
static int sh_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct sh_rtc * rtc = platform_get_drvdata ( pdev ) ;
unsigned int tmp ;
int year ;
spin_lock_irq ( & rtc - > lock ) ;
/* Reset pre-scaler & stop RTC */
tmp = readb ( rtc - > regbase + RCR2 ) ;
tmp | = RCR2_RESET ;
2007-07-26 17:31:28 +09:00
tmp & = ~ RCR2_START ;
2006-09-27 17:13:19 +09:00
writeb ( tmp , rtc - > regbase + RCR2 ) ;
writeb ( BIN2BCD ( tm - > tm_sec ) , rtc - > regbase + RSECCNT ) ;
writeb ( BIN2BCD ( tm - > tm_min ) , rtc - > regbase + RMINCNT ) ;
writeb ( BIN2BCD ( tm - > tm_hour ) , rtc - > regbase + RHRCNT ) ;
writeb ( BIN2BCD ( tm - > tm_wday ) , rtc - > regbase + RWKCNT ) ;
writeb ( BIN2BCD ( tm - > tm_mday ) , rtc - > regbase + RDAYCNT ) ;
2006-12-08 14:49:30 +09:00
writeb ( BIN2BCD ( tm - > tm_mon + 1 ) , rtc - > regbase + RMONCNT ) ;
2006-09-27 17:13:19 +09:00
2007-08-03 14:19:58 +09:00
if ( rtc - > capabilities & RTC_CAP_4_DIGIT_YEAR ) {
year = ( BIN2BCD ( ( tm - > tm_year + 1900 ) / 100 ) < < 8 ) |
BIN2BCD ( tm - > tm_year % 100 ) ;
writew ( year , rtc - > regbase + RYRCNT ) ;
} else {
year = tm - > tm_year % 100 ;
writeb ( BIN2BCD ( year ) , rtc - > regbase + RYRCNT ) ;
}
2006-09-27 17:13:19 +09:00
/* Start RTC */
tmp = readb ( rtc - > regbase + RCR2 ) ;
tmp & = ~ RCR2_RESET ;
tmp | = RCR2_RTCEN | RCR2_START ;
writeb ( tmp , rtc - > regbase + RCR2 ) ;
spin_unlock_irq ( & rtc - > lock ) ;
return 0 ;
}
2006-12-08 15:26:15 +09:00
static inline int sh_rtc_read_alarm_value ( struct sh_rtc * rtc , int reg_off )
{
unsigned int byte ;
int value = 0xff ; /* return 0xff for ignored values */
byte = readb ( rtc - > regbase + reg_off ) ;
if ( byte & AR_ENB ) {
byte & = ~ AR_ENB ; /* strip the enable bit */
value = BCD2BIN ( byte ) ;
}
return value ;
}
static int sh_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * wkalrm )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct sh_rtc * rtc = platform_get_drvdata ( pdev ) ;
struct rtc_time * tm = & wkalrm - > time ;
spin_lock_irq ( & rtc - > lock ) ;
tm - > tm_sec = sh_rtc_read_alarm_value ( rtc , RSECAR ) ;
tm - > tm_min = sh_rtc_read_alarm_value ( rtc , RMINAR ) ;
tm - > tm_hour = sh_rtc_read_alarm_value ( rtc , RHRAR ) ;
tm - > tm_wday = sh_rtc_read_alarm_value ( rtc , RWKAR ) ;
tm - > tm_mday = sh_rtc_read_alarm_value ( rtc , RDAYAR ) ;
tm - > tm_mon = sh_rtc_read_alarm_value ( rtc , RMONAR ) ;
if ( tm - > tm_mon > 0 )
tm - > tm_mon - = 1 ; /* RTC is 1-12, tm_mon is 0-11 */
tm - > tm_year = 0xffff ;
2007-01-10 23:15:32 -08:00
wkalrm - > enabled = ( readb ( rtc - > regbase + RCR1 ) & RCR1_AIE ) ? 1 : 0 ;
2006-12-08 15:26:15 +09:00
spin_unlock_irq ( & rtc - > lock ) ;
return 0 ;
}
static inline void sh_rtc_write_alarm_value ( struct sh_rtc * rtc ,
int value , int reg_off )
{
/* < 0 for a value that is ignored */
if ( value < 0 )
writeb ( 0 , rtc - > regbase + reg_off ) ;
else
writeb ( BIN2BCD ( value ) | AR_ENB , rtc - > regbase + reg_off ) ;
}
static int sh_rtc_check_alarm ( struct rtc_time * tm )
{
/*
* The original rtc says anything > 0xc0 is " don't care " or " match
* all " - most users use 0xff but rtc-dev uses -1 for the same thing.
* The original rtc doesn ' t support years - some things use - 1 and
* some 0xffff . We use - 1 to make out tests easier .
*/
if ( tm - > tm_year = = 0xffff )
tm - > tm_year = - 1 ;
if ( tm - > tm_mon > = 0xff )
tm - > tm_mon = - 1 ;
if ( tm - > tm_mday > = 0xff )
tm - > tm_mday = - 1 ;
if ( tm - > tm_wday > = 0xff )
tm - > tm_wday = - 1 ;
if ( tm - > tm_hour > = 0xff )
tm - > tm_hour = - 1 ;
if ( tm - > tm_min > = 0xff )
tm - > tm_min = - 1 ;
if ( tm - > tm_sec > = 0xff )
tm - > tm_sec = - 1 ;
if ( tm - > tm_year > 9999 | |
tm - > tm_mon > = 12 | |
tm - > tm_mday = = 0 | | tm - > tm_mday > = 32 | |
tm - > tm_wday > = 7 | |
tm - > tm_hour > = 24 | |
tm - > tm_min > = 60 | |
tm - > tm_sec > = 60 )
return - EINVAL ;
return 0 ;
}
static int sh_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * wkalrm )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct sh_rtc * rtc = platform_get_drvdata ( pdev ) ;
unsigned int rcr1 ;
struct rtc_time * tm = & wkalrm - > time ;
int mon , err ;
err = sh_rtc_check_alarm ( tm ) ;
if ( unlikely ( err < 0 ) )
return err ;
spin_lock_irq ( & rtc - > lock ) ;
2007-01-22 20:40:41 -08:00
/* disable alarm interrupt and clear the alarm flag */
2006-12-08 15:26:15 +09:00
rcr1 = readb ( rtc - > regbase + RCR1 ) ;
2007-01-22 20:40:41 -08:00
rcr1 & = ~ ( RCR1_AF | RCR1_AIE ) ;
writeb ( rcr1 , rtc - > regbase + RCR1 ) ;
2006-12-08 15:26:15 +09:00
rtc - > rearm_aie = 0 ;
/* set alarm time */
sh_rtc_write_alarm_value ( rtc , tm - > tm_sec , RSECAR ) ;
sh_rtc_write_alarm_value ( rtc , tm - > tm_min , RMINAR ) ;
sh_rtc_write_alarm_value ( rtc , tm - > tm_hour , RHRAR ) ;
sh_rtc_write_alarm_value ( rtc , tm - > tm_wday , RWKAR ) ;
sh_rtc_write_alarm_value ( rtc , tm - > tm_mday , RDAYAR ) ;
mon = tm - > tm_mon ;
if ( mon > = 0 )
mon + = 1 ;
sh_rtc_write_alarm_value ( rtc , mon , RMONAR ) ;
2007-01-22 20:40:41 -08:00
if ( wkalrm - > enabled ) {
rcr1 | = RCR1_AIE ;
writeb ( rcr1 , rtc - > regbase + RCR1 ) ;
}
2006-12-08 15:26:15 +09:00
spin_unlock_irq ( & rtc - > lock ) ;
return 0 ;
}
2006-09-27 17:13:19 +09:00
static struct rtc_class_ops sh_rtc_ops = {
. open = sh_rtc_open ,
. release = sh_rtc_release ,
. ioctl = sh_rtc_ioctl ,
. read_time = sh_rtc_read_time ,
. set_time = sh_rtc_set_time ,
2006-12-08 15:26:15 +09:00
. read_alarm = sh_rtc_read_alarm ,
. set_alarm = sh_rtc_set_alarm ,
2006-09-27 17:13:19 +09:00
. proc = sh_rtc_proc ,
} ;
static int __devinit sh_rtc_probe ( struct platform_device * pdev )
{
struct sh_rtc * rtc ;
struct resource * res ;
int ret = - ENOENT ;
rtc = kzalloc ( sizeof ( struct sh_rtc ) , GFP_KERNEL ) ;
if ( unlikely ( ! rtc ) )
return - ENOMEM ;
spin_lock_init ( & rtc - > lock ) ;
rtc - > periodic_irq = platform_get_irq ( pdev , 0 ) ;
if ( unlikely ( rtc - > periodic_irq < 0 ) ) {
dev_err ( & pdev - > dev , " No IRQ for period \n " ) ;
goto err_badres ;
}
rtc - > carry_irq = platform_get_irq ( pdev , 1 ) ;
if ( unlikely ( rtc - > carry_irq < 0 ) ) {
dev_err ( & pdev - > dev , " No IRQ for carry \n " ) ;
goto err_badres ;
}
rtc - > alarm_irq = platform_get_irq ( pdev , 2 ) ;
if ( unlikely ( rtc - > alarm_irq < 0 ) ) {
dev_err ( & pdev - > dev , " No IRQ for alarm \n " ) ;
goto err_badres ;
}
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
if ( unlikely ( res = = NULL ) ) {
dev_err ( & pdev - > dev , " No IO resource \n " ) ;
goto err_badres ;
}
rtc - > regsize = res - > end - res - > start + 1 ;
rtc - > res = request_mem_region ( res - > start , rtc - > regsize , pdev - > name ) ;
if ( unlikely ( ! rtc - > res ) ) {
ret = - EBUSY ;
goto err_badres ;
}
rtc - > regbase = ( void __iomem * ) rtc - > res - > start ;
if ( unlikely ( ! rtc - > regbase ) ) {
ret = - EINVAL ;
goto err_badmap ;
}
rtc - > rtc_dev = rtc_device_register ( " sh " , & pdev - > dev ,
& sh_rtc_ops , THIS_MODULE ) ;
if ( IS_ERR ( rtc ) ) {
ret = PTR_ERR ( rtc - > rtc_dev ) ;
goto err_badmap ;
}
2007-08-03 14:19:58 +09:00
rtc - > capabilities = RTC_DEF_CAPABILITIES ;
if ( pdev - > dev . platform_data ) {
struct sh_rtc_platform_info * pinfo = pdev - > dev . platform_data ;
/*
* Some CPUs have special capabilities in addition to the
* default set . Add those in here .
*/
rtc - > capabilities | = pinfo - > capabilities ;
}
2006-09-27 17:13:19 +09:00
platform_set_drvdata ( pdev , rtc ) ;
return 0 ;
err_badmap :
release_resource ( rtc - > res ) ;
err_badres :
kfree ( rtc ) ;
return ret ;
}
static int __devexit sh_rtc_remove ( struct platform_device * pdev )
{
struct sh_rtc * rtc = platform_get_drvdata ( pdev ) ;
if ( likely ( rtc - > rtc_dev ) )
rtc_device_unregister ( rtc - > rtc_dev ) ;
sh_rtc_setpie ( & pdev - > dev , 0 ) ;
sh_rtc_setaie ( & pdev - > dev , 0 ) ;
release_resource ( rtc - > res ) ;
platform_set_drvdata ( pdev , NULL ) ;
kfree ( rtc ) ;
return 0 ;
}
static struct platform_driver sh_rtc_platform_driver = {
. driver = {
2006-12-08 15:26:15 +09:00
. name = DRV_NAME ,
2006-09-27 17:13:19 +09:00
. owner = THIS_MODULE ,
} ,
. probe = sh_rtc_probe ,
. remove = __devexit_p ( sh_rtc_remove ) ,
} ;
static int __init sh_rtc_init ( void )
{
return platform_driver_register ( & sh_rtc_platform_driver ) ;
}
static void __exit sh_rtc_exit ( void )
{
platform_driver_unregister ( & sh_rtc_platform_driver ) ;
}
module_init ( sh_rtc_init ) ;
module_exit ( sh_rtc_exit ) ;
MODULE_DESCRIPTION ( " SuperH on-chip RTC driver " ) ;
2006-12-08 15:26:15 +09:00
MODULE_VERSION ( DRV_VERSION ) ;
MODULE_AUTHOR ( " Paul Mundt <lethal@linux-sh.org>, Jamie Lenehan <lenehan@twibble.org> " ) ;
2006-09-27 17:13:19 +09:00
MODULE_LICENSE ( " GPL " ) ;