2018-08-07 19:11:22 +03:00
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright (c) 2012 Samsung Electronics Co., Ltd
// http://www.samsung.com
2011-12-23 12:28:08 +04:00
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/i2c.h>
2013-10-16 12:56:52 +04:00
# include <linux/of.h>
2013-02-04 03:49:47 +04:00
# include <linux/of_irq.h>
2011-12-23 12:28:08 +04:00
# include <linux/interrupt.h>
# include <linux/pm_runtime.h>
# include <linux/mutex.h>
# include <linux/mfd/core.h>
2012-07-11 16:07:16 +04:00
# include <linux/mfd/samsung/core.h>
# include <linux/mfd/samsung/irq.h>
2014-03-14 15:51:57 +04:00
# include <linux/mfd/samsung/s2mpa01.h>
2013-07-02 02:02:19 +04:00
# include <linux/mfd/samsung/s2mps11.h>
2014-11-18 11:59:40 +03:00
# include <linux/mfd/samsung/s2mps13.h>
2014-02-28 14:41:44 +04:00
# include <linux/mfd/samsung/s2mps14.h>
2015-11-20 13:37:51 +03:00
# include <linux/mfd/samsung/s2mps15.h>
2014-06-25 11:14:45 +04:00
# include <linux/mfd/samsung/s2mpu02.h>
2013-07-02 02:02:19 +04:00
# include <linux/mfd/samsung/s5m8763.h>
# include <linux/mfd/samsung/s5m8767.h>
2011-12-23 12:28:08 +04:00
# include <linux/regmap.h>
2013-11-18 17:33:06 +04:00
static const struct mfd_cell s5m8751_devs [ ] = {
2019-02-14 01:24:01 +03:00
{ . name = " s5m8751-pmic " , } ,
{ . name = " s5m-charger " , } ,
{ . name = " s5m8751-codec " , } ,
2012-01-20 11:09:12 +04:00
} ;
2013-11-18 17:33:06 +04:00
static const struct mfd_cell s5m8763_devs [ ] = {
2019-02-14 01:24:01 +03:00
{ . name = " s5m8763-pmic " , } ,
{ . name = " s5m-rtc " , } ,
{ . name = " s5m-charger " , } ,
2012-01-20 11:09:12 +04:00
} ;
2013-11-18 17:33:06 +04:00
static const struct mfd_cell s5m8767_devs [ ] = {
2019-02-14 01:24:01 +03:00
{ . name = " s5m8767-pmic " , } ,
{ . name = " s5m-rtc " , } ,
2011-12-23 12:28:08 +04:00
{
2013-12-26 14:19:00 +04:00
. name = " s5m8767-clk " ,
2014-03-17 13:19:17 +04:00
. of_compatible = " samsung,s5m8767-clk " ,
2019-02-14 01:24:01 +03:00
} ,
2011-12-23 12:28:08 +04:00
} ;
2013-11-18 17:33:06 +04:00
static const struct mfd_cell s2mps11_devs [ ] = {
2019-02-14 01:24:01 +03:00
{ . name = " s2mps11-regulator " , } ,
{ . name = " s2mps14-rtc " , } ,
2012-07-11 16:07:55 +04:00
{
2013-07-07 15:44:21 +04:00
. name = " s2mps11-clk " ,
2014-03-17 13:19:17 +04:00
. of_compatible = " samsung,s2mps11-clk " ,
2019-02-14 01:24:01 +03:00
} ,
2012-07-11 16:07:55 +04:00
} ;
2014-11-18 11:59:39 +03:00
static const struct mfd_cell s2mps13_devs [ ] = {
2015-11-20 13:48:26 +03:00
{ . name = " s2mps13-regulator " , } ,
2014-11-18 11:59:39 +03:00
{ . name = " s2mps13-rtc " , } ,
{
. name = " s2mps13-clk " ,
. of_compatible = " samsung,s2mps13-clk " ,
} ,
} ;
2014-02-28 14:41:44 +04:00
static const struct mfd_cell s2mps14_devs [ ] = {
2019-02-14 01:24:01 +03:00
{ . name = " s2mps14-regulator " , } ,
{ . name = " s2mps14-rtc " , } ,
2014-02-28 14:41:44 +04:00
{
. name = " s2mps14-clk " ,
2014-03-17 13:19:17 +04:00
. of_compatible = " samsung,s2mps14-clk " ,
2019-02-14 01:24:01 +03:00
} ,
2014-02-28 14:41:44 +04:00
} ;
2015-11-20 13:37:51 +03:00
static const struct mfd_cell s2mps15_devs [ ] = {
2019-02-14 01:24:01 +03:00
{ . name = " s2mps15-regulator " , } ,
{ . name = " s2mps15-rtc " , } ,
2015-11-20 13:37:51 +03:00
{
. name = " s2mps13-clk " ,
. of_compatible = " samsung,s2mps13-clk " ,
} ,
} ;
2014-03-14 15:51:57 +04:00
static const struct mfd_cell s2mpa01_devs [ ] = {
2019-02-14 01:24:01 +03:00
{ . name = " s2mpa01-pmic " , } ,
2019-02-14 01:24:02 +03:00
{ . name = " s2mps14-rtc " , } ,
2014-03-14 15:51:57 +04:00
} ;
2014-06-25 11:14:44 +04:00
static const struct mfd_cell s2mpu02_devs [ ] = {
2019-02-14 01:24:01 +03:00
{ . name = " s2mpu02-regulator " , } ,
2014-06-25 11:14:44 +04:00
} ;
2013-02-04 03:49:47 +04:00
# ifdef CONFIG_OF
2014-05-13 14:58:46 +04:00
static const struct of_device_id sec_dt_match [ ] = {
2019-02-11 16:20:23 +03:00
{
. compatible = " samsung,s5m8767-pmic " ,
2013-02-04 03:49:47 +04:00
. data = ( void * ) S5M8767X ,
2014-03-14 15:51:57 +04:00
} , {
. compatible = " samsung,s2mps11-pmic " ,
2013-06-29 16:51:20 +04:00
. data = ( void * ) S2MPS11X ,
2014-11-18 11:59:39 +03:00
} , {
. compatible = " samsung,s2mps13-pmic " ,
. data = ( void * ) S2MPS13X ,
2014-03-14 15:51:57 +04:00
} , {
. compatible = " samsung,s2mps14-pmic " ,
2014-02-28 14:41:44 +04:00
. data = ( void * ) S2MPS14X ,
2015-11-20 13:37:51 +03:00
} , {
. compatible = " samsung,s2mps15-pmic " ,
. data = ( void * ) S2MPS15X ,
2014-03-14 15:51:57 +04:00
} , {
. compatible = " samsung,s2mpa01-pmic " ,
. data = ( void * ) S2MPA01 ,
2014-06-25 11:14:44 +04:00
} , {
. compatible = " samsung,s2mpu02-pmic " ,
. data = ( void * ) S2MPU02 ,
2014-03-14 15:51:57 +04:00
} , {
/* Sentinel */
2014-02-28 14:41:44 +04:00
} ,
2013-02-04 03:49:47 +04:00
} ;
2018-07-25 18:53:02 +03:00
MODULE_DEVICE_TABLE ( of , sec_dt_match ) ;
2013-02-04 03:49:47 +04:00
# endif
2014-03-14 15:51:57 +04:00
static bool s2mpa01_volatile ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case S2MPA01_REG_INT1M :
case S2MPA01_REG_INT2M :
case S2MPA01_REG_INT3M :
return false ;
default :
return true ;
}
}
2013-07-02 21:51:16 +04:00
static bool s2mps11_volatile ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case S2MPS11_REG_INT1M :
case S2MPS11_REG_INT2M :
case S2MPS11_REG_INT3M :
return false ;
default :
return true ;
}
}
2014-06-25 11:14:45 +04:00
static bool s2mpu02_volatile ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case S2MPU02_REG_INT1M :
case S2MPU02_REG_INT2M :
case S2MPU02_REG_INT3M :
return false ;
default :
return true ;
}
}
2013-07-02 21:51:16 +04:00
static bool s5m8763_volatile ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case S5M8763_REG_IRQM1 :
case S5M8763_REG_IRQM2 :
case S5M8763_REG_IRQM3 :
case S5M8763_REG_IRQM4 :
return false ;
default :
return true ;
}
}
2013-11-28 12:09:43 +04:00
static const struct regmap_config sec_regmap_config = {
2011-12-23 12:28:08 +04:00
. reg_bits = 8 ,
. val_bits = 8 ,
} ;
2014-03-14 15:51:57 +04:00
static const struct regmap_config s2mpa01_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = S2MPA01_REG_LDO_OVCB4 ,
. volatile_reg = s2mpa01_volatile ,
. cache_type = REGCACHE_FLAT ,
} ;
2013-11-28 12:09:43 +04:00
static const struct regmap_config s2mps11_regmap_config = {
2013-07-02 02:02:19 +04:00
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = S2MPS11_REG_L38CTRL ,
2013-07-02 21:51:16 +04:00
. volatile_reg = s2mps11_volatile ,
. cache_type = REGCACHE_FLAT ,
2013-07-02 02:02:19 +04:00
} ;
2014-11-18 11:59:40 +03:00
static const struct regmap_config s2mps13_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = S2MPS13_REG_LDODSCH5 ,
. volatile_reg = s2mps11_volatile ,
. cache_type = REGCACHE_FLAT ,
} ;
2014-02-28 14:41:44 +04:00
static const struct regmap_config s2mps14_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = S2MPS14_REG_LDODSCH3 ,
. volatile_reg = s2mps11_volatile ,
. cache_type = REGCACHE_FLAT ,
} ;
2015-11-20 13:37:51 +03:00
static const struct regmap_config s2mps15_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = S2MPS15_REG_LDODSCH4 ,
. volatile_reg = s2mps11_volatile ,
. cache_type = REGCACHE_FLAT ,
} ;
2014-06-25 11:14:45 +04:00
static const struct regmap_config s2mpu02_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = S2MPU02_REG_DVSDATA ,
. volatile_reg = s2mpu02_volatile ,
. cache_type = REGCACHE_FLAT ,
} ;
2013-11-28 12:09:43 +04:00
static const struct regmap_config s5m8763_regmap_config = {
2013-07-02 02:02:19 +04:00
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = S5M8763_REG_LBCNFG2 ,
2013-07-02 21:51:16 +04:00
. volatile_reg = s5m8763_volatile ,
. cache_type = REGCACHE_FLAT ,
2013-07-02 02:02:19 +04:00
} ;
2013-11-28 12:09:43 +04:00
static const struct regmap_config s5m8767_regmap_config = {
2013-07-02 02:02:19 +04:00
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = S5M8767_REG_LDO28CTRL ,
2013-07-02 21:51:16 +04:00
. volatile_reg = s2mps11_volatile ,
. cache_type = REGCACHE_FLAT ,
2013-07-02 02:02:19 +04:00
} ;
2013-02-04 03:49:47 +04:00
2015-10-07 03:44:39 +03:00
static void sec_pmic_dump_rev ( struct sec_pmic_dev * sec_pmic )
{
unsigned int val ;
/* For each device type, the REG_ID is always the first register */
if ( ! regmap_read ( sec_pmic - > regmap_pmic , S2MPS11_REG_ID , & val ) )
dev_dbg ( sec_pmic - > dev , " Revision: 0x%x \n " , val ) ;
}
2015-10-07 03:44:41 +03:00
static void sec_pmic_configure ( struct sec_pmic_dev * sec_pmic )
{
int err ;
if ( sec_pmic - > device_type ! = S2MPS13X )
return ;
if ( sec_pmic - > pdata - > disable_wrstbi ) {
/*
* If WRSTBI pin is pulled down this feature must be disabled
* because each Suspend to RAM will trigger buck voltage reset
* to default values .
*/
err = regmap_update_bits ( sec_pmic - > regmap_pmic ,
S2MPS13_REG_WRSTBI ,
S2MPS13_REG_WRSTBI_MASK , 0x0 ) ;
if ( err )
dev_warn ( sec_pmic - > dev ,
" Cannot initialize WRSTBI config: %d \n " ,
err ) ;
}
}
2013-02-04 03:49:47 +04:00
# ifdef CONFIG_OF
/*
* Only the common platform data elements for s5m8767 are parsed here from the
* device tree . Other sub - modules of s5m8767 such as pmic , rtc , charger and
* others have to parse their own platform data elements from device tree .
*
* The s5m8767 platform data structure is instantiated here and the drivers for
* the sub - modules need not instantiate another instance while parsing their
* platform data .
*/
2019-02-11 16:20:23 +03:00
static struct sec_platform_data *
sec_pmic_i2c_parse_dt_pdata ( struct device * dev )
2013-02-04 03:49:47 +04:00
{
struct sec_platform_data * pd ;
pd = devm_kzalloc ( dev , sizeof ( * pd ) , GFP_KERNEL ) ;
2015-02-26 12:13:51 +03:00
if ( ! pd )
2013-02-04 03:49:47 +04:00
return ERR_PTR ( - ENOMEM ) ;
/*
* ToDo : the ' wakeup ' member in the platform data is more of a linux
* specfic information . Hence , there is no binding for that yet and
* not parsed here .
*/
2015-09-14 15:12:45 +03:00
pd - > manual_poweroff = of_property_read_bool ( dev - > of_node ,
" samsung,s2mps11-acokb-ground " ) ;
2015-10-07 03:44:41 +03:00
pd - > disable_wrstbi = of_property_read_bool ( dev - > of_node ,
" samsung,s2mps11-wrstbi-ground " ) ;
2013-02-04 03:49:47 +04:00
return pd ;
}
# else
2019-02-11 16:20:23 +03:00
static struct sec_platform_data *
sec_pmic_i2c_parse_dt_pdata ( struct device * dev )
2013-02-04 03:49:47 +04:00
{
2013-12-07 10:02:15 +04:00
return NULL ;
2013-02-04 03:49:47 +04:00
}
# endif
2014-03-13 06:14:07 +04:00
static inline unsigned long sec_i2c_get_driver_data ( struct i2c_client * i2c ,
2013-02-04 03:49:47 +04:00
const struct i2c_device_id * id )
{
# ifdef CONFIG_OF
if ( i2c - > dev . of_node ) {
const struct of_device_id * match ;
2014-07-21 16:09:25 +04:00
2013-02-04 03:49:47 +04:00
match = of_match_node ( sec_dt_match , i2c - > dev . of_node ) ;
2014-03-13 06:14:07 +04:00
return ( unsigned long ) match - > data ;
2013-02-04 03:49:47 +04:00
}
# endif
2014-03-13 06:14:07 +04:00
return id - > driver_data ;
2013-02-04 03:49:47 +04:00
}
2012-07-11 16:06:55 +04:00
static int sec_pmic_probe ( struct i2c_client * i2c ,
2011-12-23 12:28:08 +04:00
const struct i2c_device_id * id )
{
2013-07-30 12:10:05 +04:00
struct sec_platform_data * pdata = dev_get_platdata ( & i2c - > dev ) ;
2014-04-14 11:40:45 +04:00
const struct regmap_config * regmap ;
2014-06-25 11:14:44 +04:00
const struct mfd_cell * sec_devs ;
2012-07-11 16:06:55 +04:00
struct sec_pmic_dev * sec_pmic ;
2014-04-23 18:13:05 +04:00
unsigned long device_type ;
2014-06-25 11:14:44 +04:00
int ret , num_sec_devs ;
2011-12-23 12:28:08 +04:00
2012-07-11 16:06:55 +04:00
sec_pmic = devm_kzalloc ( & i2c - > dev , sizeof ( struct sec_pmic_dev ) ,
2012-01-20 11:09:11 +04:00
GFP_KERNEL ) ;
2012-07-11 16:06:55 +04:00
if ( sec_pmic = = NULL )
2011-12-23 12:28:08 +04:00
return - ENOMEM ;
2012-07-11 16:06:55 +04:00
i2c_set_clientdata ( i2c , sec_pmic ) ;
sec_pmic - > dev = & i2c - > dev ;
sec_pmic - > i2c = i2c ;
sec_pmic - > irq = i2c - > irq ;
2014-04-23 18:13:05 +04:00
device_type = sec_i2c_get_driver_data ( i2c , id ) ;
2013-02-04 03:49:47 +04:00
if ( sec_pmic - > dev - > of_node ) {
pdata = sec_pmic_i2c_parse_dt_pdata ( sec_pmic - > dev ) ;
if ( IS_ERR ( pdata ) ) {
ret = PTR_ERR ( pdata ) ;
return ret ;
}
2014-04-23 18:13:05 +04:00
pdata - > device_type = device_type ;
2013-02-04 03:49:47 +04:00
}
2011-12-23 12:28:08 +04:00
if ( pdata ) {
2012-07-11 16:06:55 +04:00
sec_pmic - > device_type = pdata - > device_type ;
sec_pmic - > irq_base = pdata - > irq_base ;
sec_pmic - > wakeup = pdata - > wakeup ;
2013-02-04 03:49:47 +04:00
sec_pmic - > pdata = pdata ;
2011-12-23 12:28:08 +04:00
}
2013-07-02 02:02:19 +04:00
switch ( sec_pmic - > device_type ) {
2014-03-14 15:51:57 +04:00
case S2MPA01 :
regmap = & s2mpa01_regmap_config ;
break ;
2013-07-02 02:02:19 +04:00
case S2MPS11X :
regmap = & s2mps11_regmap_config ;
2014-02-28 14:41:44 +04:00
break ;
2014-11-18 11:59:40 +03:00
case S2MPS13X :
regmap = & s2mps13_regmap_config ;
break ;
2014-02-28 14:41:44 +04:00
case S2MPS14X :
regmap = & s2mps14_regmap_config ;
2013-07-02 02:02:19 +04:00
break ;
2015-11-20 13:37:51 +03:00
case S2MPS15X :
regmap = & s2mps15_regmap_config ;
break ;
2013-07-02 02:02:19 +04:00
case S5M8763X :
regmap = & s5m8763_regmap_config ;
break ;
case S5M8767X :
regmap = & s5m8767_regmap_config ;
break ;
2014-06-25 11:14:45 +04:00
case S2MPU02 :
regmap = & s2mpu02_regmap_config ;
break ;
2013-07-02 02:02:19 +04:00
default :
regmap = & sec_regmap_config ;
break ;
}
2013-12-13 05:12:31 +04:00
sec_pmic - > regmap_pmic = devm_regmap_init_i2c ( i2c , regmap ) ;
if ( IS_ERR ( sec_pmic - > regmap_pmic ) ) {
ret = PTR_ERR ( sec_pmic - > regmap_pmic ) ;
2011-12-23 12:28:08 +04:00
dev_err ( & i2c - > dev , " Failed to allocate register map: %d \n " ,
2012-02-28 10:23:55 +04:00
ret ) ;
2012-04-25 06:03:44 +04:00
return ret ;
2011-12-23 12:28:08 +04:00
}
2012-01-16 04:08:42 +04:00
if ( pdata & & pdata - > cfg_pmic_irq )
2011-12-23 12:28:08 +04:00
pdata - > cfg_pmic_irq ( ) ;
2012-07-11 16:06:55 +04:00
sec_irq_init ( sec_pmic ) ;
2011-12-23 12:28:08 +04:00
2012-07-11 16:06:55 +04:00
pm_runtime_set_active ( sec_pmic - > dev ) ;
2011-12-23 12:28:08 +04:00
2012-07-11 16:06:55 +04:00
switch ( sec_pmic - > device_type ) {
2012-01-20 11:09:12 +04:00
case S5M8751X :
2014-06-25 11:14:44 +04:00
sec_devs = s5m8751_devs ;
num_sec_devs = ARRAY_SIZE ( s5m8751_devs ) ;
2012-01-20 11:09:12 +04:00
break ;
case S5M8763X :
2014-06-25 11:14:44 +04:00
sec_devs = s5m8763_devs ;
num_sec_devs = ARRAY_SIZE ( s5m8763_devs ) ;
2012-01-20 11:09:12 +04:00
break ;
case S5M8767X :
2014-06-25 11:14:44 +04:00
sec_devs = s5m8767_devs ;
num_sec_devs = ARRAY_SIZE ( s5m8767_devs ) ;
2012-01-20 11:09:12 +04:00
break ;
2014-03-14 15:51:57 +04:00
case S2MPA01 :
2014-06-25 11:14:44 +04:00
sec_devs = s2mpa01_devs ;
num_sec_devs = ARRAY_SIZE ( s2mpa01_devs ) ;
2014-03-14 15:51:57 +04:00
break ;
2012-07-11 16:07:55 +04:00
case S2MPS11X :
2014-06-25 11:14:44 +04:00
sec_devs = s2mps11_devs ;
num_sec_devs = ARRAY_SIZE ( s2mps11_devs ) ;
2012-07-11 16:07:55 +04:00
break ;
2014-11-18 11:59:39 +03:00
case S2MPS13X :
sec_devs = s2mps13_devs ;
num_sec_devs = ARRAY_SIZE ( s2mps13_devs ) ;
break ;
2014-02-28 14:41:44 +04:00
case S2MPS14X :
2014-06-25 11:14:44 +04:00
sec_devs = s2mps14_devs ;
num_sec_devs = ARRAY_SIZE ( s2mps14_devs ) ;
break ;
2015-11-20 13:37:51 +03:00
case S2MPS15X :
sec_devs = s2mps15_devs ;
num_sec_devs = ARRAY_SIZE ( s2mps15_devs ) ;
break ;
2014-06-25 11:14:44 +04:00
case S2MPU02 :
sec_devs = s2mpu02_devs ;
num_sec_devs = ARRAY_SIZE ( s2mpu02_devs ) ;
2014-02-28 14:41:44 +04:00
break ;
2012-01-20 11:09:12 +04:00
default :
2019-01-29 14:25:53 +03:00
dev_err ( & i2c - > dev , " Unsupported device type (%lu) \n " ,
sec_pmic - > device_type ) ;
return - ENODEV ;
2012-01-20 11:09:12 +04:00
}
2016-04-21 15:25:56 +03:00
ret = devm_mfd_add_devices ( sec_pmic - > dev , - 1 , sec_devs , num_sec_devs ,
NULL , 0 , NULL ) ;
2013-05-15 19:00:48 +04:00
if ( ret )
2016-04-21 15:25:56 +03:00
return ret ;
2011-12-23 12:28:08 +04:00
2013-11-26 17:50:27 +04:00
device_init_wakeup ( sec_pmic - > dev , sec_pmic - > wakeup ) ;
2015-10-07 03:44:41 +03:00
sec_pmic_configure ( sec_pmic ) ;
2015-10-07 03:44:39 +03:00
sec_pmic_dump_rev ( sec_pmic ) ;
2013-11-26 17:50:27 +04:00
2011-12-23 12:28:08 +04:00
return ret ;
}
2015-09-14 15:12:45 +03:00
static void sec_pmic_shutdown ( struct i2c_client * i2c )
{
struct sec_pmic_dev * sec_pmic = i2c_get_clientdata ( i2c ) ;
unsigned int reg , mask ;
if ( ! sec_pmic - > pdata - > manual_poweroff )
return ;
switch ( sec_pmic - > device_type ) {
case S2MPS11X :
reg = S2MPS11_REG_CTRL1 ;
mask = S2MPS11_CTRL1_PWRHOLD_MASK ;
break ;
default :
/*
* Currently only one board with S2MPS11 needs this , so just
* ignore the rest .
*/
dev_warn ( sec_pmic - > dev ,
" Unsupported device %lu for manual power off \n " ,
sec_pmic - > device_type ) ;
return ;
}
regmap_update_bits ( sec_pmic - > regmap_pmic , reg , mask , 0 ) ;
}
2014-01-26 14:38:43 +04:00
# ifdef CONFIG_PM_SLEEP
2013-11-26 17:50:27 +04:00
static int sec_pmic_suspend ( struct device * dev )
{
2015-12-28 18:00:14 +03:00
struct i2c_client * i2c = to_i2c_client ( dev ) ;
2013-11-26 17:50:27 +04:00
struct sec_pmic_dev * sec_pmic = i2c_get_clientdata ( i2c ) ;
2014-04-22 18:00:25 +04:00
if ( device_may_wakeup ( dev ) )
2013-11-26 17:50:27 +04:00
enable_irq_wake ( sec_pmic - > irq ) ;
2014-04-22 18:00:25 +04:00
/*
* PMIC IRQ must be disabled during suspend for RTC alarm
* to work properly .
* When device is woken up from suspend , an
* interrupt occurs before resuming I2C bus controller .
* The interrupt is handled by regmap_irq_thread which tries
* to read RTC registers . This read fails ( I2C is still
* suspended ) and RTC Alarm interrupt is disabled .
*/
disable_irq ( sec_pmic - > irq ) ;
2013-11-26 17:50:27 +04:00
return 0 ;
}
static int sec_pmic_resume ( struct device * dev )
{
2015-12-28 18:00:14 +03:00
struct i2c_client * i2c = to_i2c_client ( dev ) ;
2013-11-26 17:50:27 +04:00
struct sec_pmic_dev * sec_pmic = i2c_get_clientdata ( i2c ) ;
2014-04-22 18:00:25 +04:00
if ( device_may_wakeup ( dev ) )
2013-11-26 17:50:27 +04:00
disable_irq_wake ( sec_pmic - > irq ) ;
2014-04-22 18:00:25 +04:00
enable_irq ( sec_pmic - > irq ) ;
2013-11-26 17:50:27 +04:00
return 0 ;
}
2014-01-26 14:38:43 +04:00
# endif /* CONFIG_PM_SLEEP */
2013-11-26 17:50:27 +04:00
static SIMPLE_DEV_PM_OPS ( sec_pmic_pm_ops , sec_pmic_suspend , sec_pmic_resume ) ;
2012-07-11 16:06:55 +04:00
static const struct i2c_device_id sec_pmic_id [ ] = {
{ " sec_pmic " , 0 } ,
2011-12-23 12:28:08 +04:00
{ }
} ;
2012-07-11 16:06:55 +04:00
MODULE_DEVICE_TABLE ( i2c , sec_pmic_id ) ;
2011-12-23 12:28:08 +04:00
2012-07-11 16:06:55 +04:00
static struct i2c_driver sec_pmic_driver = {
2011-12-23 12:28:08 +04:00
. driver = {
2012-07-11 16:06:55 +04:00
. name = " sec_pmic " ,
2013-11-26 17:50:27 +04:00
. pm = & sec_pmic_pm_ops ,
2013-02-04 03:49:47 +04:00
. of_match_table = of_match_ptr ( sec_dt_match ) ,
2011-12-23 12:28:08 +04:00
} ,
2012-07-11 16:06:55 +04:00
. probe = sec_pmic_probe ,
2015-09-14 15:12:45 +03:00
. shutdown = sec_pmic_shutdown ,
2012-07-11 16:06:55 +04:00
. id_table = sec_pmic_id ,
2011-12-23 12:28:08 +04:00
} ;
2012-07-11 16:06:55 +04:00
static int __init sec_pmic_init ( void )
2011-12-23 12:28:08 +04:00
{
2012-07-11 16:06:55 +04:00
return i2c_add_driver ( & sec_pmic_driver ) ;
2011-12-23 12:28:08 +04:00
}
2012-07-11 16:06:55 +04:00
subsys_initcall ( sec_pmic_init ) ;
2011-12-23 12:28:08 +04:00
2012-07-11 16:06:55 +04:00
static void __exit sec_pmic_exit ( void )
2011-12-23 12:28:08 +04:00
{
2012-07-11 16:06:55 +04:00
i2c_del_driver ( & sec_pmic_driver ) ;
2011-12-23 12:28:08 +04:00
}
2012-07-11 16:06:55 +04:00
module_exit ( sec_pmic_exit ) ;
2011-12-23 12:28:08 +04:00
MODULE_AUTHOR ( " Sangbeom Kim <sbkim73@samsung.com> " ) ;
MODULE_DESCRIPTION ( " Core support for the S5M MFD " ) ;
MODULE_LICENSE ( " GPL " ) ;