2013-12-06 15:32:13 +04:00
/*
* max14577 . c - Regulator driver for the Maxim 14577
*
* Copyright ( C ) 2013 Samsung Electronics
* Krzysztof Kozlowski < k . kozlowski @ 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/regulator/driver.h>
# include <linux/mfd/max14577.h>
# include <linux/mfd/max14577-private.h>
# include <linux/regulator/of_regulator.h>
struct max14577_regulator {
struct device * dev ;
struct max14577 * max14577 ;
struct regulator_dev * * regulators ;
} ;
static int max14577_reg_is_enabled ( struct regulator_dev * rdev )
{
int rid = rdev_get_id ( rdev ) ;
struct regmap * rmap = rdev - > regmap ;
u8 reg_data ;
switch ( rid ) {
case MAX14577_CHARGER :
max14577_read_reg ( rmap , MAX14577_CHG_REG_CHG_CTRL2 , & reg_data ) ;
if ( ( reg_data & CHGCTRL2_MBCHOSTEN_MASK ) = = 0 )
return 0 ;
max14577_read_reg ( rmap , MAX14577_CHG_REG_STATUS3 , & reg_data ) ;
if ( ( reg_data & STATUS3_CGMBC_MASK ) = = 0 )
return 0 ;
/* MBCHOSTEN and CGMBC are on */
return 1 ;
default :
return - EINVAL ;
}
}
static int max14577_reg_get_current_limit ( struct regulator_dev * rdev )
{
u8 reg_data ;
struct regmap * rmap = rdev - > regmap ;
if ( rdev_get_id ( rdev ) ! = MAX14577_CHARGER )
return - EINVAL ;
max14577_read_reg ( rmap , MAX14577_CHG_REG_CHG_CTRL4 , & reg_data ) ;
if ( ( reg_data & CHGCTRL4_MBCICHWRCL_MASK ) = = 0 )
return MAX14577_REGULATOR_CURRENT_LIMIT_MIN ;
reg_data = ( ( reg_data & CHGCTRL4_MBCICHWRCH_MASK ) > >
CHGCTRL4_MBCICHWRCH_SHIFT ) ;
return MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
reg_data * MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP ;
}
static int max14577_reg_set_current_limit ( struct regulator_dev * rdev ,
int min_uA , int max_uA )
{
int i , current_bits = 0xf ;
u8 reg_data ;
if ( rdev_get_id ( rdev ) ! = MAX14577_CHARGER )
return - EINVAL ;
if ( min_uA > MAX14577_REGULATOR_CURRENT_LIMIT_MAX | |
max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_MIN )
return - EINVAL ;
if ( max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START ) {
/* Less than 200 mA, so set 90mA (turn only Low Bit off) */
u8 reg_data = 0x0 < < CHGCTRL4_MBCICHWRCL_SHIFT ;
return max14577_update_reg ( rdev - > regmap ,
MAX14577_CHG_REG_CHG_CTRL4 ,
CHGCTRL4_MBCICHWRCL_MASK , reg_data ) ;
}
/* max_uA is in range: <LIMIT_HIGH_START, inifinite>, so search for
* valid current starting from LIMIT_MAX . */
for ( i = MAX14577_REGULATOR_CURRENT_LIMIT_MAX ;
i > = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START ;
i - = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP ) {
if ( i < = max_uA )
break ;
current_bits - - ;
}
BUG_ON ( current_bits < 0 ) ; /* Cannot happen */
/* Turn Low Bit on (use range 200mA-950 mA) */
reg_data = 0x1 < < CHGCTRL4_MBCICHWRCL_SHIFT ;
/* and set proper High Bits */
reg_data | = current_bits < < CHGCTRL4_MBCICHWRCH_SHIFT ;
return max14577_update_reg ( rdev - > regmap , MAX14577_CHG_REG_CHG_CTRL4 ,
CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK ,
reg_data ) ;
}
static struct regulator_ops max14577_safeout_ops = {
. is_enabled = regulator_is_enabled_regmap ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. list_voltage = regulator_list_voltage_linear ,
} ;
static struct regulator_ops max14577_charger_ops = {
. is_enabled = max14577_reg_is_enabled ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. get_current_limit = max14577_reg_get_current_limit ,
. set_current_limit = max14577_reg_set_current_limit ,
} ;
static const struct regulator_desc supported_regulators [ ] = {
[ MAX14577_SAFEOUT ] = {
. name = " SAFEOUT " ,
. id = MAX14577_SAFEOUT ,
. ops = & max14577_safeout_ops ,
. type = REGULATOR_VOLTAGE ,
. owner = THIS_MODULE ,
. n_voltages = 1 ,
. min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE ,
. enable_reg = MAX14577_REG_CONTROL2 ,
. enable_mask = CTRL2_SFOUTORD_MASK ,
} ,
[ MAX14577_CHARGER ] = {
. name = " CHARGER " ,
. id = MAX14577_CHARGER ,
. ops = & max14577_charger_ops ,
. type = REGULATOR_CURRENT ,
. owner = THIS_MODULE ,
. enable_reg = MAX14577_CHG_REG_CHG_CTRL2 ,
. enable_mask = CHGCTRL2_MBCHOSTEN_MASK ,
} ,
} ;
# ifdef CONFIG_OF
static struct of_regulator_match max14577_regulator_matches [ ] = {
{ . name = " SAFEOUT " , } ,
{ . name = " CHARGER " , } ,
} ;
static int max14577_regulator_dt_parse_pdata ( struct platform_device * pdev )
{
int ret ;
struct device_node * np ;
np = of_get_child_by_name ( pdev - > dev . parent - > of_node , " regulators " ) ;
if ( ! np ) {
dev_err ( & pdev - > dev , " Failed to get child OF node for regulators \n " ) ;
return - EINVAL ;
}
ret = of_regulator_match ( & pdev - > dev , np , max14577_regulator_matches ,
MAX14577_REG_MAX ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Error parsing regulator init data: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static inline struct regulator_init_data * match_init_data ( int index )
{
return max14577_regulator_matches [ index ] . init_data ;
}
static inline struct device_node * match_of_node ( int index )
{
return max14577_regulator_matches [ index ] . of_node ;
}
# else /* CONFIG_OF */
static int max14577_regulator_dt_parse_pdata ( struct platform_device * pdev )
{
return 0 ;
}
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 ;
}
# endif /* CONFIG_OF */
static int max14577_regulator_probe ( struct platform_device * pdev )
{
struct max14577 * max14577 = dev_get_drvdata ( pdev - > dev . parent ) ;
struct max14577_platform_data * pdata = dev_get_platdata ( max14577 - > dev ) ;
int i , ret ;
struct regulator_config config = { } ;
ret = max14577_regulator_dt_parse_pdata ( pdev ) ;
if ( ret )
return ret ;
config . dev = & pdev - > dev ;
config . regmap = max14577 - > regmap ;
for ( i = 0 ; i < ARRAY_SIZE ( supported_regulators ) ; i + + ) {
struct regulator_dev * regulator ;
/*
* Index of supported_regulators [ ] is also the id and must
* match index of pdata - > regulators [ ] .
*/
if ( pdata & & pdata - > regulators ) {
config . init_data = pdata - > regulators [ i ] . initdata ;
config . of_node = pdata - > regulators [ i ] . of_node ;
} else {
config . init_data = match_init_data ( i ) ;
config . of_node = match_of_node ( i ) ;
}
regulator = devm_regulator_register ( & pdev - > dev ,
& supported_regulators [ i ] , & config ) ;
if ( IS_ERR ( regulator ) ) {
ret = PTR_ERR ( regulator ) ;
dev_err ( & pdev - > dev ,
" Regulator init failed for ID %d with error: %d \n " ,
i , ret ) ;
return ret ;
}
}
return ret ;
}
static struct platform_driver max14577_regulator_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " max14577-regulator " ,
} ,
. probe = max14577_regulator_probe ,
} ;
static int __init max14577_regulator_init ( void )
{
BUILD_BUG_ON ( MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf ! =
MAX14577_REGULATOR_CURRENT_LIMIT_MAX ) ;
BUILD_BUG_ON ( ARRAY_SIZE ( supported_regulators ) ! = MAX14577_REG_MAX ) ;
return platform_driver_register ( & max14577_regulator_driver ) ;
}
subsys_initcall ( max14577_regulator_init ) ;
static void __exit max14577_regulator_exit ( void )
{
platform_driver_unregister ( & max14577_regulator_driver ) ;
}
module_exit ( max14577_regulator_exit ) ;
MODULE_AUTHOR ( " Krzysztof Kozlowski <k.kozlowski@samsung.com> " ) ;
MODULE_DESCRIPTION ( " MAXIM 14577 regulator driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2013-12-23 10:50:53 +04:00
MODULE_ALIAS ( " platform:max14577-regulator " ) ;