]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
Merge tag 'bypass' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator...
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Wed, 26 Sep 2012 11:29:31 +0000 (12:29 +0100)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Wed, 26 Sep 2012 11:29:31 +0000 (12:29 +0100)
regulator: Bypass mode support

Allow regulators to be put into a non-regulating mode bypassing the
input straight to the output, mostly used by low power retention modes.

Documentation/ABI/testing/sysfs-class-regulator
drivers/extcon/extcon-arizona.c
drivers/regulator/arizona-ldo1.c
drivers/regulator/arizona-micsupp.c
drivers/regulator/core.c
drivers/regulator/wm831x-ldo.c
include/linux/regulator/consumer.h
include/linux/regulator/driver.h
include/linux/regulator/machine.h

index e091fa8737929966b0939e1a96e4e9fe05d89c39..bc578bc606284bf248fe87169e8cda0dbdfa4f3a 100644 (file)
@@ -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@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.
index 427a289f32a5a2707fd417ee56468600d572cbb4..6c19833ed2d06033e9b683ad602dd6c62905bac6 100644 (file)
@@ -434,6 +434,11 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
        regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
                           ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);
 
+       ret = regulator_allow_bypass(info->micvdd, true);
+       if (ret != 0)
+               dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n",
+                        ret);
+
        pm_runtime_put(&pdev->dev);
 
        return 0;
index c8f95c07adb63aea4d174c9eab659ba11bcb5dea..80e012f14160e1b1cceb08a5bd5615a3748b0779 100644 (file)
@@ -39,6 +39,8 @@ static struct regulator_ops arizona_ldo1_ops = {
        .map_voltage = regulator_map_voltage_linear,
        .get_voltage_sel = regulator_get_voltage_sel_regmap,
        .set_voltage_sel = regulator_set_voltage_sel_regmap,
+       .get_bypass = regulator_get_bypass_regmap,
+       .set_bypass = regulator_set_bypass_regmap,
 };
 
 static const struct regulator_desc arizona_ldo1 = {
@@ -49,6 +51,8 @@ static const struct regulator_desc arizona_ldo1 = {
 
        .vsel_reg = ARIZONA_LDO1_CONTROL_1,
        .vsel_mask = ARIZONA_LDO1_VSEL_MASK,
+       .bypass_reg = ARIZONA_LDO1_CONTROL_1,
+       .bypass_mask = ARIZONA_LDO1_BYPASS,
        .min_uV = 900000,
        .uV_step = 50000,
        .n_voltages = 7,
index 450a069aa9b6ae5cde873f896a4714aaa8e48272..d9b1f82cc5bd80939206025432f2602072dfe244 100644 (file)
@@ -82,6 +82,9 @@ static struct regulator_ops arizona_micsupp_ops = {
 
        .get_voltage_sel = regulator_get_voltage_sel_regmap,
        .set_voltage_sel = regulator_set_voltage_sel_regmap,
+
+       .get_bypass = regulator_get_bypass_regmap,
+       .set_bypass = regulator_set_bypass_regmap,
 };
 
 static const struct regulator_desc arizona_micsupp = {
@@ -95,6 +98,8 @@ static const struct regulator_desc arizona_micsupp = {
        .vsel_mask = ARIZONA_LDO2_VSEL_MASK,
        .enable_reg = ARIZONA_MIC_CHARGE_PUMP_1,
        .enable_mask = ARIZONA_CPMIC_ENA,
+       .bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1,
+       .bypass_mask = ARIZONA_CPMIC_BYPASS,
 
        .owner = THIS_MODULE,
 };
index 48385318175a83fe17208e099caa46c07a01d2f2..419805cdd9d7abfd291b3cd0f00126af4c03ea6c 100644 (file)
@@ -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.
@@ -2673,6 +2698,100 @@ out:
 }
 EXPORT_SYMBOL_GPL(regulator_set_optimum_mode);
 
+/**
+ * regulator_set_bypass_regmap - Default set_bypass() using regmap
+ *
+ * @rdev: device to operate on.
+ * @enable: state to set.
+ */
+int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable)
+{
+       unsigned int val;
+
+       if (enable)
+               val = rdev->desc->bypass_mask;
+       else
+               val = 0;
+
+       return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg,
+                                 rdev->desc->bypass_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap);
+
+/**
+ * regulator_get_bypass_regmap - Default get_bypass() using regmap
+ *
+ * @rdev: device to operate on.
+ * @enable: current state.
+ */
+int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable)
+{
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val);
+       if (ret != 0)
+               return ret;
+
+       *enable = val & rdev->desc->bypass_mask;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap);
+
+/**
+ * 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: regulator source
@@ -3036,6 +3155,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) {
@@ -3124,6 +3248,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);
 }
 
 /**
index 5cb70ca1e98d1f1843347fe14d7291d7a041eceb..f203a972dedf4a83a7ed66b43ed92b3aa7c873f2 100644 (file)
@@ -237,6 +237,8 @@ static struct regulator_ops wm831x_gp_ldo_ops = {
        .set_mode = wm831x_gp_ldo_set_mode,
        .get_status = wm831x_gp_ldo_get_status,
        .get_optimum_mode = wm831x_gp_ldo_get_optimum_mode,
+       .get_bypass = regulator_get_bypass_regmap,
+       .set_bypass = regulator_set_bypass_regmap,
 
        .is_enabled = regulator_is_enabled_regmap,
        .enable = regulator_enable_regmap,
@@ -293,6 +295,8 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
        ldo->desc.vsel_mask = WM831X_LDO1_ON_VSEL_MASK;
        ldo->desc.enable_reg = WM831X_LDO_ENABLE;
        ldo->desc.enable_mask = 1 << id;
+       ldo->desc.bypass_reg = ldo->base;
+       ldo->desc.bypass_mask = WM831X_LDO1_SWI;
 
        config.dev = pdev->dev.parent;
        if (pdata)
@@ -488,6 +492,8 @@ static struct regulator_ops wm831x_aldo_ops = {
        .get_mode = wm831x_aldo_get_mode,
        .set_mode = wm831x_aldo_set_mode,
        .get_status = wm831x_aldo_get_status,
+       .set_bypass = regulator_set_bypass_regmap,
+       .get_bypass = regulator_get_bypass_regmap,
 
        .is_enabled = regulator_is_enabled_regmap,
        .enable = regulator_enable_regmap,
@@ -544,6 +550,8 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
        ldo->desc.vsel_mask = WM831X_LDO7_ON_VSEL_MASK;
        ldo->desc.enable_reg = WM831X_LDO_ENABLE;
        ldo->desc.enable_mask = 1 << id;
+       ldo->desc.bypass_reg = ldo->base;
+       ldo->desc.bypass_mask = WM831X_LDO7_SWI;
 
        config.dev = pdev->dev.parent;
        if (pdata)
index da339fd8c7559de74ad46dd3536f4b95981c89d8..ea3e358166217741269936d7b742e34ed3067799 100644 (file)
@@ -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)
 {
index bac4c871f3bd1930b481c214e932cec9666e05b6..7274a469e8d902b1b7c8b39980b2d1c3b85858ee 100644 (file)
@@ -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,
 };
@@ -67,6 +69,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
@@ -133,6 +138,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 */
 
@@ -205,6 +214,8 @@ struct regulator_desc {
        unsigned int vsel_mask;
        unsigned int enable_reg;
        unsigned int enable_mask;
+       unsigned int bypass_reg;
+       unsigned int bypass_mask;
 
        unsigned int enable_time;
 };
@@ -253,6 +264,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 */
@@ -310,6 +322,8 @@ int regulator_disable_regmap(struct regulator_dev *rdev);
 int regulator_set_voltage_time_sel(struct regulator_dev *rdev,
                                   unsigned int old_selector,
                                   unsigned int new_selector);
+int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable);
+int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable);
 
 void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data);
 
index 40dd0a394cfa95c1f470e0c2bbc6ee334005bef9..36adbc82de6ae6c58c3a224f62e5d385ce7a35ba 100644 (file)
@@ -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