2019-05-03 08:36:37 +03:00
// SPDX-License-Identifier: GPL-2.0+
//
// Regulator driver for DA9055 PMIC
//
// Copyright(c) 2012 Dialog Semiconductor Ltd.
//
// Author: David Dajun Chen <dchen@diasemi.com>
2012-11-01 12:27:56 +04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/err.h>
# include <linux/gpio.h>
2018-02-12 16:17:00 +03:00
# include <linux/gpio/consumer.h>
2012-11-01 12:27:56 +04:00
# include <linux/platform_device.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
2014-02-06 22:03:13 +04:00
# include <linux/of.h>
# include <linux/regulator/of_regulator.h>
2012-11-01 12:27:56 +04:00
# include <linux/mfd/da9055/core.h>
# include <linux/mfd/da9055/reg.h>
# include <linux/mfd/da9055/pdata.h>
# define DA9055_MIN_UA 0
# define DA9055_MAX_UA 3
# define DA9055_LDO_MODE_SYNC 0
# define DA9055_LDO_MODE_SLEEP 1
# define DA9055_BUCK_MODE_SLEEP 1
# define DA9055_BUCK_MODE_SYNC 2
# define DA9055_BUCK_MODE_AUTO 3
/* DA9055 REGULATOR IDs */
# define DA9055_ID_BUCK1 0
# define DA9055_ID_BUCK2 1
# define DA9055_ID_LDO1 2
# define DA9055_ID_LDO2 3
# define DA9055_ID_LDO3 4
# define DA9055_ID_LDO4 5
# define DA9055_ID_LDO5 6
# define DA9055_ID_LDO6 7
/* DA9055 BUCK current limit */
2019-02-28 16:40:14 +03:00
static const unsigned int da9055_current_limits [ ] = {
500000 , 600000 , 700000 , 800000
} ;
2012-11-01 12:27:56 +04:00
struct da9055_conf_reg {
int reg ;
int sel_mask ;
int en_mask ;
} ;
struct da9055_volt_reg {
int reg_a ;
int reg_b ;
int sl_shift ;
int v_mask ;
} ;
struct da9055_mode_reg {
int reg ;
int mask ;
int shift ;
} ;
struct da9055_regulator_info {
struct regulator_desc reg_desc ;
struct da9055_conf_reg conf ;
struct da9055_volt_reg volt ;
struct da9055_mode_reg mode ;
} ;
struct da9055_regulator {
struct da9055 * da9055 ;
struct da9055_regulator_info * info ;
struct regulator_dev * rdev ;
enum gpio_select reg_rselect ;
} ;
static unsigned int da9055_buck_get_mode ( struct regulator_dev * rdev )
{
struct da9055_regulator * regulator = rdev_get_drvdata ( rdev ) ;
struct da9055_regulator_info * info = regulator - > info ;
int ret , mode = 0 ;
ret = da9055_reg_read ( regulator - > da9055 , info - > mode . reg ) ;
if ( ret < 0 )
return ret ;
switch ( ( ret & info - > mode . mask ) > > info - > mode . shift ) {
case DA9055_BUCK_MODE_SYNC :
mode = REGULATOR_MODE_FAST ;
break ;
case DA9055_BUCK_MODE_AUTO :
mode = REGULATOR_MODE_NORMAL ;
break ;
case DA9055_BUCK_MODE_SLEEP :
mode = REGULATOR_MODE_STANDBY ;
break ;
}
return mode ;
}
static int da9055_buck_set_mode ( struct regulator_dev * rdev ,
unsigned int mode )
{
struct da9055_regulator * regulator = rdev_get_drvdata ( rdev ) ;
struct da9055_regulator_info * info = regulator - > info ;
int val = 0 ;
switch ( mode ) {
case REGULATOR_MODE_FAST :
val = DA9055_BUCK_MODE_SYNC < < info - > mode . shift ;
break ;
case REGULATOR_MODE_NORMAL :
val = DA9055_BUCK_MODE_AUTO < < info - > mode . shift ;
break ;
case REGULATOR_MODE_STANDBY :
val = DA9055_BUCK_MODE_SLEEP < < info - > mode . shift ;
break ;
}
return da9055_reg_update ( regulator - > da9055 , info - > mode . reg ,
info - > mode . mask , val ) ;
}
static unsigned int da9055_ldo_get_mode ( struct regulator_dev * rdev )
{
struct da9055_regulator * regulator = rdev_get_drvdata ( rdev ) ;
struct da9055_regulator_info * info = regulator - > info ;
int ret ;
ret = da9055_reg_read ( regulator - > da9055 , info - > volt . reg_b ) ;
if ( ret < 0 )
return ret ;
if ( ret > > info - > volt . sl_shift )
return REGULATOR_MODE_STANDBY ;
else
return REGULATOR_MODE_NORMAL ;
}
static int da9055_ldo_set_mode ( struct regulator_dev * rdev , unsigned int mode )
{
struct da9055_regulator * regulator = rdev_get_drvdata ( rdev ) ;
struct da9055_regulator_info * info = regulator - > info ;
struct da9055_volt_reg volt = info - > volt ;
int val = 0 ;
switch ( mode ) {
case REGULATOR_MODE_NORMAL :
case REGULATOR_MODE_FAST :
val = DA9055_LDO_MODE_SYNC ;
break ;
case REGULATOR_MODE_STANDBY :
val = DA9055_LDO_MODE_SLEEP ;
break ;
}
return da9055_reg_update ( regulator - > da9055 , volt . reg_b ,
1 < < volt . sl_shift ,
val < < volt . sl_shift ) ;
}
static int da9055_regulator_get_voltage_sel ( struct regulator_dev * rdev )
{
struct da9055_regulator * regulator = rdev_get_drvdata ( rdev ) ;
struct da9055_regulator_info * info = regulator - > info ;
struct da9055_volt_reg volt = info - > volt ;
int ret , sel ;
/*
* There are two voltage register set A & B for voltage ramping but
* either one of then can be active therefore we first determine
* the active register set .
*/
ret = da9055_reg_read ( regulator - > da9055 , info - > conf . reg ) ;
if ( ret < 0 )
return ret ;
ret & = info - > conf . sel_mask ;
/* Get the voltage for the active register set A/B */
if ( ret = = DA9055_REGUALTOR_SET_A )
ret = da9055_reg_read ( regulator - > da9055 , volt . reg_a ) ;
else
ret = da9055_reg_read ( regulator - > da9055 , volt . reg_b ) ;
if ( ret < 0 )
return ret ;
sel = ( ret & volt . v_mask ) ;
2012-11-27 06:26:14 +04:00
return sel ;
2012-11-01 12:27:56 +04:00
}
static int da9055_regulator_set_voltage_sel ( struct regulator_dev * rdev ,
unsigned int selector )
{
struct da9055_regulator * regulator = rdev_get_drvdata ( rdev ) ;
struct da9055_regulator_info * info = regulator - > info ;
int ret ;
/*
* Regulator register set A / B is not selected through GPIO therefore
* we use default register set A for voltage ramping .
*/
if ( regulator - > reg_rselect = = NO_GPIO ) {
/* Select register set A */
ret = da9055_reg_update ( regulator - > da9055 , info - > conf . reg ,
info - > conf . sel_mask , DA9055_SEL_REG_A ) ;
if ( ret < 0 )
return ret ;
/* Set the voltage */
2012-11-20 11:36:46 +04:00
return da9055_reg_update ( regulator - > da9055 , info - > volt . reg_a ,
info - > volt . v_mask , selector ) ;
2012-11-01 12:27:56 +04:00
}
/*
* Here regulator register set A / B is selected through GPIO .
* Therefore we first determine the selected register set A / B and
* then set the desired voltage for that register set A / B .
*/
ret = da9055_reg_read ( regulator - > da9055 , info - > conf . reg ) ;
if ( ret < 0 )
return ret ;
ret & = info - > conf . sel_mask ;
/* Set the voltage */
if ( ret = = DA9055_REGUALTOR_SET_A )
2012-11-20 11:36:46 +04:00
return da9055_reg_update ( regulator - > da9055 , info - > volt . reg_a ,
info - > volt . v_mask , selector ) ;
2012-11-01 12:27:56 +04:00
else
2012-11-20 11:36:46 +04:00
return da9055_reg_update ( regulator - > da9055 , info - > volt . reg_b ,
info - > volt . v_mask , selector ) ;
2012-11-01 12:27:56 +04:00
}
static int da9055_regulator_set_suspend_voltage ( struct regulator_dev * rdev ,
int uV )
{
struct da9055_regulator * regulator = rdev_get_drvdata ( rdev ) ;
struct da9055_regulator_info * info = regulator - > info ;
int ret ;
/* Select register set B for suspend voltage ramping. */
if ( regulator - > reg_rselect = = NO_GPIO ) {
ret = da9055_reg_update ( regulator - > da9055 , info - > conf . reg ,
info - > conf . sel_mask , DA9055_SEL_REG_B ) ;
if ( ret < 0 )
return ret ;
}
2012-11-27 06:26:14 +04:00
ret = regulator_map_voltage_linear ( rdev , uV , uV ) ;
2012-11-01 12:27:56 +04:00
if ( ret < 0 )
return ret ;
2012-11-20 11:36:46 +04:00
return da9055_reg_update ( regulator - > da9055 , info - > volt . reg_b ,
info - > volt . v_mask , ret ) ;
2012-11-01 12:27:56 +04:00
}
static int da9055_suspend_enable ( struct regulator_dev * rdev )
{
struct da9055_regulator * regulator = rdev_get_drvdata ( rdev ) ;
struct da9055_regulator_info * info = regulator - > info ;
/* Select register set B for voltage ramping. */
if ( regulator - > reg_rselect = = NO_GPIO )
return da9055_reg_update ( regulator - > da9055 , info - > conf . reg ,
info - > conf . sel_mask , DA9055_SEL_REG_B ) ;
else
return 0 ;
}
static int da9055_suspend_disable ( struct regulator_dev * rdev )
{
struct da9055_regulator * regulator = rdev_get_drvdata ( rdev ) ;
struct da9055_regulator_info * info = regulator - > info ;
/* Diselect register set B. */
if ( regulator - > reg_rselect = = NO_GPIO )
return da9055_reg_update ( regulator - > da9055 , info - > conf . reg ,
info - > conf . sel_mask , DA9055_SEL_REG_A ) ;
else
return 0 ;
}
2015-12-19 18:07:09 +03:00
static const struct regulator_ops da9055_buck_ops = {
2012-11-01 12:27:56 +04:00
. get_mode = da9055_buck_get_mode ,
. set_mode = da9055_buck_set_mode ,
2019-02-28 16:40:14 +03:00
. get_current_limit = regulator_get_current_limit_regmap ,
. set_current_limit = regulator_set_current_limit_regmap ,
2012-11-01 12:27:56 +04:00
. get_voltage_sel = da9055_regulator_get_voltage_sel ,
. set_voltage_sel = da9055_regulator_set_voltage_sel ,
2012-11-27 06:26:14 +04:00
. list_voltage = regulator_list_voltage_linear ,
. map_voltage = regulator_map_voltage_linear ,
2012-11-01 12:27:56 +04:00
. is_enabled = regulator_is_enabled_regmap ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. set_suspend_voltage = da9055_regulator_set_suspend_voltage ,
. set_suspend_enable = da9055_suspend_enable ,
. set_suspend_disable = da9055_suspend_disable ,
. set_suspend_mode = da9055_buck_set_mode ,
} ;
2015-12-19 18:07:09 +03:00
static const struct regulator_ops da9055_ldo_ops = {
2012-11-01 12:27:56 +04:00
. get_mode = da9055_ldo_get_mode ,
. set_mode = da9055_ldo_set_mode ,
. get_voltage_sel = da9055_regulator_get_voltage_sel ,
. set_voltage_sel = da9055_regulator_set_voltage_sel ,
2012-11-27 06:26:14 +04:00
. list_voltage = regulator_list_voltage_linear ,
. map_voltage = regulator_map_voltage_linear ,
2012-11-01 12:27:56 +04:00
. is_enabled = regulator_is_enabled_regmap ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. set_suspend_voltage = da9055_regulator_set_suspend_voltage ,
. set_suspend_enable = da9055_suspend_enable ,
. set_suspend_disable = da9055_suspend_disable ,
. set_suspend_mode = da9055_ldo_set_mode ,
} ;
# define DA9055_LDO(_id, step, min, max, vbits, voffset) \
{ \
. reg_desc = { \
. name = # _id , \
2019-03-12 18:47:59 +03:00
. of_match = of_match_ptr ( # _id ) , \
. regulators_node = of_match_ptr ( " regulators " ) , \
2012-11-01 12:27:56 +04:00
. ops = & da9055_ldo_ops , \
. type = REGULATOR_VOLTAGE , \
. id = DA9055_ID_ # # _id , \
2012-11-20 11:36:46 +04:00
. n_voltages = ( max - min ) / step + 1 + ( voffset ) , \
2012-11-01 12:27:56 +04:00
. enable_reg = DA9055_REG_BCORE_CONT + DA9055_ID_ # # _id , \
. enable_mask = 1 , \
. min_uV = ( min ) * 1000 , \
. uV_step = ( step ) * 1000 , \
2012-11-27 06:26:14 +04:00
. linear_min_sel = ( voffset ) , \
2012-11-01 12:27:56 +04:00
. owner = THIS_MODULE , \
} , \
. conf = { \
. reg = DA9055_REG_BCORE_CONT + DA9055_ID_ # # _id , \
. sel_mask = ( 1 < < 4 ) , \
. en_mask = 1 , \
} , \
. volt = { \
. reg_a = DA9055_REG_VBCORE_A + DA9055_ID_ # # _id , \
. reg_b = DA9055_REG_VBCORE_B + DA9055_ID_ # # _id , \
. sl_shift = 7 , \
. v_mask = ( 1 < < ( vbits ) ) - 1 , \
} , \
}
# define DA9055_BUCK(_id, step, min, max, vbits, voffset, mbits, sbits) \
{ \
. reg_desc = { \
. name = # _id , \
2019-03-12 18:47:59 +03:00
. of_match = of_match_ptr ( # _id ) , \
. regulators_node = of_match_ptr ( " regulators " ) , \
2012-11-01 12:27:56 +04:00
. ops = & da9055_buck_ops , \
. type = REGULATOR_VOLTAGE , \
. id = DA9055_ID_ # # _id , \
2012-11-20 11:36:46 +04:00
. n_voltages = ( max - min ) / step + 1 + ( voffset ) , \
2012-11-01 12:27:56 +04:00
. enable_reg = DA9055_REG_BCORE_CONT + DA9055_ID_ # # _id , \
. enable_mask = 1 , \
. min_uV = ( min ) * 1000 , \
. uV_step = ( step ) * 1000 , \
2012-11-27 06:26:14 +04:00
. linear_min_sel = ( voffset ) , \
2012-11-01 12:27:56 +04:00
. owner = THIS_MODULE , \
2019-02-28 16:40:14 +03:00
. curr_table = da9055_current_limits , \
. n_current_limits = ARRAY_SIZE ( da9055_current_limits ) , \
. csel_reg = DA9055_REG_BUCK_LIM , \
. csel_mask = ( mbits ) , \
2012-11-01 12:27:56 +04:00
} , \
. conf = { \
. reg = DA9055_REG_BCORE_CONT + DA9055_ID_ # # _id , \
. sel_mask = ( 1 < < 4 ) , \
. en_mask = 1 , \
} , \
. volt = { \
. reg_a = DA9055_REG_VBCORE_A + DA9055_ID_ # # _id , \
. reg_b = DA9055_REG_VBCORE_B + DA9055_ID_ # # _id , \
. sl_shift = 7 , \
. v_mask = ( 1 < < ( vbits ) ) - 1 , \
} , \
. mode = { \
. reg = DA9055_REG_BCORE_MODE , \
. mask = ( mbits ) , \
. shift = ( sbits ) , \
} , \
}
static struct da9055_regulator_info da9055_regulator_info [ ] = {
DA9055_BUCK ( BUCK1 , 25 , 725 , 2075 , 6 , 9 , 0xc , 2 ) ,
DA9055_BUCK ( BUCK2 , 25 , 925 , 2500 , 6 , 0 , 3 , 0 ) ,
DA9055_LDO ( LDO1 , 50 , 900 , 3300 , 6 , 2 ) ,
DA9055_LDO ( LDO2 , 50 , 900 , 3300 , 6 , 3 ) ,
DA9055_LDO ( LDO3 , 50 , 900 , 3300 , 6 , 2 ) ,
DA9055_LDO ( LDO4 , 50 , 900 , 3300 , 6 , 2 ) ,
DA9055_LDO ( LDO5 , 50 , 900 , 2750 , 6 , 2 ) ,
DA9055_LDO ( LDO6 , 20 , 900 , 3300 , 7 , 0 ) ,
} ;
/*
* Configures regulator to be controlled either through GPIO 1 or 2.
* GPIO can control regulator state and / or select the regulator register
* set A / B for voltage ramping .
*/
2012-12-22 01:26:06 +04:00
static int da9055_gpio_init ( struct da9055_regulator * regulator ,
struct regulator_config * config ,
struct da9055_pdata * pdata , int id )
2012-11-01 12:27:56 +04:00
{
struct da9055_regulator_info * info = regulator - > info ;
int ret = 0 ;
2014-02-06 22:03:13 +04:00
if ( ! pdata )
return 0 ;
2012-11-01 12:27:56 +04:00
if ( pdata - > gpio_ren & & pdata - > gpio_ren [ id ] ) {
char name [ 18 ] ;
int gpio_mux = pdata - > gpio_ren [ id ] ;
2018-02-12 16:17:00 +03:00
config - > ena_gpiod = pdata - > ena_gpiods [ id ] ;
2012-11-01 12:27:56 +04:00
/*
* GPI pin is muxed with regulator to control the
* regulator state .
*/
sprintf ( name , " DA9055 GPI %d " , gpio_mux ) ;
ret = devm_gpio_request_one ( config - > dev , gpio_mux , GPIOF_DIR_IN ,
name ) ;
if ( ret < 0 )
goto err ;
/*
* Let the regulator know that its state is controlled
* through GPI .
*/
ret = da9055_reg_update ( regulator - > da9055 , info - > conf . reg ,
DA9055_E_GPI_MASK ,
pdata - > reg_ren [ id ]
< < DA9055_E_GPI_SHIFT ) ;
if ( ret < 0 )
goto err ;
}
2012-11-15 20:35:14 +04:00
if ( pdata - > gpio_rsel & & pdata - > gpio_rsel [ id ] ) {
2012-11-01 12:27:56 +04:00
char name [ 18 ] ;
int gpio_mux = pdata - > gpio_rsel [ id ] ;
regulator - > reg_rselect = pdata - > reg_rsel [ id ] ;
/*
* GPI pin is muxed with regulator to select the
* regulator register set A / B for voltage ramping .
*/
sprintf ( name , " DA9055 GPI %d " , gpio_mux ) ;
ret = devm_gpio_request_one ( config - > dev , gpio_mux , GPIOF_DIR_IN ,
name ) ;
if ( ret < 0 )
goto err ;
/*
* Let the regulator know that its register set A / B
* will be selected through GPI for voltage ramping .
*/
ret = da9055_reg_update ( regulator - > da9055 , info - > conf . reg ,
DA9055_V_GPI_MASK ,
pdata - > reg_rsel [ id ]
< < DA9055_V_GPI_SHIFT ) ;
}
err :
return ret ;
}
static irqreturn_t da9055_ldo5_6_oc_irq ( int irq , void * data )
{
struct da9055_regulator * regulator = data ;
2019-02-26 17:59:59 +03:00
regulator_lock ( regulator - > rdev ) ;
2012-11-01 12:27:56 +04:00
regulator_notifier_call_chain ( regulator - > rdev ,
REGULATOR_EVENT_OVER_CURRENT , NULL ) ;
2019-02-26 17:59:59 +03:00
regulator_unlock ( regulator - > rdev ) ;
2012-11-01 12:27:56 +04:00
return IRQ_HANDLED ;
}
static inline struct da9055_regulator_info * find_regulator_info ( int id )
{
struct da9055_regulator_info * info ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( da9055_regulator_info ) ; i + + ) {
info = & da9055_regulator_info [ i ] ;
if ( info - > reg_desc . id = = id )
return info ;
}
return NULL ;
}
2012-12-22 01:26:06 +04:00
static int da9055_regulator_probe ( struct platform_device * pdev )
2012-11-01 12:27:56 +04:00
{
struct regulator_config config = { } ;
struct da9055_regulator * regulator ;
struct da9055 * da9055 = dev_get_drvdata ( pdev - > dev . parent ) ;
2013-07-30 12:20:47 +04:00
struct da9055_pdata * pdata = dev_get_platdata ( da9055 - > dev ) ;
2012-11-01 12:27:56 +04:00
int ret , irq ;
regulator = devm_kzalloc ( & pdev - > dev , sizeof ( struct da9055_regulator ) ,
GFP_KERNEL ) ;
if ( ! regulator )
return - ENOMEM ;
regulator - > info = find_regulator_info ( pdev - > id ) ;
if ( regulator - > info = = NULL ) {
dev_err ( & pdev - > dev , " invalid regulator ID specified \n " ) ;
return - EINVAL ;
}
regulator - > da9055 = da9055 ;
2019-03-12 18:47:59 +03:00
config . dev = da9055 - > dev ;
2012-11-01 12:27:56 +04:00
config . driver_data = regulator ;
config . regmap = da9055 - > regmap ;
2019-03-12 18:47:59 +03:00
if ( pdata )
2012-11-01 12:27:56 +04:00
config . init_data = pdata - > regulators [ pdev - > id ] ;
ret = da9055_gpio_init ( regulator , & config , pdata , pdev - > id ) ;
if ( ret < 0 )
return ret ;
2013-09-03 10:26:10 +04:00
regulator - > rdev = devm_regulator_register ( & pdev - > dev ,
& regulator - > info - > reg_desc ,
& config ) ;
2012-11-01 12:27:56 +04:00
if ( IS_ERR ( regulator - > rdev ) ) {
dev_err ( & pdev - > dev , " Failed to register regulator %s \n " ,
regulator - > info - > reg_desc . name ) ;
2013-09-03 10:26:10 +04:00
return PTR_ERR ( regulator - > rdev ) ;
2012-11-01 12:27:56 +04:00
}
/* Only LDO 5 and 6 has got the over current interrupt */
if ( pdev - > id = = DA9055_ID_LDO5 | | pdev - > id = = DA9055_ID_LDO6 ) {
irq = platform_get_irq_byname ( pdev , " REGULATOR " ) ;
2014-02-06 22:03:21 +04:00
if ( irq < 0 )
return irq ;
2012-11-01 12:27:56 +04:00
ret = devm_request_threaded_irq ( & pdev - > dev , irq , NULL ,
da9055_ldo5_6_oc_irq ,
IRQF_TRIGGER_HIGH |
IRQF_ONESHOT |
IRQF_PROBE_SHARED ,
pdev - > name , regulator ) ;
if ( ret ! = 0 ) {
if ( ret ! = - EBUSY ) {
dev_err ( & pdev - > dev ,
" Failed to request Regulator IRQ %d: %d \n " ,
irq , ret ) ;
2013-09-03 10:26:10 +04:00
return ret ;
2012-11-01 12:27:56 +04:00
}
}
}
platform_set_drvdata ( pdev , regulator ) ;
return 0 ;
}
static struct platform_driver da9055_regulator_driver = {
. probe = da9055_regulator_probe ,
. driver = {
. name = " da9055-regulator " ,
} ,
} ;
static int __init da9055_regulator_init ( void )
{
return platform_driver_register ( & da9055_regulator_driver ) ;
}
subsys_initcall ( da9055_regulator_init ) ;
static void __exit da9055_regulator_exit ( void )
{
platform_driver_unregister ( & da9055_regulator_driver ) ;
}
module_exit ( da9055_regulator_exit ) ;
MODULE_AUTHOR ( " David Dajun Chen <dchen@diasemi.com> " ) ;
MODULE_DESCRIPTION ( " Power Regulator driver for Dialog DA9055 PMIC " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:da9055-regulator " ) ;