Re: [PATCH 1/4] regulator: core: Support bypass mode

From: Graeme Gregory
Date: Tue Aug 28 2012 - 03:38:06 EST


Thanks Mark,

Series looks good to me and works for the palmas. I can now add the
bypass support without the side hack I currently have to do it.

Reviewed-by: Graeme Gregory <gg@xxxxxxxxxxxxxxx>

On 28/08/12 06:31, Mark Brown wrote:
> 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.
>
> 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: Mark Brown <broonie@xxxxxxxxxxxxxxxxxxxxxxxxxxx>
> ---
> Documentation/ABI/testing/sysfs-class-regulator | 21 ++++++
> drivers/regulator/core.c | 84 +++++++++++++++++++++++
> include/linux/regulator/consumer.h | 8 +++
> include/linux/regulator/driver.h | 10 +++
> include/linux/regulator/machine.h | 2 +
> 5 files changed, 125 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-class-regulator b/Documentation/ABI/testing/sysfs-class-regulator
> index e091fa8..bc578bc 100644
> --- a/Documentation/ABI/testing/sysfs-class-regulator
> +++ b/Documentation/ABI/testing/sysfs-class-regulator
> @@ -349,3 +349,24 @@ Description:
>
> This will be one of the same strings reported by
> the "state" attribute.
> +
> +What: /sys/class/regulator/.../bypass
> +Date: September 2012
> +KernelVersion: 3.7
> +Contact: Mark Brown <broonie@xxxxxxxxxxxxxxxxxxxxxxxxxxx>
> +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.
> diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
> index db7d59a..d06a82e 100644
> --- a/drivers/regulator/core.c
> +++ b/drivers/regulator/core.c
> @@ -77,6 +77,7 @@ struct regulator {
> struct device *dev;
> struct list_head list;
> unsigned int always_on:1;
> + unsigned int bypass:1;
> int uA_load;
> int min_uV;
> int max_uV;
> @@ -394,6 +395,9 @@ static ssize_t regulator_status_show(struct device *dev,
> case REGULATOR_STATUS_STANDBY:
> label = "standby";
> break;
> + case REGULATOR_STATUS_BYPASS:
> + label = "bypass";
> + break;
> case REGULATOR_STATUS_UNDEFINED:
> label = "undefined";
> break;
> @@ -585,6 +589,27 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev,
> static DEVICE_ATTR(suspend_standby_state, 0444,
> 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.
> @@ -2683,6 +2708,58 @@ out:
> 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;
> +}
> +
> +/**
> * regulator_register_notifier - register regulator event notifier
> * @regulator: regulator source
> * @nb: notifier block
> @@ -3046,6 +3123,11 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
> if (status < 0)
> return status;
> }
> + if (ops->get_bypass) {
> + status = device_create_file(dev, &dev_attr_bypass);
> + if (status < 0)
> + return status;
> + }
>
> /* some attributes are type-specific */
> if (rdev->desc->type == REGULATOR_CURRENT) {
> @@ -3134,6 +3216,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
> &rdev->use_count);
> debugfs_create_u32("open_count", 0444, rdev->debugfs,
> &rdev->open_count);
> + debugfs_create_u32("bypass_count", 0444, rdev->debugfs,
> + &rdev->bypass_count);
> }
>
> /**
> diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
> index b987bcc..07838c9 100644
> --- a/include/linux/regulator/consumer.h
> +++ b/include/linux/regulator/consumer.h
> @@ -177,6 +177,8 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode);
> unsigned int regulator_get_mode(struct regulator *regulator);
> int regulator_set_optimum_mode(struct regulator *regulator, int load_uA);
>
> +int regulator_allow_bypass(struct regulator *regulator, bool allow);
> +
> /* regulator notifier block */
> int regulator_register_notifier(struct regulator *regulator,
> struct notifier_block *nb);
> @@ -328,6 +330,12 @@ static inline int regulator_set_optimum_mode(struct regulator *regulator,
> 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,
> struct notifier_block *nb)
> {
> diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
> index c10012f..2b2670b 100644
> --- a/include/linux/regulator/driver.h
> +++ b/include/linux/regulator/driver.h
> @@ -32,6 +32,8 @@ enum regulator_status {
> REGULATOR_STATUS_NORMAL,
> REGULATOR_STATUS_IDLE,
> REGULATOR_STATUS_STANDBY,
> + /* The regulator is enabled but not regulating */
> + REGULATOR_STATUS_BYPASS,
> /* in case that any other status doesn't apply */
> REGULATOR_STATUS_UNDEFINED,
> };
> @@ -68,6 +70,9 @@ enum regulator_status {
> * @get_optimum_mode: Get the most efficient operating mode for the regulator
> * 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
> * stabilise after being enabled, in microseconds.
> * @set_ramp_delay: Set the ramp delay for the regulator. The driver should
> @@ -134,6 +139,10 @@ struct regulator_ops {
> unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV,
> 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
> * its parent PMIC enters a global STANDBY/HIBERNATE state */
>
> @@ -254,6 +263,7 @@ struct regulator_dev {
> int exclusive;
> u32 use_count;
> u32 open_count;
> + u32 bypass_count;
>
> /* lists we belong to */
> struct list_head list; /* list of all regulators */
> diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h
> index 40dd0a3..36adbc8 100644
> --- a/include/linux/regulator/machine.h
> +++ b/include/linux/regulator/machine.h
> @@ -32,6 +32,7 @@ struct regulator;
> * board/machine.
> * STATUS: Regulator can be enabled and disabled.
> * DRMS: Dynamic Regulator Mode Switching is enabled for this regulator.
> + * BYPASS: Regulator can be put into bypass mode
> */
>
> #define REGULATOR_CHANGE_VOLTAGE 0x1
> @@ -39,6 +40,7 @@ struct regulator;
> #define REGULATOR_CHANGE_MODE 0x4
> #define REGULATOR_CHANGE_STATUS 0x8
> #define REGULATOR_CHANGE_DRMS 0x10
> +#define REGULATOR_CHANGE_BYPASS 0x20
>
> /**
> * struct regulator_state - regulator state during low power system states

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/