2011-12-23 12:28:08 +04:00
/*
2012-07-11 16:06:55 +04:00
* sec - core . c
2011-12-23 12:28:08 +04:00
*
2012-07-11 16:06:55 +04:00
* Copyright ( c ) 2012 Samsung Electronics Co . , Ltd
2011-12-23 12:28:08 +04:00
* http : //www.samsung.com
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
*/
# 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-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>
# include <linux/mfd/samsung/rtc.h>
2013-07-02 02:02:19 +04:00
# include <linux/mfd/samsung/s2mps11.h>
# include <linux/mfd/samsung/s5m8763.h>
# include <linux/mfd/samsung/s5m8767.h>
2011-12-23 12:28:08 +04:00
# include <linux/regmap.h>
2012-01-20 11:09:12 +04:00
static struct mfd_cell s5m8751_devs [ ] = {
{
. name = " s5m8751-pmic " ,
} , {
. name = " s5m-charger " ,
} , {
. name = " s5m8751-codec " ,
} ,
} ;
static struct mfd_cell s5m8763_devs [ ] = {
{
. name = " s5m8763-pmic " ,
} , {
. name = " s5m-rtc " ,
} , {
. name = " s5m-charger " ,
} ,
} ;
static struct mfd_cell s5m8767_devs [ ] = {
2011-12-23 12:28:08 +04:00
{
. name = " s5m8767-pmic " ,
} , {
. name = " s5m-rtc " ,
} ,
} ;
2012-07-11 16:07:55 +04:00
static struct mfd_cell s2mps11_devs [ ] = {
{
. name = " s2mps11-pmic " ,
} ,
} ;
2013-02-04 03:49:47 +04:00
# ifdef CONFIG_OF
static struct of_device_id sec_dt_match [ ] = {
{ . compatible = " samsung,s5m8767-pmic " ,
. data = ( void * ) S5M8767X ,
} ,
2013-06-29 16:51:20 +04:00
{ . compatible = " samsung,s2mps11-pmic " ,
. data = ( void * ) S2MPS11X ,
} ,
2013-02-04 03:49:47 +04:00
{ } ,
} ;
# endif
2012-07-11 16:06:55 +04:00
int sec_reg_read ( struct sec_pmic_dev * sec_pmic , u8 reg , void * dest )
2011-12-23 12:28:08 +04:00
{
2012-07-11 16:06:55 +04:00
return regmap_read ( sec_pmic - > regmap , reg , dest ) ;
2011-12-23 12:28:08 +04:00
}
2012-07-11 16:06:55 +04:00
EXPORT_SYMBOL_GPL ( sec_reg_read ) ;
2011-12-23 12:28:08 +04:00
2012-07-11 16:06:55 +04:00
int sec_bulk_read ( struct sec_pmic_dev * sec_pmic , u8 reg , int count , u8 * buf )
2011-12-23 12:28:08 +04:00
{
2012-07-11 16:06:55 +04:00
return regmap_bulk_read ( sec_pmic - > regmap , reg , buf , count ) ;
2011-12-23 12:28:08 +04:00
}
2012-07-11 16:06:55 +04:00
EXPORT_SYMBOL_GPL ( sec_bulk_read ) ;
2011-12-23 12:28:08 +04:00
2012-07-11 16:06:55 +04:00
int sec_reg_write ( struct sec_pmic_dev * sec_pmic , u8 reg , u8 value )
2011-12-23 12:28:08 +04:00
{
2012-07-11 16:06:55 +04:00
return regmap_write ( sec_pmic - > regmap , reg , value ) ;
2011-12-23 12:28:08 +04:00
}
2012-07-11 16:06:55 +04:00
EXPORT_SYMBOL_GPL ( sec_reg_write ) ;
2011-12-23 12:28:08 +04:00
2012-07-11 16:06:55 +04:00
int sec_bulk_write ( struct sec_pmic_dev * sec_pmic , u8 reg , int count , u8 * buf )
2011-12-23 12:28:08 +04:00
{
2012-07-11 16:06:55 +04:00
return regmap_raw_write ( sec_pmic - > regmap , reg , buf , count ) ;
2011-12-23 12:28:08 +04:00
}
2012-07-11 16:06:55 +04:00
EXPORT_SYMBOL_GPL ( sec_bulk_write ) ;
2011-12-23 12:28:08 +04:00
2012-07-11 16:06:55 +04:00
int sec_reg_update ( struct sec_pmic_dev * sec_pmic , u8 reg , u8 val , u8 mask )
2011-12-23 12:28:08 +04:00
{
2012-07-11 16:06:55 +04:00
return regmap_update_bits ( sec_pmic - > regmap , reg , mask , val ) ;
2011-12-23 12:28:08 +04:00
}
2012-07-11 16:06:55 +04:00
EXPORT_SYMBOL_GPL ( sec_reg_update ) ;
2011-12-23 12:28:08 +04:00
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 ;
}
}
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 ;
}
}
2012-07-11 16:06:55 +04:00
static struct regmap_config sec_regmap_config = {
2011-12-23 12:28:08 +04:00
. reg_bits = 8 ,
. val_bits = 8 ,
} ;
2013-07-02 02:02:19 +04:00
static struct regmap_config s2mps11_regmap_config = {
. 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
} ;
static struct regmap_config s5m8763_regmap_config = {
. 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
} ;
static struct regmap_config s5m8767_regmap_config = {
. 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
# 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 .
*/
static struct sec_platform_data * sec_pmic_i2c_parse_dt_pdata (
struct device * dev )
{
struct sec_platform_data * pd ;
pd = devm_kzalloc ( dev , sizeof ( * pd ) , GFP_KERNEL ) ;
if ( ! pd ) {
dev_err ( dev , " could not allocate memory for pdata \n " ) ;
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 .
*/
return pd ;
}
# else
2013-02-04 22:31:25 +04:00
static struct sec_platform_data * sec_pmic_i2c_parse_dt_pdata (
2013-02-04 03:49:47 +04:00
struct device * dev )
{
return 0 ;
}
# endif
static inline int sec_i2c_get_driver_data ( struct i2c_client * i2c ,
const struct i2c_device_id * id )
{
# ifdef CONFIG_OF
if ( i2c - > dev . of_node ) {
const struct of_device_id * match ;
match = of_match_node ( sec_dt_match , i2c - > dev . of_node ) ;
return ( int ) match - > data ;
}
# endif
return ( int ) id - > driver_data ;
}
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 ) ;
2013-07-02 02:02:19 +04:00
const struct regmap_config * regmap ;
2012-07-11 16:06:55 +04:00
struct sec_pmic_dev * sec_pmic ;
2012-02-28 10:23:55 +04:00
int ret ;
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 ;
2013-02-04 03:49:47 +04:00
sec_pmic - > type = sec_i2c_get_driver_data ( i2c , id ) ;
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 ;
}
pdata - > device_type = sec_pmic - > type ;
}
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 - > ono = pdata - > ono ;
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 ) {
case S2MPS11X :
regmap = & s2mps11_regmap_config ;
break ;
case S5M8763X :
regmap = & s5m8763_regmap_config ;
break ;
case S5M8767X :
regmap = & s5m8767_regmap_config ;
break ;
default :
regmap = & sec_regmap_config ;
break ;
}
sec_pmic - > regmap = devm_regmap_init_i2c ( i2c , regmap ) ;
2012-07-11 16:06:55 +04:00
if ( IS_ERR ( sec_pmic - > regmap ) ) {
ret = PTR_ERR ( sec_pmic - > regmap ) ;
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-07-11 16:06:55 +04:00
sec_pmic - > rtc = i2c_new_dummy ( i2c - > adapter , RTC_I2C_ADDR ) ;
i2c_set_clientdata ( sec_pmic - > rtc , sec_pmic ) ;
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 :
2012-07-11 16:06:55 +04:00
ret = mfd_add_devices ( sec_pmic - > dev , - 1 , s5m8751_devs ,
2012-09-11 11:16:36 +04:00
ARRAY_SIZE ( s5m8751_devs ) , NULL , 0 , NULL ) ;
2012-01-20 11:09:12 +04:00
break ;
case S5M8763X :
2012-07-11 16:06:55 +04:00
ret = mfd_add_devices ( sec_pmic - > dev , - 1 , s5m8763_devs ,
2012-09-11 11:16:36 +04:00
ARRAY_SIZE ( s5m8763_devs ) , NULL , 0 , NULL ) ;
2012-01-20 11:09:12 +04:00
break ;
case S5M8767X :
2012-07-11 16:06:55 +04:00
ret = mfd_add_devices ( sec_pmic - > dev , - 1 , s5m8767_devs ,
2012-09-11 11:16:36 +04:00
ARRAY_SIZE ( s5m8767_devs ) , NULL , 0 , NULL ) ;
2012-01-20 11:09:12 +04:00
break ;
2012-07-11 16:07:55 +04:00
case S2MPS11X :
ret = mfd_add_devices ( sec_pmic - > dev , - 1 , s2mps11_devs ,
2012-09-11 11:16:36 +04:00
ARRAY_SIZE ( s2mps11_devs ) , NULL , 0 , NULL ) ;
2012-07-11 16:07:55 +04:00
break ;
2012-01-20 11:09:12 +04:00
default :
/* If this happens the probe function is problem */
BUG ( ) ;
}
2011-12-23 12:28:08 +04:00
2013-05-15 19:00:48 +04:00
if ( ret )
2011-12-23 12:28:08 +04:00
goto err ;
return ret ;
err :
2012-07-11 16:06:55 +04:00
sec_irq_exit ( sec_pmic ) ;
i2c_unregister_device ( sec_pmic - > rtc ) ;
2011-12-23 12:28:08 +04:00
return ret ;
}
2012-07-11 16:06:55 +04:00
static int sec_pmic_remove ( struct i2c_client * i2c )
2011-12-23 12:28:08 +04:00
{
2012-07-11 16:06:55 +04:00
struct sec_pmic_dev * sec_pmic = i2c_get_clientdata ( i2c ) ;
2011-12-23 12:28:08 +04:00
2012-07-11 16:06:55 +04:00
mfd_remove_devices ( sec_pmic - > dev ) ;
sec_irq_exit ( sec_pmic ) ;
i2c_unregister_device ( sec_pmic - > rtc ) ;
2011-12-23 12:28:08 +04:00
return 0 ;
}
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 " ,
2011-12-23 12:28:08 +04:00
. owner = THIS_MODULE ,
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 ,
. remove = sec_pmic_remove ,
. 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 " ) ;