2009-12-16 03:46:12 +03:00
/*
* Driver for TI BQ32000 RTC .
*
* Copyright ( C ) 2009 Semihalf .
2014-10-14 02:52:50 +04:00
* Copyright ( C ) 2014 Pavel Machek < pavel @ denx . de >
2009-12-16 03:46:12 +03:00
*
* 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 .
2014-10-14 02:52:50 +04:00
*
* You can get hardware description at
* http : //www.ti.com/lit/ds/symlink/bq32000.pdf
2009-12-16 03:46:12 +03:00
*/
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/rtc.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/bcd.h>
# define BQ32K_SECONDS 0x00 /* Seconds register address */
# define BQ32K_SECONDS_MASK 0x7F /* Mask over seconds value */
# define BQ32K_STOP 0x80 /* Oscillator Stop flat */
# define BQ32K_MINUTES 0x01 /* Minutes register address */
# define BQ32K_MINUTES_MASK 0x7F /* Mask over minutes value */
# define BQ32K_OF 0x80 /* Oscillator Failure flag */
# define BQ32K_HOURS_MASK 0x3F /* Mask over hours value */
# define BQ32K_CENT 0x40 /* Century flag */
# define BQ32K_CENT_EN 0x80 /* Century flag enable bit */
2014-10-14 02:52:50 +04:00
# define BQ32K_CALIBRATION 0x07 /* CAL_CFG1, calibration and control */
# define BQ32K_TCH2 0x08 /* Trickle charge enable */
# define BQ32K_CFG2 0x09 /* Trickle charger control */
2017-01-27 20:43:46 +03:00
# define BQ32K_TCFE BIT(6) /* Trickle charge FET bypass */
2014-10-14 02:52:50 +04:00
2018-03-08 05:27:56 +03:00
# define MAX_LEN 10 / * Maximum number of consecutive
* register for this particular RTC .
*/
2009-12-16 03:46:12 +03:00
struct bq32k_regs {
uint8_t seconds ;
uint8_t minutes ;
uint8_t cent_hours ;
uint8_t day ;
uint8_t date ;
uint8_t month ;
uint8_t years ;
} ;
static struct i2c_driver bq32k_driver ;
static int bq32k_read ( struct device * dev , void * data , uint8_t off , uint8_t len )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct i2c_msg msgs [ ] = {
{
. addr = client - > addr ,
. flags = 0 ,
. len = 1 ,
. buf = & off ,
} , {
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = len ,
. buf = data ,
}
} ;
if ( i2c_transfer ( client - > adapter , msgs , 2 ) = = 2 )
return 0 ;
return - EIO ;
}
static int bq32k_write ( struct device * dev , void * data , uint8_t off , uint8_t len )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
2018-03-08 05:27:56 +03:00
uint8_t buffer [ MAX_LEN + 1 ] ;
2009-12-16 03:46:12 +03:00
buffer [ 0 ] = off ;
memcpy ( & buffer [ 1 ] , data , len ) ;
if ( i2c_master_send ( client , buffer , len + 1 ) = = len + 1 )
return 0 ;
return - EIO ;
}
static int bq32k_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
struct bq32k_regs regs ;
int error ;
error = bq32k_read ( dev , & regs , 0 , sizeof ( regs ) ) ;
if ( error )
return error ;
2016-08-11 14:31:44 +03:00
/*
* In case of oscillator failure , the register contents should be
* considered invalid . The flag is cleared the next time the RTC is set .
*/
if ( regs . minutes & BQ32K_OF )
return - EINVAL ;
2009-12-16 03:46:12 +03:00
tm - > tm_sec = bcd2bin ( regs . seconds & BQ32K_SECONDS_MASK ) ;
2016-08-11 14:31:43 +03:00
tm - > tm_min = bcd2bin ( regs . minutes & BQ32K_MINUTES_MASK ) ;
2009-12-16 03:46:12 +03:00
tm - > tm_hour = bcd2bin ( regs . cent_hours & BQ32K_HOURS_MASK ) ;
tm - > tm_mday = bcd2bin ( regs . date ) ;
tm - > tm_wday = bcd2bin ( regs . day ) - 1 ;
tm - > tm_mon = bcd2bin ( regs . month ) - 1 ;
tm - > tm_year = bcd2bin ( regs . years ) +
( ( regs . cent_hours & BQ32K_CENT ) ? 100 : 0 ) ;
2018-02-19 18:23:56 +03:00
return 0 ;
2009-12-16 03:46:12 +03:00
}
static int bq32k_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
struct bq32k_regs regs ;
regs . seconds = bin2bcd ( tm - > tm_sec ) ;
regs . minutes = bin2bcd ( tm - > tm_min ) ;
regs . cent_hours = bin2bcd ( tm - > tm_hour ) | BQ32K_CENT_EN ;
regs . day = bin2bcd ( tm - > tm_wday + 1 ) ;
regs . date = bin2bcd ( tm - > tm_mday ) ;
regs . month = bin2bcd ( tm - > tm_mon + 1 ) ;
if ( tm - > tm_year > = 100 ) {
regs . cent_hours | = BQ32K_CENT ;
regs . years = bin2bcd ( tm - > tm_year - 100 ) ;
} else
regs . years = bin2bcd ( tm - > tm_year ) ;
return bq32k_write ( dev , & regs , 0 , sizeof ( regs ) ) ;
}
static const struct rtc_class_ops bq32k_rtc_ops = {
. read_time = bq32k_rtc_read_time ,
. set_time = bq32k_rtc_set_time ,
} ;
2014-10-14 02:52:50 +04:00
static int trickle_charger_of_init ( struct device * dev , struct device_node * node )
{
unsigned char reg ;
int error ;
u32 ohms = 0 ;
if ( of_property_read_u32 ( node , " trickle-resistor-ohms " , & ohms ) )
return 0 ;
switch ( ohms ) {
case 180 + 940 :
/*
* TCHE [ 3 : 0 ] = = 0x05 , TCH2 = = 1 , TCFE = = 0 ( charging
* over diode and 940 ohm resistor )
*/
if ( of_property_read_bool ( node , " trickle-diode-disable " ) ) {
dev_err ( dev , " diode and resistor mismatch \n " ) ;
return - EINVAL ;
}
reg = 0x05 ;
break ;
case 180 + 20000 :
/* diode disabled */
if ( ! of_property_read_bool ( node , " trickle-diode-disable " ) ) {
dev_err ( dev , " bq32k: diode and resistor mismatch \n " ) ;
return - EINVAL ;
}
2014-10-30 00:50:42 +03:00
reg = 0x45 ;
2014-10-14 02:52:50 +04:00
break ;
default :
dev_err ( dev , " invalid resistor value (%d) \n " , ohms ) ;
return - EINVAL ;
}
error = bq32k_write ( dev , & reg , BQ32K_CFG2 , 1 ) ;
if ( error )
return error ;
reg = 0x20 ;
error = bq32k_write ( dev , & reg , BQ32K_TCH2 , 1 ) ;
if ( error )
return error ;
dev_info ( dev , " Enabled trickle RTC battery charge. \n " ) ;
return 0 ;
}
2017-01-27 20:43:46 +03:00
static ssize_t bq32k_sysfs_show_tricklecharge_bypass ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
int reg , error ;
error = bq32k_read ( dev , & reg , BQ32K_CFG2 , 1 ) ;
if ( error )
return error ;
return sprintf ( buf , " %d \n " , ( reg & BQ32K_TCFE ) ? 1 : 0 ) ;
}
static ssize_t bq32k_sysfs_store_tricklecharge_bypass ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
int reg , enable , error ;
if ( kstrtoint ( buf , 0 , & enable ) )
return - EINVAL ;
error = bq32k_read ( dev , & reg , BQ32K_CFG2 , 1 ) ;
if ( error )
return error ;
if ( enable ) {
reg | = BQ32K_TCFE ;
error = bq32k_write ( dev , & reg , BQ32K_CFG2 , 1 ) ;
if ( error )
return error ;
dev_info ( dev , " Enabled trickle charge FET bypass. \n " ) ;
} else {
reg & = ~ BQ32K_TCFE ;
error = bq32k_write ( dev , & reg , BQ32K_CFG2 , 1 ) ;
if ( error )
return error ;
dev_info ( dev , " Disabled trickle charge FET bypass. \n " ) ;
}
return count ;
}
static DEVICE_ATTR ( trickle_charge_bypass , 0644 ,
bq32k_sysfs_show_tricklecharge_bypass ,
bq32k_sysfs_store_tricklecharge_bypass ) ;
static int bq32k_sysfs_register ( struct device * dev )
{
return device_create_file ( dev , & dev_attr_trickle_charge_bypass ) ;
}
static void bq32k_sysfs_unregister ( struct device * dev )
{
device_remove_file ( dev , & dev_attr_trickle_charge_bypass ) ;
}
2009-12-16 03:46:12 +03:00
static int bq32k_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct device * dev = & client - > dev ;
struct rtc_device * rtc ;
uint8_t reg ;
int error ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) )
return - ENODEV ;
/* Check Oscillator Stop flag */
error = bq32k_read ( dev , & reg , BQ32K_SECONDS , 1 ) ;
if ( ! error & & ( reg & BQ32K_STOP ) ) {
dev_warn ( dev , " Oscillator was halted. Restarting... \n " ) ;
reg & = ~ BQ32K_STOP ;
error = bq32k_write ( dev , & reg , BQ32K_SECONDS , 1 ) ;
}
if ( error )
return error ;
/* Check Oscillator Failure flag */
error = bq32k_read ( dev , & reg , BQ32K_MINUTES , 1 ) ;
if ( error )
return error ;
2016-08-11 14:31:44 +03:00
if ( reg & BQ32K_OF )
dev_warn ( dev , " Oscillator Failure. Check RTC battery. \n " ) ;
2009-12-16 03:46:12 +03:00
2015-07-08 09:56:47 +03:00
if ( client - > dev . of_node )
2014-10-14 02:52:50 +04:00
trickle_charger_of_init ( dev , client - > dev . of_node ) ;
2013-04-30 03:19:31 +04:00
rtc = devm_rtc_device_register ( & client - > dev , bq32k_driver . driver . name ,
2009-12-16 03:46:12 +03:00
& bq32k_rtc_ops , THIS_MODULE ) ;
if ( IS_ERR ( rtc ) )
return PTR_ERR ( rtc ) ;
2017-01-27 20:43:46 +03:00
error = bq32k_sysfs_register ( & client - > dev ) ;
if ( error ) {
dev_err ( & client - > dev ,
" Unable to create sysfs entries for rtc bq32000 \n " ) ;
return error ;
}
2009-12-16 03:46:12 +03:00
i2c_set_clientdata ( client , rtc ) ;
return 0 ;
}
2017-01-27 20:43:46 +03:00
static int bq32k_remove ( struct i2c_client * client )
{
bq32k_sysfs_unregister ( & client - > dev ) ;
return 0 ;
}
2009-12-16 03:46:12 +03:00
static const struct i2c_device_id bq32k_id [ ] = {
{ " bq32000 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , bq32k_id ) ;
2017-03-03 17:29:14 +03:00
static const struct of_device_id bq32k_of_match [ ] = {
{ . compatible = " ti,bq32000 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , bq32k_of_match ) ;
2009-12-16 03:46:12 +03:00
static struct i2c_driver bq32k_driver = {
. driver = {
. name = " bq32k " ,
2017-03-03 17:29:14 +03:00
. of_match_table = of_match_ptr ( bq32k_of_match ) ,
2009-12-16 03:46:12 +03:00
} ,
. probe = bq32k_probe ,
2017-01-27 20:43:46 +03:00
. remove = bq32k_remove ,
2009-12-16 03:46:12 +03:00
. id_table = bq32k_id ,
} ;
2012-03-24 02:02:31 +04:00
module_i2c_driver ( bq32k_driver ) ;
2009-12-16 03:46:12 +03:00
MODULE_AUTHOR ( " Semihalf, Piotr Ziecik <kosmo@semihalf.com> " ) ;
MODULE_DESCRIPTION ( " TI BQ32000 I2C RTC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;