2012-03-14 06:29:12 +04:00
/*
* Copyright ( C ) 2011 Freescale Semiconductor , Inc . All Rights Reserved .
*/
/*
* 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 .
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
# include <linux/slab.h>
# include <linux/device.h>
# include <linux/module.h>
2012-09-05 06:57:15 +04:00
# include <linux/mfd/syscon.h>
2012-03-14 06:29:12 +04:00
# include <linux/err.h>
# include <linux/io.h>
# include <linux/platform_device.h>
# include <linux/of.h>
# include <linux/of_address.h>
2012-09-05 06:57:15 +04:00
# include <linux/regmap.h>
2012-03-14 06:29:12 +04:00
# include <linux/regulator/driver.h>
# include <linux/regulator/of_regulator.h>
2013-01-31 20:23:53 +04:00
# define LDO_RAMP_UP_UNIT_IN_CYCLES 64 /* 64 cycles per step */
# define LDO_RAMP_UP_FREQ_IN_MHZ 24 /* cycle based on 24M OSC */
2014-02-11 17:43:44 +04:00
# define LDO_POWER_GATE 0x00
2014-02-11 17:43:45 +04:00
# define LDO_FET_FULL_ON 0x1f
2014-02-11 17:43:44 +04:00
2012-03-14 06:29:12 +04:00
struct anatop_regulator {
const char * name ;
u32 control_reg ;
2012-09-05 06:57:15 +04:00
struct regmap * anatop ;
2012-03-14 06:29:12 +04:00
int vol_bit_shift ;
int vol_bit_width ;
2013-01-31 20:23:53 +04:00
u32 delay_reg ;
int delay_bit_shift ;
int delay_bit_width ;
2012-03-14 06:29:12 +04:00
int min_bit_val ;
int min_voltage ;
int max_voltage ;
struct regulator_desc rdesc ;
struct regulator_init_data * initdata ;
2014-02-11 17:43:45 +04:00
bool bypass ;
2014-02-11 17:43:44 +04:00
int sel ;
2012-03-14 06:29:12 +04:00
} ;
2013-01-31 20:23:53 +04:00
static int anatop_regmap_set_voltage_time_sel ( struct regulator_dev * reg ,
unsigned int old_sel ,
unsigned int new_sel )
{
struct anatop_regulator * anatop_reg = rdev_get_drvdata ( reg ) ;
u32 val ;
int ret = 0 ;
/* check whether need to care about LDO ramp up speed */
if ( anatop_reg - > delay_bit_width & & new_sel > old_sel ) {
/*
* the delay for LDO ramp up time is
* based on the register setting , we need
* to calculate how many steps LDO need to
* ramp up , and how much delay needed . ( us )
*/
regmap_read ( anatop_reg - > anatop , anatop_reg - > delay_reg , & val ) ;
val = ( val > > anatop_reg - > delay_bit_shift ) &
( ( 1 < < anatop_reg - > delay_bit_width ) - 1 ) ;
2013-02-04 06:21:32 +04:00
ret = ( new_sel - old_sel ) * ( LDO_RAMP_UP_UNIT_IN_CYCLES < <
val ) / LDO_RAMP_UP_FREQ_IN_MHZ + 1 ;
2013-01-31 20:23:53 +04:00
}
return ret ;
}
2014-02-11 17:43:44 +04:00
static int anatop_regmap_enable ( struct regulator_dev * reg )
{
struct anatop_regulator * anatop_reg = rdev_get_drvdata ( reg ) ;
2014-02-11 17:43:45 +04:00
int sel ;
2014-02-11 17:43:44 +04:00
2014-02-11 17:43:45 +04:00
sel = anatop_reg - > bypass ? LDO_FET_FULL_ON : anatop_reg - > sel ;
return regulator_set_voltage_sel_regmap ( reg , sel ) ;
2014-02-11 17:43:44 +04:00
}
static int anatop_regmap_disable ( struct regulator_dev * reg )
{
return regulator_set_voltage_sel_regmap ( reg , LDO_POWER_GATE ) ;
}
static int anatop_regmap_is_enabled ( struct regulator_dev * reg )
{
return regulator_get_voltage_sel_regmap ( reg ) ! = LDO_POWER_GATE ;
}
static int anatop_regmap_core_set_voltage_sel ( struct regulator_dev * reg ,
unsigned selector )
{
struct anatop_regulator * anatop_reg = rdev_get_drvdata ( reg ) ;
int ret ;
2014-02-11 17:43:45 +04:00
if ( anatop_reg - > bypass | | ! anatop_regmap_is_enabled ( reg ) ) {
2014-02-11 17:43:44 +04:00
anatop_reg - > sel = selector ;
return 0 ;
}
ret = regulator_set_voltage_sel_regmap ( reg , selector ) ;
if ( ! ret )
anatop_reg - > sel = selector ;
return ret ;
}
static int anatop_regmap_core_get_voltage_sel ( struct regulator_dev * reg )
{
struct anatop_regulator * anatop_reg = rdev_get_drvdata ( reg ) ;
2014-02-11 17:43:45 +04:00
if ( anatop_reg - > bypass | | ! anatop_regmap_is_enabled ( reg ) )
2014-02-11 17:43:44 +04:00
return anatop_reg - > sel ;
return regulator_get_voltage_sel_regmap ( reg ) ;
}
2014-02-11 17:43:45 +04:00
static int anatop_regmap_get_bypass ( struct regulator_dev * reg , bool * enable )
{
struct anatop_regulator * anatop_reg = rdev_get_drvdata ( reg ) ;
int sel ;
sel = regulator_get_voltage_sel_regmap ( reg ) ;
if ( sel = = LDO_FET_FULL_ON )
WARN_ON ( ! anatop_reg - > bypass ) ;
else if ( sel ! = LDO_POWER_GATE )
WARN_ON ( anatop_reg - > bypass ) ;
* enable = anatop_reg - > bypass ;
return 0 ;
}
static int anatop_regmap_set_bypass ( struct regulator_dev * reg , bool enable )
{
struct anatop_regulator * anatop_reg = rdev_get_drvdata ( reg ) ;
int sel ;
if ( enable = = anatop_reg - > bypass )
return 0 ;
sel = enable ? LDO_FET_FULL_ON : anatop_reg - > sel ;
anatop_reg - > bypass = enable ;
return regulator_set_voltage_sel_regmap ( reg , sel ) ;
}
2012-03-14 06:29:12 +04:00
static struct regulator_ops anatop_rops = {
2014-02-22 08:53:18 +04:00
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
2012-06-03 19:02:34 +04:00
. list_voltage = regulator_list_voltage_linear ,
. map_voltage = regulator_map_voltage_linear ,
2012-03-14 06:29:12 +04:00
} ;
2014-02-11 17:43:44 +04:00
static struct regulator_ops anatop_core_rops = {
. enable = anatop_regmap_enable ,
. disable = anatop_regmap_disable ,
. is_enabled = anatop_regmap_is_enabled ,
. set_voltage_sel = anatop_regmap_core_set_voltage_sel ,
. set_voltage_time_sel = anatop_regmap_set_voltage_time_sel ,
. get_voltage_sel = anatop_regmap_core_get_voltage_sel ,
. list_voltage = regulator_list_voltage_linear ,
. map_voltage = regulator_map_voltage_linear ,
2014-02-11 17:43:45 +04:00
. get_bypass = anatop_regmap_get_bypass ,
. set_bypass = anatop_regmap_set_bypass ,
2014-02-11 17:43:44 +04:00
} ;
2012-11-19 22:22:22 +04:00
static int anatop_regulator_probe ( struct platform_device * pdev )
2012-03-14 06:29:12 +04:00
{
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
2012-09-05 06:57:15 +04:00
struct device_node * anatop_np ;
2012-03-14 06:29:12 +04:00
struct regulator_desc * rdesc ;
struct regulator_dev * rdev ;
struct anatop_regulator * sreg ;
struct regulator_init_data * initdata ;
2012-04-10 18:45:01 +04:00
struct regulator_config config = { } ;
2012-03-14 06:29:12 +04:00
int ret = 0 ;
2014-02-11 17:43:44 +04:00
u32 val ;
2012-03-14 06:29:12 +04:00
initdata = of_get_regulator_init_data ( dev , np ) ;
sreg = devm_kzalloc ( dev , sizeof ( * sreg ) , GFP_KERNEL ) ;
if ( ! sreg )
return - ENOMEM ;
sreg - > initdata = initdata ;
2014-01-06 16:13:15 +04:00
sreg - > name = of_get_property ( np , " regulator-name " , NULL ) ;
2012-03-14 06:29:12 +04:00
rdesc = & sreg - > rdesc ;
rdesc - > name = sreg - > name ;
rdesc - > type = REGULATOR_VOLTAGE ;
rdesc - > owner = THIS_MODULE ;
2012-09-05 06:57:15 +04:00
anatop_np = of_get_parent ( np ) ;
if ( ! anatop_np )
return - ENODEV ;
sreg - > anatop = syscon_node_to_regmap ( anatop_np ) ;
of_node_put ( anatop_np ) ;
if ( IS_ERR ( sreg - > anatop ) )
return PTR_ERR ( sreg - > anatop ) ;
2012-03-27 11:54:01 +04:00
ret = of_property_read_u32 ( np , " anatop-reg-offset " ,
& sreg - > control_reg ) ;
2012-03-14 06:29:12 +04:00
if ( ret ) {
2012-03-27 11:54:01 +04:00
dev_err ( dev , " no anatop-reg-offset property set \n " ) ;
2014-01-06 16:13:15 +04:00
return ret ;
2012-03-14 06:29:12 +04:00
}
ret = of_property_read_u32 ( np , " anatop-vol-bit-width " ,
& sreg - > vol_bit_width ) ;
if ( ret ) {
dev_err ( dev , " no anatop-vol-bit-width property set \n " ) ;
2014-01-06 16:13:15 +04:00
return ret ;
2012-03-14 06:29:12 +04:00
}
ret = of_property_read_u32 ( np , " anatop-vol-bit-shift " ,
& sreg - > vol_bit_shift ) ;
if ( ret ) {
dev_err ( dev , " no anatop-vol-bit-shift property set \n " ) ;
2014-01-06 16:13:15 +04:00
return ret ;
2012-03-14 06:29:12 +04:00
}
ret = of_property_read_u32 ( np , " anatop-min-bit-val " ,
& sreg - > min_bit_val ) ;
if ( ret ) {
dev_err ( dev , " no anatop-min-bit-val property set \n " ) ;
2014-01-06 16:13:15 +04:00
return ret ;
2012-03-14 06:29:12 +04:00
}
ret = of_property_read_u32 ( np , " anatop-min-voltage " ,
& sreg - > min_voltage ) ;
if ( ret ) {
dev_err ( dev , " no anatop-min-voltage property set \n " ) ;
2014-01-06 16:13:15 +04:00
return ret ;
2012-03-14 06:29:12 +04:00
}
ret = of_property_read_u32 ( np , " anatop-max-voltage " ,
& sreg - > max_voltage ) ;
if ( ret ) {
dev_err ( dev , " no anatop-max-voltage property set \n " ) ;
2014-01-06 16:13:15 +04:00
return ret ;
2012-03-14 06:29:12 +04:00
}
2013-01-31 20:23:53 +04:00
/* read LDO ramp up setting, only for core reg */
of_property_read_u32 ( np , " anatop-delay-reg-offset " ,
& sreg - > delay_reg ) ;
of_property_read_u32 ( np , " anatop-delay-bit-width " ,
& sreg - > delay_bit_width ) ;
of_property_read_u32 ( np , " anatop-delay-bit-shift " ,
& sreg - > delay_bit_shift ) ;
2012-12-09 04:05:45 +04:00
rdesc - > n_voltages = ( sreg - > max_voltage - sreg - > min_voltage ) / 25000 + 1
+ sreg - > min_bit_val ;
2012-05-14 07:06:44 +04:00
rdesc - > min_uV = sreg - > min_voltage ;
rdesc - > uV_step = 25000 ;
2012-12-09 04:05:45 +04:00
rdesc - > linear_min_sel = sreg - > min_bit_val ;
2012-12-09 04:07:43 +04:00
rdesc - > vsel_reg = sreg - > control_reg ;
rdesc - > vsel_mask = ( ( 1 < < sreg - > vol_bit_width ) - 1 ) < <
sreg - > vol_bit_shift ;
2012-03-14 06:29:12 +04:00
2012-04-10 18:45:01 +04:00
config . dev = & pdev - > dev ;
config . init_data = initdata ;
config . driver_data = sreg ;
config . of_node = pdev - > dev . of_node ;
2012-12-09 04:07:43 +04:00
config . regmap = sreg - > anatop ;
2012-04-10 18:45:01 +04:00
2014-02-11 17:43:44 +04:00
/* Only core regulators have the ramp up delay configuration. */
if ( sreg - > control_reg & & sreg - > delay_bit_width ) {
rdesc - > ops = & anatop_core_rops ;
ret = regmap_read ( config . regmap , rdesc - > vsel_reg , & val ) ;
if ( ret ) {
dev_err ( dev , " failed to read initial state \n " ) ;
return ret ;
}
sreg - > sel = ( val & rdesc - > vsel_mask ) > > sreg - > vol_bit_shift ;
2014-02-11 17:43:45 +04:00
if ( sreg - > sel = = LDO_FET_FULL_ON ) {
sreg - > sel = 0 ;
sreg - > bypass = true ;
}
2014-02-11 17:43:44 +04:00
} else {
rdesc - > ops = & anatop_rops ;
}
2012-03-14 06:29:12 +04:00
/* register regulator */
2013-09-04 10:30:57 +04:00
rdev = devm_regulator_register ( dev , rdesc , & config ) ;
2012-03-14 06:29:12 +04:00
if ( IS_ERR ( rdev ) ) {
dev_err ( dev , " failed to register %s \n " ,
rdesc - > name ) ;
2014-01-06 16:13:15 +04:00
return PTR_ERR ( rdev ) ;
2012-03-14 06:29:12 +04:00
}
platform_set_drvdata ( pdev , rdev ) ;
return 0 ;
}
2014-05-07 11:55:10 +04:00
static const struct of_device_id of_anatop_regulator_match_tbl [ ] = {
2012-03-14 06:29:12 +04:00
{ . compatible = " fsl,anatop-regulator " , } ,
{ /* end */ }
} ;
2012-04-02 10:57:01 +04:00
static struct platform_driver anatop_regulator_driver = {
2012-03-14 06:29:12 +04:00
. driver = {
. name = " anatop_regulator " ,
. owner = THIS_MODULE ,
. of_match_table = of_anatop_regulator_match_tbl ,
} ,
. probe = anatop_regulator_probe ,
} ;
static int __init anatop_regulator_init ( void )
{
2012-04-02 10:57:01 +04:00
return platform_driver_register ( & anatop_regulator_driver ) ;
2012-03-14 06:29:12 +04:00
}
postcore_initcall ( anatop_regulator_init ) ;
static void __exit anatop_regulator_exit ( void )
{
2012-04-02 10:57:01 +04:00
platform_driver_unregister ( & anatop_regulator_driver ) ;
2012-03-14 06:29:12 +04:00
}
module_exit ( anatop_regulator_exit ) ;
2013-10-14 12:45:51 +04:00
MODULE_AUTHOR ( " Nancy Chen <Nancy.Chen@freescale.com> " ) ;
MODULE_AUTHOR ( " Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> " ) ;
2012-03-14 06:29:12 +04:00
MODULE_DESCRIPTION ( " ANATOP Regulator driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2013-12-31 16:56:00 +04:00
MODULE_ALIAS ( " platform:anatop_regulator " ) ;