2010-01-11 15:10:08 +03:00
/*
* Regulators driver for Maxim max8925
*
* Copyright ( C ) 2009 Marvell International Ltd .
* Haojian Zhuang < haojian . zhuang @ marvell . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
2011-07-18 00:28:23 +04:00
# include <linux/module.h>
2010-01-11 15:10:08 +03:00
# include <linux/init.h>
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/platform_device.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
# include <linux/mfd/max8925.h>
# define SD1_DVM_VMIN 850000
# define SD1_DVM_VMAX 1000000
# define SD1_DVM_STEP 50000
# define SD1_DVM_SHIFT 5 /* SDCTL1 bit5 */
# define SD1_DVM_EN 6 /* SDV1 bit 6 */
2012-01-04 11:12:04 +04:00
/* bit definitions in LDO control registers */
# define LDO_SEQ_I2C 0x7 /* Power U/D by i2c */
# define LDO_SEQ_MASK 0x7 /* Power U/D sequence mask */
# define LDO_SEQ_SHIFT 2 /* Power U/D sequence offset */
# define LDO_I2C_EN 0x1 /* Enable by i2c */
# define LDO_I2C_EN_MASK 0x1 /* Enable mask by i2c */
# define LDO_I2C_EN_SHIFT 0 /* Enable offset by i2c */
2011-04-18 18:04:09 +04:00
2010-01-11 15:10:08 +03:00
struct max8925_regulator_info {
struct regulator_desc desc ;
struct regulator_dev * regulator ;
struct i2c_client * i2c ;
struct max8925_chip * chip ;
int min_uV ;
int max_uV ;
int step_uV ;
int vol_reg ;
int enable_reg ;
} ;
static inline int check_range ( struct max8925_regulator_info * info ,
int min_uV , int max_uV )
{
if ( min_uV < info - > min_uV | | min_uV > info - > max_uV )
return - EINVAL ;
return 0 ;
}
static int max8925_list_voltage ( struct regulator_dev * rdev , unsigned index )
{
struct max8925_regulator_info * info = rdev_get_drvdata ( rdev ) ;
return info - > min_uV + index * info - > step_uV ;
}
static int max8925_set_voltage ( struct regulator_dev * rdev ,
2010-11-10 17:38:29 +03:00
int min_uV , int max_uV , unsigned int * selector )
2010-01-11 15:10:08 +03:00
{
struct max8925_regulator_info * info = rdev_get_drvdata ( rdev ) ;
unsigned char data , mask ;
if ( check_range ( info , min_uV , max_uV ) ) {
dev_err ( info - > chip - > dev , " invalid voltage range (%d, %d) uV \n " ,
min_uV , max_uV ) ;
return - EINVAL ;
}
2012-03-13 12:09:28 +04:00
data = DIV_ROUND_UP ( min_uV - info - > min_uV , info - > step_uV ) ;
2010-11-10 17:38:29 +03:00
* selector = data ;
2012-04-02 05:16:08 +04:00
mask = rdev - > desc - > n_voltages - 1 ;
2010-01-11 15:10:08 +03:00
return max8925_set_bits ( info - > i2c , info - > vol_reg , mask , data ) ;
}
static int max8925_get_voltage ( struct regulator_dev * rdev )
{
struct max8925_regulator_info * info = rdev_get_drvdata ( rdev ) ;
unsigned char data , mask ;
int ret ;
ret = max8925_reg_read ( info - > i2c , info - > vol_reg ) ;
if ( ret < 0 )
return ret ;
2012-04-02 05:16:08 +04:00
mask = rdev - > desc - > n_voltages - 1 ;
data = ret & mask ;
2010-01-11 15:10:08 +03:00
return max8925_list_voltage ( rdev , data ) ;
}
static int max8925_enable ( struct regulator_dev * rdev )
{
struct max8925_regulator_info * info = rdev_get_drvdata ( rdev ) ;
return max8925_set_bits ( info - > i2c , info - > enable_reg ,
2012-01-04 11:12:04 +04:00
LDO_SEQ_MASK < < LDO_SEQ_SHIFT |
LDO_I2C_EN_MASK < < LDO_I2C_EN_SHIFT ,
LDO_SEQ_I2C < < LDO_SEQ_SHIFT |
LDO_I2C_EN < < LDO_I2C_EN_SHIFT ) ;
2010-01-11 15:10:08 +03:00
}
static int max8925_disable ( struct regulator_dev * rdev )
{
struct max8925_regulator_info * info = rdev_get_drvdata ( rdev ) ;
return max8925_set_bits ( info - > i2c , info - > enable_reg ,
2012-01-04 11:12:04 +04:00
LDO_SEQ_MASK < < LDO_SEQ_SHIFT |
LDO_I2C_EN_MASK < < LDO_I2C_EN_SHIFT ,
LDO_SEQ_I2C < < LDO_SEQ_SHIFT ) ;
2010-01-11 15:10:08 +03:00
}
static int max8925_is_enabled ( struct regulator_dev * rdev )
{
struct max8925_regulator_info * info = rdev_get_drvdata ( rdev ) ;
2012-01-04 11:12:04 +04:00
int ldo_seq , ret ;
2010-01-11 15:10:08 +03:00
2010-04-06 14:19:15 +04:00
ret = max8925_reg_read ( info - > i2c , info - > enable_reg ) ;
2010-01-11 15:10:08 +03:00
if ( ret < 0 )
return ret ;
2012-01-04 11:12:04 +04:00
ldo_seq = ( ret > > LDO_SEQ_SHIFT ) & LDO_SEQ_MASK ;
if ( ldo_seq ! = LDO_SEQ_I2C )
return 1 ;
else
return ret & ( LDO_I2C_EN_MASK < < LDO_I2C_EN_SHIFT ) ;
2010-01-11 15:10:08 +03:00
}
static int max8925_set_dvm_voltage ( struct regulator_dev * rdev , int uV )
{
struct max8925_regulator_info * info = rdev_get_drvdata ( rdev ) ;
unsigned char data , mask ;
if ( uV < SD1_DVM_VMIN | | uV > SD1_DVM_VMAX )
return - EINVAL ;
2012-03-13 12:09:28 +04:00
data = DIV_ROUND_UP ( uV - SD1_DVM_VMIN , SD1_DVM_STEP ) ;
2010-01-11 15:10:08 +03:00
data < < = SD1_DVM_SHIFT ;
mask = 3 < < SD1_DVM_SHIFT ;
return max8925_set_bits ( info - > i2c , info - > enable_reg , mask , data ) ;
}
static int max8925_set_dvm_enable ( struct regulator_dev * rdev )
{
struct max8925_regulator_info * info = rdev_get_drvdata ( rdev ) ;
return max8925_set_bits ( info - > i2c , info - > vol_reg , 1 < < SD1_DVM_EN ,
1 < < SD1_DVM_EN ) ;
}
static int max8925_set_dvm_disable ( struct regulator_dev * rdev )
{
struct max8925_regulator_info * info = rdev_get_drvdata ( rdev ) ;
return max8925_set_bits ( info - > i2c , info - > vol_reg , 1 < < SD1_DVM_EN , 0 ) ;
}
static struct regulator_ops max8925_regulator_sdv_ops = {
2012-04-02 05:14:28 +04:00
. list_voltage = max8925_list_voltage ,
2010-01-11 15:10:08 +03:00
. set_voltage = max8925_set_voltage ,
. get_voltage = max8925_get_voltage ,
. enable = max8925_enable ,
. disable = max8925_disable ,
. is_enabled = max8925_is_enabled ,
. set_suspend_voltage = max8925_set_dvm_voltage ,
. set_suspend_enable = max8925_set_dvm_enable ,
. set_suspend_disable = max8925_set_dvm_disable ,
} ;
static struct regulator_ops max8925_regulator_ldo_ops = {
2012-04-02 05:14:28 +04:00
. list_voltage = max8925_list_voltage ,
2010-01-11 15:10:08 +03:00
. set_voltage = max8925_set_voltage ,
. get_voltage = max8925_get_voltage ,
. enable = max8925_enable ,
. disable = max8925_disable ,
. is_enabled = max8925_is_enabled ,
} ;
# define MAX8925_SDV(_id, min, max, step) \
{ \
. desc = { \
. name = " SDV " # _id , \
. ops = & max8925_regulator_sdv_ops , \
. type = REGULATOR_VOLTAGE , \
. id = MAX8925_ID_SD # # _id , \
. owner = THIS_MODULE , \
2012-04-02 05:16:08 +04:00
. n_voltages = 64 , \
2010-01-11 15:10:08 +03:00
} , \
. min_uV = min * 1000 , \
. max_uV = max * 1000 , \
. step_uV = step * 1000 , \
. vol_reg = MAX8925_SDV # # _id , \
. enable_reg = MAX8925_SDCTL # # _id , \
}
# define MAX8925_LDO(_id, min, max, step) \
{ \
. desc = { \
. name = " LDO " # _id , \
. ops = & max8925_regulator_ldo_ops , \
. type = REGULATOR_VOLTAGE , \
. id = MAX8925_ID_LDO # # _id , \
. owner = THIS_MODULE , \
2012-04-02 05:16:08 +04:00
. n_voltages = 64 , \
2010-01-11 15:10:08 +03:00
} , \
. min_uV = min * 1000 , \
. max_uV = max * 1000 , \
. step_uV = step * 1000 , \
. vol_reg = MAX8925_LDOVOUT # # _id , \
. enable_reg = MAX8925_LDOCTL # # _id , \
}
static struct max8925_regulator_info max8925_regulator_info [ ] = {
MAX8925_SDV ( 1 , 637.5 , 1425 , 12.5 ) ,
MAX8925_SDV ( 2 , 650 , 2225 , 25 ) ,
MAX8925_SDV ( 3 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 1 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 2 , 650 , 2250 , 25 ) ,
MAX8925_LDO ( 3 , 650 , 2250 , 25 ) ,
MAX8925_LDO ( 4 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 5 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 6 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 7 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 8 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 9 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 10 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 11 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 12 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 13 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 14 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 15 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 16 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 17 , 650 , 2250 , 25 ) ,
MAX8925_LDO ( 18 , 650 , 2250 , 25 ) ,
MAX8925_LDO ( 19 , 750 , 3900 , 50 ) ,
MAX8925_LDO ( 20 , 750 , 3900 , 50 ) ,
} ;
2010-02-24 10:38:55 +03:00
static struct max8925_regulator_info * __devinit find_regulator_info ( int id )
2010-01-11 15:10:08 +03:00
{
struct max8925_regulator_info * ri ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( max8925_regulator_info ) ; i + + ) {
ri = & max8925_regulator_info [ i ] ;
if ( ri - > desc . id = = id )
return ri ;
}
return NULL ;
}
static int __devinit max8925_regulator_probe ( struct platform_device * pdev )
{
struct max8925_chip * chip = dev_get_drvdata ( pdev - > dev . parent ) ;
struct max8925_platform_data * pdata = chip - > dev - > platform_data ;
2012-04-04 03:50:22 +04:00
struct regulator_config config = { } ;
2010-02-24 10:38:55 +03:00
struct max8925_regulator_info * ri ;
2010-01-11 15:10:08 +03:00
struct regulator_dev * rdev ;
ri = find_regulator_info ( pdev - > id ) ;
if ( ri = = NULL ) {
dev_err ( & pdev - > dev , " invalid regulator ID specified \n " ) ;
return - EINVAL ;
}
ri - > i2c = chip - > i2c ;
ri - > chip = chip ;
2012-04-04 03:50:22 +04:00
config . dev = & pdev - > dev ;
config . init_data = pdata - > regulator [ pdev - > id ] ;
config . driver_data = ri ;
rdev = regulator_register ( & ri - > desc , & config ) ;
2010-01-11 15:10:08 +03:00
if ( IS_ERR ( rdev ) ) {
dev_err ( & pdev - > dev , " failed to register regulator %s \n " ,
ri - > desc . name ) ;
return PTR_ERR ( rdev ) ;
}
platform_set_drvdata ( pdev , rdev ) ;
return 0 ;
}
static int __devexit max8925_regulator_remove ( struct platform_device * pdev )
{
struct regulator_dev * rdev = platform_get_drvdata ( pdev ) ;
2010-02-24 10:38:55 +03:00
platform_set_drvdata ( pdev , NULL ) ;
2010-01-11 15:10:08 +03:00
regulator_unregister ( rdev ) ;
2010-02-24 10:38:55 +03:00
2010-01-11 15:10:08 +03:00
return 0 ;
}
static struct platform_driver max8925_regulator_driver = {
. driver = {
. name = " max8925-regulator " ,
. owner = THIS_MODULE ,
} ,
. probe = max8925_regulator_probe ,
. remove = __devexit_p ( max8925_regulator_remove ) ,
} ;
static int __init max8925_regulator_init ( void )
{
return platform_driver_register ( & max8925_regulator_driver ) ;
}
subsys_initcall ( max8925_regulator_init ) ;
static void __exit max8925_regulator_exit ( void )
{
platform_driver_unregister ( & max8925_regulator_driver ) ;
}
module_exit ( max8925_regulator_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Haojian Zhuang <haojian.zhuang@marvell.com> " ) ;
MODULE_DESCRIPTION ( " Regulator Driver for Maxim 8925 PMIC " ) ;
MODULE_ALIAS ( " platform:max8925-regulator " ) ;