2008-02-06 12:38:44 +03:00
/*
* Dallas DS1302 RTC Support
*
2009-08-20 07:31:49 +04:00
* Copyright ( C ) 2002 David McCullough
* Copyright ( C ) 2003 - 2007 Paul Mundt
2008-02-06 12:38:44 +03:00
*
* This file is subject to the terms and conditions of the GNU General Public
2009-08-20 07:31:49 +04:00
* License version 2. See the file " COPYING " in the main directory of
2008-02-06 12:38:44 +03:00
* this archive for more details .
*/
2009-08-20 07:31:49 +04:00
2008-02-06 12:38:44 +03:00
# include <linux/init.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/rtc.h>
# include <linux/io.h>
# include <linux/bcd.h>
# define DRV_NAME "rtc-ds1302"
2009-08-20 07:31:49 +04:00
# define DRV_VERSION "0.1.1"
2008-02-06 12:38:44 +03:00
# define RTC_CMD_READ 0x81 /* Read command */
# define RTC_CMD_WRITE 0x80 /* Write command */
# define RTC_ADDR_RAM0 0x20 /* Address of RAM0 */
# define RTC_ADDR_TCR 0x08 /* Address of trickle charge register */
# define RTC_ADDR_YEAR 0x06 /* Address of year register */
# define RTC_ADDR_DAY 0x05 /* Address of day of week register */
# define RTC_ADDR_MON 0x04 /* Address of month register */
# define RTC_ADDR_DATE 0x03 /* Address of day of month register */
# define RTC_ADDR_HOUR 0x02 /* Address of hour register */
# define RTC_ADDR_MIN 0x01 /* Address of minute register */
# define RTC_ADDR_SEC 0x00 /* Address of second register */
2010-05-25 01:33:47 +04:00
# ifdef CONFIG_SH_SECUREEDGE5410
# include <asm/rtc.h>
# include <mach/snapgear.h>
2008-02-06 12:38:44 +03:00
# define RTC_RESET 0x1000
# define RTC_IODATA 0x0800
# define RTC_SCLK 0x0400
# define set_dp(x) SECUREEDGE_WRITE_IOPORT(x, 0x1c00)
# define get_dp() SECUREEDGE_READ_IOPORT()
2010-05-25 01:33:47 +04:00
# define ds1302_set_tx()
# define ds1302_set_rx()
static inline int ds1302_hw_init ( void )
{
return 0 ;
}
static inline void ds1302_reset ( void )
{
set_dp ( get_dp ( ) & ~ ( RTC_RESET | RTC_IODATA | RTC_SCLK ) ) ;
}
static inline void ds1302_clock ( void )
{
set_dp ( get_dp ( ) | RTC_SCLK ) ; /* clock high */
set_dp ( get_dp ( ) & ~ RTC_SCLK ) ; /* clock low */
}
static inline void ds1302_start ( void )
{
set_dp ( get_dp ( ) | RTC_RESET ) ;
}
static inline void ds1302_stop ( void )
{
set_dp ( get_dp ( ) & ~ RTC_RESET ) ;
}
static inline void ds1302_txbit ( int bit )
{
set_dp ( ( get_dp ( ) & ~ RTC_IODATA ) | ( bit ? RTC_IODATA : 0 ) ) ;
}
static inline int ds1302_rxbit ( void )
{
return ! ! ( get_dp ( ) & RTC_IODATA ) ;
}
2008-02-06 12:38:44 +03:00
# else
# error "Add support for your platform"
# endif
static void ds1302_sendbits ( unsigned int val )
{
int i ;
2010-05-25 01:33:47 +04:00
ds1302_set_tx ( ) ;
2008-02-06 12:38:44 +03:00
for ( i = 8 ; ( i ) ; i - - , val > > = 1 ) {
2010-05-25 01:33:47 +04:00
ds1302_txbit ( val & 0x1 ) ;
ds1302_clock ( ) ;
2008-02-06 12:38:44 +03:00
}
}
static unsigned int ds1302_recvbits ( void )
{
unsigned int val ;
int i ;
2010-05-25 01:33:47 +04:00
ds1302_set_rx ( ) ;
2008-02-06 12:38:44 +03:00
for ( i = 0 , val = 0 ; ( i < 8 ) ; i + + ) {
2010-05-25 01:33:47 +04:00
val | = ( ds1302_rxbit ( ) < < i ) ;
ds1302_clock ( ) ;
2008-02-06 12:38:44 +03:00
}
return val ;
}
static unsigned int ds1302_readbyte ( unsigned int addr )
{
unsigned int val ;
2010-05-25 01:33:47 +04:00
ds1302_reset ( ) ;
2008-02-06 12:38:44 +03:00
2010-05-25 01:33:47 +04:00
ds1302_start ( ) ;
2008-02-06 12:38:44 +03:00
ds1302_sendbits ( ( ( addr & 0x3f ) < < 1 ) | RTC_CMD_READ ) ;
val = ds1302_recvbits ( ) ;
2010-05-25 01:33:47 +04:00
ds1302_stop ( ) ;
2008-02-06 12:38:44 +03:00
return val ;
}
static void ds1302_writebyte ( unsigned int addr , unsigned int val )
{
2010-05-25 01:33:47 +04:00
ds1302_reset ( ) ;
ds1302_start ( ) ;
2008-02-06 12:38:44 +03:00
ds1302_sendbits ( ( ( addr & 0x3f ) < < 1 ) | RTC_CMD_WRITE ) ;
ds1302_sendbits ( val ) ;
2010-05-25 01:33:47 +04:00
ds1302_stop ( ) ;
2008-02-06 12:38:44 +03:00
}
static int ds1302_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
2008-10-19 07:28:41 +04:00
tm - > tm_sec = bcd2bin ( ds1302_readbyte ( RTC_ADDR_SEC ) ) ;
tm - > tm_min = bcd2bin ( ds1302_readbyte ( RTC_ADDR_MIN ) ) ;
tm - > tm_hour = bcd2bin ( ds1302_readbyte ( RTC_ADDR_HOUR ) ) ;
tm - > tm_wday = bcd2bin ( ds1302_readbyte ( RTC_ADDR_DAY ) ) ;
tm - > tm_mday = bcd2bin ( ds1302_readbyte ( RTC_ADDR_DATE ) ) ;
tm - > tm_mon = bcd2bin ( ds1302_readbyte ( RTC_ADDR_MON ) ) - 1 ;
tm - > tm_year = bcd2bin ( ds1302_readbyte ( RTC_ADDR_YEAR ) ) ;
2008-02-06 12:38:44 +03:00
if ( tm - > tm_year < 70 )
tm - > tm_year + = 100 ;
dev_dbg ( dev , " %s: tm is secs=%d, mins=%d, hours=%d, "
" mday=%d, mon=%d, year=%d, wday=%d \n " ,
2008-04-28 13:12:00 +04:00
__func__ ,
2008-02-06 12:38:44 +03:00
tm - > tm_sec , tm - > tm_min , tm - > tm_hour ,
tm - > tm_mday , tm - > tm_mon + 1 , tm - > tm_year , tm - > tm_wday ) ;
2009-08-20 07:31:49 +04:00
return rtc_valid_tm ( tm ) ;
2008-02-06 12:38:44 +03:00
}
static int ds1302_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
/* Stop RTC */
ds1302_writebyte ( RTC_ADDR_SEC , ds1302_readbyte ( RTC_ADDR_SEC ) | 0x80 ) ;
2008-10-19 07:28:41 +04:00
ds1302_writebyte ( RTC_ADDR_SEC , bin2bcd ( tm - > tm_sec ) ) ;
ds1302_writebyte ( RTC_ADDR_MIN , bin2bcd ( tm - > tm_min ) ) ;
ds1302_writebyte ( RTC_ADDR_HOUR , bin2bcd ( tm - > tm_hour ) ) ;
ds1302_writebyte ( RTC_ADDR_DAY , bin2bcd ( tm - > tm_wday ) ) ;
ds1302_writebyte ( RTC_ADDR_DATE , bin2bcd ( tm - > tm_mday ) ) ;
ds1302_writebyte ( RTC_ADDR_MON , bin2bcd ( tm - > tm_mon + 1 ) ) ;
ds1302_writebyte ( RTC_ADDR_YEAR , bin2bcd ( tm - > tm_year % 100 ) ) ;
2008-02-06 12:38:44 +03:00
/* Start RTC */
ds1302_writebyte ( RTC_ADDR_SEC , ds1302_readbyte ( RTC_ADDR_SEC ) & ~ 0x80 ) ;
return 0 ;
}
static int ds1302_rtc_ioctl ( struct device * dev , unsigned int cmd ,
unsigned long arg )
{
switch ( cmd ) {
# ifdef RTC_SET_CHARGE
case RTC_SET_CHARGE :
{
int tcs_val ;
if ( copy_from_user ( & tcs_val , ( int __user * ) arg , sizeof ( int ) ) )
return - EFAULT ;
ds1302_writebyte ( RTC_ADDR_TCR , ( 0xa0 | tcs_val * 0xf ) ) ;
return 0 ;
}
# endif
}
return - ENOIOCTLCMD ;
}
static struct rtc_class_ops ds1302_rtc_ops = {
. read_time = ds1302_rtc_read_time ,
. set_time = ds1302_rtc_set_time ,
. ioctl = ds1302_rtc_ioctl ,
} ;
2009-08-20 07:31:49 +04:00
static int __init ds1302_rtc_probe ( struct platform_device * pdev )
2008-02-06 12:38:44 +03:00
{
2009-08-20 07:31:49 +04:00
struct rtc_device * rtc ;
2008-02-06 12:38:44 +03:00
2010-05-25 01:33:47 +04:00
if ( ds1302_hw_init ( ) ) {
dev_err ( & pdev - > dev , " Failed to init communication channel " ) ;
return - EINVAL ;
}
2008-02-06 12:38:44 +03:00
/* Reset */
2010-05-25 01:33:47 +04:00
ds1302_reset ( ) ;
2008-02-06 12:38:44 +03:00
/* Write a magic value to the DS1302 RAM, and see if it sticks. */
ds1302_writebyte ( RTC_ADDR_RAM0 , 0x42 ) ;
2010-05-25 01:33:47 +04:00
if ( ds1302_readbyte ( RTC_ADDR_RAM0 ) ! = 0x42 ) {
dev_err ( & pdev - > dev , " Failed to probe " ) ;
2008-02-06 12:38:44 +03:00
return - ENODEV ;
2010-05-25 01:33:47 +04:00
}
2008-02-06 12:38:44 +03:00
2009-08-20 07:31:49 +04:00
rtc = rtc_device_register ( " ds1302 " , & pdev - > dev ,
2008-02-06 12:38:44 +03:00
& ds1302_rtc_ops , THIS_MODULE ) ;
2009-08-20 07:31:49 +04:00
if ( IS_ERR ( rtc ) )
return PTR_ERR ( rtc ) ;
2008-02-06 12:38:44 +03:00
platform_set_drvdata ( pdev , rtc ) ;
return 0 ;
}
static int __devexit ds1302_rtc_remove ( struct platform_device * pdev )
{
2009-08-20 07:31:49 +04:00
struct rtc_device * rtc = platform_get_drvdata ( pdev ) ;
2008-02-06 12:38:44 +03:00
2009-08-20 07:31:49 +04:00
rtc_device_unregister ( rtc ) ;
2008-02-06 12:38:44 +03:00
platform_set_drvdata ( pdev , NULL ) ;
return 0 ;
}
static struct platform_driver ds1302_platform_driver = {
. driver = {
. name = DRV_NAME ,
. owner = THIS_MODULE ,
} ,
2009-11-25 00:07:23 +03:00
. remove = __devexit_p ( ds1302_rtc_remove ) ,
2008-02-06 12:38:44 +03:00
} ;
static int __init ds1302_rtc_init ( void )
{
2009-08-20 07:31:49 +04:00
return platform_driver_probe ( & ds1302_platform_driver , ds1302_rtc_probe ) ;
2008-02-06 12:38:44 +03:00
}
static void __exit ds1302_rtc_exit ( void )
{
platform_driver_unregister ( & ds1302_platform_driver ) ;
}
module_init ( ds1302_rtc_init ) ;
module_exit ( ds1302_rtc_exit ) ;
MODULE_DESCRIPTION ( " Dallas DS1302 RTC driver " ) ;
MODULE_VERSION ( DRV_VERSION ) ;
MODULE_AUTHOR ( " Paul Mundt, David McCullough " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;