2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-08-10 00:24:34 +04:00
/*
* max8907 - regulator . c - - support regulators in max8907
*
* Copyright ( C ) 2010 Gyungoh Yoo < jack . yoo @ maxim - ic . com >
* Copyright ( C ) 2010 - 2012 , NVIDIA CORPORATION . All rights reserved .
*
* Portions based on drivers / regulator / tps65910 - regulator . c ,
* Copyright 2010 Texas Instruments Inc .
* Author : Graeme Gregory < gg @ slimlogic . co . uk >
* Author : Jorge Eduardo Candelaria < jedu @ slimlogic . co . uk >
*/
# include <linux/err.h>
# include <linux/init.h>
# include <linux/mfd/core.h>
# include <linux/mfd/max8907.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
# include <linux/regulator/of_regulator.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# define MAX8907_II2RR_VERSION_MASK 0xF0
# define MAX8907_II2RR_VERSION_REV_A 0x00
# define MAX8907_II2RR_VERSION_REV_B 0x10
# define MAX8907_II2RR_VERSION_REV_C 0x30
struct max8907_regulator {
struct regulator_desc desc [ MAX8907_NUM_REGULATORS ] ;
} ;
# define REG_MBATT() \
[ MAX8907_MBATT ] = { \
. name = " MBATT " , \
. supply_name = " mbatt " , \
. id = MAX8907_MBATT , \
. ops = & max8907_mbatt_ops , \
. type = REGULATOR_VOLTAGE , \
. owner = THIS_MODULE , \
}
# define REG_LDO(ids, supply, base, min, max, step) \
[ MAX8907_ # # ids ] = { \
. name = # ids , \
. supply_name = supply , \
. id = MAX8907_ # # ids , \
. n_voltages = ( ( max ) - ( min ) ) / ( step ) + 1 , \
. ops = & max8907_ldo_ops , \
. type = REGULATOR_VOLTAGE , \
. owner = THIS_MODULE , \
. min_uV = ( min ) , \
. uV_step = ( step ) , \
. vsel_reg = ( base ) + MAX8907_VOUT , \
. vsel_mask = 0x3f , \
. enable_reg = ( base ) + MAX8907_CTL , \
. enable_mask = MAX8907_MASK_LDO_EN , \
}
# define REG_FIXED(ids, supply, voltage) \
[ MAX8907_ # # ids ] = { \
. name = # ids , \
. supply_name = supply , \
. id = MAX8907_ # # ids , \
. n_voltages = 1 , \
. ops = & max8907_fixed_ops , \
. type = REGULATOR_VOLTAGE , \
. owner = THIS_MODULE , \
. min_uV = ( voltage ) , \
}
# define REG_OUT5V(ids, supply, base, voltage) \
[ MAX8907_ # # ids ] = { \
. name = # ids , \
. supply_name = supply , \
. id = MAX8907_ # # ids , \
. n_voltages = 1 , \
. ops = & max8907_out5v_ops , \
. type = REGULATOR_VOLTAGE , \
. owner = THIS_MODULE , \
. min_uV = ( voltage ) , \
. enable_reg = ( base ) , \
. enable_mask = MAX8907_MASK_OUT5V_EN , \
}
# define REG_BBAT(ids, supply, base, min, max, step) \
[ MAX8907_ # # ids ] = { \
. name = # ids , \
. supply_name = supply , \
. id = MAX8907_ # # ids , \
. n_voltages = ( ( max ) - ( min ) ) / ( step ) + 1 , \
. ops = & max8907_bbat_ops , \
. type = REGULATOR_VOLTAGE , \
. owner = THIS_MODULE , \
. min_uV = ( min ) , \
. uV_step = ( step ) , \
. vsel_reg = ( base ) , \
. vsel_mask = MAX8907_MASK_VBBATTCV , \
}
# define LDO_750_50(id, supply, base) REG_LDO(id, supply, (base), \
750000 , 3900000 , 50000 )
# define LDO_650_25(id, supply, base) REG_LDO(id, supply, (base), \
650000 , 2225000 , 25000 )
2017-01-28 17:32:32 +03:00
static const struct regulator_ops max8907_mbatt_ops = {
2012-08-10 00:24:34 +04:00
} ;
2020-06-18 01:32:46 +03:00
static const struct regulator_ops max8907_ldo_ops = {
2012-08-10 00:24:34 +04:00
. list_voltage = regulator_list_voltage_linear ,
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. is_enabled = regulator_is_enabled_regmap ,
} ;
2017-01-28 17:32:32 +03:00
static const struct regulator_ops max8907_ldo_hwctl_ops = {
2012-08-10 00:24:34 +04:00
. list_voltage = regulator_list_voltage_linear ,
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
} ;
2017-01-28 17:32:32 +03:00
static const struct regulator_ops max8907_fixed_ops = {
2012-08-10 00:24:34 +04:00
. list_voltage = regulator_list_voltage_linear ,
} ;
2020-06-18 01:32:46 +03:00
static const struct regulator_ops max8907_out5v_ops = {
2012-08-10 00:24:34 +04:00
. list_voltage = regulator_list_voltage_linear ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. is_enabled = regulator_is_enabled_regmap ,
} ;
2017-01-28 17:32:32 +03:00
static const struct regulator_ops max8907_out5v_hwctl_ops = {
2012-08-10 00:24:34 +04:00
. list_voltage = regulator_list_voltage_linear ,
} ;
2017-01-28 17:32:32 +03:00
static const struct regulator_ops max8907_bbat_ops = {
2012-08-10 00:24:34 +04:00
. list_voltage = regulator_list_voltage_linear ,
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
} ;
2020-06-18 01:32:46 +03:00
static const struct regulator_desc max8907_regulators [ ] = {
2012-08-10 00:24:34 +04:00
REG_MBATT ( ) ,
REG_LDO ( SD1 , " in-v1 " , MAX8907_REG_SDCTL1 , 650000 , 2225000 , 25000 ) ,
REG_LDO ( SD2 , " in-v2 " , MAX8907_REG_SDCTL2 , 637500 , 1425000 , 12500 ) ,
REG_LDO ( SD3 , " in-v3 " , MAX8907_REG_SDCTL3 , 750000 , 3900000 , 50000 ) ,
LDO_750_50 ( LDO1 , " in1 " , MAX8907_REG_LDOCTL1 ) ,
LDO_650_25 ( LDO2 , " in2 " , MAX8907_REG_LDOCTL2 ) ,
LDO_650_25 ( LDO3 , " in3 " , MAX8907_REG_LDOCTL3 ) ,
LDO_750_50 ( LDO4 , " in4 " , MAX8907_REG_LDOCTL4 ) ,
LDO_750_50 ( LDO5 , " in5 " , MAX8907_REG_LDOCTL5 ) ,
LDO_750_50 ( LDO6 , " in6 " , MAX8907_REG_LDOCTL6 ) ,
LDO_750_50 ( LDO7 , " in7 " , MAX8907_REG_LDOCTL7 ) ,
LDO_750_50 ( LDO8 , " in8 " , MAX8907_REG_LDOCTL8 ) ,
LDO_750_50 ( LDO9 , " in9 " , MAX8907_REG_LDOCTL9 ) ,
LDO_750_50 ( LDO10 , " in10 " , MAX8907_REG_LDOCTL10 ) ,
LDO_750_50 ( LDO11 , " in11 " , MAX8907_REG_LDOCTL11 ) ,
LDO_750_50 ( LDO12 , " in12 " , MAX8907_REG_LDOCTL12 ) ,
LDO_750_50 ( LDO13 , " in13 " , MAX8907_REG_LDOCTL13 ) ,
LDO_750_50 ( LDO14 , " in14 " , MAX8907_REG_LDOCTL14 ) ,
LDO_750_50 ( LDO15 , " in15 " , MAX8907_REG_LDOCTL15 ) ,
LDO_750_50 ( LDO16 , " in16 " , MAX8907_REG_LDOCTL16 ) ,
LDO_650_25 ( LDO17 , " in17 " , MAX8907_REG_LDOCTL17 ) ,
LDO_650_25 ( LDO18 , " in18 " , MAX8907_REG_LDOCTL18 ) ,
LDO_750_50 ( LDO19 , " in19 " , MAX8907_REG_LDOCTL19 ) ,
LDO_750_50 ( LDO20 , " in20 " , MAX8907_REG_LDOCTL20 ) ,
REG_OUT5V ( OUT5V , " mbatt " , MAX8907_REG_OUT5VEN , 5000000 ) ,
REG_OUT5V ( OUT33V , " mbatt " , MAX8907_REG_OUT33VEN , 3300000 ) ,
REG_BBAT ( BBAT , " MBATT " , MAX8907_REG_BBAT_CNFG ,
2400000 , 3000000 , 200000 ) ,
REG_FIXED ( SDBY , " MBATT " , 1200000 ) ,
REG_FIXED ( VRTC , " MBATT " , 3300000 ) ,
} ;
# ifdef CONFIG_OF
# define MATCH(_name, _id) \
[ MAX8907_ # # _id ] = { \
. name = # _name , \
. driver_data = ( void * ) & max8907_regulators [ MAX8907_ # # _id ] , \
}
static struct of_regulator_match max8907_matches [ ] = {
MATCH ( mbatt , MBATT ) ,
MATCH ( sd1 , SD1 ) ,
MATCH ( sd2 , SD2 ) ,
MATCH ( sd3 , SD3 ) ,
MATCH ( ldo1 , LDO1 ) ,
MATCH ( ldo2 , LDO2 ) ,
MATCH ( ldo3 , LDO3 ) ,
MATCH ( ldo4 , LDO4 ) ,
MATCH ( ldo5 , LDO5 ) ,
MATCH ( ldo6 , LDO6 ) ,
MATCH ( ldo7 , LDO7 ) ,
MATCH ( ldo8 , LDO8 ) ,
MATCH ( ldo9 , LDO9 ) ,
MATCH ( ldo10 , LDO10 ) ,
MATCH ( ldo11 , LDO11 ) ,
MATCH ( ldo12 , LDO12 ) ,
MATCH ( ldo13 , LDO13 ) ,
MATCH ( ldo14 , LDO14 ) ,
MATCH ( ldo15 , LDO15 ) ,
MATCH ( ldo16 , LDO16 ) ,
MATCH ( ldo17 , LDO17 ) ,
MATCH ( ldo18 , LDO18 ) ,
MATCH ( ldo19 , LDO19 ) ,
MATCH ( ldo20 , LDO20 ) ,
MATCH ( out5v , OUT5V ) ,
MATCH ( out33v , OUT33V ) ,
MATCH ( bbat , BBAT ) ,
MATCH ( sdby , SDBY ) ,
MATCH ( vrtc , VRTC ) ,
} ;
static int max8907_regulator_parse_dt ( struct platform_device * pdev )
{
2013-01-27 17:16:56 +04:00
struct device_node * np , * regulators ;
2012-08-10 00:24:34 +04:00
int ret ;
2014-09-10 07:50:39 +04:00
np = pdev - > dev . parent - > of_node ;
2013-01-27 17:16:56 +04:00
if ( ! np )
2012-08-10 00:24:34 +04:00
return 0 ;
2014-02-14 15:49:50 +04:00
regulators = of_get_child_by_name ( np , " regulators " ) ;
2012-08-10 00:24:34 +04:00
if ( ! regulators ) {
dev_err ( & pdev - > dev , " regulators node not found \n " ) ;
return - EINVAL ;
}
2013-01-25 06:20:29 +04:00
ret = of_regulator_match ( & pdev - > dev , regulators , max8907_matches ,
2012-08-10 00:24:34 +04:00
ARRAY_SIZE ( max8907_matches ) ) ;
2013-01-27 17:16:56 +04:00
of_node_put ( regulators ) ;
2012-08-10 00:24:34 +04:00
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Error parsing regulator init data: %d \n " ,
ret ) ;
return ret ;
}
return 0 ;
}
2012-08-20 19:39:10 +04:00
static inline struct regulator_init_data * match_init_data ( int index )
{
return max8907_matches [ index ] . init_data ;
}
static inline struct device_node * match_of_node ( int index )
{
return max8907_matches [ index ] . of_node ;
}
2012-08-10 00:24:34 +04:00
# else
static int max8907_regulator_parse_dt ( struct platform_device * pdev )
{
return 0 ;
}
2012-08-20 19:39:10 +04:00
static inline struct regulator_init_data * match_init_data ( int index )
{
return NULL ;
}
static inline struct device_node * match_of_node ( int index )
{
return NULL ;
}
2012-08-10 00:24:34 +04:00
# endif
2012-11-19 22:22:22 +04:00
static int max8907_regulator_probe ( struct platform_device * pdev )
2012-08-10 00:24:34 +04:00
{
struct max8907 * max8907 = dev_get_drvdata ( pdev - > dev . parent ) ;
struct max8907_platform_data * pdata = dev_get_platdata ( max8907 - > dev ) ;
int ret ;
struct max8907_regulator * pmic ;
unsigned int val ;
int i ;
struct regulator_config config = { } ;
struct regulator_init_data * idata ;
const char * mbatt_rail_name = NULL ;
ret = max8907_regulator_parse_dt ( pdev ) ;
if ( ret )
return ret ;
pmic = devm_kzalloc ( & pdev - > dev , sizeof ( * pmic ) , GFP_KERNEL ) ;
2014-02-20 12:53:07 +04:00
if ( ! pmic )
2012-08-10 00:24:34 +04:00
return - ENOMEM ;
2014-02-20 12:53:07 +04:00
2012-08-10 00:24:34 +04:00
platform_set_drvdata ( pdev , pmic ) ;
memcpy ( pmic - > desc , max8907_regulators , sizeof ( pmic - > desc ) ) ;
/* Backwards compatibility with MAX8907B; SD1 uses different voltages */
2019-10-03 20:58:13 +03:00
ret = regmap_read ( max8907 - > regmap_gen , MAX8907_REG_II2RR , & val ) ;
if ( ret )
return ret ;
2012-08-10 00:24:34 +04:00
if ( ( val & MAX8907_II2RR_VERSION_MASK ) = =
MAX8907_II2RR_VERSION_REV_B ) {
pmic - > desc [ MAX8907_SD1 ] . min_uV = 637500 ;
pmic - > desc [ MAX8907_SD1 ] . uV_step = 12500 ;
2012-08-16 08:26:02 +04:00
pmic - > desc [ MAX8907_SD1 ] . n_voltages =
( 1425000 - 637500 ) / 12500 + 1 ;
2012-08-10 00:24:34 +04:00
}
for ( i = 0 ; i < MAX8907_NUM_REGULATORS ; i + + ) {
2014-03-10 12:32:48 +04:00
struct regulator_dev * rdev ;
2012-08-10 00:24:34 +04:00
config . dev = pdev - > dev . parent ;
if ( pdata )
idata = pdata - > init_data [ i ] ;
else
2012-08-20 19:39:10 +04:00
idata = match_init_data ( i ) ;
2012-08-10 00:24:34 +04:00
config . init_data = idata ;
config . driver_data = pmic ;
config . regmap = max8907 - > regmap_gen ;
2012-08-20 19:39:10 +04:00
config . of_node = match_of_node ( i ) ;
2012-08-10 00:24:34 +04:00
switch ( pmic - > desc [ i ] . id ) {
case MAX8907_MBATT :
2012-08-23 22:19:18 +04:00
if ( idata & & idata - > constraints . name )
mbatt_rail_name = idata - > constraints . name ;
else
mbatt_rail_name = pmic - > desc [ i ] . name ;
2012-08-10 00:24:34 +04:00
break ;
case MAX8907_BBAT :
case MAX8907_SDBY :
case MAX8907_VRTC :
idata - > supply_regulator = mbatt_rail_name ;
break ;
}
if ( pmic - > desc [ i ] . ops = = & max8907_ldo_ops ) {
2019-10-03 20:58:13 +03:00
ret = regmap_read ( config . regmap , pmic - > desc [ i ] . enable_reg ,
2012-08-10 00:24:34 +04:00
& val ) ;
2019-10-03 20:58:13 +03:00
if ( ret )
return ret ;
2012-08-10 00:24:34 +04:00
if ( ( val & MAX8907_MASK_LDO_SEQ ) ! =
MAX8907_MASK_LDO_SEQ )
pmic - > desc [ i ] . ops = & max8907_ldo_hwctl_ops ;
} else if ( pmic - > desc [ i ] . ops = = & max8907_out5v_ops ) {
2019-10-03 20:58:13 +03:00
ret = regmap_read ( config . regmap , pmic - > desc [ i ] . enable_reg ,
2012-08-10 00:24:34 +04:00
& val ) ;
2019-10-03 20:58:13 +03:00
if ( ret )
return ret ;
2012-08-10 00:24:34 +04:00
if ( ( val & ( MAX8907_MASK_OUT5V_VINEN |
MAX8907_MASK_OUT5V_ENSRC ) ) ! =
MAX8907_MASK_OUT5V_ENSRC )
pmic - > desc [ i ] . ops = & max8907_out5v_hwctl_ops ;
}
2014-03-10 12:32:48 +04:00
rdev = devm_regulator_register ( & pdev - > dev ,
2013-09-04 09:37:59 +04:00
& pmic - > desc [ i ] , & config ) ;
2014-03-10 12:32:48 +04:00
if ( IS_ERR ( rdev ) ) {
2012-08-10 00:24:34 +04:00
dev_err ( & pdev - > dev ,
" failed to register %s regulator \n " ,
pmic - > desc [ i ] . name ) ;
2014-03-10 12:32:48 +04:00
return PTR_ERR ( rdev ) ;
2012-08-10 00:24:34 +04:00
}
}
return 0 ;
}
static struct platform_driver max8907_regulator_driver = {
. driver = {
. name = " max8907-regulator " ,
} ,
. probe = max8907_regulator_probe ,
} ;
static int __init max8907_regulator_init ( void )
{
return platform_driver_register ( & max8907_regulator_driver ) ;
}
subsys_initcall ( max8907_regulator_init ) ;
static void __exit max8907_reg_exit ( void )
{
platform_driver_unregister ( & max8907_regulator_driver ) ;
}
module_exit ( max8907_reg_exit ) ;
MODULE_DESCRIPTION ( " MAX8907 regulator driver " ) ;
MODULE_AUTHOR ( " Gyungoh Yoo <jack.yoo@maxim-ic.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2012-08-25 10:56:48 +04:00
MODULE_ALIAS ( " platform:max8907-regulator " ) ;