2008-10-10 18:58:15 +04:00
/*
* wm8350 . c - - Voltage and current regulation for the Wolfson WM8350 PMIC
*
* Copyright 2007 , 2008 Wolfson Microelectronics PLC .
*
* Author : Liam Girdwood
* linux @ wolfsonmicro . com
*
* 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 .
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/bitops.h>
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/mfd/wm8350/core.h>
# include <linux/mfd/wm8350/pmic.h>
# include <linux/platform_device.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
2009-03-02 19:32:47 +03:00
/* Maximum value possible for VSEL */
# define WM8350_DCDC_MAX_VSEL 0x66
2008-10-10 18:58:15 +04:00
/* Microamps */
static const int isink_cur [ ] = {
4 ,
5 ,
6 ,
7 ,
8 ,
10 ,
11 ,
14 ,
16 ,
19 ,
23 ,
27 ,
32 ,
39 ,
46 ,
54 ,
65 ,
77 ,
92 ,
109 ,
130 ,
154 ,
183 ,
218 ,
259 ,
308 ,
367 ,
436 ,
518 ,
616 ,
733 ,
872 ,
1037 ,
1233 ,
1466 ,
1744 ,
2073 ,
2466 ,
2933 ,
3487 ,
4147 ,
4932 ,
5865 ,
6975 ,
8294 ,
9864 ,
11730 ,
13949 ,
16589 ,
19728 ,
23460 ,
27899 ,
33178 ,
39455 ,
46920 ,
55798 ,
66355 ,
78910 ,
93840 ,
111596 ,
132710 ,
157820 ,
187681 ,
223191
} ;
static int get_isink_val ( int min_uA , int max_uA , u16 * setting )
{
int i ;
2019-03-12 19:33:55 +03:00
for ( i = ARRAY_SIZE ( isink_cur ) - 1 ; i > = 0 ; i - - ) {
2008-10-10 18:58:15 +04:00
if ( min_uA < = isink_cur [ i ] & & max_uA > = isink_cur [ i ] ) {
* setting = i ;
return 0 ;
}
}
return - EINVAL ;
}
static int wm8350_isink_set_current ( struct regulator_dev * rdev , int min_uA ,
int max_uA )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int isink = rdev_get_id ( rdev ) ;
u16 val , setting ;
int ret ;
ret = get_isink_val ( min_uA , max_uA , & setting ) ;
if ( ret ! = 0 )
return ret ;
switch ( isink ) {
case WM8350_ISINK_A :
val = wm8350_reg_read ( wm8350 , WM8350_CURRENT_SINK_DRIVER_A ) &
~ WM8350_CS1_ISEL_MASK ;
wm8350_reg_write ( wm8350 , WM8350_CURRENT_SINK_DRIVER_A ,
val | setting ) ;
break ;
case WM8350_ISINK_B :
val = wm8350_reg_read ( wm8350 , WM8350_CURRENT_SINK_DRIVER_B ) &
~ WM8350_CS1_ISEL_MASK ;
wm8350_reg_write ( wm8350 , WM8350_CURRENT_SINK_DRIVER_B ,
val | setting ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int wm8350_isink_get_current ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int isink = rdev_get_id ( rdev ) ;
u16 val ;
switch ( isink ) {
case WM8350_ISINK_A :
val = wm8350_reg_read ( wm8350 , WM8350_CURRENT_SINK_DRIVER_A ) &
WM8350_CS1_ISEL_MASK ;
break ;
case WM8350_ISINK_B :
val = wm8350_reg_read ( wm8350 , WM8350_CURRENT_SINK_DRIVER_B ) &
WM8350_CS1_ISEL_MASK ;
break ;
default :
return 0 ;
}
2012-03-27 11:21:45 +04:00
return isink_cur [ val ] ;
2008-10-10 18:58:15 +04:00
}
/* turn on ISINK followed by DCDC */
static int wm8350_isink_enable ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int isink = rdev_get_id ( rdev ) ;
switch ( isink ) {
case WM8350_ISINK_A :
switch ( wm8350 - > pmic . isink_A_dcdc ) {
case WM8350_DCDC_2 :
case WM8350_DCDC_5 :
wm8350_set_bits ( wm8350 , WM8350_POWER_MGMT_7 ,
WM8350_CS1_ENA ) ;
wm8350_set_bits ( wm8350 , WM8350_CSA_FLASH_CONTROL ,
WM8350_CS1_DRIVE ) ;
wm8350_set_bits ( wm8350 , WM8350_DCDC_LDO_REQUESTED ,
1 < < ( wm8350 - > pmic . isink_A_dcdc -
WM8350_DCDC_1 ) ) ;
break ;
default :
return - EINVAL ;
}
break ;
case WM8350_ISINK_B :
switch ( wm8350 - > pmic . isink_B_dcdc ) {
case WM8350_DCDC_2 :
case WM8350_DCDC_5 :
wm8350_set_bits ( wm8350 , WM8350_POWER_MGMT_7 ,
WM8350_CS2_ENA ) ;
wm8350_set_bits ( wm8350 , WM8350_CSB_FLASH_CONTROL ,
WM8350_CS2_DRIVE ) ;
wm8350_set_bits ( wm8350 , WM8350_DCDC_LDO_REQUESTED ,
1 < < ( wm8350 - > pmic . isink_B_dcdc -
WM8350_DCDC_1 ) ) ;
break ;
default :
return - EINVAL ;
}
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int wm8350_isink_disable ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int isink = rdev_get_id ( rdev ) ;
switch ( isink ) {
case WM8350_ISINK_A :
switch ( wm8350 - > pmic . isink_A_dcdc ) {
case WM8350_DCDC_2 :
case WM8350_DCDC_5 :
wm8350_clear_bits ( wm8350 , WM8350_DCDC_LDO_REQUESTED ,
1 < < ( wm8350 - > pmic . isink_A_dcdc -
WM8350_DCDC_1 ) ) ;
wm8350_clear_bits ( wm8350 , WM8350_POWER_MGMT_7 ,
WM8350_CS1_ENA ) ;
break ;
default :
return - EINVAL ;
}
break ;
case WM8350_ISINK_B :
switch ( wm8350 - > pmic . isink_B_dcdc ) {
case WM8350_DCDC_2 :
case WM8350_DCDC_5 :
wm8350_clear_bits ( wm8350 , WM8350_DCDC_LDO_REQUESTED ,
1 < < ( wm8350 - > pmic . isink_B_dcdc -
WM8350_DCDC_1 ) ) ;
wm8350_clear_bits ( wm8350 , WM8350_POWER_MGMT_7 ,
WM8350_CS2_ENA ) ;
break ;
default :
return - EINVAL ;
}
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int wm8350_isink_is_enabled ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int isink = rdev_get_id ( rdev ) ;
switch ( isink ) {
case WM8350_ISINK_A :
return wm8350_reg_read ( wm8350 , WM8350_CURRENT_SINK_DRIVER_A ) &
0x8000 ;
case WM8350_ISINK_B :
return wm8350_reg_read ( wm8350 , WM8350_CURRENT_SINK_DRIVER_B ) &
0x8000 ;
}
return - EINVAL ;
}
2010-01-04 20:24:01 +03:00
static int wm8350_isink_enable_time ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int isink = rdev_get_id ( rdev ) ;
int reg ;
switch ( isink ) {
case WM8350_ISINK_A :
reg = wm8350_reg_read ( wm8350 , WM8350_CSA_FLASH_CONTROL ) ;
break ;
case WM8350_ISINK_B :
reg = wm8350_reg_read ( wm8350 , WM8350_CSB_FLASH_CONTROL ) ;
break ;
default :
return - EINVAL ;
}
if ( reg & WM8350_CS1_FLASH_MODE ) {
switch ( reg & WM8350_CS1_ON_RAMP_MASK ) {
case 0 :
return 0 ;
case 1 :
return 1950 ;
case 2 :
return 3910 ;
case 3 :
return 7800 ;
}
} else {
switch ( reg & WM8350_CS1_ON_RAMP_MASK ) {
case 0 :
return 0 ;
case 1 :
return 250000 ;
case 2 :
return 500000 ;
case 3 :
return 1000000 ;
}
}
return - EINVAL ;
}
2008-10-10 18:58:15 +04:00
int wm8350_isink_set_flash ( struct wm8350 * wm8350 , int isink , u16 mode ,
u16 trigger , u16 duration , u16 on_ramp , u16 off_ramp ,
u16 drive )
{
switch ( isink ) {
case WM8350_ISINK_A :
wm8350_reg_write ( wm8350 , WM8350_CSA_FLASH_CONTROL ,
( mode ? WM8350_CS1_FLASH_MODE : 0 ) |
( trigger ? WM8350_CS1_TRIGSRC : 0 ) |
duration | on_ramp | off_ramp | drive ) ;
break ;
case WM8350_ISINK_B :
wm8350_reg_write ( wm8350 , WM8350_CSB_FLASH_CONTROL ,
( mode ? WM8350_CS2_FLASH_MODE : 0 ) |
( trigger ? WM8350_CS2_TRIGSRC : 0 ) |
duration | on_ramp | off_ramp | drive ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm8350_isink_set_flash ) ;
static int wm8350_dcdc_set_suspend_voltage ( struct regulator_dev * rdev , int uV )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
2012-06-12 16:10:26 +04:00
int sel , volt_reg , dcdc = rdev_get_id ( rdev ) ;
2008-10-10 18:58:15 +04:00
u16 val ;
2012-06-12 16:10:26 +04:00
dev_dbg ( wm8350 - > dev , " %s %d mV %d \n " , __func__ , dcdc , uV / 1000 ) ;
2008-10-10 18:58:15 +04:00
switch ( dcdc ) {
case WM8350_DCDC_1 :
volt_reg = WM8350_DCDC1_LOW_POWER ;
break ;
case WM8350_DCDC_3 :
volt_reg = WM8350_DCDC3_LOW_POWER ;
break ;
case WM8350_DCDC_4 :
volt_reg = WM8350_DCDC4_LOW_POWER ;
break ;
case WM8350_DCDC_6 :
volt_reg = WM8350_DCDC6_LOW_POWER ;
break ;
case WM8350_DCDC_2 :
case WM8350_DCDC_5 :
default :
return - EINVAL ;
}
2012-06-12 16:10:26 +04:00
sel = regulator_map_voltage_linear ( rdev , uV , uV ) ;
if ( sel < 0 )
2014-02-18 14:40:57 +04:00
return sel ;
2012-06-12 16:10:26 +04:00
2008-10-10 18:58:15 +04:00
/* all DCDCs have same mV bits */
val = wm8350_reg_read ( wm8350 , volt_reg ) & ~ WM8350_DC1_VSEL_MASK ;
2012-06-12 16:10:26 +04:00
wm8350_reg_write ( wm8350 , volt_reg , val | sel ) ;
2008-10-10 18:58:15 +04:00
return 0 ;
}
static int wm8350_dcdc_set_suspend_enable ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int dcdc = rdev_get_id ( rdev ) ;
u16 val ;
switch ( dcdc ) {
case WM8350_DCDC_1 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC1_LOW_POWER )
& ~ WM8350_DCDC_HIB_MODE_MASK ;
wm8350_reg_write ( wm8350 , WM8350_DCDC1_LOW_POWER ,
2012-03-23 02:25:05 +04:00
val | wm8350 - > pmic . dcdc1_hib_mode ) ;
2008-10-10 18:58:15 +04:00
break ;
case WM8350_DCDC_3 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC3_LOW_POWER )
& ~ WM8350_DCDC_HIB_MODE_MASK ;
wm8350_reg_write ( wm8350 , WM8350_DCDC3_LOW_POWER ,
2012-03-23 02:25:05 +04:00
val | wm8350 - > pmic . dcdc3_hib_mode ) ;
2008-10-10 18:58:15 +04:00
break ;
case WM8350_DCDC_4 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC4_LOW_POWER )
& ~ WM8350_DCDC_HIB_MODE_MASK ;
wm8350_reg_write ( wm8350 , WM8350_DCDC4_LOW_POWER ,
2012-03-23 02:25:05 +04:00
val | wm8350 - > pmic . dcdc4_hib_mode ) ;
2008-10-10 18:58:15 +04:00
break ;
case WM8350_DCDC_6 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC6_LOW_POWER )
& ~ WM8350_DCDC_HIB_MODE_MASK ;
wm8350_reg_write ( wm8350 , WM8350_DCDC6_LOW_POWER ,
2012-03-23 02:25:05 +04:00
val | wm8350 - > pmic . dcdc6_hib_mode ) ;
2008-10-10 18:58:15 +04:00
break ;
case WM8350_DCDC_2 :
case WM8350_DCDC_5 :
default :
return - EINVAL ;
}
return 0 ;
}
static int wm8350_dcdc_set_suspend_disable ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int dcdc = rdev_get_id ( rdev ) ;
u16 val ;
switch ( dcdc ) {
case WM8350_DCDC_1 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC1_LOW_POWER ) ;
wm8350 - > pmic . dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK ;
wm8350_reg_write ( wm8350 , WM8350_DCDC1_LOW_POWER ,
2012-03-29 06:47:36 +04:00
val | WM8350_DCDC_HIB_MODE_DIS ) ;
2008-10-10 18:58:15 +04:00
break ;
case WM8350_DCDC_3 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC3_LOW_POWER ) ;
wm8350 - > pmic . dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK ;
wm8350_reg_write ( wm8350 , WM8350_DCDC3_LOW_POWER ,
2012-03-29 06:47:36 +04:00
val | WM8350_DCDC_HIB_MODE_DIS ) ;
2008-10-10 18:58:15 +04:00
break ;
case WM8350_DCDC_4 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC4_LOW_POWER ) ;
wm8350 - > pmic . dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK ;
wm8350_reg_write ( wm8350 , WM8350_DCDC4_LOW_POWER ,
2012-03-29 06:47:36 +04:00
val | WM8350_DCDC_HIB_MODE_DIS ) ;
2008-10-10 18:58:15 +04:00
break ;
case WM8350_DCDC_6 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC6_LOW_POWER ) ;
wm8350 - > pmic . dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK ;
wm8350_reg_write ( wm8350 , WM8350_DCDC6_LOW_POWER ,
2012-03-29 06:47:36 +04:00
val | WM8350_DCDC_HIB_MODE_DIS ) ;
2008-10-10 18:58:15 +04:00
break ;
case WM8350_DCDC_2 :
case WM8350_DCDC_5 :
default :
return - EINVAL ;
}
return 0 ;
}
static int wm8350_dcdc25_set_suspend_enable ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int dcdc = rdev_get_id ( rdev ) ;
u16 val ;
switch ( dcdc ) {
case WM8350_DCDC_2 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC2_CONTROL )
& ~ WM8350_DC2_HIB_MODE_MASK ;
wm8350_reg_write ( wm8350 , WM8350_DCDC2_CONTROL , val |
2012-03-23 02:27:10 +04:00
( WM8350_DC2_HIB_MODE_ACTIVE < < WM8350_DC2_HIB_MODE_SHIFT ) ) ;
2008-10-10 18:58:15 +04:00
break ;
case WM8350_DCDC_5 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC5_CONTROL )
2012-03-23 02:27:10 +04:00
& ~ WM8350_DC5_HIB_MODE_MASK ;
2008-10-10 18:58:15 +04:00
wm8350_reg_write ( wm8350 , WM8350_DCDC5_CONTROL , val |
2012-03-23 02:27:10 +04:00
( WM8350_DC5_HIB_MODE_ACTIVE < < WM8350_DC5_HIB_MODE_SHIFT ) ) ;
2008-10-10 18:58:15 +04:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int wm8350_dcdc25_set_suspend_disable ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int dcdc = rdev_get_id ( rdev ) ;
u16 val ;
switch ( dcdc ) {
case WM8350_DCDC_2 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC2_CONTROL )
& ~ WM8350_DC2_HIB_MODE_MASK ;
wm8350_reg_write ( wm8350 , WM8350_DCDC2_CONTROL , val |
2012-03-23 02:27:10 +04:00
( WM8350_DC2_HIB_MODE_DISABLE < < WM8350_DC2_HIB_MODE_SHIFT ) ) ;
2008-10-10 18:58:15 +04:00
break ;
case WM8350_DCDC_5 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC5_CONTROL )
2012-03-23 02:27:10 +04:00
& ~ WM8350_DC5_HIB_MODE_MASK ;
2008-10-10 18:58:15 +04:00
wm8350_reg_write ( wm8350 , WM8350_DCDC5_CONTROL , val |
2012-03-23 02:27:10 +04:00
( WM8350_DC5_HIB_MODE_DISABLE < < WM8350_DC5_HIB_MODE_SHIFT ) ) ;
2008-10-10 18:58:15 +04:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int wm8350_dcdc_set_suspend_mode ( struct regulator_dev * rdev ,
unsigned int mode )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int dcdc = rdev_get_id ( rdev ) ;
u16 * hib_mode ;
switch ( dcdc ) {
case WM8350_DCDC_1 :
hib_mode = & wm8350 - > pmic . dcdc1_hib_mode ;
break ;
case WM8350_DCDC_3 :
hib_mode = & wm8350 - > pmic . dcdc3_hib_mode ;
break ;
case WM8350_DCDC_4 :
hib_mode = & wm8350 - > pmic . dcdc4_hib_mode ;
break ;
case WM8350_DCDC_6 :
hib_mode = & wm8350 - > pmic . dcdc6_hib_mode ;
break ;
case WM8350_DCDC_2 :
case WM8350_DCDC_5 :
default :
return - EINVAL ;
}
switch ( mode ) {
case REGULATOR_MODE_NORMAL :
* hib_mode = WM8350_DCDC_HIB_MODE_IMAGE ;
break ;
case REGULATOR_MODE_IDLE :
* hib_mode = WM8350_DCDC_HIB_MODE_STANDBY ;
break ;
case REGULATOR_MODE_STANDBY :
* hib_mode = WM8350_DCDC_HIB_MODE_LDO_IM ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2013-07-03 02:35:42 +04:00
static const struct regulator_linear_range wm8350_ldo_ranges [ ] = {
2013-10-11 05:32:18 +04:00
REGULATOR_LINEAR_RANGE ( 900000 , 0 , 15 , 50000 ) ,
REGULATOR_LINEAR_RANGE ( 1800000 , 16 , 31 , 100000 ) ,
2013-07-03 02:35:42 +04:00
} ;
2012-06-12 16:10:26 +04:00
2008-10-10 18:58:15 +04:00
static int wm8350_ldo_set_suspend_voltage ( struct regulator_dev * rdev , int uV )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
2012-06-12 16:10:26 +04:00
int sel , volt_reg , ldo = rdev_get_id ( rdev ) ;
2008-10-10 18:58:15 +04:00
u16 val ;
2012-06-12 16:10:26 +04:00
dev_dbg ( wm8350 - > dev , " %s %d mV %d \n " , __func__ , ldo , uV / 1000 ) ;
2008-10-10 18:58:15 +04:00
switch ( ldo ) {
case WM8350_LDO_1 :
volt_reg = WM8350_LDO1_LOW_POWER ;
break ;
case WM8350_LDO_2 :
volt_reg = WM8350_LDO2_LOW_POWER ;
break ;
case WM8350_LDO_3 :
volt_reg = WM8350_LDO3_LOW_POWER ;
break ;
case WM8350_LDO_4 :
volt_reg = WM8350_LDO4_LOW_POWER ;
break ;
default :
return - EINVAL ;
}
2013-07-03 02:35:42 +04:00
sel = regulator_map_voltage_linear_range ( rdev , uV , uV ) ;
2012-06-12 16:10:26 +04:00
if ( sel < 0 )
2014-02-18 14:40:57 +04:00
return sel ;
2012-06-12 16:10:26 +04:00
2008-10-10 18:58:15 +04:00
/* all LDOs have same mV bits */
val = wm8350_reg_read ( wm8350 , volt_reg ) & ~ WM8350_LDO1_VSEL_MASK ;
2012-06-12 16:10:26 +04:00
wm8350_reg_write ( wm8350 , volt_reg , val | sel ) ;
2008-10-10 18:58:15 +04:00
return 0 ;
}
static int wm8350_ldo_set_suspend_enable ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int volt_reg , ldo = rdev_get_id ( rdev ) ;
u16 val ;
switch ( ldo ) {
case WM8350_LDO_1 :
volt_reg = WM8350_LDO1_LOW_POWER ;
break ;
case WM8350_LDO_2 :
volt_reg = WM8350_LDO2_LOW_POWER ;
break ;
case WM8350_LDO_3 :
volt_reg = WM8350_LDO3_LOW_POWER ;
break ;
case WM8350_LDO_4 :
volt_reg = WM8350_LDO4_LOW_POWER ;
break ;
default :
return - EINVAL ;
}
/* all LDOs have same mV bits */
val = wm8350_reg_read ( wm8350 , volt_reg ) & ~ WM8350_LDO1_HIB_MODE_MASK ;
wm8350_reg_write ( wm8350 , volt_reg , val ) ;
return 0 ;
}
static int wm8350_ldo_set_suspend_disable ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int volt_reg , ldo = rdev_get_id ( rdev ) ;
u16 val ;
switch ( ldo ) {
case WM8350_LDO_1 :
volt_reg = WM8350_LDO1_LOW_POWER ;
break ;
case WM8350_LDO_2 :
volt_reg = WM8350_LDO2_LOW_POWER ;
break ;
case WM8350_LDO_3 :
volt_reg = WM8350_LDO3_LOW_POWER ;
break ;
case WM8350_LDO_4 :
volt_reg = WM8350_LDO4_LOW_POWER ;
break ;
default :
return - EINVAL ;
}
/* all LDOs have same mV bits */
val = wm8350_reg_read ( wm8350 , volt_reg ) & ~ WM8350_LDO1_HIB_MODE_MASK ;
2012-03-29 06:47:36 +04:00
wm8350_reg_write ( wm8350 , volt_reg , val | WM8350_LDO1_HIB_MODE_DIS ) ;
2008-10-10 18:58:15 +04:00
return 0 ;
}
int wm8350_dcdc_set_slot ( struct wm8350 * wm8350 , int dcdc , u16 start ,
u16 stop , u16 fault )
{
int slot_reg ;
u16 val ;
dev_dbg ( wm8350 - > dev , " %s %d start %d stop %d \n " ,
__func__ , dcdc , start , stop ) ;
/* slot valid ? */
if ( start > 15 | | stop > 15 )
return - EINVAL ;
switch ( dcdc ) {
case WM8350_DCDC_1 :
slot_reg = WM8350_DCDC1_TIMEOUTS ;
break ;
case WM8350_DCDC_2 :
slot_reg = WM8350_DCDC2_TIMEOUTS ;
break ;
case WM8350_DCDC_3 :
slot_reg = WM8350_DCDC3_TIMEOUTS ;
break ;
case WM8350_DCDC_4 :
slot_reg = WM8350_DCDC4_TIMEOUTS ;
break ;
case WM8350_DCDC_5 :
slot_reg = WM8350_DCDC5_TIMEOUTS ;
break ;
case WM8350_DCDC_6 :
slot_reg = WM8350_DCDC6_TIMEOUTS ;
break ;
default :
return - EINVAL ;
}
val = wm8350_reg_read ( wm8350 , slot_reg ) &
~ ( WM8350_DC1_ENSLOT_MASK | WM8350_DC1_SDSLOT_MASK |
WM8350_DC1_ERRACT_MASK ) ;
wm8350_reg_write ( wm8350 , slot_reg ,
val | ( start < < WM8350_DC1_ENSLOT_SHIFT ) |
( stop < < WM8350_DC1_SDSLOT_SHIFT ) |
( fault < < WM8350_DC1_ERRACT_SHIFT ) ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm8350_dcdc_set_slot ) ;
int wm8350_ldo_set_slot ( struct wm8350 * wm8350 , int ldo , u16 start , u16 stop )
{
int slot_reg ;
u16 val ;
dev_dbg ( wm8350 - > dev , " %s %d start %d stop %d \n " ,
__func__ , ldo , start , stop ) ;
/* slot valid ? */
if ( start > 15 | | stop > 15 )
return - EINVAL ;
switch ( ldo ) {
case WM8350_LDO_1 :
slot_reg = WM8350_LDO1_TIMEOUTS ;
break ;
case WM8350_LDO_2 :
slot_reg = WM8350_LDO2_TIMEOUTS ;
break ;
case WM8350_LDO_3 :
slot_reg = WM8350_LDO3_TIMEOUTS ;
break ;
case WM8350_LDO_4 :
slot_reg = WM8350_LDO4_TIMEOUTS ;
break ;
default :
return - EINVAL ;
}
val = wm8350_reg_read ( wm8350 , slot_reg ) & ~ WM8350_LDO1_SDSLOT_MASK ;
wm8350_reg_write ( wm8350 , slot_reg , val | ( ( start < < 10 ) | ( stop < < 6 ) ) ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm8350_ldo_set_slot ) ;
int wm8350_dcdc25_set_mode ( struct wm8350 * wm8350 , int dcdc , u16 mode ,
u16 ilim , u16 ramp , u16 feedback )
{
u16 val ;
dev_dbg ( wm8350 - > dev , " %s %d mode: %s %s \n " , __func__ , dcdc ,
mode ? " normal " : " boost " , ilim ? " low " : " normal " ) ;
switch ( dcdc ) {
case WM8350_DCDC_2 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC2_CONTROL )
& ~ ( WM8350_DC2_MODE_MASK | WM8350_DC2_ILIM_MASK |
WM8350_DC2_RMP_MASK | WM8350_DC2_FBSRC_MASK ) ;
wm8350_reg_write ( wm8350 , WM8350_DCDC2_CONTROL , val |
( mode < < WM8350_DC2_MODE_SHIFT ) |
( ilim < < WM8350_DC2_ILIM_SHIFT ) |
( ramp < < WM8350_DC2_RMP_SHIFT ) |
( feedback < < WM8350_DC2_FBSRC_SHIFT ) ) ;
break ;
case WM8350_DCDC_5 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC5_CONTROL )
& ~ ( WM8350_DC5_MODE_MASK | WM8350_DC5_ILIM_MASK |
WM8350_DC5_RMP_MASK | WM8350_DC5_FBSRC_MASK ) ;
wm8350_reg_write ( wm8350 , WM8350_DCDC5_CONTROL , val |
( mode < < WM8350_DC5_MODE_SHIFT ) |
( ilim < < WM8350_DC5_ILIM_SHIFT ) |
( ramp < < WM8350_DC5_RMP_SHIFT ) |
( feedback < < WM8350_DC5_FBSRC_SHIFT ) ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm8350_dcdc25_set_mode ) ;
static int force_continuous_enable ( struct wm8350 * wm8350 , int dcdc , int enable )
{
int reg = 0 , ret ;
switch ( dcdc ) {
case WM8350_DCDC_1 :
reg = WM8350_DCDC1_FORCE_PWM ;
break ;
case WM8350_DCDC_3 :
reg = WM8350_DCDC3_FORCE_PWM ;
break ;
case WM8350_DCDC_4 :
reg = WM8350_DCDC4_FORCE_PWM ;
break ;
case WM8350_DCDC_6 :
reg = WM8350_DCDC6_FORCE_PWM ;
break ;
default :
return - EINVAL ;
}
if ( enable )
ret = wm8350_set_bits ( wm8350 , reg ,
WM8350_DCDC1_FORCE_PWM_ENA ) ;
else
ret = wm8350_clear_bits ( wm8350 , reg ,
WM8350_DCDC1_FORCE_PWM_ENA ) ;
return ret ;
}
static int wm8350_dcdc_set_mode ( struct regulator_dev * rdev , unsigned int mode )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int dcdc = rdev_get_id ( rdev ) ;
u16 val ;
if ( dcdc < WM8350_DCDC_1 | | dcdc > WM8350_DCDC_6 )
return - EINVAL ;
if ( dcdc = = WM8350_DCDC_2 | | dcdc = = WM8350_DCDC_5 )
return - EINVAL ;
val = 1 < < ( dcdc - WM8350_DCDC_1 ) ;
switch ( mode ) {
case REGULATOR_MODE_FAST :
/* force continuous mode */
wm8350_set_bits ( wm8350 , WM8350_DCDC_ACTIVE_OPTIONS , val ) ;
wm8350_clear_bits ( wm8350 , WM8350_DCDC_SLEEP_OPTIONS , val ) ;
force_continuous_enable ( wm8350 , dcdc , 1 ) ;
break ;
case REGULATOR_MODE_NORMAL :
/* active / pulse skipping */
wm8350_set_bits ( wm8350 , WM8350_DCDC_ACTIVE_OPTIONS , val ) ;
wm8350_clear_bits ( wm8350 , WM8350_DCDC_SLEEP_OPTIONS , val ) ;
force_continuous_enable ( wm8350 , dcdc , 0 ) ;
break ;
case REGULATOR_MODE_IDLE :
/* standby mode */
force_continuous_enable ( wm8350 , dcdc , 0 ) ;
wm8350_clear_bits ( wm8350 , WM8350_DCDC_SLEEP_OPTIONS , val ) ;
wm8350_clear_bits ( wm8350 , WM8350_DCDC_ACTIVE_OPTIONS , val ) ;
break ;
case REGULATOR_MODE_STANDBY :
/* LDO mode */
force_continuous_enable ( wm8350 , dcdc , 0 ) ;
wm8350_set_bits ( wm8350 , WM8350_DCDC_SLEEP_OPTIONS , val ) ;
break ;
}
return 0 ;
}
static unsigned int wm8350_dcdc_get_mode ( struct regulator_dev * rdev )
{
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
int dcdc = rdev_get_id ( rdev ) ;
u16 mask , sleep , active , force ;
int mode = REGULATOR_MODE_NORMAL ;
2009-02-26 22:24:20 +03:00
int reg ;
2008-10-10 18:58:15 +04:00
2009-02-26 22:24:20 +03:00
switch ( dcdc ) {
case WM8350_DCDC_1 :
reg = WM8350_DCDC1_FORCE_PWM ;
break ;
case WM8350_DCDC_3 :
reg = WM8350_DCDC3_FORCE_PWM ;
break ;
case WM8350_DCDC_4 :
reg = WM8350_DCDC4_FORCE_PWM ;
break ;
case WM8350_DCDC_6 :
reg = WM8350_DCDC6_FORCE_PWM ;
break ;
default :
2008-10-10 18:58:15 +04:00
return - EINVAL ;
2009-02-26 22:24:20 +03:00
}
2008-10-10 18:58:15 +04:00
mask = 1 < < ( dcdc - WM8350_DCDC_1 ) ;
active = wm8350_reg_read ( wm8350 , WM8350_DCDC_ACTIVE_OPTIONS ) & mask ;
2009-02-26 22:24:20 +03:00
force = wm8350_reg_read ( wm8350 , reg ) & WM8350_DCDC1_FORCE_PWM_ENA ;
2008-10-10 18:58:15 +04:00
sleep = wm8350_reg_read ( wm8350 , WM8350_DCDC_SLEEP_OPTIONS ) & mask ;
2009-02-26 22:24:20 +03:00
2008-10-10 18:58:15 +04:00
dev_dbg ( wm8350 - > dev , " mask %x active %x sleep %x force %x " ,
mask , active , sleep , force ) ;
if ( active & & ! sleep ) {
if ( force )
mode = REGULATOR_MODE_FAST ;
else
mode = REGULATOR_MODE_NORMAL ;
} else if ( ! active & & ! sleep )
mode = REGULATOR_MODE_IDLE ;
2010-09-06 10:06:07 +04:00
else if ( sleep )
2008-10-10 18:58:15 +04:00
mode = REGULATOR_MODE_STANDBY ;
return mode ;
}
static unsigned int wm8350_ldo_get_mode ( struct regulator_dev * rdev )
{
return REGULATOR_MODE_NORMAL ;
}
struct wm8350_dcdc_efficiency {
int uA_load_min ;
int uA_load_max ;
unsigned int mode ;
} ;
static const struct wm8350_dcdc_efficiency dcdc1_6_efficiency [ ] = {
{ 0 , 10000 , REGULATOR_MODE_STANDBY } , /* 0 - 10mA - LDO */
{ 10000 , 100000 , REGULATOR_MODE_IDLE } , /* 10mA - 100mA - Standby */
{ 100000 , 1000000 , REGULATOR_MODE_NORMAL } , /* > 100mA - Active */
{ - 1 , - 1 , REGULATOR_MODE_NORMAL } ,
} ;
static const struct wm8350_dcdc_efficiency dcdc3_4_efficiency [ ] = {
{ 0 , 10000 , REGULATOR_MODE_STANDBY } , /* 0 - 10mA - LDO */
{ 10000 , 100000 , REGULATOR_MODE_IDLE } , /* 10mA - 100mA - Standby */
{ 100000 , 800000 , REGULATOR_MODE_NORMAL } , /* > 100mA - Active */
{ - 1 , - 1 , REGULATOR_MODE_NORMAL } ,
} ;
static unsigned int get_mode ( int uA , const struct wm8350_dcdc_efficiency * eff )
{
int i = 0 ;
while ( eff [ i ] . uA_load_min ! = - 1 ) {
if ( uA > = eff [ i ] . uA_load_min & & uA < = eff [ i ] . uA_load_max )
return eff [ i ] . mode ;
2018-04-20 12:26:23 +03:00
i + + ;
2008-10-10 18:58:15 +04:00
}
return REGULATOR_MODE_NORMAL ;
}
/* Query the regulator for it's most efficient mode @ uV,uA
* WM8350 regulator efficiency is pretty similar over
* different input and output uV .
*/
static unsigned int wm8350_dcdc_get_optimum_mode ( struct regulator_dev * rdev ,
int input_uV , int output_uV ,
int output_uA )
{
int dcdc = rdev_get_id ( rdev ) , mode ;
switch ( dcdc ) {
case WM8350_DCDC_1 :
case WM8350_DCDC_6 :
mode = get_mode ( output_uA , dcdc1_6_efficiency ) ;
break ;
case WM8350_DCDC_3 :
case WM8350_DCDC_4 :
mode = get_mode ( output_uA , dcdc3_4_efficiency ) ;
break ;
default :
mode = REGULATOR_MODE_NORMAL ;
break ;
}
return mode ;
}
2015-12-19 18:31:24 +03:00
static const struct regulator_ops wm8350_dcdc_ops = {
2012-05-10 01:22:30 +04:00
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
2012-05-01 00:00:10 +04:00
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
2012-05-10 01:22:30 +04:00
. list_voltage = regulator_list_voltage_linear ,
2012-06-03 19:12:16 +04:00
. map_voltage = regulator_map_voltage_linear ,
2012-05-01 00:08:59 +04:00
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. is_enabled = regulator_is_enabled_regmap ,
2008-10-10 18:58:15 +04:00
. get_mode = wm8350_dcdc_get_mode ,
. set_mode = wm8350_dcdc_set_mode ,
. get_optimum_mode = wm8350_dcdc_get_optimum_mode ,
. set_suspend_voltage = wm8350_dcdc_set_suspend_voltage ,
. set_suspend_enable = wm8350_dcdc_set_suspend_enable ,
. set_suspend_disable = wm8350_dcdc_set_suspend_disable ,
. set_suspend_mode = wm8350_dcdc_set_suspend_mode ,
} ;
2015-12-19 18:31:24 +03:00
static const struct regulator_ops wm8350_dcdc2_5_ops = {
2012-05-01 00:08:59 +04:00
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. is_enabled = regulator_is_enabled_regmap ,
2008-10-10 18:58:15 +04:00
. set_suspend_enable = wm8350_dcdc25_set_suspend_enable ,
. set_suspend_disable = wm8350_dcdc25_set_suspend_disable ,
} ;
2015-12-19 18:31:24 +03:00
static const struct regulator_ops wm8350_ldo_ops = {
2013-07-03 02:35:42 +04:00
. map_voltage = regulator_map_voltage_linear_range ,
2012-05-10 01:27:41 +04:00
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
2012-05-01 00:00:10 +04:00
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
2013-07-03 02:35:42 +04:00
. list_voltage = regulator_list_voltage_linear_range ,
2012-05-01 00:08:59 +04:00
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. is_enabled = regulator_is_enabled_regmap ,
2008-10-10 18:58:15 +04:00
. get_mode = wm8350_ldo_get_mode ,
. set_suspend_voltage = wm8350_ldo_set_suspend_voltage ,
. set_suspend_enable = wm8350_ldo_set_suspend_enable ,
. set_suspend_disable = wm8350_ldo_set_suspend_disable ,
} ;
2015-12-19 18:31:24 +03:00
static const struct regulator_ops wm8350_isink_ops = {
2008-10-10 18:58:15 +04:00
. set_current_limit = wm8350_isink_set_current ,
. get_current_limit = wm8350_isink_get_current ,
. enable = wm8350_isink_enable ,
. disable = wm8350_isink_disable ,
. is_enabled = wm8350_isink_is_enabled ,
2010-01-04 20:24:01 +03:00
. enable_time = wm8350_isink_enable_time ,
2008-10-10 18:58:15 +04:00
} ;
2012-04-03 23:47:15 +04:00
static const struct regulator_desc wm8350_reg [ NUM_WM8350_REGULATORS ] = {
2008-10-10 18:58:15 +04:00
{
. name = " DCDC1 " ,
. id = WM8350_DCDC_1 ,
. ops = & wm8350_dcdc_ops ,
. irq = WM8350_IRQ_UV_DC1 ,
. type = REGULATOR_VOLTAGE ,
2009-03-02 19:32:47 +03:00
. n_voltages = WM8350_DCDC_MAX_VSEL + 1 ,
2012-05-10 01:22:30 +04:00
. min_uV = 850000 ,
. uV_step = 25000 ,
2012-05-01 00:00:10 +04:00
. vsel_reg = WM8350_DCDC1_CONTROL ,
. vsel_mask = WM8350_DC1_VSEL_MASK ,
2012-05-01 00:08:59 +04:00
. enable_reg = WM8350_DCDC_LDO_REQUESTED ,
. enable_mask = WM8350_DC1_ENA ,
2008-10-10 18:58:15 +04:00
. owner = THIS_MODULE ,
} ,
{
. name = " DCDC2 " ,
. id = WM8350_DCDC_2 ,
. ops = & wm8350_dcdc2_5_ops ,
. irq = WM8350_IRQ_UV_DC2 ,
. type = REGULATOR_VOLTAGE ,
2012-05-01 00:08:59 +04:00
. enable_reg = WM8350_DCDC_LDO_REQUESTED ,
. enable_mask = WM8350_DC2_ENA ,
2008-10-10 18:58:15 +04:00
. owner = THIS_MODULE ,
} ,
{
. name = " DCDC3 " ,
. id = WM8350_DCDC_3 ,
. ops = & wm8350_dcdc_ops ,
. irq = WM8350_IRQ_UV_DC3 ,
. type = REGULATOR_VOLTAGE ,
2009-03-02 19:32:47 +03:00
. n_voltages = WM8350_DCDC_MAX_VSEL + 1 ,
2012-05-10 01:22:30 +04:00
. min_uV = 850000 ,
. uV_step = 25000 ,
2012-05-01 00:00:10 +04:00
. vsel_reg = WM8350_DCDC3_CONTROL ,
. vsel_mask = WM8350_DC3_VSEL_MASK ,
2012-05-01 00:08:59 +04:00
. enable_reg = WM8350_DCDC_LDO_REQUESTED ,
. enable_mask = WM8350_DC3_ENA ,
2008-10-10 18:58:15 +04:00
. owner = THIS_MODULE ,
} ,
{
. name = " DCDC4 " ,
. id = WM8350_DCDC_4 ,
. ops = & wm8350_dcdc_ops ,
. irq = WM8350_IRQ_UV_DC4 ,
. type = REGULATOR_VOLTAGE ,
2009-03-02 19:32:47 +03:00
. n_voltages = WM8350_DCDC_MAX_VSEL + 1 ,
2012-06-12 13:18:49 +04:00
. min_uV = 850000 ,
. uV_step = 25000 ,
2012-05-01 00:00:10 +04:00
. vsel_reg = WM8350_DCDC4_CONTROL ,
. vsel_mask = WM8350_DC4_VSEL_MASK ,
2012-05-01 00:08:59 +04:00
. enable_reg = WM8350_DCDC_LDO_REQUESTED ,
. enable_mask = WM8350_DC4_ENA ,
2008-10-10 18:58:15 +04:00
. owner = THIS_MODULE ,
} ,
{
. name = " DCDC5 " ,
. id = WM8350_DCDC_5 ,
. ops = & wm8350_dcdc2_5_ops ,
. irq = WM8350_IRQ_UV_DC5 ,
. type = REGULATOR_VOLTAGE ,
2012-05-01 00:08:59 +04:00
. enable_reg = WM8350_DCDC_LDO_REQUESTED ,
. enable_mask = WM8350_DC5_ENA ,
2008-10-10 18:58:15 +04:00
. owner = THIS_MODULE ,
} ,
{
. name = " DCDC6 " ,
. id = WM8350_DCDC_6 ,
. ops = & wm8350_dcdc_ops ,
. irq = WM8350_IRQ_UV_DC6 ,
. type = REGULATOR_VOLTAGE ,
2009-03-02 19:32:47 +03:00
. n_voltages = WM8350_DCDC_MAX_VSEL + 1 ,
2012-05-10 01:22:30 +04:00
. min_uV = 850000 ,
. uV_step = 25000 ,
2012-05-01 00:00:10 +04:00
. vsel_reg = WM8350_DCDC6_CONTROL ,
. vsel_mask = WM8350_DC6_VSEL_MASK ,
2012-05-01 00:08:59 +04:00
. enable_reg = WM8350_DCDC_LDO_REQUESTED ,
. enable_mask = WM8350_DC6_ENA ,
2008-10-10 18:58:15 +04:00
. owner = THIS_MODULE ,
} ,
{
. name = " LDO1 " ,
. id = WM8350_LDO_1 ,
. ops = & wm8350_ldo_ops ,
. irq = WM8350_IRQ_UV_LDO1 ,
. type = REGULATOR_VOLTAGE ,
2009-03-02 19:32:47 +03:00
. n_voltages = WM8350_LDO1_VSEL_MASK + 1 ,
2013-07-03 02:35:42 +04:00
. linear_ranges = wm8350_ldo_ranges ,
. n_linear_ranges = ARRAY_SIZE ( wm8350_ldo_ranges ) ,
2012-05-01 00:00:10 +04:00
. vsel_reg = WM8350_LDO1_CONTROL ,
. vsel_mask = WM8350_LDO1_VSEL_MASK ,
2012-05-01 00:08:59 +04:00
. enable_reg = WM8350_DCDC_LDO_REQUESTED ,
. enable_mask = WM8350_LDO1_ENA ,
2008-10-10 18:58:15 +04:00
. owner = THIS_MODULE ,
} ,
{
. name = " LDO2 " ,
. id = WM8350_LDO_2 ,
. ops = & wm8350_ldo_ops ,
. irq = WM8350_IRQ_UV_LDO2 ,
. type = REGULATOR_VOLTAGE ,
2009-03-02 19:32:47 +03:00
. n_voltages = WM8350_LDO2_VSEL_MASK + 1 ,
2013-07-03 02:35:42 +04:00
. linear_ranges = wm8350_ldo_ranges ,
. n_linear_ranges = ARRAY_SIZE ( wm8350_ldo_ranges ) ,
2012-05-01 00:00:10 +04:00
. vsel_reg = WM8350_LDO2_CONTROL ,
. vsel_mask = WM8350_LDO2_VSEL_MASK ,
2012-05-01 00:08:59 +04:00
. enable_reg = WM8350_DCDC_LDO_REQUESTED ,
. enable_mask = WM8350_LDO2_ENA ,
2008-10-10 18:58:15 +04:00
. owner = THIS_MODULE ,
} ,
{
. name = " LDO3 " ,
. id = WM8350_LDO_3 ,
. ops = & wm8350_ldo_ops ,
. irq = WM8350_IRQ_UV_LDO3 ,
. type = REGULATOR_VOLTAGE ,
2009-03-02 19:32:47 +03:00
. n_voltages = WM8350_LDO3_VSEL_MASK + 1 ,
2013-07-03 02:35:42 +04:00
. linear_ranges = wm8350_ldo_ranges ,
. n_linear_ranges = ARRAY_SIZE ( wm8350_ldo_ranges ) ,
2012-05-01 00:00:10 +04:00
. vsel_reg = WM8350_LDO3_CONTROL ,
. vsel_mask = WM8350_LDO3_VSEL_MASK ,
2012-05-01 00:08:59 +04:00
. enable_reg = WM8350_DCDC_LDO_REQUESTED ,
. enable_mask = WM8350_LDO3_ENA ,
2008-10-10 18:58:15 +04:00
. owner = THIS_MODULE ,
} ,
{
. name = " LDO4 " ,
. id = WM8350_LDO_4 ,
. ops = & wm8350_ldo_ops ,
. irq = WM8350_IRQ_UV_LDO4 ,
. type = REGULATOR_VOLTAGE ,
2009-03-02 19:32:47 +03:00
. n_voltages = WM8350_LDO4_VSEL_MASK + 1 ,
2013-07-03 02:35:42 +04:00
. linear_ranges = wm8350_ldo_ranges ,
. n_linear_ranges = ARRAY_SIZE ( wm8350_ldo_ranges ) ,
2012-05-01 00:00:10 +04:00
. vsel_reg = WM8350_LDO4_CONTROL ,
. vsel_mask = WM8350_LDO4_VSEL_MASK ,
2012-05-01 00:08:59 +04:00
. enable_reg = WM8350_DCDC_LDO_REQUESTED ,
. enable_mask = WM8350_LDO4_ENA ,
2008-10-10 18:58:15 +04:00
. owner = THIS_MODULE ,
} ,
{
. name = " ISINKA " ,
. id = WM8350_ISINK_A ,
. ops = & wm8350_isink_ops ,
. irq = WM8350_IRQ_CS1 ,
. type = REGULATOR_CURRENT ,
. owner = THIS_MODULE ,
} ,
{
. name = " ISINKB " ,
. id = WM8350_ISINK_B ,
. ops = & wm8350_isink_ops ,
. irq = WM8350_IRQ_CS2 ,
. type = REGULATOR_CURRENT ,
. owner = THIS_MODULE ,
} ,
} ;
2009-11-04 19:10:51 +03:00
static irqreturn_t pmic_uv_handler ( int irq , void * data )
2008-10-10 18:58:15 +04:00
{
struct regulator_dev * rdev = ( struct regulator_dev * ) data ;
2018-11-19 00:56:17 +03:00
regulator_lock ( rdev ) ;
2008-10-10 18:58:15 +04:00
if ( irq = = WM8350_IRQ_CS1 | | irq = = WM8350_IRQ_CS2 )
regulator_notifier_call_chain ( rdev ,
REGULATOR_EVENT_REGULATION_OUT ,
2015-02-23 19:13:31 +03:00
NULL ) ;
2008-10-10 18:58:15 +04:00
else
regulator_notifier_call_chain ( rdev ,
REGULATOR_EVENT_UNDER_VOLTAGE ,
2015-02-23 19:13:31 +03:00
NULL ) ;
2018-11-19 00:56:17 +03:00
regulator_unlock ( rdev ) ;
2009-11-04 19:10:51 +03:00
return IRQ_HANDLED ;
2008-10-10 18:58:15 +04:00
}
static int wm8350_regulator_probe ( struct platform_device * pdev )
{
struct wm8350 * wm8350 = dev_get_drvdata ( & pdev - > dev ) ;
2012-04-04 03:50:22 +04:00
struct regulator_config config = { } ;
2008-10-10 18:58:15 +04:00
struct regulator_dev * rdev ;
int ret ;
u16 val ;
if ( pdev - > id < WM8350_DCDC_1 | | pdev - > id > WM8350_ISINK_B )
return - ENODEV ;
/* do any regulatior specific init */
switch ( pdev - > id ) {
case WM8350_DCDC_1 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC1_LOW_POWER ) ;
wm8350 - > pmic . dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK ;
break ;
case WM8350_DCDC_3 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC3_LOW_POWER ) ;
wm8350 - > pmic . dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK ;
break ;
case WM8350_DCDC_4 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC4_LOW_POWER ) ;
wm8350 - > pmic . dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK ;
break ;
case WM8350_DCDC_6 :
val = wm8350_reg_read ( wm8350 , WM8350_DCDC6_LOW_POWER ) ;
wm8350 - > pmic . dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK ;
break ;
}
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 = dev_get_drvdata ( & pdev - > dev ) ;
2012-05-01 00:00:10 +04:00
config . regmap = wm8350 - > regmap ;
2012-04-04 03:50:22 +04:00
2008-10-10 18:58:15 +04:00
/* register regulator */
2013-08-31 15:00:37 +04:00
rdev = devm_regulator_register ( & pdev - > dev , & wm8350_reg [ pdev - > id ] ,
& config ) ;
2008-10-10 18:58:15 +04:00
if ( IS_ERR ( rdev ) ) {
dev_err ( & pdev - > dev , " failed to register %s \n " ,
wm8350_reg [ pdev - > id ] . name ) ;
return PTR_ERR ( rdev ) ;
}
/* register regulator IRQ */
ret = wm8350_register_irq ( wm8350 , wm8350_reg [ pdev - > id ] . irq ,
2009-11-04 19:10:51 +03:00
pmic_uv_handler , 0 , " UV " , rdev ) ;
2008-10-10 18:58:15 +04:00
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to register regulator %s IRQ \n " ,
wm8350_reg [ pdev - > id ] . name ) ;
return ret ;
}
return 0 ;
}
static int wm8350_regulator_remove ( struct platform_device * pdev )
{
struct regulator_dev * rdev = platform_get_drvdata ( pdev ) ;
struct wm8350 * wm8350 = rdev_get_drvdata ( rdev ) ;
2010-01-05 16:59:07 +03:00
wm8350_free_irq ( wm8350 , wm8350_reg [ pdev - > id ] . irq , rdev ) ;
2008-10-10 18:58:15 +04:00
return 0 ;
}
int wm8350_register_regulator ( struct wm8350 * wm8350 , int reg ,
struct regulator_init_data * initdata )
{
struct platform_device * pdev ;
int ret ;
2009-06-16 00:30:39 +04:00
if ( reg < 0 | | reg > = NUM_WM8350_REGULATORS )
return - EINVAL ;
2008-10-10 18:58:15 +04:00
if ( wm8350 - > pmic . pdev [ reg ] )
return - EBUSY ;
2008-12-19 01:12:16 +03:00
if ( reg > = WM8350_DCDC_1 & & reg < = WM8350_DCDC_6 & &
reg > wm8350 - > pmic . max_dcdc )
return - ENODEV ;
if ( reg > = WM8350_ISINK_A & & reg < = WM8350_ISINK_B & &
reg > wm8350 - > pmic . max_isink )
return - ENODEV ;
2008-10-10 18:58:15 +04:00
pdev = platform_device_alloc ( " wm8350-regulator " , reg ) ;
if ( ! pdev )
return - ENOMEM ;
wm8350 - > pmic . pdev [ reg ] = pdev ;
initdata - > driver_data = wm8350 ;
pdev - > dev . platform_data = initdata ;
pdev - > dev . parent = wm8350 - > dev ;
platform_set_drvdata ( pdev , wm8350 ) ;
ret = platform_device_add ( pdev ) ;
if ( ret ! = 0 ) {
dev_err ( wm8350 - > dev , " Failed to register regulator %d: %d \n " ,
reg , ret ) ;
2010-07-26 06:41:58 +04:00
platform_device_put ( pdev ) ;
2008-10-10 18:58:15 +04:00
wm8350 - > pmic . pdev [ reg ] = NULL ;
}
return ret ;
}
EXPORT_SYMBOL_GPL ( wm8350_register_regulator ) ;
2008-12-04 19:52:33 +03:00
/**
* wm8350_register_led - Register a WM8350 LED output
*
* @ param wm8350 The WM8350 device to configure .
* @ param lednum LED device index to create .
* @ param dcdc The DCDC to use for the LED .
* @ param isink The ISINK to use for the LED .
* @ param pdata Configuration for the LED .
*
* The WM8350 supports the use of an ISINK together with a DCDC to
* provide a power - efficient LED driver . This function registers the
* regulators and instantiates the platform device for a LED . The
* operating modes for the LED regulators must be configured using
* wm8350_isink_set_flash ( ) , wm8350_dcdc25_set_mode ( ) and
* wm8350_dcdc_set_slot ( ) prior to calling this function .
*/
int wm8350_register_led ( struct wm8350 * wm8350 , int lednum , int dcdc , int isink ,
struct wm8350_led_platform_data * pdata )
{
struct wm8350_led * led ;
struct platform_device * pdev ;
int ret ;
2009-01-17 18:06:40 +03:00
if ( lednum > = ARRAY_SIZE ( wm8350 - > pmic . led ) | | lednum < 0 ) {
2008-12-04 19:52:33 +03:00
dev_err ( wm8350 - > dev , " Invalid LED index %d \n " , lednum ) ;
return - ENODEV ;
}
led = & wm8350 - > pmic . led [ lednum ] ;
if ( led - > pdev ) {
dev_err ( wm8350 - > dev , " LED %d already allocated \n " , lednum ) ;
return - EINVAL ;
}
pdev = platform_device_alloc ( " wm8350-led " , lednum ) ;
if ( pdev = = NULL ) {
dev_err ( wm8350 - > dev , " Failed to allocate LED %d \n " , lednum ) ;
return - ENOMEM ;
}
2012-02-02 17:45:09 +04:00
led - > isink_consumer . dev_name = dev_name ( & pdev - > dev ) ;
2008-12-04 19:52:33 +03:00
led - > isink_consumer . supply = " led_isink " ;
led - > isink_init . num_consumer_supplies = 1 ;
led - > isink_init . consumer_supplies = & led - > isink_consumer ;
led - > isink_init . constraints . min_uA = 0 ;
led - > isink_init . constraints . max_uA = pdata - > max_uA ;
2010-01-04 18:30:54 +03:00
led - > isink_init . constraints . valid_ops_mask
= REGULATOR_CHANGE_CURRENT | REGULATOR_CHANGE_STATUS ;
2008-12-04 19:52:33 +03:00
led - > isink_init . constraints . valid_modes_mask = REGULATOR_MODE_NORMAL ;
ret = wm8350_register_regulator ( wm8350 , isink , & led - > isink_init ) ;
if ( ret ! = 0 ) {
platform_device_put ( pdev ) ;
return ret ;
}
2012-02-02 17:45:09 +04:00
led - > dcdc_consumer . dev_name = dev_name ( & pdev - > dev ) ;
2008-12-04 19:52:33 +03:00
led - > dcdc_consumer . supply = " led_vcc " ;
led - > dcdc_init . num_consumer_supplies = 1 ;
led - > dcdc_init . consumer_supplies = & led - > dcdc_consumer ;
led - > dcdc_init . constraints . valid_modes_mask = REGULATOR_MODE_NORMAL ;
2010-01-04 18:30:54 +03:00
led - > dcdc_init . constraints . valid_ops_mask = REGULATOR_CHANGE_STATUS ;
2008-12-04 19:52:33 +03:00
ret = wm8350_register_regulator ( wm8350 , dcdc , & led - > dcdc_init ) ;
if ( ret ! = 0 ) {
platform_device_put ( pdev ) ;
return ret ;
}
switch ( isink ) {
case WM8350_ISINK_A :
wm8350 - > pmic . isink_A_dcdc = dcdc ;
break ;
case WM8350_ISINK_B :
wm8350 - > pmic . isink_B_dcdc = dcdc ;
break ;
}
pdev - > dev . platform_data = pdata ;
pdev - > dev . parent = wm8350 - > dev ;
ret = platform_device_add ( pdev ) ;
if ( ret ! = 0 ) {
dev_err ( wm8350 - > dev , " Failed to register LED %d: %d \n " ,
lednum , ret ) ;
platform_device_put ( pdev ) ;
return ret ;
}
led - > pdev = pdev ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm8350_register_led ) ;
2008-10-10 18:58:15 +04:00
static struct platform_driver wm8350_regulator_driver = {
. probe = wm8350_regulator_probe ,
. remove = wm8350_regulator_remove ,
. driver = {
. name = " wm8350-regulator " ,
} ,
} ;
static int __init wm8350_regulator_init ( void )
{
return platform_driver_register ( & wm8350_regulator_driver ) ;
}
subsys_initcall ( wm8350_regulator_init ) ;
static void __exit wm8350_regulator_exit ( void )
{
platform_driver_unregister ( & wm8350_regulator_driver ) ;
}
module_exit ( wm8350_regulator_exit ) ;
/* Module information */
MODULE_AUTHOR ( " Liam Girdwood " ) ;
MODULE_DESCRIPTION ( " WM8350 voltage and current regulator driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-04-28 14:13:55 +04:00
MODULE_ALIAS ( " platform:wm8350-regulator " ) ;