2006-03-27 13:16:47 +04:00
/*
* ST M48T86 / Dallas DS12887 RTC driver
* Copyright ( c ) 2006 Tower Technologies
*
* Author : Alessandro Zummo < a . zummo @ towertech . it >
*
* 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 .
*
* This drivers only supports the clock running in BCD and 24 H mode .
* If it will be ever adapted to binary and 12 H mode , care must be taken
* to not introduce bugs .
*/
# include <linux/module.h>
# include <linux/rtc.h>
# include <linux/platform_device.h>
2016-06-26 23:57:52 +03:00
# include <linux/platform_data/rtc-m48t86.h>
2006-03-27 13:16:47 +04:00
# include <linux/bcd.h>
2017-02-10 21:11:56 +03:00
# include <linux/io.h>
2006-03-27 13:16:47 +04:00
2017-02-10 21:11:55 +03:00
# define M48T86_SEC 0x00
# define M48T86_SECALRM 0x01
# define M48T86_MIN 0x02
# define M48T86_MINALRM 0x03
# define M48T86_HOUR 0x04
# define M48T86_HOURALRM 0x05
# define M48T86_DOW 0x06 /* 1 = sunday */
# define M48T86_DOM 0x07
# define M48T86_MONTH 0x08 /* 1 - 12 */
# define M48T86_YEAR 0x09 /* 0 - 99 */
# define M48T86_A 0x0a
# define M48T86_B 0x0b
# define M48T86_B_SET BIT(7)
# define M48T86_B_DM BIT(2)
# define M48T86_B_H24 BIT(1)
# define M48T86_C 0x0c
# define M48T86_D 0x0d
# define M48T86_D_VRT BIT(7)
2017-02-10 21:11:57 +03:00
# define M48T86_NVRAM(x) (0x0e + (x))
# define M48T86_NVRAM_LEN 114
2006-03-27 13:16:47 +04:00
2017-02-10 21:11:56 +03:00
struct m48t86_rtc_info {
void __iomem * index_reg ;
void __iomem * data_reg ;
struct rtc_device * rtc ;
struct m48t86_ops * ops ;
} ;
static unsigned char m48t86_readb ( struct device * dev , unsigned long addr )
{
struct m48t86_rtc_info * info = dev_get_drvdata ( dev ) ;
unsigned char value ;
if ( info - > ops ) {
value = info - > ops - > readbyte ( addr ) ;
} else {
writeb ( addr , info - > index_reg ) ;
value = readb ( info - > data_reg ) ;
}
return value ;
}
static void m48t86_writeb ( struct device * dev ,
unsigned char value , unsigned long addr )
{
struct m48t86_rtc_info * info = dev_get_drvdata ( dev ) ;
if ( info - > ops ) {
info - > ops - > writebyte ( value , addr ) ;
} else {
writeb ( addr , info - > index_reg ) ;
writeb ( value , info - > data_reg ) ;
}
}
2006-03-27 13:16:47 +04:00
static int m48t86_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
unsigned char reg ;
2017-02-10 21:11:56 +03:00
reg = m48t86_readb ( dev , M48T86_B ) ;
2006-03-27 13:16:47 +04:00
2017-02-10 21:11:55 +03:00
if ( reg & M48T86_B_DM ) {
2006-03-27 13:16:47 +04:00
/* data (binary) mode */
2017-02-10 21:11:56 +03:00
tm - > tm_sec = m48t86_readb ( dev , M48T86_SEC ) ;
tm - > tm_min = m48t86_readb ( dev , M48T86_MIN ) ;
tm - > tm_hour = m48t86_readb ( dev , M48T86_HOUR ) & 0x3f ;
tm - > tm_mday = m48t86_readb ( dev , M48T86_DOM ) ;
2006-03-27 13:16:47 +04:00
/* tm_mon is 0-11 */
2017-02-10 21:11:56 +03:00
tm - > tm_mon = m48t86_readb ( dev , M48T86_MONTH ) - 1 ;
tm - > tm_year = m48t86_readb ( dev , M48T86_YEAR ) + 100 ;
tm - > tm_wday = m48t86_readb ( dev , M48T86_DOW ) ;
2006-03-27 13:16:47 +04:00
} else {
/* bcd mode */
2017-02-10 21:11:56 +03:00
tm - > tm_sec = bcd2bin ( m48t86_readb ( dev , M48T86_SEC ) ) ;
tm - > tm_min = bcd2bin ( m48t86_readb ( dev , M48T86_MIN ) ) ;
tm - > tm_hour = bcd2bin ( m48t86_readb ( dev , M48T86_HOUR ) &
0x3f ) ;
tm - > tm_mday = bcd2bin ( m48t86_readb ( dev , M48T86_DOM ) ) ;
2006-03-27 13:16:47 +04:00
/* tm_mon is 0-11 */
2017-02-10 21:11:56 +03:00
tm - > tm_mon = bcd2bin ( m48t86_readb ( dev , M48T86_MONTH ) ) - 1 ;
tm - > tm_year = bcd2bin ( m48t86_readb ( dev , M48T86_YEAR ) ) + 100 ;
tm - > tm_wday = bcd2bin ( m48t86_readb ( dev , M48T86_DOW ) ) ;
2006-03-27 13:16:47 +04:00
}
/* correct the hour if the clock is in 12h mode */
2017-02-10 21:11:55 +03:00
if ( ! ( reg & M48T86_B_H24 ) )
2017-02-10 21:11:56 +03:00
if ( m48t86_readb ( dev , M48T86_HOUR ) & 0x80 )
2006-03-27 13:16:47 +04:00
tm - > tm_hour + = 12 ;
2010-08-11 05:02:17 +04:00
return rtc_valid_tm ( tm ) ;
2006-03-27 13:16:47 +04:00
}
static int m48t86_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
unsigned char reg ;
2017-02-10 21:11:56 +03:00
reg = m48t86_readb ( dev , M48T86_B ) ;
2006-03-27 13:16:47 +04:00
/* update flag and 24h mode */
2017-02-10 21:11:55 +03:00
reg | = M48T86_B_SET | M48T86_B_H24 ;
2017-02-10 21:11:56 +03:00
m48t86_writeb ( dev , reg , M48T86_B ) ;
2006-03-27 13:16:47 +04:00
2017-02-10 21:11:55 +03:00
if ( reg & M48T86_B_DM ) {
2006-03-27 13:16:47 +04:00
/* data (binary) mode */
2017-02-10 21:11:56 +03:00
m48t86_writeb ( dev , tm - > tm_sec , M48T86_SEC ) ;
m48t86_writeb ( dev , tm - > tm_min , M48T86_MIN ) ;
m48t86_writeb ( dev , tm - > tm_hour , M48T86_HOUR ) ;
m48t86_writeb ( dev , tm - > tm_mday , M48T86_DOM ) ;
m48t86_writeb ( dev , tm - > tm_mon + 1 , M48T86_MONTH ) ;
m48t86_writeb ( dev , tm - > tm_year % 100 , M48T86_YEAR ) ;
m48t86_writeb ( dev , tm - > tm_wday , M48T86_DOW ) ;
2006-03-27 13:16:47 +04:00
} else {
/* bcd mode */
2017-02-10 21:11:56 +03:00
m48t86_writeb ( dev , bin2bcd ( tm - > tm_sec ) , M48T86_SEC ) ;
m48t86_writeb ( dev , bin2bcd ( tm - > tm_min ) , M48T86_MIN ) ;
m48t86_writeb ( dev , bin2bcd ( tm - > tm_hour ) , M48T86_HOUR ) ;
m48t86_writeb ( dev , bin2bcd ( tm - > tm_mday ) , M48T86_DOM ) ;
m48t86_writeb ( dev , bin2bcd ( tm - > tm_mon + 1 ) , M48T86_MONTH ) ;
m48t86_writeb ( dev , bin2bcd ( tm - > tm_year % 100 ) , M48T86_YEAR ) ;
m48t86_writeb ( dev , bin2bcd ( tm - > tm_wday ) , M48T86_DOW ) ;
2006-03-27 13:16:47 +04:00
}
/* update ended */
2017-02-10 21:11:55 +03:00
reg & = ~ M48T86_B_SET ;
2017-02-10 21:11:56 +03:00
m48t86_writeb ( dev , reg , M48T86_B ) ;
2006-03-27 13:16:47 +04:00
return 0 ;
}
static int m48t86_rtc_proc ( struct device * dev , struct seq_file * seq )
{
unsigned char reg ;
2017-02-10 21:11:56 +03:00
reg = m48t86_readb ( dev , M48T86_B ) ;
2006-03-27 13:16:47 +04:00
seq_printf ( seq , " mode \t \t : %s \n " ,
2017-02-10 21:11:55 +03:00
( reg & M48T86_B_DM ) ? " binary " : " bcd " ) ;
2006-03-27 13:16:47 +04:00
2017-02-10 21:11:56 +03:00
reg = m48t86_readb ( dev , M48T86_D ) ;
2006-03-27 13:16:47 +04:00
seq_printf ( seq , " battery \t \t : %s \n " ,
2017-02-10 21:11:55 +03:00
( reg & M48T86_D_VRT ) ? " ok " : " exhausted " ) ;
2006-03-27 13:16:47 +04:00
return 0 ;
}
2006-10-01 10:28:17 +04:00
static const struct rtc_class_ops m48t86_rtc_ops = {
2006-03-27 13:16:47 +04:00
. read_time = m48t86_rtc_read_time ,
. set_time = m48t86_rtc_set_time ,
. proc = m48t86_rtc_proc ,
} ;
2017-02-10 21:11:57 +03:00
static ssize_t m48t86_nvram_read ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * attr ,
char * buf , loff_t off , size_t count )
{
struct device * dev = kobj_to_dev ( kobj ) ;
unsigned int i ;
for ( i = 0 ; i < count ; i + + )
buf [ i ] = m48t86_readb ( dev , M48T86_NVRAM ( off + i ) ) ;
return count ;
}
static ssize_t m48t86_nvram_write ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * attr ,
char * buf , loff_t off , size_t count )
{
struct device * dev = kobj_to_dev ( kobj ) ;
unsigned int i ;
for ( i = 0 ; i < count ; i + + )
m48t86_writeb ( dev , buf [ i ] , M48T86_NVRAM ( off + i ) ) ;
return count ;
}
static BIN_ATTR ( nvram , 0644 , m48t86_nvram_read , m48t86_nvram_write ,
M48T86_NVRAM_LEN ) ;
2017-02-10 21:11:56 +03:00
static int m48t86_rtc_probe ( struct platform_device * pdev )
2006-03-27 13:16:47 +04:00
{
2017-02-10 21:11:56 +03:00
struct m48t86_rtc_info * info ;
2006-03-27 13:16:47 +04:00
unsigned char reg ;
2013-04-30 03:19:42 +04:00
2017-02-10 21:11:56 +03:00
info = devm_kzalloc ( & pdev - > dev , sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info )
return - ENOMEM ;
info - > ops = dev_get_platdata ( & pdev - > dev ) ;
if ( ! info - > ops ) {
struct resource * res ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENODEV ;
info - > index_reg = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( info - > index_reg ) )
return PTR_ERR ( info - > index_reg ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( ! res )
return - ENODEV ;
info - > data_reg = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( info - > data_reg ) )
return PTR_ERR ( info - > data_reg ) ;
}
2006-03-27 13:16:47 +04:00
2017-02-10 21:11:56 +03:00
dev_set_drvdata ( & pdev - > dev , info ) ;
2006-03-27 13:16:47 +04:00
2017-02-10 21:11:56 +03:00
info - > rtc = devm_rtc_device_register ( & pdev - > dev , " m48t86 " ,
& m48t86_rtc_ops , THIS_MODULE ) ;
if ( IS_ERR ( info - > rtc ) )
return PTR_ERR ( info - > rtc ) ;
2006-03-27 13:16:47 +04:00
/* read battery status */
2017-02-10 21:11:56 +03:00
reg = m48t86_readb ( & pdev - > dev , M48T86_D ) ;
dev_info ( & pdev - > dev , " battery %s \n " ,
2017-02-10 21:11:55 +03:00
( reg & M48T86_D_VRT ) ? " ok " : " exhausted " ) ;
2006-03-27 13:16:47 +04:00
2017-02-10 21:11:57 +03:00
if ( device_create_bin_file ( & pdev - > dev , & bin_attr_nvram ) )
dev_err ( & pdev - > dev , " failed to create nvram sysfs entry \n " ) ;
return 0 ;
}
static int m48t86_rtc_remove ( struct platform_device * pdev )
{
device_remove_bin_file ( & pdev - > dev , & bin_attr_nvram ) ;
2006-03-27 13:16:47 +04:00
return 0 ;
}
static struct platform_driver m48t86_rtc_platform_driver = {
. driver = {
. name = " rtc-m48t86 " ,
} ,
. probe = m48t86_rtc_probe ,
2017-02-10 21:11:57 +03:00
. remove = m48t86_rtc_remove ,
2006-03-27 13:16:47 +04:00
} ;
2012-01-11 03:10:48 +04:00
module_platform_driver ( m48t86_rtc_platform_driver ) ;
2006-03-27 13:16:47 +04:00
MODULE_AUTHOR ( " Alessandro Zummo <a.zummo@towertech.it> " ) ;
MODULE_DESCRIPTION ( " M48T86 RTC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-04-11 08:29:25 +04:00
MODULE_ALIAS ( " platform:rtc-m48t86 " ) ;