2011-06-09 23:50:27 +04:00
/*
* tps65912 . c - - TI tps65912
*
* Copyright 2011 Texas Instruments Inc .
*
* Author : Margarita Olaya Cabrera < magi @ 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 .
*
* This driver is based on wm8350 implementation .
*/
# 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/delay.h>
# include <linux/slab.h>
# include <linux/gpio.h>
# include <linux/mfd/tps65912.h>
/* DCDC's */
# define TPS65912_REG_DCDC1 0
# define TPS65912_REG_DCDC2 1
# define TPS65912_REG_DCDC3 2
# define TPS65912_REG_DCDC4 3
/* LDOs */
# define TPS65912_REG_LDO1 4
# define TPS65912_REG_LDO2 5
# define TPS65912_REG_LDO3 6
# define TPS65912_REG_LDO4 7
# define TPS65912_REG_LDO5 8
# define TPS65912_REG_LDO6 9
# define TPS65912_REG_LDO7 10
# define TPS65912_REG_LDO8 11
# define TPS65912_REG_LDO9 12
# define TPS65912_REG_LDO10 13
/* Number of step-down converters available */
# define TPS65912_NUM_DCDC 4
/* Number of LDO voltage regulators available */
# define TPS65912_NUM_LDO 10
/* Number of total regulators available */
# define TPS65912_NUM_REGULATOR (TPS65912_NUM_DCDC + TPS65912_NUM_LDO)
# define TPS65912_REG_ENABLED 0x80
# define OP_SELREG_MASK 0x40
# define OP_SELREG_SHIFT 6
struct tps_info {
const char * name ;
} ;
static struct tps_info tps65912_regs [ ] = {
{
. name = " DCDC1 " ,
} ,
{
. name = " DCDC2 " ,
} ,
{
. name = " DCDC3 " ,
} ,
{
. name = " DCDC4 " ,
} ,
{
. name = " LDO1 " ,
} ,
{
. name = " LDO2 " ,
} ,
{
. name = " LDO3 " ,
} ,
{
. name = " LDO4 " ,
} ,
{
. name = " LDO5 " ,
} ,
{
. name = " LDO6 " ,
} ,
{
. name = " LDO7 " ,
} ,
{
. name = " LDO8 " ,
} ,
{
. name = " LDO9 " ,
} ,
{
. name = " LDO10 " ,
} ,
} ;
struct tps65912_reg {
struct regulator_desc desc [ TPS65912_NUM_REGULATOR ] ;
struct tps65912 * mfd ;
struct regulator_dev * rdev [ TPS65912_NUM_REGULATOR ] ;
struct tps_info * info [ TPS65912_NUM_REGULATOR ] ;
/* for read/write access */
struct mutex io_lock ;
int mode ;
int ( * get_ctrl_reg ) ( int ) ;
2012-03-09 03:05:41 +04:00
int dcdc_range [ TPS65912_NUM_DCDC ] ;
2011-06-09 23:50:27 +04:00
int pwm_mode_reg ;
int eco_reg ;
} ;
static int tps65912_get_range ( struct tps65912_reg * pmic , int id )
{
struct tps65912 * mfd = pmic - > mfd ;
2012-03-09 03:05:41 +04:00
int range ;
2011-06-09 23:50:27 +04:00
switch ( id ) {
case TPS65912_REG_DCDC1 :
2012-03-09 03:05:41 +04:00
range = tps65912_reg_read ( mfd , TPS65912_DCDC1_LIMIT ) ;
break ;
2011-06-09 23:50:27 +04:00
case TPS65912_REG_DCDC2 :
2012-03-09 03:05:41 +04:00
range = tps65912_reg_read ( mfd , TPS65912_DCDC2_LIMIT ) ;
break ;
2011-06-09 23:50:27 +04:00
case TPS65912_REG_DCDC3 :
2012-03-09 03:05:41 +04:00
range = tps65912_reg_read ( mfd , TPS65912_DCDC3_LIMIT ) ;
break ;
2011-06-09 23:50:27 +04:00
case TPS65912_REG_DCDC4 :
2012-03-09 03:05:41 +04:00
range = tps65912_reg_read ( mfd , TPS65912_DCDC4_LIMIT ) ;
break ;
2011-06-09 23:50:27 +04:00
default :
return 0 ;
}
2012-03-09 03:05:41 +04:00
if ( range > = 0 )
range = ( range & DCDC_LIMIT_RANGE_MASK )
> > DCDC_LIMIT_RANGE_SHIFT ;
pmic - > dcdc_range [ id ] = range ;
return range ;
2011-06-09 23:50:27 +04:00
}
static unsigned long tps65912_vsel_to_uv_range0 ( u8 vsel )
{
unsigned long uv ;
uv = ( ( vsel * 12500 ) + 500000 ) ;
return uv ;
}
static unsigned long tps65912_vsel_to_uv_range1 ( u8 vsel )
{
unsigned long uv ;
uv = ( ( vsel * 12500 ) + 700000 ) ;
return uv ;
}
static unsigned long tps65912_vsel_to_uv_range2 ( u8 vsel )
{
unsigned long uv ;
uv = ( ( vsel * 25000 ) + 500000 ) ;
return uv ;
}
static unsigned long tps65912_vsel_to_uv_range3 ( u8 vsel )
{
unsigned long uv ;
if ( vsel = = 0x3f )
uv = 3800000 ;
else
uv = ( ( vsel * 50000 ) + 500000 ) ;
return uv ;
}
static unsigned long tps65912_vsel_to_uv_ldo ( u8 vsel )
{
unsigned long uv = 0 ;
if ( vsel < = 32 )
uv = ( ( vsel * 25000 ) + 800000 ) ;
else if ( vsel > 32 & & vsel < = 60 )
uv = ( ( ( vsel - 32 ) * 50000 ) + 1600000 ) ;
else if ( vsel > 60 )
uv = ( ( ( vsel - 60 ) * 100000 ) + 3000000 ) ;
return uv ;
}
static int tps65912_get_ctrl_register ( int id )
{
2012-03-13 02:40:48 +04:00
if ( id > = TPS65912_REG_DCDC1 & & id < = TPS65912_REG_LDO4 )
return id * 3 + TPS65912_DCDC1_AVS ;
else if ( id > = TPS65912_REG_LDO5 & & id < = TPS65912_REG_LDO10 )
return id - TPS65912_REG_LDO5 + TPS65912_LDO5 ;
else
2011-06-09 23:50:27 +04:00
return - EINVAL ;
}
2012-03-13 02:40:48 +04:00
static int tps65912_get_sel_register ( struct tps65912_reg * pmic , int id )
2011-06-09 23:50:27 +04:00
{
struct tps65912 * mfd = pmic - > mfd ;
2012-03-13 02:40:48 +04:00
int opvsel ;
2011-06-09 23:50:27 +04:00
u8 reg = 0 ;
2012-03-13 02:40:48 +04:00
if ( id > = TPS65912_REG_DCDC1 & & id < = TPS65912_REG_LDO4 ) {
opvsel = tps65912_reg_read ( mfd , id * 3 + TPS65912_DCDC1_OP ) ;
if ( opvsel & OP_SELREG_MASK )
reg = id * 3 + TPS65912_DCDC1_AVS ;
2011-06-09 23:50:27 +04:00
else
2012-03-13 02:40:48 +04:00
reg = id * 3 + TPS65912_DCDC1_OP ;
} else if ( id > = TPS65912_REG_LDO5 & & id < = TPS65912_REG_LDO10 ) {
reg = id - TPS65912_REG_LDO5 + TPS65912_LDO5 ;
} else {
2011-06-09 23:50:27 +04:00
return - EINVAL ;
}
return reg ;
}
static int tps65912_get_mode_regiters ( struct tps65912_reg * pmic , int id )
{
switch ( id ) {
case TPS65912_REG_DCDC1 :
pmic - > pwm_mode_reg = TPS65912_DCDC1_CTRL ;
pmic - > eco_reg = TPS65912_DCDC1_AVS ;
break ;
case TPS65912_REG_DCDC2 :
pmic - > pwm_mode_reg = TPS65912_DCDC2_CTRL ;
pmic - > eco_reg = TPS65912_DCDC2_AVS ;
break ;
case TPS65912_REG_DCDC3 :
pmic - > pwm_mode_reg = TPS65912_DCDC3_CTRL ;
pmic - > eco_reg = TPS65912_DCDC3_AVS ;
break ;
case TPS65912_REG_DCDC4 :
pmic - > pwm_mode_reg = TPS65912_DCDC4_CTRL ;
pmic - > eco_reg = TPS65912_DCDC4_AVS ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int tps65912_reg_is_enabled ( struct regulator_dev * dev )
{
struct tps65912_reg * pmic = rdev_get_drvdata ( dev ) ;
struct tps65912 * mfd = pmic - > mfd ;
int reg , value , id = rdev_get_id ( dev ) ;
if ( id < TPS65912_REG_DCDC1 | | id > TPS65912_REG_LDO10 )
return - EINVAL ;
reg = pmic - > get_ctrl_reg ( id ) ;
if ( reg < 0 )
return reg ;
value = tps65912_reg_read ( mfd , reg ) ;
if ( value < 0 )
return value ;
return value & TPS65912_REG_ENABLED ;
}
static int tps65912_reg_enable ( struct regulator_dev * dev )
{
struct tps65912_reg * pmic = rdev_get_drvdata ( dev ) ;
struct tps65912 * mfd = pmic - > mfd ;
int id = rdev_get_id ( dev ) ;
2011-07-06 22:57:18 +04:00
int reg ;
2011-06-09 23:50:27 +04:00
if ( id < TPS65912_REG_DCDC1 | | id > TPS65912_REG_LDO10 )
return - EINVAL ;
reg = pmic - > get_ctrl_reg ( id ) ;
if ( reg < 0 )
return reg ;
return tps65912_set_bits ( mfd , reg , TPS65912_REG_ENABLED ) ;
}
static int tps65912_reg_disable ( struct regulator_dev * dev )
{
struct tps65912_reg * pmic = rdev_get_drvdata ( dev ) ;
struct tps65912 * mfd = pmic - > mfd ;
int id = rdev_get_id ( dev ) , reg ;
reg = pmic - > get_ctrl_reg ( id ) ;
if ( reg < 0 )
return reg ;
return tps65912_clear_bits ( mfd , reg , TPS65912_REG_ENABLED ) ;
}
static int tps65912_set_mode ( struct regulator_dev * dev , unsigned int mode )
{
struct tps65912_reg * pmic = rdev_get_drvdata ( dev ) ;
struct tps65912 * mfd = pmic - > mfd ;
int pwm_mode , eco , id = rdev_get_id ( dev ) ;
tps65912_get_mode_regiters ( pmic , id ) ;
pwm_mode = tps65912_reg_read ( mfd , pmic - > pwm_mode_reg ) ;
eco = tps65912_reg_read ( mfd , pmic - > eco_reg ) ;
pwm_mode & = DCDCCTRL_DCDC_MODE_MASK ;
eco & = DCDC_AVS_ECO_MASK ;
switch ( mode ) {
case REGULATOR_MODE_FAST :
/* Verify if mode alredy set */
if ( pwm_mode & & ! eco )
break ;
tps65912_set_bits ( mfd , pmic - > pwm_mode_reg , DCDCCTRL_DCDC_MODE_MASK ) ;
tps65912_clear_bits ( mfd , pmic - > eco_reg , DCDC_AVS_ECO_MASK ) ;
break ;
case REGULATOR_MODE_NORMAL :
case REGULATOR_MODE_IDLE :
if ( ! pwm_mode & & ! eco )
break ;
tps65912_clear_bits ( mfd , pmic - > pwm_mode_reg , DCDCCTRL_DCDC_MODE_MASK ) ;
tps65912_clear_bits ( mfd , pmic - > eco_reg , DCDC_AVS_ECO_MASK ) ;
break ;
case REGULATOR_MODE_STANDBY :
if ( ! pwm_mode & & eco )
break ;
tps65912_clear_bits ( mfd , pmic - > pwm_mode_reg , DCDCCTRL_DCDC_MODE_MASK ) ;
tps65912_set_bits ( mfd , pmic - > eco_reg , DCDC_AVS_ECO_MASK ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static unsigned int tps65912_get_mode ( struct regulator_dev * dev )
{
struct tps65912_reg * pmic = rdev_get_drvdata ( dev ) ;
struct tps65912 * mfd = pmic - > mfd ;
int pwm_mode , eco , mode = 0 , id = rdev_get_id ( dev ) ;
tps65912_get_mode_regiters ( pmic , id ) ;
pwm_mode = tps65912_reg_read ( mfd , pmic - > pwm_mode_reg ) ;
eco = tps65912_reg_read ( mfd , pmic - > eco_reg ) ;
pwm_mode & = DCDCCTRL_DCDC_MODE_MASK ;
eco & = DCDC_AVS_ECO_MASK ;
if ( pwm_mode & & ! eco )
mode = REGULATOR_MODE_FAST ;
else if ( ! pwm_mode & & ! eco )
mode = REGULATOR_MODE_NORMAL ;
else if ( ! pwm_mode & & eco )
mode = REGULATOR_MODE_STANDBY ;
return mode ;
}
2012-03-08 08:07:37 +04:00
static int tps65912_list_voltage_dcdc ( struct regulator_dev * dev ,
unsigned selector )
2011-06-09 23:50:27 +04:00
{
struct tps65912_reg * pmic = rdev_get_drvdata ( dev ) ;
2012-03-08 08:07:37 +04:00
int range , voltage = 0 , id = rdev_get_id ( dev ) ;
2011-06-09 23:50:27 +04:00
2012-03-09 03:05:41 +04:00
if ( id > TPS65912_REG_DCDC4 )
2011-06-09 23:50:27 +04:00
return - EINVAL ;
2012-03-09 03:05:41 +04:00
range = pmic - > dcdc_range [ id ] ;
2011-06-09 23:50:27 +04:00
switch ( range ) {
case 0 :
/* 0.5 - 1.2875V in 12.5mV steps */
2012-03-08 08:07:37 +04:00
voltage = tps65912_vsel_to_uv_range0 ( selector ) ;
2011-06-09 23:50:27 +04:00
break ;
case 1 :
/* 0.7 - 1.4875V in 12.5mV steps */
2012-03-08 08:07:37 +04:00
voltage = tps65912_vsel_to_uv_range1 ( selector ) ;
2011-06-09 23:50:27 +04:00
break ;
case 2 :
/* 0.5 - 2.075V in 25mV steps */
2012-03-08 08:07:37 +04:00
voltage = tps65912_vsel_to_uv_range2 ( selector ) ;
2011-06-09 23:50:27 +04:00
break ;
case 3 :
/* 0.5 - 3.8V in 50mV steps */
2012-03-08 08:07:37 +04:00
voltage = tps65912_vsel_to_uv_range3 ( selector ) ;
2011-06-09 23:50:27 +04:00
break ;
}
return voltage ;
}
2012-03-08 08:07:37 +04:00
static int tps65912_get_voltage_dcdc ( struct regulator_dev * dev )
{
struct tps65912_reg * pmic = rdev_get_drvdata ( dev ) ;
struct tps65912 * mfd = pmic - > mfd ;
int id = rdev_get_id ( dev ) ;
2012-03-14 06:20:01 +04:00
int reg , vsel ;
2012-03-08 08:07:37 +04:00
2012-03-14 06:20:01 +04:00
reg = tps65912_get_sel_register ( pmic , id ) ;
if ( reg < 0 )
return reg ;
2012-03-08 08:07:37 +04:00
2012-03-14 06:20:01 +04:00
vsel = tps65912_reg_read ( mfd , reg ) ;
2012-03-08 08:07:37 +04:00
vsel & = 0x3F ;
return tps65912_list_voltage_dcdc ( dev , vsel ) ;
}
2012-03-14 06:18:45 +04:00
static int tps65912_set_voltage_sel ( struct regulator_dev * dev ,
2012-03-09 06:22:20 +04:00
unsigned selector )
2011-06-09 23:50:27 +04:00
{
struct tps65912_reg * pmic = rdev_get_drvdata ( dev ) ;
struct tps65912 * mfd = pmic - > mfd ;
int id = rdev_get_id ( dev ) ;
int value ;
u8 reg ;
2012-03-13 02:40:48 +04:00
reg = tps65912_get_sel_register ( pmic , id ) ;
2011-06-09 23:50:27 +04:00
value = tps65912_reg_read ( mfd , reg ) ;
value & = 0xC0 ;
return tps65912_reg_write ( mfd , reg , selector | value ) ;
}
static int tps65912_get_voltage_ldo ( struct regulator_dev * dev )
{
struct tps65912_reg * pmic = rdev_get_drvdata ( dev ) ;
struct tps65912 * mfd = pmic - > mfd ;
int id = rdev_get_id ( dev ) ;
int vsel = 0 ;
u8 reg ;
2012-03-13 02:40:48 +04:00
reg = tps65912_get_sel_register ( pmic , id ) ;
2011-06-09 23:50:27 +04:00
vsel = tps65912_reg_read ( mfd , reg ) ;
vsel & = 0x3F ;
return tps65912_vsel_to_uv_ldo ( vsel ) ;
}
static int tps65912_list_voltage_ldo ( struct regulator_dev * dev ,
unsigned selector )
{
int ldo = rdev_get_id ( dev ) ;
if ( ldo < TPS65912_REG_LDO1 | | ldo > TPS65912_REG_LDO10 )
return - EINVAL ;
return tps65912_vsel_to_uv_ldo ( selector ) ;
}
/* Operations permitted on DCDCx */
static struct regulator_ops tps65912_ops_dcdc = {
. is_enabled = tps65912_reg_is_enabled ,
. enable = tps65912_reg_enable ,
. disable = tps65912_reg_disable ,
. set_mode = tps65912_set_mode ,
. get_mode = tps65912_get_mode ,
. get_voltage = tps65912_get_voltage_dcdc ,
2012-03-14 06:18:45 +04:00
. set_voltage_sel = tps65912_set_voltage_sel ,
2011-06-09 23:50:27 +04:00
. list_voltage = tps65912_list_voltage_dcdc ,
} ;
/* Operations permitted on LDOx */
static struct regulator_ops tps65912_ops_ldo = {
. is_enabled = tps65912_reg_is_enabled ,
. enable = tps65912_reg_enable ,
. disable = tps65912_reg_disable ,
. get_voltage = tps65912_get_voltage_ldo ,
2012-03-14 06:18:45 +04:00
. set_voltage_sel = tps65912_set_voltage_sel ,
2011-06-09 23:50:27 +04:00
. list_voltage = tps65912_list_voltage_ldo ,
} ;
static __devinit int tps65912_probe ( struct platform_device * pdev )
{
struct tps65912 * tps65912 = dev_get_drvdata ( pdev - > dev . parent ) ;
struct tps_info * info ;
struct regulator_init_data * reg_data ;
struct regulator_dev * rdev ;
struct tps65912_reg * pmic ;
struct tps65912_board * pmic_plat_data ;
int i , err ;
pmic_plat_data = dev_get_platdata ( tps65912 - > dev ) ;
if ( ! pmic_plat_data )
return - EINVAL ;
reg_data = pmic_plat_data - > tps65912_pmic_init_data ;
pmic = kzalloc ( sizeof ( * pmic ) , GFP_KERNEL ) ;
if ( ! pmic )
return - ENOMEM ;
mutex_init ( & pmic - > io_lock ) ;
pmic - > mfd = tps65912 ;
platform_set_drvdata ( pdev , pmic ) ;
pmic - > get_ctrl_reg = & tps65912_get_ctrl_register ;
info = tps65912_regs ;
for ( i = 0 ; i < TPS65912_NUM_REGULATOR ; i + + , info + + , reg_data + + ) {
int range = 0 ;
/* Register the regulators */
pmic - > info [ i ] = info ;
pmic - > desc [ i ] . name = info - > name ;
pmic - > desc [ i ] . id = i ;
pmic - > desc [ i ] . n_voltages = 64 ;
pmic - > desc [ i ] . ops = ( i > TPS65912_REG_DCDC4 ?
& tps65912_ops_ldo : & tps65912_ops_dcdc ) ;
pmic - > desc [ i ] . type = REGULATOR_VOLTAGE ;
pmic - > desc [ i ] . owner = THIS_MODULE ;
range = tps65912_get_range ( pmic , i ) ;
rdev = regulator_register ( & pmic - > desc [ i ] ,
2011-11-18 15:17:19 +04:00
tps65912 - > dev , reg_data , pmic , NULL ) ;
2011-06-09 23:50:27 +04:00
if ( IS_ERR ( rdev ) ) {
dev_err ( tps65912 - > dev ,
" failed to register %s regulator \n " ,
pdev - > name ) ;
err = PTR_ERR ( rdev ) ;
goto err ;
}
/* Save regulator for cleanup */
pmic - > rdev [ i ] = rdev ;
}
return 0 ;
err :
while ( - - i > = 0 )
regulator_unregister ( pmic - > rdev [ i ] ) ;
kfree ( pmic ) ;
return err ;
}
static int __devexit tps65912_remove ( struct platform_device * pdev )
{
struct tps65912_reg * tps65912_reg = platform_get_drvdata ( pdev ) ;
int i ;
for ( i = 0 ; i < TPS65912_NUM_REGULATOR ; i + + )
regulator_unregister ( tps65912_reg - > rdev [ i ] ) ;
kfree ( tps65912_reg ) ;
return 0 ;
}
static struct platform_driver tps65912_driver = {
. driver = {
. name = " tps65912-pmic " ,
. owner = THIS_MODULE ,
} ,
. probe = tps65912_probe ,
. remove = __devexit_p ( tps65912_remove ) ,
} ;
static int __init tps65912_init ( void )
{
return platform_driver_register ( & tps65912_driver ) ;
}
subsys_initcall ( tps65912_init ) ;
static void __exit tps65912_cleanup ( void )
{
platform_driver_unregister ( & tps65912_driver ) ;
}
module_exit ( tps65912_cleanup ) ;
MODULE_AUTHOR ( " Margarita Olaya Cabrera <magi@slimlogic.co.uk> " ) ;
MODULE_DESCRIPTION ( " TPS65912 voltage regulator driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:tps65912-pmic " ) ;