2013-12-24 06:34:28 +04:00
/*
2014-07-05 17:20:54 +04:00
* act8865 - regulator . c - Voltage regulation for active - semi ACT88xx PMUs
*
* http : //www.active-semi.com/products/power-management-units/act88xx/
2013-12-24 06:34:28 +04:00
*
* Copyright ( C ) 2013 Atmel Corporation
*
* 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/init.h>
# include <linux/i2c.h>
# include <linux/err.h>
# include <linux/platform_device.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/act8865.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/regulator/of_regulator.h>
# include <linux/regmap.h>
2014-07-05 17:20:55 +04:00
/*
* ACT8846 Global Register Map .
*/
# define ACT8846_SYS0 0x00
# define ACT8846_SYS1 0x01
# define ACT8846_REG1_VSET 0x10
# define ACT8846_REG1_CTRL 0x12
# define ACT8846_REG2_VSET0 0x20
# define ACT8846_REG2_VSET1 0x21
# define ACT8846_REG2_CTRL 0x22
# define ACT8846_REG3_VSET0 0x30
# define ACT8846_REG3_VSET1 0x31
# define ACT8846_REG3_CTRL 0x32
# define ACT8846_REG4_VSET0 0x40
# define ACT8846_REG4_VSET1 0x41
# define ACT8846_REG4_CTRL 0x42
# define ACT8846_REG5_VSET 0x50
# define ACT8846_REG5_CTRL 0x51
# define ACT8846_REG6_VSET 0x58
# define ACT8846_REG6_CTRL 0x59
# define ACT8846_REG7_VSET 0x60
# define ACT8846_REG7_CTRL 0x61
# define ACT8846_REG8_VSET 0x68
# define ACT8846_REG8_CTRL 0x69
# define ACT8846_REG9_VSET 0x70
# define ACT8846_REG9_CTRL 0x71
# define ACT8846_REG10_VSET 0x80
# define ACT8846_REG10_CTRL 0x81
# define ACT8846_REG11_VSET 0x90
# define ACT8846_REG11_CTRL 0x91
# define ACT8846_REG12_VSET 0xa0
# define ACT8846_REG12_CTRL 0xa1
# define ACT8846_REG13_CTRL 0xb1
2013-12-24 06:34:28 +04:00
/*
* ACT8865 Global Register Map .
*/
# define ACT8865_SYS_MODE 0x00
# define ACT8865_SYS_CTRL 0x01
# define ACT8865_DCDC1_VSET1 0x20
# define ACT8865_DCDC1_VSET2 0x21
# define ACT8865_DCDC1_CTRL 0x22
# define ACT8865_DCDC2_VSET1 0x30
# define ACT8865_DCDC2_VSET2 0x31
# define ACT8865_DCDC2_CTRL 0x32
# define ACT8865_DCDC3_VSET1 0x40
# define ACT8865_DCDC3_VSET2 0x41
# define ACT8865_DCDC3_CTRL 0x42
# define ACT8865_LDO1_VSET 0x50
# define ACT8865_LDO1_CTRL 0x51
# define ACT8865_LDO2_VSET 0x54
# define ACT8865_LDO2_CTRL 0x55
# define ACT8865_LDO3_VSET 0x60
# define ACT8865_LDO3_CTRL 0x61
# define ACT8865_LDO4_VSET 0x64
# define ACT8865_LDO4_CTRL 0x65
/*
* Field Definitions .
*/
# define ACT8865_ENA 0x80 /* ON - [7] */
# define ACT8865_VSEL_MASK 0x3F /* VSET - [5:0] */
/*
* ACT8865 voltage number
*/
# define ACT8865_VOLTAGE_NUM 64
struct act8865 {
struct regmap * regmap ;
} ;
static const struct regmap_config act8865_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
} ;
2014-07-05 17:20:54 +04:00
static const struct regulator_linear_range act8865_voltage_ranges [ ] = {
2013-12-24 06:34:28 +04:00
REGULATOR_LINEAR_RANGE ( 600000 , 0 , 23 , 25000 ) ,
REGULATOR_LINEAR_RANGE ( 1200000 , 24 , 47 , 50000 ) ,
REGULATOR_LINEAR_RANGE ( 2400000 , 48 , 63 , 100000 ) ,
} ;
static struct regulator_ops act8865_ops = {
. list_voltage = regulator_list_voltage_linear_range ,
. map_voltage = regulator_map_voltage_linear_range ,
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. is_enabled = regulator_is_enabled_regmap ,
} ;
2014-07-05 17:20:54 +04:00
# define ACT88xx_REG(_name, _family, _id, _vsel_reg) \
[ _family # # _ID_ # # _id ] = { \
. name = _name , \
. id = _family # # _ID_ # # _id , \
. type = REGULATOR_VOLTAGE , \
. ops = & act8865_ops , \
. n_voltages = ACT8865_VOLTAGE_NUM , \
. linear_ranges = act8865_voltage_ranges , \
. n_linear_ranges = ARRAY_SIZE ( act8865_voltage_ranges ) , \
. vsel_reg = _family # # _ # # _id # # _ # # _vsel_reg , \
. vsel_mask = ACT8865_VSEL_MASK , \
. enable_reg = _family # # _ # # _id # # _CTRL , \
. enable_mask = ACT8865_ENA , \
. owner = THIS_MODULE , \
}
2014-07-05 17:20:55 +04:00
static const struct regulator_desc act8846_regulators [ ] = {
ACT88xx_REG ( " REG1 " , ACT8846 , REG1 , VSET ) ,
ACT88xx_REG ( " REG2 " , ACT8846 , REG2 , VSET0 ) ,
ACT88xx_REG ( " REG3 " , ACT8846 , REG3 , VSET0 ) ,
ACT88xx_REG ( " REG4 " , ACT8846 , REG4 , VSET0 ) ,
ACT88xx_REG ( " REG5 " , ACT8846 , REG5 , VSET ) ,
ACT88xx_REG ( " REG6 " , ACT8846 , REG6 , VSET ) ,
ACT88xx_REG ( " REG7 " , ACT8846 , REG7 , VSET ) ,
ACT88xx_REG ( " REG8 " , ACT8846 , REG8 , VSET ) ,
ACT88xx_REG ( " REG9 " , ACT8846 , REG9 , VSET ) ,
ACT88xx_REG ( " REG10 " , ACT8846 , REG10 , VSET ) ,
ACT88xx_REG ( " REG11 " , ACT8846 , REG11 , VSET ) ,
ACT88xx_REG ( " REG12 " , ACT8846 , REG12 , VSET ) ,
} ;
2014-07-05 17:20:54 +04:00
static const struct regulator_desc act8865_regulators [ ] = {
ACT88xx_REG ( " DCDC_REG1 " , ACT8865 , DCDC1 , VSET1 ) ,
ACT88xx_REG ( " DCDC_REG2 " , ACT8865 , DCDC2 , VSET1 ) ,
ACT88xx_REG ( " DCDC_REG3 " , ACT8865 , DCDC3 , VSET1 ) ,
ACT88xx_REG ( " LDO_REG1 " , ACT8865 , LDO1 , VSET ) ,
ACT88xx_REG ( " LDO_REG2 " , ACT8865 , LDO2 , VSET ) ,
ACT88xx_REG ( " LDO_REG3 " , ACT8865 , LDO3 , VSET ) ,
ACT88xx_REG ( " LDO_REG4 " , ACT8865 , LDO4 , VSET ) ,
2013-12-24 06:34:28 +04:00
} ;
# ifdef CONFIG_OF
static const struct of_device_id act8865_dt_ids [ ] = {
2014-07-05 17:20:55 +04:00
{ . compatible = " active-semi,act8846 " , . data = ( void * ) ACT8846 } ,
2014-07-05 17:20:54 +04:00
{ . compatible = " active-semi,act8865 " , . data = ( void * ) ACT8865 } ,
2013-12-24 06:34:28 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , act8865_dt_ids ) ;
2014-07-05 17:20:55 +04:00
static struct of_regulator_match act8846_matches [ ] = {
[ ACT8846_ID_REG1 ] = { . name = " REG1 " } ,
[ ACT8846_ID_REG2 ] = { . name = " REG2 " } ,
[ ACT8846_ID_REG3 ] = { . name = " REG3 " } ,
[ ACT8846_ID_REG4 ] = { . name = " REG4 " } ,
[ ACT8846_ID_REG5 ] = { . name = " REG5 " } ,
[ ACT8846_ID_REG6 ] = { . name = " REG6 " } ,
[ ACT8846_ID_REG7 ] = { . name = " REG7 " } ,
[ ACT8846_ID_REG8 ] = { . name = " REG8 " } ,
[ ACT8846_ID_REG9 ] = { . name = " REG9 " } ,
[ ACT8846_ID_REG10 ] = { . name = " REG10 " } ,
[ ACT8846_ID_REG11 ] = { . name = " REG11 " } ,
[ ACT8846_ID_REG12 ] = { . name = " REG12 " } ,
} ;
2013-12-24 06:34:28 +04:00
static struct of_regulator_match act8865_matches [ ] = {
[ ACT8865_ID_DCDC1 ] = { . name = " DCDC_REG1 " } ,
[ ACT8865_ID_DCDC2 ] = { . name = " DCDC_REG2 " } ,
[ ACT8865_ID_DCDC3 ] = { . name = " DCDC_REG3 " } ,
[ ACT8865_ID_LDO1 ] = { . name = " LDO_REG1 " } ,
[ ACT8865_ID_LDO2 ] = { . name = " LDO_REG2 " } ,
[ ACT8865_ID_LDO3 ] = { . name = " LDO_REG3 " } ,
[ ACT8865_ID_LDO4 ] = { . name = " LDO_REG4 " } ,
} ;
static int act8865_pdata_from_dt ( struct device * dev ,
struct device_node * * of_node ,
2014-07-05 17:20:54 +04:00
struct act8865_platform_data * pdata ,
2014-07-08 01:40:47 +04:00
unsigned long type )
2013-12-24 06:34:28 +04:00
{
2014-07-08 01:40:47 +04:00
int matched , i , num_matches ;
2013-12-24 06:34:28 +04:00
struct device_node * np ;
struct act8865_regulator_data * regulator ;
2014-07-08 01:40:47 +04:00
struct of_regulator_match * matches ;
2013-12-24 06:34:28 +04:00
2014-02-14 15:49:54 +04:00
np = of_get_child_by_name ( dev - > of_node , " regulators " ) ;
2013-12-24 06:34:28 +04:00
if ( ! np ) {
dev_err ( dev , " missing 'regulators' subnode in DT \n " ) ;
return - EINVAL ;
}
2014-07-08 01:40:47 +04:00
switch ( type ) {
case ACT8846 :
matches = act8846_matches ;
num_matches = ARRAY_SIZE ( act8846_matches ) ;
break ;
case ACT8865 :
matches = act8865_matches ;
num_matches = ARRAY_SIZE ( act8865_matches ) ;
break ;
default :
dev_err ( dev , " invalid device id %lu \n " , type ) ;
return - EINVAL ;
}
2014-07-05 17:20:54 +04:00
matched = of_regulator_match ( dev , np , matches , num_matches ) ;
2014-02-17 13:03:29 +04:00
of_node_put ( np ) ;
2013-12-24 06:34:28 +04:00
if ( matched < = 0 )
return matched ;
pdata - > regulators = devm_kzalloc ( dev ,
2014-07-05 17:20:54 +04:00
sizeof ( struct act8865_regulator_data ) *
num_matches , GFP_KERNEL ) ;
2014-02-18 14:41:08 +04:00
if ( ! pdata - > regulators )
2013-12-24 06:34:28 +04:00
return - ENOMEM ;
2014-07-05 17:20:54 +04:00
pdata - > num_regulators = num_matches ;
2013-12-24 06:34:28 +04:00
regulator = pdata - > regulators ;
2014-07-05 17:20:54 +04:00
for ( i = 0 ; i < num_matches ; i + + ) {
2013-12-24 06:34:28 +04:00
regulator - > id = i ;
2014-07-05 17:20:54 +04:00
regulator - > name = matches [ i ] . name ;
regulator - > platform_data = matches [ i ] . init_data ;
of_node [ i ] = matches [ i ] . of_node ;
2013-12-24 06:34:28 +04:00
regulator + + ;
}
return 0 ;
}
# else
static inline int act8865_pdata_from_dt ( struct device * dev ,
struct device_node * * of_node ,
2014-07-08 01:40:47 +04:00
struct act8865_platform_data * pdata ,
unsigned long type )
2013-12-24 06:34:28 +04:00
{
return 0 ;
}
# endif
2014-06-22 19:31:41 +04:00
static struct regulator_init_data
* act8865_get_init_data ( int id , struct act8865_platform_data * pdata )
{
int i ;
if ( ! pdata )
return NULL ;
for ( i = 0 ; i < pdata - > num_regulators ; i + + ) {
if ( pdata - > regulators [ i ] . id = = id )
return pdata - > regulators [ i ] . platform_data ;
}
return NULL ;
}
2013-12-24 06:34:28 +04:00
static int act8865_pmic_probe ( struct i2c_client * client ,
2014-07-05 17:20:54 +04:00
const struct i2c_device_id * i2c_id )
2013-12-24 06:34:28 +04:00
{
2014-07-05 17:20:54 +04:00
static const struct regulator_desc * regulators ;
struct act8865_platform_data pdata_of , * pdata ;
2013-12-24 06:34:28 +04:00
struct device * dev = & client - > dev ;
2014-07-05 17:20:54 +04:00
struct device_node * * of_node ;
int i , ret , num_regulators ;
2013-12-24 06:34:28 +04:00
struct act8865 * act8865 ;
2014-07-05 17:20:54 +04:00
unsigned long type ;
pdata = dev_get_platdata ( dev ) ;
2013-12-24 06:34:28 +04:00
if ( dev - > of_node & & ! pdata ) {
const struct of_device_id * id ;
id = of_match_device ( of_match_ptr ( act8865_dt_ids ) , dev ) ;
if ( ! id )
return - ENODEV ;
2014-07-05 17:20:54 +04:00
type = ( unsigned long ) id - > data ;
} else {
type = i2c_id - > driver_data ;
}
switch ( type ) {
2014-07-05 17:20:55 +04:00
case ACT8846 :
regulators = act8846_regulators ;
num_regulators = ARRAY_SIZE ( act8846_regulators ) ;
break ;
2014-07-05 17:20:54 +04:00
case ACT8865 :
regulators = act8865_regulators ;
num_regulators = ARRAY_SIZE ( act8865_regulators ) ;
break ;
default :
dev_err ( dev , " invalid device id %lu \n " , type ) ;
return - EINVAL ;
}
of_node = devm_kzalloc ( dev , sizeof ( struct device_node * ) *
num_regulators , GFP_KERNEL ) ;
if ( ! of_node )
return - ENOMEM ;
if ( dev - > of_node & & ! pdata ) {
2014-07-08 01:40:47 +04:00
ret = act8865_pdata_from_dt ( dev , of_node , & pdata_of , type ) ;
2013-12-24 06:34:28 +04:00
if ( ret < 0 )
return ret ;
pdata = & pdata_of ;
}
2014-07-05 17:20:54 +04:00
if ( pdata - > num_regulators > num_regulators ) {
dev_err ( dev , " too many regulators: %d \n " ,
pdata - > num_regulators ) ;
2013-12-24 06:34:28 +04:00
return - EINVAL ;
}
2013-12-26 10:52:43 +04:00
act8865 = devm_kzalloc ( dev , sizeof ( struct act8865 ) , GFP_KERNEL ) ;
2013-12-24 06:34:28 +04:00
if ( ! act8865 )
return - ENOMEM ;
act8865 - > regmap = devm_regmap_init_i2c ( client , & act8865_regmap_config ) ;
if ( IS_ERR ( act8865 - > regmap ) ) {
2014-06-30 11:32:09 +04:00
ret = PTR_ERR ( act8865 - > regmap ) ;
2013-12-24 06:34:28 +04:00
dev_err ( & client - > dev , " Failed to allocate register map: %d \n " ,
2014-06-30 11:32:09 +04:00
ret ) ;
return ret ;
2013-12-24 06:34:28 +04:00
}
/* Finally register devices */
2014-07-05 17:20:54 +04:00
for ( i = 0 ; i < num_regulators ; i + + ) {
const struct regulator_desc * desc = & regulators [ i ] ;
struct regulator_config config = { } ;
struct regulator_dev * rdev ;
2013-12-24 06:34:28 +04:00
config . dev = dev ;
2014-06-22 19:31:41 +04:00
config . init_data = act8865_get_init_data ( desc - > id , pdata ) ;
2013-12-24 06:34:28 +04:00
config . of_node = of_node [ i ] ;
config . driver_data = act8865 ;
config . regmap = act8865 - > regmap ;
2014-06-22 19:31:41 +04:00
rdev = devm_regulator_register ( & client - > dev , desc , & config ) ;
2014-03-08 17:19:07 +04:00
if ( IS_ERR ( rdev ) ) {
2014-06-22 19:31:41 +04:00
dev_err ( dev , " failed to register %s \n " , desc - > name ) ;
2014-03-08 17:19:07 +04:00
return PTR_ERR ( rdev ) ;
2013-12-24 06:34:28 +04:00
}
}
i2c_set_clientdata ( client , act8865 ) ;
2014-07-05 17:20:54 +04:00
devm_kfree ( dev , of_node ) ;
2013-12-24 06:34:28 +04:00
return 0 ;
}
static const struct i2c_device_id act8865_ids [ ] = {
2014-07-05 17:20:55 +04:00
{ . name = " act8846 " , . driver_data = ACT8846 } ,
2014-07-05 17:20:54 +04:00
{ . name = " act8865 " , . driver_data = ACT8865 } ,
2013-12-24 06:34:28 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , act8865_ids ) ;
static struct i2c_driver act8865_pmic_driver = {
. driver = {
. name = " act8865 " ,
. owner = THIS_MODULE ,
} ,
. probe = act8865_pmic_probe ,
. id_table = act8865_ids ,
} ;
module_i2c_driver ( act8865_pmic_driver ) ;
2014-07-05 17:20:54 +04:00
MODULE_DESCRIPTION ( " active-semi act88xx voltage regulator driver " ) ;
2013-12-24 06:34:28 +04:00
MODULE_AUTHOR ( " Wenyou Yang <wenyou.yang@atmel.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;