2011-12-09 18:18:20 +04:00
/*
* da9052 - regulator . c : Regulator driver for DA9052
*
* Copyright ( c ) 2011 Dialog Semiconductor Ltd .
*
* Author : David Dajun Chen < dchen @ diasemi . 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/platform_device.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
2012-04-13 17:37:41 +04:00
# ifdef CONFIG_OF
2012-04-16 12:54:24 +04:00
# include <linux/of.h>
2012-04-13 17:37:41 +04:00
# include <linux/regulator/of_regulator.h>
# endif
2011-12-09 18:18:20 +04:00
# include <linux/mfd/da9052/da9052.h>
# include <linux/mfd/da9052/reg.h>
# include <linux/mfd/da9052/pdata.h>
/* Buck step size */
# define DA9052_BUCK_PERI_3uV_STEP 100000
# define DA9052_BUCK_PERI_REG_MAP_UPTO_3uV 24
# define DA9052_CONST_3uV 3000000
# define DA9052_MIN_UA 0
# define DA9052_MAX_UA 3
# define DA9052_CURRENT_RANGE 4
/* Bit masks */
# define DA9052_BUCK_ILIM_MASK_EVEN 0x0c
# define DA9052_BUCK_ILIM_MASK_ODD 0xc0
2012-03-15 15:58:39 +04:00
/* DA9052 REGULATOR IDs */
# define DA9052_ID_BUCK1 0
# define DA9052_ID_BUCK2 1
# define DA9052_ID_BUCK3 2
# define DA9052_ID_BUCK4 3
# define DA9052_ID_LDO1 4
# define DA9052_ID_LDO2 5
# define DA9052_ID_LDO3 6
# define DA9052_ID_LDO4 7
# define DA9052_ID_LDO5 8
# define DA9052_ID_LDO6 9
# define DA9052_ID_LDO7 10
# define DA9052_ID_LDO8 11
# define DA9052_ID_LDO9 12
# define DA9052_ID_LDO10 13
2011-12-09 18:18:20 +04:00
static const u32 da9052_current_limits [ 3 ] [ 4 ] = {
{ 700000 , 800000 , 1000000 , 1200000 } , /* DA9052-BC BUCKs */
{ 1600000 , 2000000 , 2400000 , 3000000 } , /* DA9053-AA/Bx BUCK-CORE */
{ 800000 , 1000000 , 1200000 , 1500000 } , /* DA9053-AA/Bx BUCK-PRO,
* BUCK - MEM and BUCK - PERI
*/
} ;
struct da9052_regulator_info {
struct regulator_desc reg_desc ;
int step_uV ;
int min_uV ;
int max_uV ;
} ;
struct da9052_regulator {
struct da9052 * da9052 ;
struct da9052_regulator_info * info ;
struct regulator_dev * rdev ;
} ;
static int verify_range ( struct da9052_regulator_info * info ,
int min_uV , int max_uV )
{
if ( min_uV > info - > max_uV | | max_uV < info - > min_uV )
return - EINVAL ;
return 0 ;
}
static int da9052_dcdc_get_current_limit ( struct regulator_dev * rdev )
{
struct da9052_regulator * regulator = rdev_get_drvdata ( rdev ) ;
int offset = rdev_get_id ( rdev ) ;
int ret , row = 2 ;
ret = da9052_reg_read ( regulator - > da9052 , DA9052_BUCKA_REG + offset / 2 ) ;
if ( ret < 0 )
return ret ;
/* Determine the even or odd position of the buck current limit
* register field
*/
if ( offset % 2 = = 0 )
ret = ( ret & DA9052_BUCK_ILIM_MASK_EVEN ) > > 2 ;
else
ret = ( ret & DA9052_BUCK_ILIM_MASK_ODD ) > > 6 ;
/* Select the appropriate current limit range */
if ( regulator - > da9052 - > chip_id = = DA9052 )
row = 0 ;
else if ( offset = = 0 )
row = 1 ;
return da9052_current_limits [ row ] [ ret ] ;
}
static int da9052_dcdc_set_current_limit ( struct regulator_dev * rdev , int min_uA ,
int max_uA )
{
struct da9052_regulator * regulator = rdev_get_drvdata ( rdev ) ;
int offset = rdev_get_id ( rdev ) ;
int reg_val = 0 ;
int i , row = 2 ;
/* Select the appropriate current limit range */
if ( regulator - > da9052 - > chip_id = = DA9052 )
row = 0 ;
else if ( offset = = 0 )
row = 1 ;
2012-08-08 16:23:54 +04:00
for ( i = DA9052_CURRENT_RANGE - 1 ; i > = 0 ; i - - ) {
2012-11-26 11:23:38 +04:00
if ( ( min_uA < = da9052_current_limits [ row ] [ i ] ) & &
( da9052_current_limits [ row ] [ i ] < = max_uA ) ) {
2011-12-09 18:18:20 +04:00
reg_val = i ;
break ;
}
}
2012-11-26 11:23:38 +04:00
if ( i < 0 )
return - EINVAL ;
2011-12-09 18:18:20 +04:00
/* Determine the even or odd position of the buck current limit
* register field
*/
if ( offset % 2 = = 0 )
return da9052_reg_update ( regulator - > da9052 ,
DA9052_BUCKA_REG + offset / 2 ,
DA9052_BUCK_ILIM_MASK_EVEN ,
reg_val < < 2 ) ;
else
return da9052_reg_update ( regulator - > da9052 ,
DA9052_BUCKA_REG + offset / 2 ,
DA9052_BUCK_ILIM_MASK_ODD ,
reg_val < < 6 ) ;
}
static int da9052_list_voltage ( struct regulator_dev * rdev ,
unsigned int selector )
{
struct da9052_regulator * regulator = rdev_get_drvdata ( rdev ) ;
struct da9052_regulator_info * info = regulator - > info ;
2012-03-15 16:00:07 +04:00
int id = rdev_get_id ( rdev ) ;
2011-12-09 18:18:20 +04:00
int volt_uV ;
2012-03-15 16:00:07 +04:00
if ( ( id = = DA9052_ID_BUCK4 ) & & ( regulator - > da9052 - > chip_id = = DA9052 )
& & ( selector > = DA9052_BUCK_PERI_REG_MAP_UPTO_3uV ) ) {
volt_uV = ( ( DA9052_BUCK_PERI_REG_MAP_UPTO_3uV * info - > step_uV )
+ info - > min_uV ) ;
volt_uV + = ( selector - DA9052_BUCK_PERI_REG_MAP_UPTO_3uV )
* ( DA9052_BUCK_PERI_3uV_STEP ) ;
} else {
volt_uV = ( selector * info - > step_uV ) + info - > min_uV ;
}
2011-12-09 18:18:20 +04:00
if ( volt_uV > info - > max_uV )
return - EINVAL ;
return volt_uV ;
}
2012-05-16 09:12:35 +04:00
static int da9052_map_voltage ( struct regulator_dev * rdev ,
int min_uV , int max_uV )
2011-12-09 18:18:20 +04:00
{
struct da9052_regulator * regulator = rdev_get_drvdata ( rdev ) ;
struct da9052_regulator_info * info = regulator - > info ;
2012-03-15 16:00:07 +04:00
int id = rdev_get_id ( rdev ) ;
2012-05-16 09:12:35 +04:00
int ret , sel ;
2011-12-09 18:18:20 +04:00
ret = verify_range ( info , min_uV , max_uV ) ;
if ( ret < 0 )
return ret ;
if ( min_uV < info - > min_uV )
min_uV = info - > min_uV ;
2012-03-15 16:00:07 +04:00
if ( ( id = = DA9052_ID_BUCK4 ) & & ( regulator - > da9052 - > chip_id = = DA9052 )
& & ( min_uV > = DA9052_CONST_3uV ) ) {
2012-05-16 09:12:35 +04:00
sel = DA9052_BUCK_PERI_REG_MAP_UPTO_3uV +
DIV_ROUND_UP ( min_uV - DA9052_CONST_3uV ,
DA9052_BUCK_PERI_3uV_STEP ) ;
2012-03-15 16:00:07 +04:00
} else {
2012-05-16 09:12:35 +04:00
sel = DIV_ROUND_UP ( min_uV - info - > min_uV , info - > step_uV ) ;
2012-03-15 16:00:07 +04:00
}
2011-12-09 18:18:20 +04:00
2012-05-16 09:12:35 +04:00
ret = da9052_list_voltage ( rdev , sel ) ;
2011-12-09 18:18:20 +04:00
if ( ret < 0 )
return ret ;
2012-05-16 09:12:35 +04:00
return sel ;
}
2011-12-09 18:18:20 +04:00
static struct regulator_ops da9052_dcdc_ops = {
. get_current_limit = da9052_dcdc_get_current_limit ,
. set_current_limit = da9052_dcdc_set_current_limit ,
. list_voltage = da9052_list_voltage ,
2012-05-16 09:12:35 +04:00
. map_voltage = da9052_map_voltage ,
2012-04-24 05:54:38 +04:00
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
2012-12-18 05:32:52 +04:00
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
2012-04-17 07:34:32 +04:00
. is_enabled = regulator_is_enabled_regmap ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
2011-12-09 18:18:20 +04:00
} ;
static struct regulator_ops da9052_ldo_ops = {
. list_voltage = da9052_list_voltage ,
2012-05-16 09:12:35 +04:00
. map_voltage = da9052_map_voltage ,
2012-04-24 05:54:38 +04:00
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
2012-12-18 05:32:52 +04:00
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
2012-04-17 07:34:32 +04:00
. is_enabled = regulator_is_enabled_regmap ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
2011-12-09 18:18:20 +04:00
} ;
# define DA9052_LDO(_id, step, min, max, sbits, ebits, abits) \
{ \
. reg_desc = { \
2012-03-15 15:58:39 +04:00
. name = # _id , \
2011-12-09 18:18:20 +04:00
. ops = & da9052_ldo_ops , \
. type = REGULATOR_VOLTAGE , \
2012-03-15 15:58:39 +04:00
. id = DA9052_ID_ # # _id , \
2012-03-05 16:04:32 +04:00
. n_voltages = ( max - min ) / step + 1 , \
2011-12-09 18:18:20 +04:00
. owner = THIS_MODULE , \
2012-04-24 05:54:38 +04:00
. vsel_reg = DA9052_BUCKCORE_REG + DA9052_ID_ # # _id , \
. vsel_mask = ( 1 < < ( sbits ) ) - 1 , \
2012-12-18 05:32:52 +04:00
. apply_reg = DA9052_SUPPLY_REG , \
. apply_bit = ( abits ) , \
2012-04-17 07:34:32 +04:00
. enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_ # # _id , \
. enable_mask = 1 < < ( ebits ) , \
2011-12-09 18:18:20 +04:00
} , \
. min_uV = ( min ) * 1000 , \
. max_uV = ( max ) * 1000 , \
. step_uV = ( step ) * 1000 , \
}
# define DA9052_DCDC(_id, step, min, max, sbits, ebits, abits) \
{ \
. reg_desc = { \
2012-03-15 15:58:39 +04:00
. name = # _id , \
2011-12-09 18:18:20 +04:00
. ops = & da9052_dcdc_ops , \
. type = REGULATOR_VOLTAGE , \
2012-03-15 15:58:39 +04:00
. id = DA9052_ID_ # # _id , \
2012-03-05 16:04:32 +04:00
. n_voltages = ( max - min ) / step + 1 , \
2011-12-09 18:18:20 +04:00
. owner = THIS_MODULE , \
2012-04-24 05:54:38 +04:00
. vsel_reg = DA9052_BUCKCORE_REG + DA9052_ID_ # # _id , \
. vsel_mask = ( 1 < < ( sbits ) ) - 1 , \
2012-12-18 05:32:52 +04:00
. apply_reg = DA9052_SUPPLY_REG , \
. apply_bit = ( abits ) , \
2012-04-17 07:34:32 +04:00
. enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_ # # _id , \
. enable_mask = 1 < < ( ebits ) , \
2011-12-09 18:18:20 +04:00
} , \
. min_uV = ( min ) * 1000 , \
. max_uV = ( max ) * 1000 , \
. step_uV = ( step ) * 1000 , \
}
2011-12-20 13:12:00 +04:00
static struct da9052_regulator_info da9052_regulator_info [ ] = {
2012-03-15 15:58:39 +04:00
DA9052_DCDC ( BUCK1 , 25 , 500 , 2075 , 6 , 6 , DA9052_SUPPLY_VBCOREGO ) ,
DA9052_DCDC ( BUCK2 , 25 , 500 , 2075 , 6 , 6 , DA9052_SUPPLY_VBPROGO ) ,
DA9052_DCDC ( BUCK3 , 25 , 925 , 2500 , 6 , 6 , DA9052_SUPPLY_VBMEMGO ) ,
2012-03-15 16:00:07 +04:00
DA9052_DCDC ( BUCK4 , 50 , 1800 , 3600 , 5 , 6 , 0 ) ,
2012-03-15 15:58:39 +04:00
DA9052_LDO ( LDO1 , 50 , 600 , 1800 , 5 , 6 , 0 ) ,
2012-03-15 16:00:07 +04:00
DA9052_LDO ( LDO2 , 25 , 600 , 1800 , 6 , 6 , DA9052_SUPPLY_VLDO2GO ) ,
DA9052_LDO ( LDO3 , 25 , 1725 , 3300 , 6 , 6 , DA9052_SUPPLY_VLDO3GO ) ,
2012-03-15 15:58:39 +04:00
DA9052_LDO ( LDO4 , 25 , 1725 , 3300 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO5 , 50 , 1200 , 3600 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO6 , 50 , 1200 , 3600 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO7 , 50 , 1200 , 3600 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO8 , 50 , 1200 , 3600 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO9 , 50 , 1250 , 3650 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO10 , 50 , 1200 , 3600 , 6 , 6 , 0 ) ,
2011-12-09 18:18:20 +04:00
} ;
2011-12-20 13:12:00 +04:00
static struct da9052_regulator_info da9053_regulator_info [ ] = {
2012-03-15 15:58:39 +04:00
DA9052_DCDC ( BUCK1 , 25 , 500 , 2075 , 6 , 6 , DA9052_SUPPLY_VBCOREGO ) ,
DA9052_DCDC ( BUCK2 , 25 , 500 , 2075 , 6 , 6 , DA9052_SUPPLY_VBPROGO ) ,
DA9052_DCDC ( BUCK3 , 25 , 925 , 2500 , 6 , 6 , DA9052_SUPPLY_VBMEMGO ) ,
2012-03-15 16:00:07 +04:00
DA9052_DCDC ( BUCK4 , 25 , 925 , 2500 , 6 , 6 , 0 ) ,
2012-03-15 15:58:39 +04:00
DA9052_LDO ( LDO1 , 50 , 600 , 1800 , 5 , 6 , 0 ) ,
2012-03-15 16:00:07 +04:00
DA9052_LDO ( LDO2 , 25 , 600 , 1800 , 6 , 6 , DA9052_SUPPLY_VLDO2GO ) ,
DA9052_LDO ( LDO3 , 25 , 1725 , 3300 , 6 , 6 , DA9052_SUPPLY_VLDO3GO ) ,
2012-03-15 15:58:39 +04:00
DA9052_LDO ( LDO4 , 25 , 1725 , 3300 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO5 , 50 , 1200 , 3600 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO6 , 50 , 1200 , 3600 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO7 , 50 , 1200 , 3600 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO8 , 50 , 1200 , 3600 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO9 , 50 , 1250 , 3650 , 6 , 6 , 0 ) ,
DA9052_LDO ( LDO10 , 50 , 1200 , 3600 , 6 , 6 , 0 ) ,
2011-12-09 18:18:20 +04:00
} ;
static inline struct da9052_regulator_info * find_regulator_info ( u8 chip_id ,
int id )
{
struct da9052_regulator_info * info ;
int i ;
2011-12-15 17:29:53 +04:00
switch ( chip_id ) {
case DA9052 :
2011-12-09 18:18:20 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( da9052_regulator_info ) ; i + + ) {
info = & da9052_regulator_info [ i ] ;
if ( info - > reg_desc . id = = id )
return info ;
}
2011-12-15 17:29:53 +04:00
break ;
case DA9053_AA :
case DA9053_BA :
case DA9053_BB :
2011-12-09 18:18:20 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( da9053_regulator_info ) ; i + + ) {
info = & da9053_regulator_info [ i ] ;
if ( info - > reg_desc . id = = id )
return info ;
}
2011-12-15 17:29:53 +04:00
break ;
2011-12-09 18:18:20 +04:00
}
return NULL ;
}
2012-11-19 22:22:22 +04:00
static int da9052_regulator_probe ( struct platform_device * pdev )
2011-12-09 18:18:20 +04:00
{
2012-04-04 03:50:22 +04:00
struct regulator_config config = { } ;
2011-12-09 18:18:20 +04:00
struct da9052_regulator * regulator ;
struct da9052 * da9052 ;
struct da9052_pdata * pdata ;
2011-12-15 17:29:53 +04:00
regulator = devm_kzalloc ( & pdev - > dev , sizeof ( struct da9052_regulator ) ,
GFP_KERNEL ) ;
2011-12-09 18:18:20 +04:00
if ( ! regulator )
return - ENOMEM ;
da9052 = dev_get_drvdata ( pdev - > dev . parent ) ;
pdata = da9052 - > dev - > platform_data ;
regulator - > da9052 = da9052 ;
regulator - > info = find_regulator_info ( regulator - > da9052 - > chip_id ,
pdev - > id ) ;
if ( regulator - > info = = NULL ) {
dev_err ( & pdev - > dev , " invalid regulator ID specified \n " ) ;
2012-04-05 08:08:58 +04:00
return - EINVAL ;
2011-12-09 18:18:20 +04:00
}
2012-04-04 03:50:22 +04:00
config . dev = & pdev - > dev ;
config . driver_data = regulator ;
2012-04-17 07:34:32 +04:00
config . regmap = da9052 - > regmap ;
2012-04-13 17:37:41 +04:00
if ( pdata & & pdata - > regulators ) {
config . init_data = pdata - > regulators [ pdev - > id ] ;
} else {
# ifdef CONFIG_OF
2013-01-27 17:16:56 +04:00
struct device_node * nproot , * np ;
2012-04-13 17:37:41 +04:00
2013-01-27 17:16:56 +04:00
nproot = of_node_get ( da9052 - > dev - > of_node ) ;
2012-04-13 17:37:41 +04:00
if ( ! nproot )
return - ENODEV ;
nproot = of_find_node_by_name ( nproot , " regulators " ) ;
if ( ! nproot )
return - ENODEV ;
2012-07-07 05:46:45 +04:00
for_each_child_of_node ( nproot , np ) {
2012-04-13 17:37:41 +04:00
if ( ! of_node_cmp ( np - > name ,
regulator - > info - > reg_desc . name ) ) {
config . init_data = of_get_regulator_init_data (
& pdev - > dev , np ) ;
2012-07-12 18:23:25 +04:00
config . of_node = np ;
2012-04-13 17:37:41 +04:00
break ;
}
}
2013-01-27 17:16:56 +04:00
of_node_put ( nproot ) ;
2012-04-13 17:37:41 +04:00
# endif
}
2012-04-04 03:50:22 +04:00
2011-12-09 18:18:20 +04:00
regulator - > rdev = regulator_register ( & regulator - > info - > reg_desc ,
2012-04-04 03:50:22 +04:00
& config ) ;
2011-12-09 18:18:20 +04:00
if ( IS_ERR ( regulator - > rdev ) ) {
dev_err ( & pdev - > dev , " failed to register regulator %s \n " ,
regulator - > info - > reg_desc . name ) ;
2012-04-05 08:08:58 +04:00
return PTR_ERR ( regulator - > rdev ) ;
2011-12-09 18:18:20 +04:00
}
platform_set_drvdata ( pdev , regulator ) ;
return 0 ;
}
2012-11-19 22:26:10 +04:00
static int da9052_regulator_remove ( struct platform_device * pdev )
2011-12-09 18:18:20 +04:00
{
struct da9052_regulator * regulator = platform_get_drvdata ( pdev ) ;
regulator_unregister ( regulator - > rdev ) ;
return 0 ;
}
static struct platform_driver da9052_regulator_driver = {
. probe = da9052_regulator_probe ,
2012-11-19 22:20:42 +04:00
. remove = da9052_regulator_remove ,
2011-12-09 18:18:20 +04:00
. driver = {
. name = " da9052-regulator " ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init da9052_regulator_init ( void )
{
return platform_driver_register ( & da9052_regulator_driver ) ;
}
subsys_initcall ( da9052_regulator_init ) ;
static void __exit da9052_regulator_exit ( void )
{
platform_driver_unregister ( & da9052_regulator_driver ) ;
}
module_exit ( da9052_regulator_exit ) ;
MODULE_AUTHOR ( " David Dajun Chen <dchen@diasemi.com> " ) ;
MODULE_DESCRIPTION ( " Power Regulator driver for Dialog DA9052 PMIC " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:da9052-regulator " ) ;