2019-05-28 19:57:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-03-09 15:02:38 +03:00
/*
* Core driver for TPS61050 / 61052 boost converters , used for while LED
* driving , audio power amplification , white LED flash , and generic
* boost conversion . Additionally it provides a 1 - bit GPIO pin ( out or in )
* and a flash synchronization pin to synchronize flash events when used as
* flashgun .
*
* Copyright ( C ) 2011 ST - Ericsson SA
* Written on behalf of Linaro for ST - Ericsson
*
* Author : Linus Walleij < linus . walleij @ linaro . org >
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/i2c.h>
2015-10-02 19:14:41 +03:00
# include <linux/regmap.h>
2011-03-09 15:02:38 +03:00
# include <linux/gpio.h>
# include <linux/spinlock.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/mfd/core.h>
# include <linux/mfd/tps6105x.h>
2015-10-02 19:14:41 +03:00
static struct regmap_config tps6105x_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = TPS6105X_REG_3 ,
} ;
2011-03-09 15:02:38 +03:00
2012-11-19 22:23:04 +04:00
static int tps6105x_startup ( struct tps6105x * tps6105x )
2011-03-09 15:02:38 +03:00
{
int ret ;
2015-10-02 19:14:41 +03:00
unsigned int regval ;
2011-03-09 15:02:38 +03:00
2015-10-02 19:14:41 +03:00
ret = regmap_read ( tps6105x - > regmap , TPS6105X_REG_0 , & regval ) ;
2011-03-09 15:02:38 +03:00
if ( ret )
return ret ;
switch ( regval > > TPS6105X_REG0_MODE_SHIFT ) {
case TPS6105X_REG0_MODE_SHUTDOWN :
dev_info ( & tps6105x - > client - > dev ,
" TPS6105x found in SHUTDOWN mode \n " ) ;
break ;
case TPS6105X_REG0_MODE_TORCH :
dev_info ( & tps6105x - > client - > dev ,
" TPS6105x found in TORCH mode \n " ) ;
break ;
case TPS6105X_REG0_MODE_TORCH_FLASH :
dev_info ( & tps6105x - > client - > dev ,
" TPS6105x found in FLASH mode \n " ) ;
break ;
case TPS6105X_REG0_MODE_VOLTAGE :
dev_info ( & tps6105x - > client - > dev ,
" TPS6105x found in VOLTAGE mode \n " ) ;
break ;
default :
break ;
}
return ret ;
}
/*
2015-09-25 19:57:09 +03:00
* MFD cells - we always have a GPIO cell and we have one cell
* which is selected operation mode .
2011-03-09 15:02:38 +03:00
*/
2015-09-25 19:57:09 +03:00
static struct mfd_cell tps6105x_gpio_cell = {
. name = " tps6105x-gpio " ,
} ;
static struct mfd_cell tps6105x_leds_cell = {
. name = " tps6105x-leds " ,
} ;
static struct mfd_cell tps6105x_flash_cell = {
. name = " tps6105x-flash " ,
2011-03-09 15:02:38 +03:00
} ;
2015-09-25 19:57:09 +03:00
static struct mfd_cell tps6105x_regulator_cell = {
. name = " tps6105x-regulator " ,
} ;
static int tps6105x_add_device ( struct tps6105x * tps6105x ,
struct mfd_cell * cell )
{
cell - > platform_data = tps6105x ;
cell - > pdata_size = sizeof ( * tps6105x ) ;
return mfd_add_devices ( & tps6105x - > client - > dev ,
PLATFORM_DEVID_AUTO , cell , 1 , NULL , 0 , NULL ) ;
}
2019-11-19 18:46:08 +03:00
static struct tps6105x_platform_data * tps6105x_parse_dt ( struct device * dev )
{
struct device_node * np = dev - > of_node ;
struct tps6105x_platform_data * pdata ;
struct device_node * child ;
if ( ! np )
return ERR_PTR ( - EINVAL ) ;
if ( of_get_available_child_count ( np ) > 1 ) {
dev_err ( dev , " cannot support multiple operational modes " ) ;
return ERR_PTR ( - EINVAL ) ;
}
pdata = devm_kzalloc ( dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return ERR_PTR ( - ENOMEM ) ;
pdata - > mode = TPS6105X_MODE_SHUTDOWN ;
for_each_available_child_of_node ( np , child ) {
if ( child - > name & & ! of_node_cmp ( child - > name , " regulator " ) )
pdata - > mode = TPS6105X_MODE_VOLTAGE ;
else if ( child - > name & & ! of_node_cmp ( child - > name , " led " ) )
pdata - > mode = TPS6105X_MODE_TORCH ;
}
return pdata ;
}
2012-11-19 22:23:04 +04:00
static int tps6105x_probe ( struct i2c_client * client ,
2011-03-09 15:02:38 +03:00
const struct i2c_device_id * id )
{
struct tps6105x * tps6105x ;
struct tps6105x_platform_data * pdata ;
int ret ;
2015-09-25 19:57:09 +03:00
pdata = dev_get_platdata ( & client - > dev ) ;
2019-11-19 18:46:08 +03:00
if ( ! pdata )
pdata = tps6105x_parse_dt ( & client - > dev ) ;
if ( IS_ERR ( pdata ) ) {
dev_err ( & client - > dev , " No platform data or DT found " ) ;
return PTR_ERR ( pdata ) ;
2015-09-25 19:57:09 +03:00
}
2011-03-09 15:02:38 +03:00
2014-07-19 12:30:10 +04:00
tps6105x = devm_kmalloc ( & client - > dev , sizeof ( * tps6105x ) , GFP_KERNEL ) ;
2011-03-09 15:02:38 +03:00
if ( ! tps6105x )
return - ENOMEM ;
2015-10-02 19:14:41 +03:00
tps6105x - > regmap = devm_regmap_init_i2c ( client , & tps6105x_regmap_config ) ;
if ( IS_ERR ( tps6105x - > regmap ) )
return PTR_ERR ( tps6105x - > regmap ) ;
2011-03-09 15:02:38 +03:00
i2c_set_clientdata ( client , tps6105x ) ;
tps6105x - > client = client ;
tps6105x - > pdata = pdata ;
ret = tps6105x_startup ( tps6105x ) ;
if ( ret ) {
dev_err ( & client - > dev , " chip initialization failed \n " ) ;
2014-07-19 12:30:10 +04:00
return ret ;
2011-03-09 15:02:38 +03:00
}
2015-09-25 19:57:09 +03:00
ret = tps6105x_add_device ( tps6105x , & tps6105x_gpio_cell ) ;
if ( ret )
return ret ;
2011-03-09 15:02:38 +03:00
switch ( pdata - > mode ) {
case TPS6105X_MODE_SHUTDOWN :
dev_info ( & client - > dev ,
" present, not used for anything, only GPIO \n " ) ;
break ;
case TPS6105X_MODE_TORCH :
2015-09-25 19:57:09 +03:00
ret = tps6105x_add_device ( tps6105x , & tps6105x_leds_cell ) ;
2011-03-09 15:02:38 +03:00
break ;
case TPS6105X_MODE_TORCH_FLASH :
2015-09-25 19:57:09 +03:00
ret = tps6105x_add_device ( tps6105x , & tps6105x_flash_cell ) ;
2011-03-09 15:02:38 +03:00
break ;
case TPS6105X_MODE_VOLTAGE :
2015-09-25 19:57:09 +03:00
ret = tps6105x_add_device ( tps6105x , & tps6105x_regulator_cell ) ;
2011-03-09 15:02:38 +03:00
break ;
default :
2015-09-25 19:57:09 +03:00
dev_warn ( & client - > dev , " invalid mode: %d \n " , pdata - > mode ) ;
2011-03-09 15:02:38 +03:00
break ;
}
2015-09-25 19:57:09 +03:00
if ( ret )
mfd_remove_devices ( & client - > dev ) ;
2011-03-09 15:02:38 +03:00
2015-09-25 19:57:09 +03:00
return ret ;
2011-03-09 15:02:38 +03:00
}
2012-11-19 22:26:01 +04:00
static int tps6105x_remove ( struct i2c_client * client )
2011-03-09 15:02:38 +03:00
{
struct tps6105x * tps6105x = i2c_get_clientdata ( client ) ;
mfd_remove_devices ( & client - > dev ) ;
/* Put chip in shutdown mode */
2015-10-02 19:14:41 +03:00
regmap_update_bits ( tps6105x - > regmap , TPS6105X_REG_0 ,
2011-03-09 15:02:38 +03:00
TPS6105X_REG0_MODE_MASK ,
TPS6105X_MODE_SHUTDOWN < < TPS6105X_REG0_MODE_SHIFT ) ;
return 0 ;
}
static const struct i2c_device_id tps6105x_id [ ] = {
{ " tps61050 " , 0 } ,
{ " tps61052 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tps6105x_id ) ;
2017-06-15 21:49:27 +03:00
static const struct of_device_id tps6105x_of_match [ ] = {
{ . compatible = " ti,tps61050 " } ,
{ . compatible = " ti,tps61052 " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , tps6105x_of_match ) ;
2011-03-09 15:02:38 +03:00
static struct i2c_driver tps6105x_driver = {
. driver = {
. name = " tps6105x " ,
2017-06-15 21:49:27 +03:00
. of_match_table = tps6105x_of_match ,
2011-03-09 15:02:38 +03:00
} ,
. probe = tps6105x_probe ,
2012-11-19 22:20:24 +04:00
. remove = tps6105x_remove ,
2011-03-09 15:02:38 +03:00
. id_table = tps6105x_id ,
} ;
static int __init tps6105x_init ( void )
{
return i2c_add_driver ( & tps6105x_driver ) ;
}
subsys_initcall ( tps6105x_init ) ;
static void __exit tps6105x_exit ( void )
{
i2c_del_driver ( & tps6105x_driver ) ;
}
module_exit ( tps6105x_exit ) ;
MODULE_AUTHOR ( " Linus Walleij " ) ;
MODULE_DESCRIPTION ( " TPS6105x White LED Boost Converter Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;