2014-09-03 17:51:44 +04:00
/*
* MFD core driver for Rockchip RK808
*
* Copyright ( c ) 2014 , Fuzhou Rockchip Electronics Co . , Ltd
*
* Author : Chris Zhong < zyw @ rock - chips . com >
* Author : Zhang Qing < zhangqing @ rock - chips . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*/
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/mfd/rk808.h>
# include <linux/mfd/core.h>
# include <linux/module.h>
# include <linux/regmap.h>
struct rk808_reg_data {
int addr ;
int mask ;
int value ;
} ;
2014-09-10 03:06:04 +04:00
static bool rk808_is_volatile_reg ( struct device * dev , unsigned int reg )
{
/*
* Notes :
* - Technically the ROUND_30s bit makes RTC_CTRL_REG volatile , but
* we don ' t use that feature . It ' s better to cache .
* - It ' s unlikely we care that RK808_DEVCTRL_REG is volatile since
* bits are cleared in case when we shutoff anyway , but better safe .
*/
switch ( reg ) {
case RK808_SECONDS_REG . . . RK808_WEEKS_REG :
case RK808_RTC_STATUS_REG :
case RK808_VB_MON_REG :
case RK808_THERMAL_REG :
case RK808_DCDC_UV_STS_REG :
case RK808_LDO_UV_STS_REG :
case RK808_DCDC_PG_REG :
case RK808_LDO_PG_REG :
case RK808_DEVCTRL_REG :
case RK808_INT_STS_REG1 :
case RK808_INT_STS_REG2 :
return true ;
}
return false ;
}
2014-09-03 17:51:44 +04:00
static const struct regmap_config rk808_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = RK808_IO_POL_REG ,
2014-09-10 03:06:04 +04:00
. cache_type = REGCACHE_RBTREE ,
. volatile_reg = rk808_is_volatile_reg ,
2014-09-03 17:51:44 +04:00
} ;
static struct resource rtc_resources [ ] = {
{
. start = RK808_IRQ_RTC_ALARM ,
. end = RK808_IRQ_RTC_ALARM ,
. flags = IORESOURCE_IRQ ,
}
} ;
static const struct mfd_cell rk808s [ ] = {
{ . name = " rk808-clkout " , } ,
{ . name = " rk808-regulator " , } ,
{
. name = " rk808-rtc " ,
. num_resources = ARRAY_SIZE ( rtc_resources ) ,
. resources = & rtc_resources [ 0 ] ,
} ,
} ;
static const struct rk808_reg_data pre_init_reg [ ] = {
{ RK808_BUCK3_CONFIG_REG , BUCK_ILMIN_MASK , BUCK_ILMIN_150MA } ,
{ RK808_BUCK4_CONFIG_REG , BUCK_ILMIN_MASK , BUCK_ILMIN_200MA } ,
{ RK808_BOOST_CONFIG_REG , BOOST_ILMIN_MASK , BOOST_ILMIN_100MA } ,
{ RK808_BUCK1_CONFIG_REG , BUCK1_RATE_MASK , BUCK_ILMIN_200MA } ,
{ RK808_BUCK2_CONFIG_REG , BUCK2_RATE_MASK , BUCK_ILMIN_200MA } ,
2015-02-28 13:09:06 +03:00
{ RK808_DCDC_UV_ACT_REG , BUCK_UV_ACT_MASK , BUCK_UV_ACT_DISABLE } ,
2014-09-03 17:51:44 +04:00
{ RK808_VB_MON_REG , MASK_ALL , VB_LO_ACT |
VB_LO_SEL_3500MV } ,
} ;
static const struct regmap_irq rk808_irqs [ ] = {
/* INT_STS */
[ RK808_IRQ_VOUT_LO ] = {
. mask = RK808_IRQ_VOUT_LO_MSK ,
. reg_offset = 0 ,
} ,
[ RK808_IRQ_VB_LO ] = {
. mask = RK808_IRQ_VB_LO_MSK ,
. reg_offset = 0 ,
} ,
[ RK808_IRQ_PWRON ] = {
. mask = RK808_IRQ_PWRON_MSK ,
. reg_offset = 0 ,
} ,
[ RK808_IRQ_PWRON_LP ] = {
. mask = RK808_IRQ_PWRON_LP_MSK ,
. reg_offset = 0 ,
} ,
[ RK808_IRQ_HOTDIE ] = {
. mask = RK808_IRQ_HOTDIE_MSK ,
. reg_offset = 0 ,
} ,
[ RK808_IRQ_RTC_ALARM ] = {
. mask = RK808_IRQ_RTC_ALARM_MSK ,
. reg_offset = 0 ,
} ,
[ RK808_IRQ_RTC_PERIOD ] = {
. mask = RK808_IRQ_RTC_PERIOD_MSK ,
. reg_offset = 0 ,
} ,
/* INT_STS2 */
[ RK808_IRQ_PLUG_IN_INT ] = {
. mask = RK808_IRQ_PLUG_IN_INT_MSK ,
. reg_offset = 1 ,
} ,
[ RK808_IRQ_PLUG_OUT_INT ] = {
. mask = RK808_IRQ_PLUG_OUT_INT_MSK ,
. reg_offset = 1 ,
} ,
} ;
static struct regmap_irq_chip rk808_irq_chip = {
. name = " rk808 " ,
. irqs = rk808_irqs ,
. num_irqs = ARRAY_SIZE ( rk808_irqs ) ,
. num_regs = 2 ,
. irq_reg_stride = 2 ,
. status_base = RK808_INT_STS_REG1 ,
. mask_base = RK808_INT_STS_MSK_REG1 ,
. ack_base = RK808_INT_STS_REG1 ,
. init_ack_masked = true ,
} ;
static struct i2c_client * rk808_i2c_client ;
static void rk808_device_shutdown ( void )
{
int ret ;
struct rk808 * rk808 = i2c_get_clientdata ( rk808_i2c_client ) ;
if ( ! rk808 ) {
dev_warn ( & rk808_i2c_client - > dev ,
" have no rk808, so do nothing here \n " ) ;
return ;
}
ret = regmap_update_bits ( rk808 - > regmap ,
RK808_DEVCTRL_REG ,
DEV_OFF_RST , DEV_OFF_RST ) ;
if ( ret )
dev_err ( & rk808_i2c_client - > dev , " power off error! \n " ) ;
}
static int rk808_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct device_node * np = client - > dev . of_node ;
struct rk808 * rk808 ;
int pm_off = 0 ;
int ret ;
int i ;
if ( ! client - > irq ) {
dev_err ( & client - > dev , " No interrupt support, no core IRQ \n " ) ;
return - EINVAL ;
}
rk808 = devm_kzalloc ( & client - > dev , sizeof ( * rk808 ) , GFP_KERNEL ) ;
if ( ! rk808 )
return - ENOMEM ;
rk808 - > regmap = devm_regmap_init_i2c ( client , & rk808_regmap_config ) ;
if ( IS_ERR ( rk808 - > regmap ) ) {
dev_err ( & client - > dev , " regmap initialization failed \n " ) ;
return PTR_ERR ( rk808 - > regmap ) ;
}
for ( i = 0 ; i < ARRAY_SIZE ( pre_init_reg ) ; i + + ) {
ret = regmap_update_bits ( rk808 - > regmap , pre_init_reg [ i ] . addr ,
pre_init_reg [ i ] . mask ,
pre_init_reg [ i ] . value ) ;
if ( ret ) {
dev_err ( & client - > dev ,
" 0x%x write err \n " , pre_init_reg [ i ] . addr ) ;
return ret ;
}
}
ret = regmap_add_irq_chip ( rk808 - > regmap , client - > irq ,
IRQF_ONESHOT , - 1 ,
& rk808_irq_chip , & rk808 - > irq_data ) ;
if ( ret ) {
dev_err ( & client - > dev , " Failed to add irq_chip %d \n " , ret ) ;
return ret ;
}
rk808 - > i2c = client ;
i2c_set_clientdata ( client , rk808 ) ;
ret = mfd_add_devices ( & client - > dev , - 1 ,
rk808s , ARRAY_SIZE ( rk808s ) ,
NULL , 0 , regmap_irq_get_domain ( rk808 - > irq_data ) ) ;
if ( ret ) {
dev_err ( & client - > dev , " failed to add MFD devices %d \n " , ret ) ;
goto err_irq ;
}
pm_off = of_property_read_bool ( np ,
" rockchip,system-power-controller " ) ;
if ( pm_off & & ! pm_power_off ) {
rk808_i2c_client = client ;
pm_power_off = rk808_device_shutdown ;
}
return 0 ;
err_irq :
regmap_del_irq_chip ( client - > irq , rk808 - > irq_data ) ;
return ret ;
}
static int rk808_remove ( struct i2c_client * client )
{
struct rk808 * rk808 = i2c_get_clientdata ( client ) ;
regmap_del_irq_chip ( client - > irq , rk808 - > irq_data ) ;
mfd_remove_devices ( & client - > dev ) ;
pm_power_off = NULL ;
return 0 ;
}
2015-03-16 22:21:36 +03:00
static const struct of_device_id rk808_of_match [ ] = {
2014-09-03 17:51:44 +04:00
{ . compatible = " rockchip,rk808 " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , rk808_of_match ) ;
static const struct i2c_device_id rk808_ids [ ] = {
{ " rk808 " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , rk808_ids ) ;
static struct i2c_driver rk808_i2c_driver = {
. driver = {
. name = " rk808 " ,
. of_match_table = rk808_of_match ,
} ,
. probe = rk808_probe ,
. remove = rk808_remove ,
. id_table = rk808_ids ,
} ;
module_i2c_driver ( rk808_i2c_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Chris Zhong <zyw@rock-chips.com> " ) ;
MODULE_AUTHOR ( " Zhang Qing <zhangqing@rock-chips.com> " ) ;
MODULE_DESCRIPTION ( " RK808 PMIC driver " ) ;