Commit f59c8f9f authored by Mark Brown's avatar Mark Brown

regulator: core: Support bypass mode

Many regulators support a bypass mode where they simply switch their
input supply to the output. This is mainly used in low power retention
states where power consumption is extremely low so higher voltage or
less clean supplies can be used.

Support this by providing ops for the drivers and a consumer API which
allows the device to be put into bypass mode if all consumers enable it
and the machine enables permission for this.

This is not supported as a mode since the existing modes are rarely used
due to fuzzy definition and mostly redundant with modern hardware which is
able to respond promptly to load changes.
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Reviewed-by: default avatarGraeme Gregory <gg@slimlogic.co.uk>
parent 4cbe5a55
...@@ -349,3 +349,24 @@ Description: ...@@ -349,3 +349,24 @@ Description:
This will be one of the same strings reported by This will be one of the same strings reported by
the "state" attribute. the "state" attribute.
What: /sys/class/regulator/.../bypass
Date: September 2012
KernelVersion: 3.7
Contact: Mark Brown <broonie@opensource.wolfsonmicro.com>
Description:
Some regulator directories will contain a field called
bypass. This indicates if the device is in bypass mode.
This will be one of the following strings:
'enabled'
'disabled'
'unknown'
'enabled' means the regulator is in bypass mode.
'disabled' means that the regulator is regulating.
'unknown' means software cannot determine the state, or
the reported state is invalid.
...@@ -77,6 +77,7 @@ struct regulator { ...@@ -77,6 +77,7 @@ struct regulator {
struct device *dev; struct device *dev;
struct list_head list; struct list_head list;
unsigned int always_on:1; unsigned int always_on:1;
unsigned int bypass:1;
int uA_load; int uA_load;
int min_uV; int min_uV;
int max_uV; int max_uV;
...@@ -394,6 +395,9 @@ static ssize_t regulator_status_show(struct device *dev, ...@@ -394,6 +395,9 @@ static ssize_t regulator_status_show(struct device *dev,
case REGULATOR_STATUS_STANDBY: case REGULATOR_STATUS_STANDBY:
label = "standby"; label = "standby";
break; break;
case REGULATOR_STATUS_BYPASS:
label = "bypass";
break;
case REGULATOR_STATUS_UNDEFINED: case REGULATOR_STATUS_UNDEFINED:
label = "undefined"; label = "undefined";
break; break;
...@@ -585,6 +589,27 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev, ...@@ -585,6 +589,27 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev,
static DEVICE_ATTR(suspend_standby_state, 0444, static DEVICE_ATTR(suspend_standby_state, 0444,
regulator_suspend_standby_state_show, NULL); regulator_suspend_standby_state_show, NULL);
static ssize_t regulator_bypass_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
const char *report;
bool bypass;
int ret;
ret = rdev->desc->ops->get_bypass(rdev, &bypass);
if (ret != 0)
report = "unknown";
else if (bypass)
report = "enabled";
else
report = "disabled";
return sprintf(buf, "%s\n", report);
}
static DEVICE_ATTR(bypass, 0444,
regulator_bypass_show, NULL);
/* /*
* These are the only attributes are present for all regulators. * These are the only attributes are present for all regulators.
...@@ -2673,6 +2698,59 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) ...@@ -2673,6 +2698,59 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
} }
EXPORT_SYMBOL_GPL(regulator_set_optimum_mode); EXPORT_SYMBOL_GPL(regulator_set_optimum_mode);
/**
* regulator_allow_bypass - allow the regulator to go into bypass mode
*
* @regulator: Regulator to configure
* @allow: enable or disable bypass mode
*
* Allow the regulator to go into bypass mode if all other consumers
* for the regulator also enable bypass mode and the machine
* constraints allow this. Bypass mode means that the regulator is
* simply passing the input directly to the output with no regulation.
*/
int regulator_allow_bypass(struct regulator *regulator, bool enable)
{
struct regulator_dev *rdev = regulator->rdev;
int ret = 0;
if (!rdev->desc->ops->set_bypass)
return 0;
if (rdev->constraints &&
!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_BYPASS))
return 0;
mutex_lock(&rdev->mutex);
if (enable && !regulator->bypass) {
rdev->bypass_count++;
if (rdev->bypass_count == rdev->open_count) {
ret = rdev->desc->ops->set_bypass(rdev, enable);
if (ret != 0)
rdev->bypass_count--;
}
} else if (!enable && regulator->bypass) {
rdev->bypass_count--;
if (rdev->bypass_count != rdev->open_count) {
ret = rdev->desc->ops->set_bypass(rdev, enable);
if (ret != 0)
rdev->bypass_count++;
}
}
if (ret == 0)
regulator->bypass = enable;
mutex_unlock(&rdev->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(regulator_allow_bypass);
/** /**
* regulator_register_notifier - register regulator event notifier * regulator_register_notifier - register regulator event notifier
* @regulator: regulator source * @regulator: regulator source
...@@ -3036,6 +3114,11 @@ static int add_regulator_attributes(struct regulator_dev *rdev) ...@@ -3036,6 +3114,11 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
if (status < 0) if (status < 0)
return status; return status;
} }
if (ops->get_bypass) {
status = device_create_file(dev, &dev_attr_bypass);
if (status < 0)
return status;
}
/* some attributes are type-specific */ /* some attributes are type-specific */
if (rdev->desc->type == REGULATOR_CURRENT) { if (rdev->desc->type == REGULATOR_CURRENT) {
...@@ -3124,6 +3207,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) ...@@ -3124,6 +3207,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
&rdev->use_count); &rdev->use_count);
debugfs_create_u32("open_count", 0444, rdev->debugfs, debugfs_create_u32("open_count", 0444, rdev->debugfs,
&rdev->open_count); &rdev->open_count);
debugfs_create_u32("bypass_count", 0444, rdev->debugfs,
&rdev->bypass_count);
} }
/** /**
......
...@@ -177,6 +177,8 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode); ...@@ -177,6 +177,8 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode);
unsigned int regulator_get_mode(struct regulator *regulator); unsigned int regulator_get_mode(struct regulator *regulator);
int regulator_set_optimum_mode(struct regulator *regulator, int load_uA); int regulator_set_optimum_mode(struct regulator *regulator, int load_uA);
int regulator_allow_bypass(struct regulator *regulator, bool allow);
/* regulator notifier block */ /* regulator notifier block */
int regulator_register_notifier(struct regulator *regulator, int regulator_register_notifier(struct regulator *regulator,
struct notifier_block *nb); struct notifier_block *nb);
...@@ -328,6 +330,12 @@ static inline int regulator_set_optimum_mode(struct regulator *regulator, ...@@ -328,6 +330,12 @@ static inline int regulator_set_optimum_mode(struct regulator *regulator,
return REGULATOR_MODE_NORMAL; return REGULATOR_MODE_NORMAL;
} }
static inline int regulator_allow_bypass(struct regulator *regulator,
bool allow)
{
return 0;
}
static inline int regulator_register_notifier(struct regulator *regulator, static inline int regulator_register_notifier(struct regulator *regulator,
struct notifier_block *nb) struct notifier_block *nb)
{ {
......
...@@ -32,6 +32,8 @@ enum regulator_status { ...@@ -32,6 +32,8 @@ enum regulator_status {
REGULATOR_STATUS_NORMAL, REGULATOR_STATUS_NORMAL,
REGULATOR_STATUS_IDLE, REGULATOR_STATUS_IDLE,
REGULATOR_STATUS_STANDBY, REGULATOR_STATUS_STANDBY,
/* The regulator is enabled but not regulating */
REGULATOR_STATUS_BYPASS,
/* in case that any other status doesn't apply */ /* in case that any other status doesn't apply */
REGULATOR_STATUS_UNDEFINED, REGULATOR_STATUS_UNDEFINED,
}; };
...@@ -67,6 +69,9 @@ enum regulator_status { ...@@ -67,6 +69,9 @@ enum regulator_status {
* @get_optimum_mode: Get the most efficient operating mode for the regulator * @get_optimum_mode: Get the most efficient operating mode for the regulator
* when running with the specified parameters. * when running with the specified parameters.
* *
* @set_bypass: Set the regulator in bypass mode.
* @get_bypass: Get the regulator bypass mode state.
*
* @enable_time: Time taken for the regulator voltage output voltage to * @enable_time: Time taken for the regulator voltage output voltage to
* stabilise after being enabled, in microseconds. * stabilise after being enabled, in microseconds.
* @set_ramp_delay: Set the ramp delay for the regulator. The driver should * @set_ramp_delay: Set the ramp delay for the regulator. The driver should
...@@ -133,6 +138,10 @@ struct regulator_ops { ...@@ -133,6 +138,10 @@ struct regulator_ops {
unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV, unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV,
int output_uV, int load_uA); int output_uV, int load_uA);
/* control and report on bypass mode */
int (*set_bypass)(struct regulator_dev *dev, bool enable);
int (*get_bypass)(struct regulator_dev *dev, bool *enable);
/* the operations below are for configuration of regulator state when /* the operations below are for configuration of regulator state when
* its parent PMIC enters a global STANDBY/HIBERNATE state */ * its parent PMIC enters a global STANDBY/HIBERNATE state */
...@@ -253,6 +262,7 @@ struct regulator_dev { ...@@ -253,6 +262,7 @@ struct regulator_dev {
int exclusive; int exclusive;
u32 use_count; u32 use_count;
u32 open_count; u32 open_count;
u32 bypass_count;
/* lists we belong to */ /* lists we belong to */
struct list_head list; /* list of all regulators */ struct list_head list; /* list of all regulators */
......
...@@ -32,6 +32,7 @@ struct regulator; ...@@ -32,6 +32,7 @@ struct regulator;
* board/machine. * board/machine.
* STATUS: Regulator can be enabled and disabled. * STATUS: Regulator can be enabled and disabled.
* DRMS: Dynamic Regulator Mode Switching is enabled for this regulator. * DRMS: Dynamic Regulator Mode Switching is enabled for this regulator.
* BYPASS: Regulator can be put into bypass mode
*/ */
#define REGULATOR_CHANGE_VOLTAGE 0x1 #define REGULATOR_CHANGE_VOLTAGE 0x1
...@@ -39,6 +40,7 @@ struct regulator; ...@@ -39,6 +40,7 @@ struct regulator;
#define REGULATOR_CHANGE_MODE 0x4 #define REGULATOR_CHANGE_MODE 0x4
#define REGULATOR_CHANGE_STATUS 0x8 #define REGULATOR_CHANGE_STATUS 0x8
#define REGULATOR_CHANGE_DRMS 0x10 #define REGULATOR_CHANGE_DRMS 0x10
#define REGULATOR_CHANGE_BYPASS 0x20
/** /**
* struct regulator_state - regulator state during low power system states * struct regulator_state - regulator state during low power system states
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment