2019-04-17 17:16:32 +03:00
// SPDX-License-Identifier: GPL-2.0+
//
// Regulator support for WM8400
//
// Copyright 2008 Wolfson Microelectronics PLC.
//
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
2008-09-11 14:12:01 +04:00
# include <linux/bug.h>
# include <linux/err.h>
# include <linux/kernel.h>
2011-07-18 00:28:23 +04:00
# include <linux/module.h>
2008-09-11 14:12:01 +04:00
# include <linux/regulator/driver.h>
# include <linux/mfd/wm8400-private.h>
2020-05-08 18:43:36 +03:00
static const struct linear_range wm8400_ldo_ranges [ ] = {
2013-10-11 05:32:18 +04:00
REGULATOR_LINEAR_RANGE ( 900000 , 0 , 14 , 50000 ) ,
REGULATOR_LINEAR_RANGE ( 1700000 , 15 , 31 , 100000 ) ,
2013-07-03 02:26:36 +04:00
} ;
2008-09-11 14:12:01 +04:00
2015-12-19 18:31:24 +03:00
static const struct regulator_ops wm8400_ldo_ops = {
2012-05-10 03:12:09 +04:00
. is_enabled = regulator_is_enabled_regmap ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
2013-07-03 02:26:36 +04:00
. list_voltage = regulator_list_voltage_linear_range ,
2012-05-10 03:12:09 +04:00
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
2013-07-03 02:26:36 +04:00
. map_voltage = regulator_map_voltage_linear_range ,
2008-09-11 14:12:01 +04:00
} ;
static unsigned int wm8400_dcdc_get_mode ( struct regulator_dev * dev )
{
2019-02-27 04:30:53 +03:00
struct regmap * rmap = rdev_get_regmap ( dev ) ;
2008-09-11 14:12:01 +04:00
int offset = ( rdev_get_id ( dev ) - WM8400_DCDC1 ) * 2 ;
u16 data [ 2 ] ;
int ret ;
2019-02-27 04:30:53 +03:00
ret = regmap_bulk_read ( rmap , WM8400_DCDC1_CONTROL_1 + offset , data , 2 ) ;
2008-09-11 14:12:01 +04:00
if ( ret ! = 0 )
return 0 ;
/* Datasheet: hibernate */
if ( data [ 0 ] & WM8400_DC1_SLEEP )
return REGULATOR_MODE_STANDBY ;
/* Datasheet: standby */
if ( ! ( data [ 0 ] & WM8400_DC1_ACTIVE ) )
return REGULATOR_MODE_IDLE ;
/* Datasheet: active with or without force PWM */
if ( data [ 1 ] & WM8400_DC1_FRC_PWM )
return REGULATOR_MODE_FAST ;
else
return REGULATOR_MODE_NORMAL ;
}
static int wm8400_dcdc_set_mode ( struct regulator_dev * dev , unsigned int mode )
{
2019-02-27 04:30:53 +03:00
struct regmap * rmap = rdev_get_regmap ( dev ) ;
2008-09-11 14:12:01 +04:00
int offset = ( rdev_get_id ( dev ) - WM8400_DCDC1 ) * 2 ;
int ret ;
switch ( mode ) {
case REGULATOR_MODE_FAST :
/* Datasheet: active with force PWM */
2019-02-27 04:30:53 +03:00
ret = regmap_update_bits ( rmap , WM8400_DCDC1_CONTROL_2 + offset ,
2008-09-11 14:12:01 +04:00
WM8400_DC1_FRC_PWM , WM8400_DC1_FRC_PWM ) ;
if ( ret ! = 0 )
return ret ;
2019-02-27 04:30:53 +03:00
return regmap_update_bits ( rmap , WM8400_DCDC1_CONTROL_1 + offset ,
2008-09-11 14:12:01 +04:00
WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP ,
WM8400_DC1_ACTIVE ) ;
case REGULATOR_MODE_NORMAL :
/* Datasheet: active */
2019-02-27 04:30:53 +03:00
ret = regmap_update_bits ( rmap , WM8400_DCDC1_CONTROL_2 + offset ,
2008-09-11 14:12:01 +04:00
WM8400_DC1_FRC_PWM , 0 ) ;
if ( ret ! = 0 )
return ret ;
2019-02-27 04:30:53 +03:00
return regmap_update_bits ( rmap , WM8400_DCDC1_CONTROL_1 + offset ,
2008-09-11 14:12:01 +04:00
WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP ,
WM8400_DC1_ACTIVE ) ;
case REGULATOR_MODE_IDLE :
/* Datasheet: standby */
2019-02-27 04:30:53 +03:00
return regmap_update_bits ( rmap , WM8400_DCDC1_CONTROL_1 + offset ,
2012-08-01 05:15:11 +04:00
WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP , 0 ) ;
2008-09-11 14:12:01 +04:00
default :
return - EINVAL ;
}
}
static unsigned int wm8400_dcdc_get_optimum_mode ( struct regulator_dev * dev ,
int input_uV , int output_uV ,
int load_uA )
{
return REGULATOR_MODE_NORMAL ;
}
2015-12-19 18:31:24 +03:00
static const struct regulator_ops wm8400_dcdc_ops = {
2012-05-10 03:12:09 +04:00
. is_enabled = regulator_is_enabled_regmap ,
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. list_voltage = regulator_list_voltage_linear ,
2012-05-31 13:40:22 +04:00
. map_voltage = regulator_map_voltage_linear ,
2012-05-10 03:12:09 +04:00
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
2008-09-11 14:12:01 +04:00
. get_mode = wm8400_dcdc_get_mode ,
. set_mode = wm8400_dcdc_set_mode ,
. get_optimum_mode = wm8400_dcdc_get_optimum_mode ,
} ;
static struct regulator_desc regulators [ ] = {
{
. name = " LDO1 " ,
. id = WM8400_LDO1 ,
. ops = & wm8400_ldo_ops ,
2012-05-10 03:12:09 +04:00
. enable_reg = WM8400_LDO1_CONTROL ,
. enable_mask = WM8400_LDO1_ENA ,
2009-03-10 19:28:35 +03:00
. n_voltages = WM8400_LDO1_VSEL_MASK + 1 ,
2013-07-03 02:26:36 +04:00
. linear_ranges = wm8400_ldo_ranges ,
. n_linear_ranges = ARRAY_SIZE ( wm8400_ldo_ranges ) ,
2012-05-10 03:12:09 +04:00
. vsel_reg = WM8400_LDO1_CONTROL ,
. vsel_mask = WM8400_LDO1_VSEL_MASK ,
2008-09-11 14:12:01 +04:00
. type = REGULATOR_VOLTAGE ,
. owner = THIS_MODULE ,
} ,
{
. name = " LDO2 " ,
. id = WM8400_LDO2 ,
. ops = & wm8400_ldo_ops ,
2012-05-10 03:12:09 +04:00
. enable_reg = WM8400_LDO2_CONTROL ,
. enable_mask = WM8400_LDO2_ENA ,
2009-03-10 19:28:35 +03:00
. n_voltages = WM8400_LDO2_VSEL_MASK + 1 ,
2013-07-03 02:26:36 +04:00
. linear_ranges = wm8400_ldo_ranges ,
. n_linear_ranges = ARRAY_SIZE ( wm8400_ldo_ranges ) ,
2008-09-11 14:12:01 +04:00
. type = REGULATOR_VOLTAGE ,
2012-05-10 03:12:09 +04:00
. vsel_reg = WM8400_LDO2_CONTROL ,
. vsel_mask = WM8400_LDO2_VSEL_MASK ,
2008-09-11 14:12:01 +04:00
. owner = THIS_MODULE ,
} ,
{
. name = " LDO3 " ,
. id = WM8400_LDO3 ,
. ops = & wm8400_ldo_ops ,
2012-05-10 03:12:09 +04:00
. enable_reg = WM8400_LDO3_CONTROL ,
. enable_mask = WM8400_LDO3_ENA ,
2009-03-10 19:28:35 +03:00
. n_voltages = WM8400_LDO3_VSEL_MASK + 1 ,
2013-07-03 02:26:36 +04:00
. linear_ranges = wm8400_ldo_ranges ,
. n_linear_ranges = ARRAY_SIZE ( wm8400_ldo_ranges ) ,
2012-05-10 03:12:09 +04:00
. vsel_reg = WM8400_LDO3_CONTROL ,
. vsel_mask = WM8400_LDO3_VSEL_MASK ,
2008-09-11 14:12:01 +04:00
. type = REGULATOR_VOLTAGE ,
. owner = THIS_MODULE ,
} ,
{
. name = " LDO4 " ,
. id = WM8400_LDO4 ,
. ops = & wm8400_ldo_ops ,
2012-05-10 03:12:09 +04:00
. enable_reg = WM8400_LDO4_CONTROL ,
. enable_mask = WM8400_LDO4_ENA ,
2009-03-10 19:28:35 +03:00
. n_voltages = WM8400_LDO4_VSEL_MASK + 1 ,
2013-07-03 02:26:36 +04:00
. linear_ranges = wm8400_ldo_ranges ,
. n_linear_ranges = ARRAY_SIZE ( wm8400_ldo_ranges ) ,
2012-05-10 03:12:09 +04:00
. vsel_reg = WM8400_LDO4_CONTROL ,
. vsel_mask = WM8400_LDO4_VSEL_MASK ,
2008-09-11 14:12:01 +04:00
. type = REGULATOR_VOLTAGE ,
. owner = THIS_MODULE ,
} ,
{
. name = " DCDC1 " ,
. id = WM8400_DCDC1 ,
. ops = & wm8400_dcdc_ops ,
2012-05-10 03:12:09 +04:00
. enable_reg = WM8400_DCDC1_CONTROL_1 ,
. enable_mask = WM8400_DC1_ENA_MASK ,
2009-03-10 19:28:35 +03:00
. n_voltages = WM8400_DC1_VSEL_MASK + 1 ,
2012-05-10 03:12:09 +04:00
. vsel_reg = WM8400_DCDC1_CONTROL_1 ,
. vsel_mask = WM8400_DC1_VSEL_MASK ,
. min_uV = 850000 ,
. uV_step = 25000 ,
2008-09-11 14:12:01 +04:00
. type = REGULATOR_VOLTAGE ,
. owner = THIS_MODULE ,
} ,
{
. name = " DCDC2 " ,
. id = WM8400_DCDC2 ,
. ops = & wm8400_dcdc_ops ,
2012-05-10 03:12:09 +04:00
. enable_reg = WM8400_DCDC2_CONTROL_1 ,
2019-02-27 08:54:21 +03:00
. enable_mask = WM8400_DC2_ENA_MASK ,
2009-03-10 19:28:35 +03:00
. n_voltages = WM8400_DC2_VSEL_MASK + 1 ,
2012-05-10 03:12:09 +04:00
. vsel_reg = WM8400_DCDC2_CONTROL_1 ,
. vsel_mask = WM8400_DC2_VSEL_MASK ,
. min_uV = 850000 ,
. uV_step = 25000 ,
2008-09-11 14:12:01 +04:00
. type = REGULATOR_VOLTAGE ,
. owner = THIS_MODULE ,
} ,
} ;
2012-11-19 22:22:22 +04:00
static int wm8400_regulator_probe ( struct platform_device * pdev )
2008-09-11 14:12:01 +04:00
{
Regulators: wm8400 - cleanup platform driver data handling
On Wed, Feb 24, 2010 at 08:40:56PM +0000, Mark Brown wrote:
> On Wed, Feb 24, 2010 at 11:21:26AM -0800, Dmitry Torokhov wrote:
> > On Wed, Feb 24, 2010 at 07:14:03PM +0000, Mark Brown wrote:
>
> > > This doesn't help unless you also provide a way for users to obtain a
> > > struct wm8400.
>
> > Why would they need it? Only code that creates instances of wm8400 needs
> > to know the definition of the sturcture, the rest can simply pass the
> > pointer around.
>
> > I guess there is disconnect between us and I do not see any users of
> > wm8400_register_regulator() in linux-next... Is there another tree I
> > could peek at?
>
> There are no users in mainline. This would be called by board specific
> code from the init callback of the wm8400 - you'd need to pass that
> callback the struct wm8400.
>
> In any case, this is clearly an unrelated change to whatever else you
> were doing to the driver so should be split off into a separate patch,
> but if this is being changed at all then it'd be much more sensible to
> change it to use a more modern pattern which completely removes the
> wm8400_register_regulator() function and just uses platform data.
Fair enough, I removed the offending part, updated patch below.
--
Dmitry
regulator: wm8400 - cleanup platform driver data handling
Driver data set by platform_set_drvdata() is for private use of
the driver currently bound to teh device and not for use by parent,
subsystem and anyone else.
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
2010-02-25 12:55:37 +03:00
struct wm8400 * wm8400 = container_of ( pdev , struct wm8400 , regulators [ pdev - > id ] ) ;
2012-04-04 03:50:22 +04:00
struct regulator_config config = { } ;
2008-09-11 14:12:01 +04:00
struct regulator_dev * rdev ;
2012-04-04 03:50:22 +04:00
config . dev = & pdev - > dev ;
2013-07-30 12:20:47 +04:00
config . init_data = dev_get_platdata ( & pdev - > dev ) ;
2012-04-04 03:50:22 +04:00
config . driver_data = wm8400 ;
2012-05-10 03:12:09 +04:00
config . regmap = wm8400 - > regmap ;
2008-09-11 14:12:01 +04:00
2013-08-31 15:00:57 +04:00
rdev = devm_regulator_register ( & pdev - > dev , & regulators [ pdev - > id ] ,
& config ) ;
2008-09-11 14:12:01 +04:00
if ( IS_ERR ( rdev ) )
return PTR_ERR ( rdev ) ;
Regulators: wm8400 - cleanup platform driver data handling
On Wed, Feb 24, 2010 at 08:40:56PM +0000, Mark Brown wrote:
> On Wed, Feb 24, 2010 at 11:21:26AM -0800, Dmitry Torokhov wrote:
> > On Wed, Feb 24, 2010 at 07:14:03PM +0000, Mark Brown wrote:
>
> > > This doesn't help unless you also provide a way for users to obtain a
> > > struct wm8400.
>
> > Why would they need it? Only code that creates instances of wm8400 needs
> > to know the definition of the sturcture, the rest can simply pass the
> > pointer around.
>
> > I guess there is disconnect between us and I do not see any users of
> > wm8400_register_regulator() in linux-next... Is there another tree I
> > could peek at?
>
> There are no users in mainline. This would be called by board specific
> code from the init callback of the wm8400 - you'd need to pass that
> callback the struct wm8400.
>
> In any case, this is clearly an unrelated change to whatever else you
> were doing to the driver so should be split off into a separate patch,
> but if this is being changed at all then it'd be much more sensible to
> change it to use a more modern pattern which completely removes the
> wm8400_register_regulator() function and just uses platform data.
Fair enough, I removed the offending part, updated patch below.
--
Dmitry
regulator: wm8400 - cleanup platform driver data handling
Driver data set by platform_set_drvdata() is for private use of
the driver currently bound to teh device and not for use by parent,
subsystem and anyone else.
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
2010-02-25 12:55:37 +03:00
platform_set_drvdata ( pdev , rdev ) ;
2008-09-11 14:12:01 +04:00
return 0 ;
}
static struct platform_driver wm8400_regulator_driver = {
. driver = {
. name = " wm8400-regulator " ,
} ,
. probe = wm8400_regulator_probe ,
} ;
/**
* wm8400_register_regulator - enable software control of a WM8400 regulator
*
* This function enables software control of a WM8400 regulator via
* the regulator API . It is intended to be called from the
* platform_init ( ) callback of the WM8400 MFD driver .
*
2020-06-26 09:57:37 +03:00
* @ dev : The WM8400 device to operate on .
* @ reg : The regulator to control .
* @ initdata : Regulator initdata for the regulator .
2008-09-11 14:12:01 +04:00
*/
int wm8400_register_regulator ( struct device * dev , int reg ,
struct regulator_init_data * initdata )
{
2009-05-01 02:21:37 +04:00
struct wm8400 * wm8400 = dev_get_drvdata ( dev ) ;
2008-09-11 14:12:01 +04:00
if ( wm8400 - > regulators [ reg ] . name )
return - EBUSY ;
initdata - > driver_data = wm8400 ;
wm8400 - > regulators [ reg ] . name = " wm8400-regulator " ;
wm8400 - > regulators [ reg ] . id = reg ;
wm8400 - > regulators [ reg ] . dev . parent = dev ;
wm8400 - > regulators [ reg ] . dev . platform_data = initdata ;
return platform_device_register ( & wm8400 - > regulators [ reg ] ) ;
}
EXPORT_SYMBOL_GPL ( wm8400_register_regulator ) ;
static int __init wm8400_regulator_init ( void )
{
return platform_driver_register ( & wm8400_regulator_driver ) ;
}
2009-04-27 21:21:18 +04:00
subsys_initcall ( wm8400_regulator_init ) ;
2008-09-11 14:12:01 +04:00
static void __exit wm8400_regulator_exit ( void )
{
platform_driver_unregister ( & wm8400_regulator_driver ) ;
}
module_exit ( wm8400_regulator_exit ) ;
MODULE_AUTHOR ( " Mark Brown <broonie@opensource.wolfsonmicro.com> " ) ;
MODULE_DESCRIPTION ( " WM8400 regulator driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:wm8400-regulator " ) ;