2012-05-18 19:53:57 +04:00
/*
* Driver for Regulator part of Palmas PMIC Chips
*
* Copyright 2011 - 2012 Texas Instruments Inc .
*
* Author : Graeme Gregory < gg @ slimlogic . co . uk >
*
* 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/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/err.h>
# include <linux/platform_device.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
# include <linux/slab.h>
# include <linux/regmap.h>
# include <linux/mfd/palmas.h>
2012-08-28 15:47:40 +04:00
# include <linux/of.h>
# include <linux/of_platform.h>
# include <linux/regulator/of_regulator.h>
2012-05-18 19:53:57 +04:00
struct regs_info {
char * name ;
u8 vsel_addr ;
u8 ctrl_addr ;
u8 tstep_addr ;
} ;
static const struct regs_info palmas_regs_info [ ] = {
{
. name = " SMPS12 " ,
. vsel_addr = PALMAS_SMPS12_VOLTAGE ,
. ctrl_addr = PALMAS_SMPS12_CTRL ,
. tstep_addr = PALMAS_SMPS12_TSTEP ,
} ,
{
. name = " SMPS123 " ,
. vsel_addr = PALMAS_SMPS12_VOLTAGE ,
. ctrl_addr = PALMAS_SMPS12_CTRL ,
. tstep_addr = PALMAS_SMPS12_TSTEP ,
} ,
{
. name = " SMPS3 " ,
. vsel_addr = PALMAS_SMPS3_VOLTAGE ,
. ctrl_addr = PALMAS_SMPS3_CTRL ,
} ,
{
. name = " SMPS45 " ,
. vsel_addr = PALMAS_SMPS45_VOLTAGE ,
. ctrl_addr = PALMAS_SMPS45_CTRL ,
. tstep_addr = PALMAS_SMPS45_TSTEP ,
} ,
{
. name = " SMPS457 " ,
. vsel_addr = PALMAS_SMPS45_VOLTAGE ,
. ctrl_addr = PALMAS_SMPS45_CTRL ,
. tstep_addr = PALMAS_SMPS45_TSTEP ,
} ,
{
. name = " SMPS6 " ,
. vsel_addr = PALMAS_SMPS6_VOLTAGE ,
. ctrl_addr = PALMAS_SMPS6_CTRL ,
. tstep_addr = PALMAS_SMPS6_TSTEP ,
} ,
{
. name = " SMPS7 " ,
. vsel_addr = PALMAS_SMPS7_VOLTAGE ,
. ctrl_addr = PALMAS_SMPS7_CTRL ,
} ,
{
. name = " SMPS8 " ,
. vsel_addr = PALMAS_SMPS8_VOLTAGE ,
. ctrl_addr = PALMAS_SMPS8_CTRL ,
. tstep_addr = PALMAS_SMPS8_TSTEP ,
} ,
{
. name = " SMPS9 " ,
. vsel_addr = PALMAS_SMPS9_VOLTAGE ,
. ctrl_addr = PALMAS_SMPS9_CTRL ,
} ,
{
. name = " SMPS10 " ,
} ,
{
. name = " LDO1 " ,
. vsel_addr = PALMAS_LDO1_VOLTAGE ,
. ctrl_addr = PALMAS_LDO1_CTRL ,
} ,
{
. name = " LDO2 " ,
. vsel_addr = PALMAS_LDO2_VOLTAGE ,
. ctrl_addr = PALMAS_LDO2_CTRL ,
} ,
{
. name = " LDO3 " ,
. vsel_addr = PALMAS_LDO3_VOLTAGE ,
. ctrl_addr = PALMAS_LDO3_CTRL ,
} ,
{
. name = " LDO4 " ,
. vsel_addr = PALMAS_LDO4_VOLTAGE ,
. ctrl_addr = PALMAS_LDO4_CTRL ,
} ,
{
. name = " LDO5 " ,
. vsel_addr = PALMAS_LDO5_VOLTAGE ,
. ctrl_addr = PALMAS_LDO5_CTRL ,
} ,
{
. name = " LDO6 " ,
. vsel_addr = PALMAS_LDO6_VOLTAGE ,
. ctrl_addr = PALMAS_LDO6_CTRL ,
} ,
{
. name = " LDO7 " ,
. vsel_addr = PALMAS_LDO7_VOLTAGE ,
. ctrl_addr = PALMAS_LDO7_CTRL ,
} ,
{
. name = " LDO8 " ,
. vsel_addr = PALMAS_LDO8_VOLTAGE ,
. ctrl_addr = PALMAS_LDO8_CTRL ,
} ,
{
. name = " LDO9 " ,
. vsel_addr = PALMAS_LDO9_VOLTAGE ,
. ctrl_addr = PALMAS_LDO9_CTRL ,
} ,
{
. name = " LDOLN " ,
. vsel_addr = PALMAS_LDOLN_VOLTAGE ,
. ctrl_addr = PALMAS_LDOLN_CTRL ,
} ,
{
. name = " LDOUSB " ,
. vsel_addr = PALMAS_LDOUSB_VOLTAGE ,
. ctrl_addr = PALMAS_LDOUSB_CTRL ,
} ,
} ;
# define SMPS_CTRL_MODE_OFF 0x00
# define SMPS_CTRL_MODE_ON 0x01
# define SMPS_CTRL_MODE_ECO 0x02
# define SMPS_CTRL_MODE_PWM 0x03
/* These values are derived from the data sheet. And are the number of steps
* where there is a voltage change , the ranges at beginning and end of register
* max / min values where there are no change are ommitted .
*
* So they are basically ( maxV - minV ) / stepV
*/
# define PALMAS_SMPS_NUM_VOLTAGES 116
# define PALMAS_SMPS10_NUM_VOLTAGES 2
# define PALMAS_LDO_NUM_VOLTAGES 50
# define SMPS10_VSEL (1<<3)
# define SMPS10_BOOST_EN (1<<2)
# define SMPS10_BYPASS_EN (1<<1)
# define SMPS10_SWITCH_EN (1<<0)
# define REGULATOR_SLAVE 0
static int palmas_smps_read ( struct palmas * palmas , unsigned int reg ,
unsigned int * dest )
{
unsigned int addr ;
addr = PALMAS_BASE_TO_REG ( PALMAS_SMPS_BASE , reg ) ;
return regmap_read ( palmas - > regmap [ REGULATOR_SLAVE ] , addr , dest ) ;
}
static int palmas_smps_write ( struct palmas * palmas , unsigned int reg ,
unsigned int value )
{
unsigned int addr ;
addr = PALMAS_BASE_TO_REG ( PALMAS_SMPS_BASE , reg ) ;
return regmap_write ( palmas - > regmap [ REGULATOR_SLAVE ] , addr , value ) ;
}
static int palmas_ldo_read ( struct palmas * palmas , unsigned int reg ,
unsigned int * dest )
{
unsigned int addr ;
addr = PALMAS_BASE_TO_REG ( PALMAS_LDO_BASE , reg ) ;
return regmap_read ( palmas - > regmap [ REGULATOR_SLAVE ] , addr , dest ) ;
}
static int palmas_ldo_write ( struct palmas * palmas , unsigned int reg ,
unsigned int value )
{
unsigned int addr ;
addr = PALMAS_BASE_TO_REG ( PALMAS_LDO_BASE , reg ) ;
return regmap_write ( palmas - > regmap [ REGULATOR_SLAVE ] , addr , value ) ;
}
static int palmas_is_enabled_smps ( struct regulator_dev * dev )
{
struct palmas_pmic * pmic = rdev_get_drvdata ( dev ) ;
int id = rdev_get_id ( dev ) ;
unsigned int reg ;
palmas_smps_read ( pmic - > palmas , palmas_regs_info [ id ] . ctrl_addr , & reg ) ;
reg & = PALMAS_SMPS12_CTRL_STATUS_MASK ;
reg > > = PALMAS_SMPS12_CTRL_STATUS_SHIFT ;
return ! ! ( reg ) ;
}
static int palmas_enable_smps ( struct regulator_dev * dev )
{
struct palmas_pmic * pmic = rdev_get_drvdata ( dev ) ;
int id = rdev_get_id ( dev ) ;
unsigned int reg ;
palmas_smps_read ( pmic - > palmas , palmas_regs_info [ id ] . ctrl_addr , & reg ) ;
reg & = ~ PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK ;
reg | = SMPS_CTRL_MODE_ON ;
palmas_smps_write ( pmic - > palmas , palmas_regs_info [ id ] . ctrl_addr , reg ) ;
return 0 ;
}
static int palmas_disable_smps ( struct regulator_dev * dev )
{
struct palmas_pmic * pmic = rdev_get_drvdata ( dev ) ;
int id = rdev_get_id ( dev ) ;
unsigned int reg ;
palmas_smps_read ( pmic - > palmas , palmas_regs_info [ id ] . ctrl_addr , & reg ) ;
reg & = ~ PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK ;
palmas_smps_write ( pmic - > palmas , palmas_regs_info [ id ] . ctrl_addr , reg ) ;
return 0 ;
}
static int palmas_set_mode_smps ( struct regulator_dev * dev , unsigned int mode )
{
struct palmas_pmic * pmic = rdev_get_drvdata ( dev ) ;
int id = rdev_get_id ( dev ) ;
unsigned int reg ;
palmas_smps_read ( pmic - > palmas , palmas_regs_info [ id ] . ctrl_addr , & reg ) ;
2012-06-07 13:08:21 +04:00
reg & = ~ PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK ;
2012-05-18 19:53:57 +04:00
switch ( mode ) {
case REGULATOR_MODE_NORMAL :
reg | = SMPS_CTRL_MODE_ON ;
break ;
case REGULATOR_MODE_IDLE :
reg | = SMPS_CTRL_MODE_ECO ;
break ;
case REGULATOR_MODE_FAST :
reg | = SMPS_CTRL_MODE_PWM ;
break ;
default :
return - EINVAL ;
}
palmas_smps_write ( pmic - > palmas , palmas_regs_info [ id ] . ctrl_addr , reg ) ;
return 0 ;
}
static unsigned int palmas_get_mode_smps ( struct regulator_dev * dev )
{
struct palmas_pmic * pmic = rdev_get_drvdata ( dev ) ;
int id = rdev_get_id ( dev ) ;
unsigned int reg ;
palmas_smps_read ( pmic - > palmas , palmas_regs_info [ id ] . ctrl_addr , & reg ) ;
reg & = PALMAS_SMPS12_CTRL_STATUS_MASK ;
reg > > = PALMAS_SMPS12_CTRL_STATUS_SHIFT ;
switch ( reg ) {
case SMPS_CTRL_MODE_ON :
return REGULATOR_MODE_NORMAL ;
case SMPS_CTRL_MODE_ECO :
return REGULATOR_MODE_IDLE ;
case SMPS_CTRL_MODE_PWM :
return REGULATOR_MODE_FAST ;
}
return 0 ;
}
static int palmas_list_voltage_smps ( struct regulator_dev * dev ,
unsigned selector )
{
struct palmas_pmic * pmic = rdev_get_drvdata ( dev ) ;
int id = rdev_get_id ( dev ) ;
int mult = 1 ;
if ( ! selector )
return 0 ;
/* Read the multiplier set in VSEL register to return
* the correct voltage .
*/
if ( pmic - > range [ id ] )
mult = 2 ;
/* Voltage is (0.49V + (selector * 0.01V)) * RANGE
* as defined in data sheet . RANGE is either x1 or x2
*/
return ( 490000 + ( selector * 10000 ) ) * mult ;
}
static int palmas_get_voltage_smps_sel ( struct regulator_dev * dev )
{
struct palmas_pmic * pmic = rdev_get_drvdata ( dev ) ;
int id = rdev_get_id ( dev ) ;
int selector ;
unsigned int reg ;
unsigned int addr ;
addr = palmas_regs_info [ id ] . vsel_addr ;
palmas_smps_read ( pmic - > palmas , addr , & reg ) ;
selector = reg & PALMAS_SMPS12_VOLTAGE_VSEL_MASK ;
/* Adjust selector to match list_voltage ranges */
if ( ( selector > 0 ) & & ( selector < 6 ) )
selector = 6 ;
if ( ! selector )
selector = 5 ;
if ( selector > 121 )
selector = 121 ;
selector - = 5 ;
return selector ;
}
static int palmas_set_voltage_smps_sel ( struct regulator_dev * dev ,
unsigned selector )
{
struct palmas_pmic * pmic = rdev_get_drvdata ( dev ) ;
int id = rdev_get_id ( dev ) ;
unsigned int reg = 0 ;
unsigned int addr ;
addr = palmas_regs_info [ id ] . vsel_addr ;
/* Make sure we don't change the value of RANGE */
if ( pmic - > range [ id ] )
reg | = PALMAS_SMPS12_VOLTAGE_RANGE ;
/* Adjust the linux selector into range used in VSEL register */
if ( selector )
reg | = selector + 5 ;
palmas_smps_write ( pmic - > palmas , addr , reg ) ;
return 0 ;
}
static int palmas_map_voltage_smps ( struct regulator_dev * rdev ,
int min_uV , int max_uV )
{
2012-07-17 07:29:03 +04:00
struct palmas_pmic * pmic = rdev_get_drvdata ( rdev ) ;
int id = rdev_get_id ( rdev ) ;
2012-05-18 19:53:57 +04:00
int ret , voltage ;
2012-07-17 07:29:03 +04:00
if ( min_uV = = 0 )
return 0 ;
if ( pmic - > range [ id ] ) { /* RANGE is x2 */
if ( min_uV < 1000000 )
min_uV = 1000000 ;
ret = DIV_ROUND_UP ( min_uV - 1000000 , 20000 ) + 1 ;
} else { /* RANGE is x1 */
if ( min_uV < 500000 )
min_uV = 500000 ;
ret = DIV_ROUND_UP ( min_uV - 500000 , 10000 ) + 1 ;
}
2012-05-18 19:53:57 +04:00
/* Map back into a voltage to verify we're still in bounds */
voltage = palmas_list_voltage_smps ( rdev , ret ) ;
if ( voltage < min_uV | | voltage > max_uV )
return - EINVAL ;
return ret ;
}
static struct regulator_ops palmas_ops_smps = {
. is_enabled = palmas_is_enabled_smps ,
. enable = palmas_enable_smps ,
. disable = palmas_disable_smps ,
. set_mode = palmas_set_mode_smps ,
. get_mode = palmas_get_mode_smps ,
. get_voltage_sel = palmas_get_voltage_smps_sel ,
. set_voltage_sel = palmas_set_voltage_smps_sel ,
. list_voltage = palmas_list_voltage_smps ,
. map_voltage = palmas_map_voltage_smps ,
} ;
static struct regulator_ops palmas_ops_smps10 = {
. is_enabled = regulator_is_enabled_regmap ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
2012-05-22 08:26:42 +04:00
. list_voltage = regulator_list_voltage_linear ,
. map_voltage = regulator_map_voltage_linear ,
2012-05-18 19:53:57 +04:00
} ;
static int palmas_is_enabled_ldo ( struct regulator_dev * dev )
{
struct palmas_pmic * pmic = rdev_get_drvdata ( dev ) ;
int id = rdev_get_id ( dev ) ;
unsigned int reg ;
palmas_ldo_read ( pmic - > palmas , palmas_regs_info [ id ] . ctrl_addr , & reg ) ;
reg & = PALMAS_LDO1_CTRL_STATUS ;
return ! ! ( reg ) ;
}
static struct regulator_ops palmas_ops_ldo = {
. is_enabled = palmas_is_enabled_ldo ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
2012-07-18 08:34:08 +04:00
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
2012-11-27 06:27:34 +04:00
. list_voltage = regulator_list_voltage_linear ,
. map_voltage = regulator_map_voltage_linear ,
2012-05-18 19:53:57 +04:00
} ;
/*
* setup the hardware based sleep configuration of the SMPS / LDO regulators
* from the platform data . This is different to the software based control
* supported by the regulator framework as it is controlled by toggling
* pins on the PMIC such as PREQ , SYSEN , . . .
*/
static int palmas_smps_init ( struct palmas * palmas , int id ,
struct palmas_reg_init * reg_init )
{
unsigned int reg ;
unsigned int addr ;
int ret ;
addr = palmas_regs_info [ id ] . ctrl_addr ;
ret = palmas_smps_read ( palmas , addr , & reg ) ;
if ( ret )
return ret ;
2012-06-06 16:01:38 +04:00
switch ( id ) {
case PALMAS_REG_SMPS10 :
if ( reg_init - > mode_sleep ) {
reg & = ~ PALMAS_SMPS10_CTRL_MODE_SLEEP_MASK ;
reg | = reg_init - > mode_sleep < <
PALMAS_SMPS10_CTRL_MODE_SLEEP_SHIFT ;
}
break ;
default :
2012-05-18 19:53:57 +04:00
if ( reg_init - > warm_reset )
reg | = PALMAS_SMPS12_CTRL_WR_S ;
if ( reg_init - > roof_floor )
reg | = PALMAS_SMPS12_CTRL_ROOF_FLOOR_EN ;
if ( reg_init - > mode_sleep ) {
reg & = ~ PALMAS_SMPS12_CTRL_MODE_SLEEP_MASK ;
reg | = reg_init - > mode_sleep < <
PALMAS_SMPS12_CTRL_MODE_SLEEP_SHIFT ;
}
}
2012-06-06 16:01:38 +04:00
2012-05-18 19:53:57 +04:00
ret = palmas_smps_write ( palmas , addr , reg ) ;
if ( ret )
return ret ;
if ( palmas_regs_info [ id ] . tstep_addr & & reg_init - > tstep ) {
addr = palmas_regs_info [ id ] . tstep_addr ;
reg = reg_init - > tstep & PALMAS_SMPS12_TSTEP_TSTEP_MASK ;
ret = palmas_smps_write ( palmas , addr , reg ) ;
if ( ret )
return ret ;
}
if ( palmas_regs_info [ id ] . vsel_addr & & reg_init - > vsel ) {
addr = palmas_regs_info [ id ] . vsel_addr ;
reg = reg_init - > vsel ;
ret = palmas_smps_write ( palmas , addr , reg ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static int palmas_ldo_init ( struct palmas * palmas , int id ,
struct palmas_reg_init * reg_init )
{
unsigned int reg ;
unsigned int addr ;
int ret ;
addr = palmas_regs_info [ id ] . ctrl_addr ;
2012-07-18 08:31:59 +04:00
ret = palmas_ldo_read ( palmas , addr , & reg ) ;
2012-05-18 19:53:57 +04:00
if ( ret )
return ret ;
if ( reg_init - > warm_reset )
reg | = PALMAS_LDO1_CTRL_WR_S ;
if ( reg_init - > mode_sleep )
reg | = PALMAS_LDO1_CTRL_MODE_SLEEP ;
2012-07-18 08:31:59 +04:00
ret = palmas_ldo_write ( palmas , addr , reg ) ;
2012-05-18 19:53:57 +04:00
if ( ret )
return ret ;
return 0 ;
}
2012-08-28 15:47:40 +04:00
static struct of_regulator_match palmas_matches [ ] = {
{ . name = " smps12 " , } ,
{ . name = " smps123 " , } ,
{ . name = " smps3 " , } ,
{ . name = " smps45 " , } ,
{ . name = " smps457 " , } ,
{ . name = " smps6 " , } ,
{ . name = " smps7 " , } ,
{ . name = " smps8 " , } ,
{ . name = " smps9 " , } ,
{ . name = " smps10 " , } ,
{ . name = " ldo1 " , } ,
{ . name = " ldo2 " , } ,
{ . name = " ldo3 " , } ,
{ . name = " ldo4 " , } ,
{ . name = " ldo5 " , } ,
{ . name = " ldo6 " , } ,
{ . name = " ldo7 " , } ,
{ . name = " ldo8 " , } ,
{ . name = " ldo9 " , } ,
{ . name = " ldoln " , } ,
{ . name = " ldousb " , } ,
} ;
static void __devinit palmas_dt_to_pdata ( struct device * dev ,
struct device_node * node ,
struct palmas_pmic_platform_data * pdata )
{
struct device_node * regulators ;
u32 prop ;
int idx , ret ;
regulators = of_find_node_by_name ( node , " regulators " ) ;
if ( ! regulators ) {
dev_info ( dev , " regulator node not found \n " ) ;
return ;
}
ret = of_regulator_match ( dev , regulators , palmas_matches ,
PALMAS_NUM_REGS ) ;
if ( ret < 0 ) {
dev_err ( dev , " Error parsing regulator init data: %d \n " , ret ) ;
return ;
}
for ( idx = 0 ; idx < PALMAS_NUM_REGS ; idx + + ) {
if ( ! palmas_matches [ idx ] . init_data | |
! palmas_matches [ idx ] . of_node )
continue ;
pdata - > reg_data [ idx ] = palmas_matches [ idx ] . init_data ;
pdata - > reg_init [ idx ] = devm_kzalloc ( dev ,
sizeof ( struct palmas_reg_init ) , GFP_KERNEL ) ;
ret = of_property_read_u32 ( palmas_matches [ idx ] . of_node ,
" ti,warm_reset " , & prop ) ;
if ( ! ret )
pdata - > reg_init [ idx ] - > warm_reset = prop ;
ret = of_property_read_u32 ( palmas_matches [ idx ] . of_node ,
" ti,roof_floor " , & prop ) ;
if ( ! ret )
pdata - > reg_init [ idx ] - > roof_floor = prop ;
ret = of_property_read_u32 ( palmas_matches [ idx ] . of_node ,
" ti,mode_sleep " , & prop ) ;
if ( ! ret )
pdata - > reg_init [ idx ] - > mode_sleep = prop ;
ret = of_property_read_u32 ( palmas_matches [ idx ] . of_node ,
" ti,warm_reset " , & prop ) ;
if ( ! ret )
pdata - > reg_init [ idx ] - > warm_reset = prop ;
ret = of_property_read_u32 ( palmas_matches [ idx ] . of_node ,
" ti,tstep " , & prop ) ;
if ( ! ret )
pdata - > reg_init [ idx ] - > tstep = prop ;
ret = of_property_read_u32 ( palmas_matches [ idx ] . of_node ,
" ti,vsel " , & prop ) ;
if ( ! ret )
pdata - > reg_init [ idx ] - > vsel = prop ;
}
ret = of_property_read_u32 ( node , " ti,ldo6_vibrator " , & prop ) ;
if ( ! ret )
pdata - > ldo6_vibrator = prop ;
}
2012-05-18 19:53:57 +04:00
static __devinit int palmas_probe ( struct platform_device * pdev )
{
struct palmas * palmas = dev_get_drvdata ( pdev - > dev . parent ) ;
struct palmas_pmic_platform_data * pdata = pdev - > dev . platform_data ;
2012-08-28 15:47:40 +04:00
struct device_node * node = pdev - > dev . of_node ;
2012-05-18 19:53:57 +04:00
struct regulator_dev * rdev ;
struct regulator_config config = { } ;
struct palmas_pmic * pmic ;
struct palmas_reg_init * reg_init ;
int id = 0 , ret ;
unsigned int addr , reg ;
2012-08-28 15:47:40 +04:00
if ( node & & ! pdata ) {
pdata = devm_kzalloc ( & pdev - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
palmas_dt_to_pdata ( & pdev - > dev , node , pdata ) ;
}
2012-05-18 19:53:57 +04:00
pmic = devm_kzalloc ( & pdev - > dev , sizeof ( * pmic ) , GFP_KERNEL ) ;
if ( ! pmic )
return - ENOMEM ;
pmic - > dev = & pdev - > dev ;
pmic - > palmas = palmas ;
palmas - > pmic = pmic ;
platform_set_drvdata ( pdev , pmic ) ;
ret = palmas_smps_read ( palmas , PALMAS_SMPS_CTRL , & reg ) ;
if ( ret )
2012-07-18 08:33:20 +04:00
return ret ;
2012-05-18 19:53:57 +04:00
if ( reg & PALMAS_SMPS_CTRL_SMPS12_SMPS123_EN )
pmic - > smps123 = 1 ;
if ( reg & PALMAS_SMPS_CTRL_SMPS45_SMPS457_EN )
pmic - > smps457 = 1 ;
config . regmap = palmas - > regmap [ REGULATOR_SLAVE ] ;
config . dev = & pdev - > dev ;
config . driver_data = pmic ;
for ( id = 0 ; id < PALMAS_REG_LDO1 ; id + + ) {
/*
* Miss out regulators which are not available due
* to slaving configurations .
*/
switch ( id ) {
case PALMAS_REG_SMPS12 :
case PALMAS_REG_SMPS3 :
if ( pmic - > smps123 )
continue ;
break ;
case PALMAS_REG_SMPS123 :
if ( ! pmic - > smps123 )
continue ;
break ;
case PALMAS_REG_SMPS45 :
case PALMAS_REG_SMPS7 :
if ( pmic - > smps457 )
continue ;
break ;
case PALMAS_REG_SMPS457 :
if ( ! pmic - > smps457 )
continue ;
}
/* Register the regulators */
pmic - > desc [ id ] . name = palmas_regs_info [ id ] . name ;
pmic - > desc [ id ] . id = id ;
2012-06-06 16:01:38 +04:00
switch ( id ) {
case PALMAS_REG_SMPS10 :
2012-05-18 19:53:57 +04:00
pmic - > desc [ id ] . n_voltages = PALMAS_SMPS10_NUM_VOLTAGES ;
pmic - > desc [ id ] . ops = & palmas_ops_smps10 ;
2012-07-18 08:31:03 +04:00
pmic - > desc [ id ] . vsel_reg =
PALMAS_BASE_TO_REG ( PALMAS_SMPS_BASE ,
PALMAS_SMPS10_CTRL ) ;
2012-05-18 19:53:57 +04:00
pmic - > desc [ id ] . vsel_mask = SMPS10_VSEL ;
2012-06-22 16:36:20 +04:00
pmic - > desc [ id ] . enable_reg =
PALMAS_BASE_TO_REG ( PALMAS_SMPS_BASE ,
PALMAS_SMPS10_STATUS ) ;
2012-05-18 19:53:57 +04:00
pmic - > desc [ id ] . enable_mask = SMPS10_BOOST_EN ;
2012-05-22 08:26:42 +04:00
pmic - > desc [ id ] . min_uV = 3750000 ;
pmic - > desc [ id ] . uV_step = 1250000 ;
2012-06-06 16:01:38 +04:00
break ;
default :
pmic - > desc [ id ] . ops = & palmas_ops_smps ;
pmic - > desc [ id ] . n_voltages = PALMAS_SMPS_NUM_VOLTAGES ;
2012-05-18 19:53:57 +04:00
}
pmic - > desc [ id ] . type = REGULATOR_VOLTAGE ;
pmic - > desc [ id ] . owner = THIS_MODULE ;
/* Initialise sleep/init values from platform data */
2012-08-28 15:47:40 +04:00
if ( pdata ) {
2012-05-18 19:53:57 +04:00
reg_init = pdata - > reg_init [ id ] ;
if ( reg_init ) {
ret = palmas_smps_init ( palmas , id , reg_init ) ;
if ( ret )
goto err_unregister_regulator ;
}
}
/*
* read and store the RANGE bit for later use
* This must be done before regulator is probed otherwise
* we error in probe with unsuportable ranges .
*/
if ( id ! = PALMAS_REG_SMPS10 ) {
addr = palmas_regs_info [ id ] . vsel_addr ;
ret = palmas_smps_read ( pmic - > palmas , addr , & reg ) ;
if ( ret )
goto err_unregister_regulator ;
if ( reg & PALMAS_SMPS12_VOLTAGE_RANGE )
pmic - > range [ id ] = 1 ;
}
2012-08-28 15:47:40 +04:00
if ( pdata )
2012-05-18 19:53:57 +04:00
config . init_data = pdata - > reg_data [ id ] ;
else
config . init_data = NULL ;
2012-08-28 15:47:40 +04:00
config . of_node = palmas_matches [ id ] . of_node ;
2012-05-18 19:53:57 +04:00
rdev = regulator_register ( & pmic - > desc [ id ] , & config ) ;
if ( IS_ERR ( rdev ) ) {
dev_err ( & pdev - > dev ,
" failed to register %s regulator \n " ,
pdev - > name ) ;
ret = PTR_ERR ( rdev ) ;
goto err_unregister_regulator ;
}
/* Save regulator for cleanup */
pmic - > rdev [ id ] = rdev ;
}
/* Start this loop from the id left from previous loop */
for ( ; id < PALMAS_NUM_REGS ; id + + ) {
/* Miss out regulators which are not available due
* to alternate functions .
*/
/* Register the regulators */
pmic - > desc [ id ] . name = palmas_regs_info [ id ] . name ;
pmic - > desc [ id ] . id = id ;
pmic - > desc [ id ] . n_voltages = PALMAS_LDO_NUM_VOLTAGES ;
pmic - > desc [ id ] . ops = & palmas_ops_ldo ;
pmic - > desc [ id ] . type = REGULATOR_VOLTAGE ;
pmic - > desc [ id ] . owner = THIS_MODULE ;
2012-11-27 06:27:34 +04:00
pmic - > desc [ id ] . min_uV = 900000 ;
pmic - > desc [ id ] . uV_step = 50000 ;
pmic - > desc [ id ] . linear_min_sel = 1 ;
2012-07-18 08:34:08 +04:00
pmic - > desc [ id ] . vsel_reg = PALMAS_BASE_TO_REG ( PALMAS_LDO_BASE ,
palmas_regs_info [ id ] . vsel_addr ) ;
pmic - > desc [ id ] . vsel_mask = PALMAS_LDO1_VOLTAGE_VSEL_MASK ;
2012-06-22 16:36:20 +04:00
pmic - > desc [ id ] . enable_reg = PALMAS_BASE_TO_REG ( PALMAS_LDO_BASE ,
palmas_regs_info [ id ] . ctrl_addr ) ;
2012-05-18 19:53:57 +04:00
pmic - > desc [ id ] . enable_mask = PALMAS_LDO1_CTRL_MODE_ACTIVE ;
2012-08-28 15:47:40 +04:00
if ( pdata )
2012-05-18 19:53:57 +04:00
config . init_data = pdata - > reg_data [ id ] ;
else
config . init_data = NULL ;
2012-08-28 15:47:40 +04:00
config . of_node = palmas_matches [ id ] . of_node ;
2012-05-18 19:53:57 +04:00
rdev = regulator_register ( & pmic - > desc [ id ] , & config ) ;
if ( IS_ERR ( rdev ) ) {
dev_err ( & pdev - > dev ,
" failed to register %s regulator \n " ,
pdev - > name ) ;
ret = PTR_ERR ( rdev ) ;
goto err_unregister_regulator ;
}
/* Save regulator for cleanup */
pmic - > rdev [ id ] = rdev ;
/* Initialise sleep/init values from platform data */
2012-08-28 15:47:40 +04:00
if ( pdata ) {
2012-05-18 19:53:57 +04:00
reg_init = pdata - > reg_init [ id ] ;
if ( reg_init ) {
ret = palmas_ldo_init ( palmas , id , reg_init ) ;
2012-07-18 08:33:20 +04:00
if ( ret ) {
regulator_unregister ( pmic - > rdev [ id ] ) ;
2012-05-18 19:53:57 +04:00
goto err_unregister_regulator ;
2012-07-18 08:33:20 +04:00
}
2012-05-18 19:53:57 +04:00
}
}
}
return 0 ;
err_unregister_regulator :
while ( - - id > = 0 )
regulator_unregister ( pmic - > rdev [ id ] ) ;
return ret ;
}
static int __devexit palmas_remove ( struct platform_device * pdev )
{
struct palmas_pmic * pmic = platform_get_drvdata ( pdev ) ;
int id ;
for ( id = 0 ; id < PALMAS_NUM_REGS ; id + + )
regulator_unregister ( pmic - > rdev [ id ] ) ;
return 0 ;
}
2012-08-28 15:47:40 +04:00
static struct of_device_id __devinitdata of_palmas_match_tbl [ ] = {
{ . compatible = " ti,palmas-pmic " , } ,
{ /* end */ }
} ;
2012-05-18 19:53:57 +04:00
static struct platform_driver palmas_driver = {
. driver = {
. name = " palmas-pmic " ,
2012-08-28 15:47:40 +04:00
. of_match_table = of_palmas_match_tbl ,
2012-05-18 19:53:57 +04:00
. owner = THIS_MODULE ,
} ,
. probe = palmas_probe ,
. remove = __devexit_p ( palmas_remove ) ,
} ;
static int __init palmas_init ( void )
{
return platform_driver_register ( & palmas_driver ) ;
}
subsys_initcall ( palmas_init ) ;
static void __exit palmas_exit ( void )
{
platform_driver_unregister ( & palmas_driver ) ;
}
module_exit ( palmas_exit ) ;
MODULE_AUTHOR ( " Graeme Gregory <gg@slimlogic.co.uk> " ) ;
MODULE_DESCRIPTION ( " Palmas voltage regulator driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:palmas-pmic " ) ;
2012-08-28 15:47:40 +04:00
MODULE_DEVICE_TABLE ( of , of_palmas_match_tbl ) ;