Merge tag 'regulator-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Jan 2018 19:32:44 +0000 (11:32 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Jan 2018 19:32:44 +0000 (11:32 -0800)
Pull regulator updates from Mark Brown:
 "This is a quiet release in terms of code volume but a fairly big one
  in terms of framework changes - we've got one long awaited feature in
  the form of runtime configuration of suspend and the start of coupled
  regulator support too:

   - Support for modifying the voltage and enable configuration devices
     will have in suspend, contributed by Chunyan Zhang.

   - Support for the Spreadtrum SC2731, contributed by Erick Chen.

   - The start of changes to support coupled regulators from Maciej
     Purski, the rest of the series should arrive for v4.17"

* tag 'regulator-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator:
  regulator: Fix build error
  regulator: core: Refactor regulator_list_voltage()
  regulator: core: Move of_find_regulator_by_node() to of_regulator.c
  regulator: add PM suspend and resume hooks
  regulator: empty the old suspend functions
  regulator: leave one item to record whether regulator is enabled
  regulator: make regulator voltage be an array to support more states
  regulator: added support for suspend states
  regulator: qcom_spmi: Use regmap helpers for enable/disable/is_enabled callback
  regulator: sc2731: Fix defines for SC2731_WR_UNLOCK and SC2731_PWR_WR_PROT_VALUE
  regulator: fix incorrect indentation of two assignment statements
  regulator: sc2731: Add regulator driver to support Spreadtrum SC2731 PMIC
  regulator: Add Spreadtrum SC2731 regulator documentation
  regulator: Update code examples in documentation
  MAINTAINERS: regulator: Add Documentation/power/regulator/
  regulator: tps65218: Add NULL test for devm_kzalloc call
  regulator: tps65218: Remove unused enum tps65218_regulators

14 files changed:
Documentation/devicetree/bindings/regulator/regulator.txt
Documentation/devicetree/bindings/regulator/sprd,sc2731-regulator.txt [new file with mode: 0644]
Documentation/power/regulator/machine.txt
MAINTAINERS
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/core.c
drivers/regulator/internal.h
drivers/regulator/of_regulator.c
drivers/regulator/qcom_spmi-regulator.c
drivers/regulator/sc2731-regulator.c [new file with mode: 0644]
drivers/regulator/tps65218-regulator.c
include/linux/regulator/driver.h
include/linux/regulator/machine.h

index 3cbf56ce66ea9871ad2e41068baaef6171ae7ca3..2babe15b618d91d0f91d141da5bc0a77a37c5c48 100644 (file)
@@ -42,8 +42,16 @@ Optional properties:
 - regulator-state-[mem/disk] node has following common properties:
        - regulator-on-in-suspend: regulator should be on in suspend state.
        - regulator-off-in-suspend: regulator should be off in suspend state.
-       - regulator-suspend-microvolt: regulator should be set to this voltage
-         in suspend.
+       - regulator-suspend-min-microvolt: minimum voltage may be set in
+         suspend state.
+       - regulator-suspend-max-microvolt: maximum voltage may be set in
+         suspend state.
+       - regulator-suspend-microvolt: the default voltage which regulator
+         would be set in suspend. This property is now deprecated, instead
+         setting voltage for suspend mode via the API which regulator
+         driver provides is recommended.
+       - regulator-changeable-in-suspend: whether the default voltage and
+         the regulator on/off in suspend can be changed in runtime.
        - regulator-mode: operating mode in the given suspend state.
          The set of possible operating modes depends on the capabilities of
          every hardware so the valid modes are documented on each regulator
diff --git a/Documentation/devicetree/bindings/regulator/sprd,sc2731-regulator.txt b/Documentation/devicetree/bindings/regulator/sprd,sc2731-regulator.txt
new file mode 100644 (file)
index 0000000..63dc078
--- /dev/null
@@ -0,0 +1,43 @@
+Spreadtrum SC2731 Voltage regulators
+
+The SC2731 integrates low-voltage and low quiescent current DCDC/LDO.
+14 LDO and 3 DCDCs are designed for external use. All DCDCs/LDOs have
+their own bypass (power-down) control signals. External tantalum or MLCC
+ceramic capacitors are recommended to use with these LDOs.
+
+Required properties:
+ - compatible: should be "sprd,sc27xx-regulator".
+
+List of regulators provided by this controller. It is named according to
+its regulator type, BUCK_<name> and LDO_<name>. The definition for each
+of these nodes is defined using the standard binding for regulators at
+Documentation/devicetree/bindings/regulator/regulator.txt.
+
+The valid names for regulators are:
+BUCK:
+       BUCK_CPU0, BUCK_CPU1, BUCK_RF
+LDO:
+       LDO_CAMA0, LDO_CAMA1, LDO_CAMMOT, LDO_VLDO, LDO_EMMCCORE, LDO_SDCORE,
+       LDO_SDIO, LDO_WIFIPA, LDO_USB33, LDO_CAMD0, LDO_CAMD1, LDO_CON,
+       LDO_CAMIO, LDO_SRAM
+
+Example:
+       regulators {
+               compatible = "sprd,sc27xx-regulator";
+
+               vddarm0: BUCK_CPU0 {
+                       regulator-name = "vddarm0";
+                       regulator-min-microvolt = <400000>;
+                       regulator-max-microvolt = <1996875>;
+                       regulator-ramp-delay = <25000>;
+                       regulator-always-on;
+               };
+
+               vddcama0: LDO_CAMA0 {
+                       regulator-name = "vddcama0";
+                       regulator-min-microvolt = <1200000>;
+                       regulator-max-microvolt = <3750000>;
+                       regulator-enable-ramp-delay = <100>;
+               };
+               ...
+       };
index 757e3b53dc11a8acbb4d048487caab90b0ea8ebd..eff4dcaaa252b0e5491c4477710c6b82f6fb3ba9 100644 (file)
@@ -23,16 +23,12 @@ struct regulator_consumer_supply {
 e.g. for the machine above
 
 static struct regulator_consumer_supply regulator1_consumers[] = {
-{
-       .dev_name       = "dev_name(consumer B)",
-       .supply         = "Vcc",
-},};
+       REGULATOR_SUPPLY("Vcc", "consumer B"),
+};
 
 static struct regulator_consumer_supply regulator2_consumers[] = {
-{
-       .dev    = "dev_name(consumer A"),
-       .supply = "Vcc",
-},};
+       REGULATOR_SUPPLY("Vcc", "consumer A"),
+};
 
 This maps Regulator-1 to the 'Vcc' supply for Consumer B and maps Regulator-2
 to the 'Vcc' supply for Consumer A.
@@ -78,20 +74,20 @@ static struct regulator_init_data regulator2_data = {
 Finally the regulator devices must be registered in the usual manner.
 
 static struct platform_device regulator_devices[] = {
-{
-       .name = "regulator",
-       .id = DCDC_1,
-       .dev = {
-               .platform_data = &regulator1_data,
+       {
+               .name = "regulator",
+               .id = DCDC_1,
+               .dev = {
+                       .platform_data = &regulator1_data,
+               },
        },
-},
-{
-       .name = "regulator",
-       .id = DCDC_2,
-       .dev = {
-               .platform_data = &regulator2_data,
+       {
+               .name = "regulator",
+               .id = DCDC_2,
+               .dev = {
+                       .platform_data = &regulator2_data,
+               },
        },
-},
 };
 /* register regulator 1 device */
 platform_device_register(&regulator_devices[0]);
index 0b5249149695d16a51fbac5eecba8b60434ef201..d9da617b65c4c7c9236058475231f8c3cf9f1386 100644 (file)
@@ -14670,6 +14670,7 @@ W:      http://www.slimlogic.co.uk/?p=48
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git
 S:     Supported
 F:     Documentation/devicetree/bindings/regulator/
+F:     Documentation/power/regulator/
 F:     drivers/regulator/
 F:     include/dt-bindings/regulator/
 F:     include/linux/regulator/
index 96cd55f9e3c5ca77d4a64e27c45d755b3ef23743..b27417ca188a0acd510899b11fcd6617115e4e5f 100644 (file)
@@ -744,6 +744,13 @@ config REGULATOR_S5M8767
         via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and
         supports DVS mode with 8bits of output voltage control.
 
+config REGULATOR_SC2731
+       tristate "Spreadtrum SC2731 power regulator driver"
+       depends on MFD_SC27XX_PMIC || COMPILE_TEST
+       help
+         This driver provides support for the voltage regulators on the
+         SC2731 PMIC.
+
 config REGULATOR_SKY81452
        tristate "Skyworks Solutions SKY81452 voltage regulator"
        depends on MFD_SKY81452
index 80ffc57a9ca3e3f102a33bee772574c611d5d2e5..19fea09ba10a2be775d205a9aeaf3ef5d1b60719 100644 (file)
@@ -95,6 +95,7 @@ obj-$(CONFIG_REGULATOR_RT5033)        += rt5033-regulator.o
 obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
 obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
 obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
+obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o
 obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
 obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
 obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o
index b64b7916507f28adb03b600387356ad9c8fcd26e..42681c10cbe4fa9f7f001be90ff60dcdd3cf0db8 100644 (file)
@@ -58,8 +58,6 @@ static bool has_full_constraints;
 
 static struct dentry *debugfs_root;
 
-static struct class regulator_class;
-
 /*
  * struct regulator_map
  *
@@ -112,11 +110,6 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
                                          const char *supply_name);
 static void _regulator_put(struct regulator *regulator);
 
-static struct regulator_dev *dev_to_rdev(struct device *dev)
-{
-       return container_of(dev, struct regulator_dev, dev);
-}
-
 static const char *rdev_get_name(struct regulator_dev *rdev)
 {
        if (rdev->constraints && rdev->constraints->name)
@@ -236,26 +229,35 @@ static int regulator_check_voltage(struct regulator_dev *rdev,
        return 0;
 }
 
+/* return 0 if the state is valid */
+static int regulator_check_states(suspend_state_t state)
+{
+       return (state > PM_SUSPEND_MAX || state == PM_SUSPEND_TO_IDLE);
+}
+
 /* Make sure we select a voltage that suits the needs of all
  * regulator consumers
  */
 static int regulator_check_consumers(struct regulator_dev *rdev,
-                                    int *min_uV, int *max_uV)
+                                    int *min_uV, int *max_uV,
+                                    suspend_state_t state)
 {
        struct regulator *regulator;
+       struct regulator_voltage *voltage;
 
        list_for_each_entry(regulator, &rdev->consumer_list, list) {
+               voltage = &regulator->voltage[state];
                /*
                 * Assume consumers that didn't say anything are OK
                 * with anything in the constraint range.
                 */
-               if (!regulator->min_uV && !regulator->max_uV)
+               if (!voltage->min_uV && !voltage->max_uV)
                        continue;
 
-               if (*max_uV > regulator->max_uV)
-                       *max_uV = regulator->max_uV;
-               if (*min_uV < regulator->min_uV)
-                       *min_uV = regulator->min_uV;
+               if (*max_uV > voltage->max_uV)
+                       *max_uV = voltage->max_uV;
+               if (*min_uV < voltage->min_uV)
+                       *min_uV = voltage->min_uV;
        }
 
        if (*min_uV > *max_uV) {
@@ -324,6 +326,24 @@ static int regulator_mode_constrain(struct regulator_dev *rdev,
        return -EINVAL;
 }
 
+static inline struct regulator_state *
+regulator_get_suspend_state(struct regulator_dev *rdev, suspend_state_t state)
+{
+       if (rdev->constraints == NULL)
+               return NULL;
+
+       switch (state) {
+       case PM_SUSPEND_STANDBY:
+               return &rdev->constraints->state_standby;
+       case PM_SUSPEND_MEM:
+               return &rdev->constraints->state_mem;
+       case PM_SUSPEND_MAX:
+               return &rdev->constraints->state_disk;
+       default:
+               return NULL;
+       }
+}
+
 static ssize_t regulator_uV_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
@@ -731,29 +751,32 @@ static int drms_uA_update(struct regulator_dev *rdev)
 }
 
 static int suspend_set_state(struct regulator_dev *rdev,
-       struct regulator_state *rstate)
+                                   suspend_state_t state)
 {
        int ret = 0;
+       struct regulator_state *rstate;
+
+       rstate = regulator_get_suspend_state(rdev, state);
+       if (rstate == NULL)
+               return -EINVAL;
 
        /* If we have no suspend mode configration don't set anything;
         * only warn if the driver implements set_suspend_voltage or
         * set_suspend_mode callback.
         */
-       if (!rstate->enabled && !rstate->disabled) {
+       if (rstate->enabled != ENABLE_IN_SUSPEND &&
+           rstate->enabled != DISABLE_IN_SUSPEND) {
                if (rdev->desc->ops->set_suspend_voltage ||
                    rdev->desc->ops->set_suspend_mode)
                        rdev_warn(rdev, "No configuration\n");
                return 0;
        }
 
-       if (rstate->enabled && rstate->disabled) {
-               rdev_err(rdev, "invalid configuration\n");
-               return -EINVAL;
-       }
-
-       if (rstate->enabled && rdev->desc->ops->set_suspend_enable)
+       if (rstate->enabled == ENABLE_IN_SUSPEND &&
+               rdev->desc->ops->set_suspend_enable)
                ret = rdev->desc->ops->set_suspend_enable(rdev);
-       else if (rstate->disabled && rdev->desc->ops->set_suspend_disable)
+       else if (rstate->enabled == DISABLE_IN_SUSPEND &&
+               rdev->desc->ops->set_suspend_disable)
                ret = rdev->desc->ops->set_suspend_disable(rdev);
        else /* OK if set_suspend_enable or set_suspend_disable is NULL */
                ret = 0;
@@ -778,28 +801,8 @@ static int suspend_set_state(struct regulator_dev *rdev,
                        return ret;
                }
        }
-       return ret;
-}
-
-/* locks held by caller */
-static int suspend_prepare(struct regulator_dev *rdev, suspend_state_t state)
-{
-       if (!rdev->constraints)
-               return -EINVAL;
 
-       switch (state) {
-       case PM_SUSPEND_STANDBY:
-               return suspend_set_state(rdev,
-                       &rdev->constraints->state_standby);
-       case PM_SUSPEND_MEM:
-               return suspend_set_state(rdev,
-                       &rdev->constraints->state_mem);
-       case PM_SUSPEND_MAX:
-               return suspend_set_state(rdev,
-                       &rdev->constraints->state_disk);
-       default:
-               return -EINVAL;
-       }
+       return ret;
 }
 
 static void print_constraints(struct regulator_dev *rdev)
@@ -1068,7 +1071,7 @@ static int set_machine_constraints(struct regulator_dev *rdev,
 
        /* do we need to setup our suspend state */
        if (rdev->constraints->initial_state) {
-               ret = suspend_prepare(rdev, rdev->constraints->initial_state);
+               ret = suspend_set_state(rdev, rdev->constraints->initial_state);
                if (ret < 0) {
                        rdev_err(rdev, "failed to set suspend state\n");
                        return ret;
@@ -1356,9 +1359,9 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
                debugfs_create_u32("uA_load", 0444, regulator->debugfs,
                                   &regulator->uA_load);
                debugfs_create_u32("min_uV", 0444, regulator->debugfs,
-                                  &regulator->min_uV);
+                                  &regulator->voltage[PM_SUSPEND_ON].min_uV);
                debugfs_create_u32("max_uV", 0444, regulator->debugfs,
-                                  &regulator->max_uV);
+                                  &regulator->voltage[PM_SUSPEND_ON].max_uV);
                debugfs_create_file("constraint_flags", 0444,
                                    regulator->debugfs, regulator,
                                    &constraint_flags_fops);
@@ -1417,20 +1420,6 @@ static void regulator_supply_alias(struct device **dev, const char **supply)
        }
 }
 
-static int of_node_match(struct device *dev, const void *data)
-{
-       return dev->of_node == data;
-}
-
-static struct regulator_dev *of_find_regulator_by_node(struct device_node *np)
-{
-       struct device *dev;
-
-       dev = class_find_device(&regulator_class, NULL, np, of_node_match);
-
-       return dev ? dev_to_rdev(dev) : NULL;
-}
-
 static int regulator_match(struct device *dev, const void *data)
 {
        struct regulator_dev *r = dev_to_rdev(dev);
@@ -2468,10 +2457,9 @@ static int _regulator_is_enabled(struct regulator_dev *rdev)
        return rdev->desc->ops->is_enabled(rdev);
 }
 
-static int _regulator_list_voltage(struct regulator *regulator,
-                                   unsigned selector, int lock)
+static int _regulator_list_voltage(struct regulator_dev *rdev,
+                                  unsigned selector, int lock)
 {
-       struct regulator_dev *rdev = regulator->rdev;
        const struct regulator_ops *ops = rdev->desc->ops;
        int ret;
 
@@ -2487,7 +2475,8 @@ static int _regulator_list_voltage(struct regulator *regulator,
                if (lock)
                        mutex_unlock(&rdev->mutex);
        } else if (rdev->is_switch && rdev->supply) {
-               ret = _regulator_list_voltage(rdev->supply, selector, lock);
+               ret = _regulator_list_voltage(rdev->supply->rdev,
+                                             selector, lock);
        } else {
                return -EINVAL;
        }
@@ -2563,7 +2552,7 @@ EXPORT_SYMBOL_GPL(regulator_count_voltages);
  */
 int regulator_list_voltage(struct regulator *regulator, unsigned selector)
 {
-       return _regulator_list_voltage(regulator, selector, 1);
+       return _regulator_list_voltage(regulator->rdev, selector, 1);
 }
 EXPORT_SYMBOL_GPL(regulator_list_voltage);
 
@@ -2605,8 +2594,8 @@ int regulator_get_hardware_vsel_register(struct regulator *regulator,
        if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap)
                return -EOPNOTSUPP;
 
-        *vsel_reg = rdev->desc->vsel_reg;
-        *vsel_mask = rdev->desc->vsel_mask;
+       *vsel_reg = rdev->desc->vsel_reg;
+       *vsel_mask = rdev->desc->vsel_mask;
 
         return 0;
 }
@@ -2897,10 +2886,38 @@ out:
        return ret;
 }
 
+static int _regulator_do_set_suspend_voltage(struct regulator_dev *rdev,
+                                 int min_uV, int max_uV, suspend_state_t state)
+{
+       struct regulator_state *rstate;
+       int uV, sel;
+
+       rstate = regulator_get_suspend_state(rdev, state);
+       if (rstate == NULL)
+               return -EINVAL;
+
+       if (min_uV < rstate->min_uV)
+               min_uV = rstate->min_uV;
+       if (max_uV > rstate->max_uV)
+               max_uV = rstate->max_uV;
+
+       sel = regulator_map_voltage(rdev, min_uV, max_uV);
+       if (sel < 0)
+               return sel;
+
+       uV = rdev->desc->ops->list_voltage(rdev, sel);
+       if (uV >= min_uV && uV <= max_uV)
+               rstate->uV = uV;
+
+       return 0;
+}
+
 static int regulator_set_voltage_unlocked(struct regulator *regulator,
-                                         int min_uV, int max_uV)
+                                         int min_uV, int max_uV,
+                                         suspend_state_t state)
 {
        struct regulator_dev *rdev = regulator->rdev;
+       struct regulator_voltage *voltage = &regulator->voltage[state];
        int ret = 0;
        int old_min_uV, old_max_uV;
        int current_uV;
@@ -2911,7 +2928,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
         * should be a noop (some cpufreq implementations use the same
         * voltage for multiple frequencies, for example).
         */
-       if (regulator->min_uV == min_uV && regulator->max_uV == max_uV)
+       if (voltage->min_uV == min_uV && voltage->max_uV == max_uV)
                goto out;
 
        /* If we're trying to set a range that overlaps the current voltage,
@@ -2921,8 +2938,8 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
        if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) {
                current_uV = _regulator_get_voltage(rdev);
                if (min_uV <= current_uV && current_uV <= max_uV) {
-                       regulator->min_uV = min_uV;
-                       regulator->max_uV = max_uV;
+                       voltage->min_uV = min_uV;
+                       voltage->max_uV = max_uV;
                        goto out;
                }
        }
@@ -2940,12 +2957,12 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
                goto out;
 
        /* restore original values in case of error */
-       old_min_uV = regulator->min_uV;
-       old_max_uV = regulator->max_uV;
-       regulator->min_uV = min_uV;
-       regulator->max_uV = max_uV;
+       old_min_uV = voltage->min_uV;
+       old_max_uV = voltage->max_uV;
+       voltage->min_uV = min_uV;
+       voltage->max_uV = max_uV;
 
-       ret = regulator_check_consumers(rdev, &min_uV, &max_uV);
+       ret = regulator_check_consumers(rdev, &min_uV, &max_uV, state);
        if (ret < 0)
                goto out2;
 
@@ -2963,7 +2980,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
                        goto out2;
                }
 
-               best_supply_uV = _regulator_list_voltage(regulator, selector, 0);
+               best_supply_uV = _regulator_list_voltage(rdev, selector, 0);
                if (best_supply_uV < 0) {
                        ret = best_supply_uV;
                        goto out2;
@@ -2982,7 +2999,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
 
        if (supply_change_uV > 0) {
                ret = regulator_set_voltage_unlocked(rdev->supply,
-                               best_supply_uV, INT_MAX);
+                               best_supply_uV, INT_MAX, state);
                if (ret) {
                        dev_err(&rdev->dev, "Failed to increase supply voltage: %d\n",
                                        ret);
@@ -2990,13 +3007,17 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
                }
        }
 
-       ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
+       if (state == PM_SUSPEND_ON)
+               ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
+       else
+               ret = _regulator_do_set_suspend_voltage(rdev, min_uV,
+                                                       max_uV, state);
        if (ret < 0)
                goto out2;
 
        if (supply_change_uV < 0) {
                ret = regulator_set_voltage_unlocked(rdev->supply,
-                               best_supply_uV, INT_MAX);
+                               best_supply_uV, INT_MAX, state);
                if (ret)
                        dev_warn(&rdev->dev, "Failed to decrease supply voltage: %d\n",
                                        ret);
@@ -3007,8 +3028,8 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
 out:
        return ret;
 out2:
-       regulator->min_uV = old_min_uV;
-       regulator->max_uV = old_max_uV;
+       voltage->min_uV = old_min_uV;
+       voltage->max_uV = old_max_uV;
 
        return ret;
 }
@@ -3037,7 +3058,8 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)
 
        regulator_lock_supply(regulator->rdev);
 
-       ret = regulator_set_voltage_unlocked(regulator, min_uV, max_uV);
+       ret = regulator_set_voltage_unlocked(regulator, min_uV, max_uV,
+                                            PM_SUSPEND_ON);
 
        regulator_unlock_supply(regulator->rdev);
 
@@ -3045,6 +3067,89 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)
 }
 EXPORT_SYMBOL_GPL(regulator_set_voltage);
 
+static inline int regulator_suspend_toggle(struct regulator_dev *rdev,
+                                          suspend_state_t state, bool en)
+{
+       struct regulator_state *rstate;
+
+       rstate = regulator_get_suspend_state(rdev, state);
+       if (rstate == NULL)
+               return -EINVAL;
+
+       if (!rstate->changeable)
+               return -EPERM;
+
+       rstate->enabled = en;
+
+       return 0;
+}
+
+int regulator_suspend_enable(struct regulator_dev *rdev,
+                                   suspend_state_t state)
+{
+       return regulator_suspend_toggle(rdev, state, true);
+}
+EXPORT_SYMBOL_GPL(regulator_suspend_enable);
+
+int regulator_suspend_disable(struct regulator_dev *rdev,
+                                    suspend_state_t state)
+{
+       struct regulator *regulator;
+       struct regulator_voltage *voltage;
+
+       /*
+        * if any consumer wants this regulator device keeping on in
+        * suspend states, don't set it as disabled.
+        */
+       list_for_each_entry(regulator, &rdev->consumer_list, list) {
+               voltage = &regulator->voltage[state];
+               if (voltage->min_uV || voltage->max_uV)
+                       return 0;
+       }
+
+       return regulator_suspend_toggle(rdev, state, false);
+}
+EXPORT_SYMBOL_GPL(regulator_suspend_disable);
+
+static int _regulator_set_suspend_voltage(struct regulator *regulator,
+                                         int min_uV, int max_uV,
+                                         suspend_state_t state)
+{
+       struct regulator_dev *rdev = regulator->rdev;
+       struct regulator_state *rstate;
+
+       rstate = regulator_get_suspend_state(rdev, state);
+       if (rstate == NULL)
+               return -EINVAL;
+
+       if (rstate->min_uV == rstate->max_uV) {
+               rdev_err(rdev, "The suspend voltage can't be changed!\n");
+               return -EPERM;
+       }
+
+       return regulator_set_voltage_unlocked(regulator, min_uV, max_uV, state);
+}
+
+int regulator_set_suspend_voltage(struct regulator *regulator, int min_uV,
+                                 int max_uV, suspend_state_t state)
+{
+       int ret = 0;
+
+       /* PM_SUSPEND_ON is handled by regulator_set_voltage() */
+       if (regulator_check_states(state) || state == PM_SUSPEND_ON)
+               return -EINVAL;
+
+       regulator_lock_supply(regulator->rdev);
+
+       ret = _regulator_set_suspend_voltage(regulator, min_uV,
+                                            max_uV, state);
+
+       regulator_unlock_supply(regulator->rdev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_suspend_voltage);
+
 /**
  * regulator_set_voltage_time - get raise/fall time
  * @regulator: regulator source
@@ -3138,6 +3243,7 @@ EXPORT_SYMBOL_GPL(regulator_set_voltage_time_sel);
 int regulator_sync_voltage(struct regulator *regulator)
 {
        struct regulator_dev *rdev = regulator->rdev;
+       struct regulator_voltage *voltage = &regulator->voltage[PM_SUSPEND_ON];
        int ret, min_uV, max_uV;
 
        mutex_lock(&rdev->mutex);
@@ -3149,20 +3255,20 @@ int regulator_sync_voltage(struct regulator *regulator)
        }
 
        /* This is only going to work if we've had a voltage configured. */
-       if (!regulator->min_uV && !regulator->max_uV) {
+       if (!voltage->min_uV && !voltage->max_uV) {
                ret = -EINVAL;
                goto out;
        }
 
-       min_uV = regulator->min_uV;
-       max_uV = regulator->max_uV;
+       min_uV = voltage->min_uV;
+       max_uV = voltage->max_uV;
 
        /* This should be a paranoia check... */
        ret = regulator_check_voltage(rdev, &min_uV, &max_uV);
        if (ret < 0)
                goto out;
 
-       ret = regulator_check_consumers(rdev, &min_uV, &max_uV);
+       ret = regulator_check_consumers(rdev, &min_uV, &max_uV, 0);
        if (ret < 0)
                goto out;
 
@@ -3918,12 +4024,6 @@ static void regulator_dev_release(struct device *dev)
        kfree(rdev);
 }
 
-static struct class regulator_class = {
-       .name = "regulator",
-       .dev_release = regulator_dev_release,
-       .dev_groups = regulator_dev_groups,
-};
-
 static void rdev_init_debugfs(struct regulator_dev *rdev)
 {
        struct device *parent = rdev->dev.parent;
@@ -4174,81 +4274,86 @@ void regulator_unregister(struct regulator_dev *rdev)
 }
 EXPORT_SYMBOL_GPL(regulator_unregister);
 
-static int _regulator_suspend_prepare(struct device *dev, void *data)
+#ifdef CONFIG_SUSPEND
+static int _regulator_suspend_late(struct device *dev, void *data)
 {
        struct regulator_dev *rdev = dev_to_rdev(dev);
-       const suspend_state_t *state = data;
+       suspend_state_t *state = data;
        int ret;
 
        mutex_lock(&rdev->mutex);
-       ret = suspend_prepare(rdev, *state);
+       ret = suspend_set_state(rdev, *state);
        mutex_unlock(&rdev->mutex);
 
        return ret;
 }
 
 /**
- * regulator_suspend_prepare - prepare regulators for system wide suspend
+ * regulator_suspend_late - prepare regulators for system wide suspend
  * @state: system suspend state
  *
  * Configure each regulator with it's suspend operating parameters for state.
- * This will usually be called by machine suspend code prior to supending.
  */
-int regulator_suspend_prepare(suspend_state_t state)
+static int regulator_suspend_late(struct device *dev)
 {
-       /* ON is handled by regulator active state */
-       if (state == PM_SUSPEND_ON)
-               return -EINVAL;
+       suspend_state_t state = pm_suspend_target_state;
 
        return class_for_each_device(&regulator_class, NULL, &state,
-                                    _regulator_suspend_prepare);
+                                    _regulator_suspend_late);
 }
-EXPORT_SYMBOL_GPL(regulator_suspend_prepare);
-
-static int _regulator_suspend_finish(struct device *dev, void *data)
+static int _regulator_resume_early(struct device *dev, void *data)
 {
+       int ret = 0;
        struct regulator_dev *rdev = dev_to_rdev(dev);
-       int ret;
+       suspend_state_t *state = data;
+       struct regulator_state *rstate;
+
+       rstate = regulator_get_suspend_state(rdev, *state);
+       if (rstate == NULL)
+               return -EINVAL;
 
        mutex_lock(&rdev->mutex);
-       if (rdev->use_count > 0  || rdev->constraints->always_on) {
-               if (!_regulator_is_enabled(rdev)) {
-                       ret = _regulator_do_enable(rdev);
-                       if (ret)
-                               dev_err(dev,
-                                       "Failed to resume regulator %d\n",
-                                       ret);
-               }
-       } else {
-               if (!have_full_constraints())
-                       goto unlock;
-               if (!_regulator_is_enabled(rdev))
-                       goto unlock;
 
-               ret = _regulator_do_disable(rdev);
-               if (ret)
-                       dev_err(dev, "Failed to suspend regulator %d\n", ret);
-       }
-unlock:
+       if (rdev->desc->ops->resume_early &&
+           (rstate->enabled == ENABLE_IN_SUSPEND ||
+            rstate->enabled == DISABLE_IN_SUSPEND))
+               ret = rdev->desc->ops->resume_early(rdev);
+
        mutex_unlock(&rdev->mutex);
 
-       /* Keep processing regulators in spite of any errors */
-       return 0;
+       return ret;
 }
 
-/**
- * regulator_suspend_finish - resume regulators from system wide suspend
- *
- * Turn on regulators that might be turned off by regulator_suspend_prepare
- * and that should be turned on according to the regulators properties.
- */
-int regulator_suspend_finish(void)
+static int regulator_resume_early(struct device *dev)
 {
-       return class_for_each_device(&regulator_class, NULL, NULL,
-                                    _regulator_suspend_finish);
+       suspend_state_t state = pm_suspend_target_state;
+
+       return class_for_each_device(&regulator_class, NULL, &state,
+                                    _regulator_resume_early);
 }
-EXPORT_SYMBOL_GPL(regulator_suspend_finish);
 
+#else /* !CONFIG_SUSPEND */
+
+#define regulator_suspend_late NULL
+#define regulator_resume_early NULL
+
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops __maybe_unused regulator_pm_ops = {
+       .suspend_late   = regulator_suspend_late,
+       .resume_early   = regulator_resume_early,
+};
+#endif
+
+struct class regulator_class = {
+       .name = "regulator",
+       .dev_release = regulator_dev_release,
+       .dev_groups = regulator_dev_groups,
+#ifdef CONFIG_PM
+       .pm = &regulator_pm_ops,
+#endif
+};
 /**
  * regulator_has_full_constraints - the system has fully specified constraints
  *
@@ -4424,8 +4529,8 @@ static void regulator_summary_show_subtree(struct seq_file *s,
                switch (rdev->desc->type) {
                case REGULATOR_VOLTAGE:
                        seq_printf(s, "%37dmV %5dmV",
-                                  consumer->min_uV / 1000,
-                                  consumer->max_uV / 1000);
+                                  consumer->voltage[PM_SUSPEND_ON].min_uV / 1000,
+                                  consumer->voltage[PM_SUSPEND_ON].max_uV / 1000);
                        break;
                case REGULATOR_CURRENT:
                        break;
index 66a8ea0c83868ebb9a371e52d54761fe1bcc3671..abfd56e8c78aa8f63e029204b60d4e27ae053b2a 100644 (file)
 #ifndef __REGULATOR_INTERNAL_H
 #define __REGULATOR_INTERNAL_H
 
+#include <linux/suspend.h>
+
+#define REGULATOR_STATES_NUM   (PM_SUSPEND_MAX + 1)
+
+struct regulator_voltage {
+       int min_uV;
+       int max_uV;
+};
+
 /*
  * struct regulator
  *
  * One for each consumer device.
+ * @voltage - a voltage array for each state of runtime, i.e.:
+ *            PM_SUSPEND_ON
+ *            PM_SUSPEND_TO_IDLE
+ *            PM_SUSPEND_STANDBY
+ *            PM_SUSPEND_MEM
+ *            PM_SUSPEND_MAX
  */
 struct regulator {
        struct device *dev;
@@ -27,14 +42,22 @@ struct regulator {
        unsigned int always_on:1;
        unsigned int bypass:1;
        int uA_load;
-       int min_uV;
-       int max_uV;
+       struct regulator_voltage voltage[REGULATOR_STATES_NUM];
        const char *supply_name;
        struct device_attribute dev_attr;
        struct regulator_dev *rdev;
        struct dentry *debugfs;
 };
 
+extern struct class regulator_class;
+
+static inline struct regulator_dev *dev_to_rdev(struct device *dev)
+{
+       return container_of(dev, struct regulator_dev, dev);
+}
+
+struct regulator_dev *of_find_regulator_by_node(struct device_node *np);
+
 #ifdef CONFIG_OF
 struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
                                 const struct regulator_desc *desc,
index 14637a01ba2d3db64d3357ab3e4ea7ad2915ca8b..092ed6efb3ec91a1ee9f45445d6a2f6e1076aa13 100644 (file)
@@ -177,14 +177,30 @@ static void of_get_regulation_constraints(struct device_node *np,
 
                if (of_property_read_bool(suspend_np,
                                        "regulator-on-in-suspend"))
-                       suspend_state->enabled = true;
+                       suspend_state->enabled = ENABLE_IN_SUSPEND;
                else if (of_property_read_bool(suspend_np,
                                        "regulator-off-in-suspend"))
-                       suspend_state->disabled = true;
+                       suspend_state->enabled = DISABLE_IN_SUSPEND;
+               else
+                       suspend_state->enabled = DO_NOTHING_IN_SUSPEND;
+
+               if (!of_property_read_u32(np, "regulator-suspend-min-microvolt",
+                                         &pval))
+                       suspend_state->min_uV = pval;
+
+               if (!of_property_read_u32(np, "regulator-suspend-max-microvolt",
+                                         &pval))
+                       suspend_state->max_uV = pval;
 
                if (!of_property_read_u32(suspend_np,
                                        "regulator-suspend-microvolt", &pval))
                        suspend_state->uV = pval;
+               else /* otherwise use min_uV as default suspend voltage */
+                       suspend_state->uV = suspend_state->min_uV;
+
+               if (of_property_read_bool(suspend_np,
+                                       "regulator-changeable-in-suspend"))
+                       suspend_state->changeable = true;
 
                if (i == PM_SUSPEND_MEM)
                        constraints->initial_state = PM_SUSPEND_MEM;
@@ -376,3 +392,17 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
 
        return init_data;
 }
+
+static int of_node_match(struct device *dev, const void *data)
+{
+       return dev->of_node == data;
+}
+
+struct regulator_dev *of_find_regulator_by_node(struct device_node *np)
+{
+       struct device *dev;
+
+       dev = class_find_device(&regulator_class, NULL, np, of_node_match);
+
+       return dev ? dev_to_rdev(dev) : NULL;
+}
index 0241ada47d04bce724e3baebfa7a88dd520cca90..63c7a0c17777e6f888f59b7affd38f4873a70b7f 100644 (file)
@@ -486,24 +486,6 @@ static int spmi_vreg_update_bits(struct spmi_regulator *vreg, u16 addr, u8 val,
        return regmap_update_bits(vreg->regmap, vreg->base + addr, mask, val);
 }
 
-static int spmi_regulator_common_is_enabled(struct regulator_dev *rdev)
-{
-       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
-       u8 reg;
-
-       spmi_vreg_read(vreg, SPMI_COMMON_REG_ENABLE, &reg, 1);
-
-       return (reg & SPMI_COMMON_ENABLE_MASK) == SPMI_COMMON_ENABLE;
-}
-
-static int spmi_regulator_common_enable(struct regulator_dev *rdev)
-{
-       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
-
-       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
-               SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
-}
-
 static int spmi_regulator_vs_enable(struct regulator_dev *rdev)
 {
        struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
@@ -513,7 +495,7 @@ static int spmi_regulator_vs_enable(struct regulator_dev *rdev)
                vreg->vs_enable_time = ktime_get();
        }
 
-       return spmi_regulator_common_enable(rdev);
+       return regulator_enable_regmap(rdev);
 }
 
 static int spmi_regulator_vs_ocp(struct regulator_dev *rdev)
@@ -524,14 +506,6 @@ static int spmi_regulator_vs_ocp(struct regulator_dev *rdev)
        return spmi_vreg_write(vreg, SPMI_VS_REG_OCP, &reg, 1);
 }
 
-static int spmi_regulator_common_disable(struct regulator_dev *rdev)
-{
-       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
-
-       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
-               SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
-}
-
 static int spmi_regulator_select_voltage(struct spmi_regulator *vreg,
                                         int min_uV, int max_uV)
 {
@@ -1062,9 +1036,9 @@ static irqreturn_t spmi_regulator_vs_ocp_isr(int irq, void *data)
 }
 
 static struct regulator_ops spmi_smps_ops = {
-       .enable                 = spmi_regulator_common_enable,
-       .disable                = spmi_regulator_common_disable,
-       .is_enabled             = spmi_regulator_common_is_enabled,
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
        .set_voltage_sel        = spmi_regulator_common_set_voltage,
        .set_voltage_time_sel   = spmi_regulator_set_voltage_time_sel,
        .get_voltage_sel        = spmi_regulator_common_get_voltage,
@@ -1077,9 +1051,9 @@ static struct regulator_ops spmi_smps_ops = {
 };
 
 static struct regulator_ops spmi_ldo_ops = {
-       .enable                 = spmi_regulator_common_enable,
-       .disable                = spmi_regulator_common_disable,
-       .is_enabled             = spmi_regulator_common_is_enabled,
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
        .set_voltage_sel        = spmi_regulator_common_set_voltage,
        .get_voltage_sel        = spmi_regulator_common_get_voltage,
        .map_voltage            = spmi_regulator_common_map_voltage,
@@ -1094,9 +1068,9 @@ static struct regulator_ops spmi_ldo_ops = {
 };
 
 static struct regulator_ops spmi_ln_ldo_ops = {
-       .enable                 = spmi_regulator_common_enable,
-       .disable                = spmi_regulator_common_disable,
-       .is_enabled             = spmi_regulator_common_is_enabled,
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
        .set_voltage_sel        = spmi_regulator_common_set_voltage,
        .get_voltage_sel        = spmi_regulator_common_get_voltage,
        .map_voltage            = spmi_regulator_common_map_voltage,
@@ -1107,8 +1081,8 @@ static struct regulator_ops spmi_ln_ldo_ops = {
 
 static struct regulator_ops spmi_vs_ops = {
        .enable                 = spmi_regulator_vs_enable,
-       .disable                = spmi_regulator_common_disable,
-       .is_enabled             = spmi_regulator_common_is_enabled,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
        .set_pull_down          = spmi_regulator_common_set_pull_down,
        .set_soft_start         = spmi_regulator_common_set_soft_start,
        .set_over_current_protection = spmi_regulator_vs_ocp,
@@ -1117,9 +1091,9 @@ static struct regulator_ops spmi_vs_ops = {
 };
 
 static struct regulator_ops spmi_boost_ops = {
-       .enable                 = spmi_regulator_common_enable,
-       .disable                = spmi_regulator_common_disable,
-       .is_enabled             = spmi_regulator_common_is_enabled,
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
        .set_voltage_sel        = spmi_regulator_single_range_set_voltage,
        .get_voltage_sel        = spmi_regulator_single_range_get_voltage,
        .map_voltage            = spmi_regulator_single_map_voltage,
@@ -1128,9 +1102,9 @@ static struct regulator_ops spmi_boost_ops = {
 };
 
 static struct regulator_ops spmi_ftsmps_ops = {
-       .enable                 = spmi_regulator_common_enable,
-       .disable                = spmi_regulator_common_disable,
-       .is_enabled             = spmi_regulator_common_is_enabled,
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
        .set_voltage_sel        = spmi_regulator_common_set_voltage,
        .set_voltage_time_sel   = spmi_regulator_set_voltage_time_sel,
        .get_voltage_sel        = spmi_regulator_common_get_voltage,
@@ -1143,9 +1117,9 @@ static struct regulator_ops spmi_ftsmps_ops = {
 };
 
 static struct regulator_ops spmi_ult_lo_smps_ops = {
-       .enable                 = spmi_regulator_common_enable,
-       .disable                = spmi_regulator_common_disable,
-       .is_enabled             = spmi_regulator_common_is_enabled,
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
        .set_voltage_sel        = spmi_regulator_ult_lo_smps_set_voltage,
        .set_voltage_time_sel   = spmi_regulator_set_voltage_time_sel,
        .get_voltage_sel        = spmi_regulator_ult_lo_smps_get_voltage,
@@ -1157,9 +1131,9 @@ static struct regulator_ops spmi_ult_lo_smps_ops = {
 };
 
 static struct regulator_ops spmi_ult_ho_smps_ops = {
-       .enable                 = spmi_regulator_common_enable,
-       .disable                = spmi_regulator_common_disable,
-       .is_enabled             = spmi_regulator_common_is_enabled,
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
        .set_voltage_sel        = spmi_regulator_single_range_set_voltage,
        .set_voltage_time_sel   = spmi_regulator_set_voltage_time_sel,
        .get_voltage_sel        = spmi_regulator_single_range_get_voltage,
@@ -1172,9 +1146,9 @@ static struct regulator_ops spmi_ult_ho_smps_ops = {
 };
 
 static struct regulator_ops spmi_ult_ldo_ops = {
-       .enable                 = spmi_regulator_common_enable,
-       .disable                = spmi_regulator_common_disable,
-       .is_enabled             = spmi_regulator_common_is_enabled,
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
        .set_voltage_sel        = spmi_regulator_single_range_set_voltage,
        .get_voltage_sel        = spmi_regulator_single_range_get_voltage,
        .map_voltage            = spmi_regulator_single_map_voltage,
@@ -1711,6 +1685,9 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
                vreg->desc.id = -1;
                vreg->desc.owner = THIS_MODULE;
                vreg->desc.type = REGULATOR_VOLTAGE;
+               vreg->desc.enable_reg = reg->base + SPMI_COMMON_REG_ENABLE;
+               vreg->desc.enable_mask = SPMI_COMMON_ENABLE_MASK;
+               vreg->desc.enable_val = SPMI_COMMON_ENABLE;
                vreg->desc.name = name = reg->name;
                vreg->desc.supply_name = reg->supply;
                vreg->desc.of_match = reg->name;
@@ -1723,6 +1700,7 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
 
                config.dev = dev;
                config.driver_data = vreg;
+               config.regmap = regmap;
                rdev = devm_regulator_register(dev, &vreg->desc, &config);
                if (IS_ERR(rdev)) {
                        dev_err(dev, "failed to register %s\n", name);
diff --git a/drivers/regulator/sc2731-regulator.c b/drivers/regulator/sc2731-regulator.c
new file mode 100644 (file)
index 0000000..eb2bdf0
--- /dev/null
@@ -0,0 +1,256 @@
+ //SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017 Spreadtrum Communications Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+/*
+ * SC2731 regulator lock register
+ */
+#define SC2731_PWR_WR_PROT             0xf0c
+#define SC2731_WR_UNLOCK_VALUE         0x6e7f
+
+/*
+ * SC2731 enable register
+ */
+#define SC2731_POWER_PD_SW             0xc28
+#define SC2731_LDO_CAMA0_PD            0xcfc
+#define SC2731_LDO_CAMA1_PD            0xd04
+#define SC2731_LDO_CAMMOT_PD           0xd0c
+#define SC2731_LDO_VLDO_PD             0xd6c
+#define SC2731_LDO_EMMCCORE_PD         0xd2c
+#define SC2731_LDO_SDCORE_PD           0xd74
+#define SC2731_LDO_SDIO_PD             0xd70
+#define SC2731_LDO_WIFIPA_PD           0xd4c
+#define SC2731_LDO_USB33_PD            0xd5c
+#define SC2731_LDO_CAMD0_PD            0xd7c
+#define SC2731_LDO_CAMD1_PD            0xd84
+#define SC2731_LDO_CON_PD              0xd8c
+#define SC2731_LDO_CAMIO_PD            0xd94
+#define SC2731_LDO_SRAM_PD             0xd78
+
+/*
+ * SC2731 enable mask
+ */
+#define SC2731_DCDC_CPU0_PD_MASK       BIT(4)
+#define SC2731_DCDC_CPU1_PD_MASK       BIT(3)
+#define SC2731_DCDC_RF_PD_MASK         BIT(11)
+#define SC2731_LDO_CAMA0_PD_MASK       BIT(0)
+#define SC2731_LDO_CAMA1_PD_MASK       BIT(0)
+#define SC2731_LDO_CAMMOT_PD_MASK      BIT(0)
+#define SC2731_LDO_VLDO_PD_MASK                BIT(0)
+#define SC2731_LDO_EMMCCORE_PD_MASK    BIT(0)
+#define SC2731_LDO_SDCORE_PD_MASK      BIT(0)
+#define SC2731_LDO_SDIO_PD_MASK                BIT(0)
+#define SC2731_LDO_WIFIPA_PD_MASK      BIT(0)
+#define SC2731_LDO_USB33_PD_MASK       BIT(0)
+#define SC2731_LDO_CAMD0_PD_MASK       BIT(0)
+#define SC2731_LDO_CAMD1_PD_MASK       BIT(0)
+#define SC2731_LDO_CON_PD_MASK         BIT(0)
+#define SC2731_LDO_CAMIO_PD_MASK       BIT(0)
+#define SC2731_LDO_SRAM_PD_MASK                BIT(0)
+
+/*
+ * SC2731 vsel register
+ */
+#define SC2731_DCDC_CPU0_VOL           0xc54
+#define SC2731_DCDC_CPU1_VOL           0xc64
+#define SC2731_DCDC_RF_VOL             0xcb8
+#define SC2731_LDO_CAMA0_VOL           0xd00
+#define SC2731_LDO_CAMA1_VOL           0xd08
+#define SC2731_LDO_CAMMOT_VOL          0xd10
+#define SC2731_LDO_VLDO_VOL            0xd28
+#define SC2731_LDO_EMMCCORE_VOL                0xd30
+#define SC2731_LDO_SDCORE_VOL          0xd38
+#define SC2731_LDO_SDIO_VOL            0xd40
+#define SC2731_LDO_WIFIPA_VOL          0xd50
+#define SC2731_LDO_USB33_VOL           0xd60
+#define SC2731_LDO_CAMD0_VOL           0xd80
+#define SC2731_LDO_CAMD1_VOL           0xd88
+#define SC2731_LDO_CON_VOL             0xd90
+#define SC2731_LDO_CAMIO_VOL           0xd98
+#define SC2731_LDO_SRAM_VOL            0xdB0
+
+/*
+ * SC2731 vsel register mask
+ */
+#define SC2731_DCDC_CPU0_VOL_MASK      GENMASK(8, 0)
+#define SC2731_DCDC_CPU1_VOL_MASK      GENMASK(8, 0)
+#define SC2731_DCDC_RF_VOL_MASK                GENMASK(8, 0)
+#define SC2731_LDO_CAMA0_VOL_MASK      GENMASK(7, 0)
+#define SC2731_LDO_CAMA1_VOL_MASK      GENMASK(7, 0)
+#define SC2731_LDO_CAMMOT_VOL_MASK     GENMASK(7, 0)
+#define SC2731_LDO_VLDO_VOL_MASK       GENMASK(7, 0)
+#define SC2731_LDO_EMMCCORE_VOL_MASK   GENMASK(7, 0)
+#define SC2731_LDO_SDCORE_VOL_MASK     GENMASK(7, 0)
+#define SC2731_LDO_SDIO_VOL_MASK       GENMASK(7, 0)
+#define SC2731_LDO_WIFIPA_VOL_MASK     GENMASK(7, 0)
+#define SC2731_LDO_USB33_VOL_MASK      GENMASK(7, 0)
+#define SC2731_LDO_CAMD0_VOL_MASK      GENMASK(6, 0)
+#define SC2731_LDO_CAMD1_VOL_MASK      GENMASK(6, 0)
+#define SC2731_LDO_CON_VOL_MASK                GENMASK(6, 0)
+#define SC2731_LDO_CAMIO_VOL_MASK      GENMASK(6, 0)
+#define SC2731_LDO_SRAM_VOL_MASK       GENMASK(6, 0)
+
+enum sc2731_regulator_id {
+       SC2731_BUCK_CPU0,
+       SC2731_BUCK_CPU1,
+       SC2731_BUCK_RF,
+       SC2731_LDO_CAMA0,
+       SC2731_LDO_CAMA1,
+       SC2731_LDO_CAMMOT,
+       SC2731_LDO_VLDO,
+       SC2731_LDO_EMMCCORE,
+       SC2731_LDO_SDCORE,
+       SC2731_LDO_SDIO,
+       SC2731_LDO_WIFIPA,
+       SC2731_LDO_USB33,
+       SC2731_LDO_CAMD0,
+       SC2731_LDO_CAMD1,
+       SC2731_LDO_CON,
+       SC2731_LDO_CAMIO,
+       SC2731_LDO_SRAM,
+};
+
+static const struct regulator_ops sc2731_regu_linear_ops = {
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+       .is_enabled = regulator_is_enabled_regmap,
+       .list_voltage = regulator_list_voltage_linear,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+#define SC2731_REGU_LINEAR(_id, en_reg, en_mask, vreg, vmask,  \
+                         vstep, vmin, vmax) {                  \
+       .name                   = #_id,                         \
+       .of_match               = of_match_ptr(#_id),           \
+       .ops                    = &sc2731_regu_linear_ops,      \
+       .type                   = REGULATOR_VOLTAGE,            \
+       .id                     = SC2731_##_id,                 \
+       .owner                  = THIS_MODULE,                  \
+       .min_uV                 = vmin,                         \
+       .n_voltages             = ((vmax) - (vmin)) / (vstep) + 1,      \
+       .uV_step                = vstep,                        \
+       .enable_is_inverted     = true,                         \
+       .enable_val             = 0,                            \
+       .enable_reg             = en_reg,                       \
+       .enable_mask            = en_mask,                      \
+       .vsel_reg               = vreg,                         \
+       .vsel_mask              = vmask,                        \
+}
+
+static struct regulator_desc regulators[] = {
+       SC2731_REGU_LINEAR(BUCK_CPU0, SC2731_POWER_PD_SW,
+                          SC2731_DCDC_CPU0_PD_MASK, SC2731_DCDC_CPU0_VOL,
+                          SC2731_DCDC_CPU0_VOL_MASK, 3125, 400000, 1996875),
+       SC2731_REGU_LINEAR(BUCK_CPU1, SC2731_POWER_PD_SW,
+                          SC2731_DCDC_CPU1_PD_MASK, SC2731_DCDC_CPU1_VOL,
+                          SC2731_DCDC_CPU1_VOL_MASK, 3125, 400000, 1996875),
+       SC2731_REGU_LINEAR(BUCK_RF, SC2731_POWER_PD_SW, SC2731_DCDC_RF_PD_MASK,
+                          SC2731_DCDC_RF_VOL, SC2731_DCDC_RF_VOL_MASK,
+                          3125, 600000, 2196875),
+       SC2731_REGU_LINEAR(LDO_CAMA0, SC2731_LDO_CAMA0_PD,
+                          SC2731_LDO_CAMA0_PD_MASK, SC2731_LDO_CAMA0_VOL,
+                          SC2731_LDO_CAMA0_VOL_MASK, 10000, 1200000, 3750000),
+       SC2731_REGU_LINEAR(LDO_CAMA1, SC2731_LDO_CAMA1_PD,
+                          SC2731_LDO_CAMA1_PD_MASK, SC2731_LDO_CAMA1_VOL,
+                          SC2731_LDO_CAMA1_VOL_MASK, 10000, 1200000, 3750000),
+       SC2731_REGU_LINEAR(LDO_CAMMOT, SC2731_LDO_CAMMOT_PD,
+                          SC2731_LDO_CAMMOT_PD_MASK, SC2731_LDO_CAMMOT_VOL,
+                          SC2731_LDO_CAMMOT_VOL_MASK, 10000, 1200000, 3750000),
+       SC2731_REGU_LINEAR(LDO_VLDO, SC2731_LDO_VLDO_PD,
+                          SC2731_LDO_VLDO_PD_MASK, SC2731_LDO_VLDO_VOL,
+                          SC2731_LDO_VLDO_VOL_MASK, 10000, 1200000, 3750000),
+       SC2731_REGU_LINEAR(LDO_EMMCCORE, SC2731_LDO_EMMCCORE_PD,
+                          SC2731_LDO_EMMCCORE_PD_MASK, SC2731_LDO_EMMCCORE_VOL,
+                          SC2731_LDO_EMMCCORE_VOL_MASK, 10000, 1200000,
+                          3750000),
+       SC2731_REGU_LINEAR(LDO_SDCORE, SC2731_LDO_SDCORE_PD,
+                          SC2731_LDO_SDCORE_PD_MASK, SC2731_LDO_SDCORE_VOL,
+                          SC2731_LDO_SDCORE_VOL_MASK, 10000, 1200000, 3750000),
+       SC2731_REGU_LINEAR(LDO_SDIO, SC2731_LDO_SDIO_PD,
+                          SC2731_LDO_SDIO_PD_MASK, SC2731_LDO_SDIO_VOL,
+                          SC2731_LDO_SDIO_VOL_MASK, 10000, 1200000, 3750000),
+       SC2731_REGU_LINEAR(LDO_WIFIPA, SC2731_LDO_WIFIPA_PD,
+                          SC2731_LDO_WIFIPA_PD_MASK, SC2731_LDO_WIFIPA_VOL,
+                          SC2731_LDO_WIFIPA_VOL_MASK, 10000, 1200000, 3750000),
+       SC2731_REGU_LINEAR(LDO_USB33, SC2731_LDO_USB33_PD,
+                          SC2731_LDO_USB33_PD_MASK, SC2731_LDO_USB33_VOL,
+                          SC2731_LDO_USB33_VOL_MASK, 10000, 1200000, 3750000),
+       SC2731_REGU_LINEAR(LDO_CAMD0, SC2731_LDO_CAMD0_PD,
+                          SC2731_LDO_CAMD0_PD_MASK, SC2731_LDO_CAMD0_VOL,
+                          SC2731_LDO_CAMD0_VOL_MASK, 6250, 1000000, 1793750),
+       SC2731_REGU_LINEAR(LDO_CAMD1, SC2731_LDO_CAMD1_PD,
+                          SC2731_LDO_CAMD1_PD_MASK, SC2731_LDO_CAMD1_VOL,
+                          SC2731_LDO_CAMD1_VOL_MASK, 6250, 1000000, 1793750),
+       SC2731_REGU_LINEAR(LDO_CON, SC2731_LDO_CON_PD,
+                          SC2731_LDO_CON_PD_MASK, SC2731_LDO_CON_VOL,
+                          SC2731_LDO_CON_VOL_MASK, 6250, 1000000, 1793750),
+       SC2731_REGU_LINEAR(LDO_CAMIO, SC2731_LDO_CAMIO_PD,
+                          SC2731_LDO_CAMIO_PD_MASK, SC2731_LDO_CAMIO_VOL,
+                          SC2731_LDO_CAMIO_VOL_MASK, 6250, 1000000, 1793750),
+       SC2731_REGU_LINEAR(LDO_SRAM, SC2731_LDO_SRAM_PD,
+                          SC2731_LDO_SRAM_PD_MASK, SC2731_LDO_SRAM_VOL,
+                          SC2731_LDO_SRAM_VOL_MASK, 6250, 1000000, 1793750),
+};
+
+static int sc2731_regulator_unlock(struct regmap *regmap)
+{
+       return regmap_write(regmap, SC2731_PWR_WR_PROT,
+                           SC2731_WR_UNLOCK_VALUE);
+}
+
+static int sc2731_regulator_probe(struct platform_device *pdev)
+{
+       int i, ret;
+       struct regmap *regmap;
+       struct regulator_config config = { };
+       struct regulator_dev *rdev;
+
+       regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!regmap) {
+               dev_err(&pdev->dev, "failed to get regmap.\n");
+               return -ENODEV;
+       }
+
+       ret = sc2731_regulator_unlock(regmap);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to release regulator lock\n");
+               return ret;
+       }
+
+       config.dev = &pdev->dev;
+       config.regmap = regmap;
+
+       for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+               rdev = devm_regulator_register(&pdev->dev, &regulators[i],
+                                              &config);
+               if (IS_ERR(rdev)) {
+                       dev_err(&pdev->dev, "failed to register regulator %s\n",
+                               regulators[i].name);
+                       return PTR_ERR(rdev);
+               }
+       }
+
+       return 0;
+}
+
+static struct platform_driver sc2731_regulator_driver = {
+       .driver = {
+               .name = "sc27xx-regulator",
+       },
+       .probe = sc2731_regulator_probe,
+};
+
+module_platform_driver(sc2731_regulator_driver);
+
+MODULE_AUTHOR("Chen Junhui <erick.chen@spreadtrum.com>");
+MODULE_DESCRIPTION("Spreadtrum SC2731 regulator driver");
+MODULE_LICENSE("GPL v2");
index bc489958fed7537ffd6bbb559796b8f95f5d39a4..1827185beacc4e7f9079ae68a170f9c39003560c 100644 (file)
@@ -28,9 +28,6 @@
 #include <linux/regulator/machine.h>
 #include <linux/mfd/tps65218.h>
 
-enum tps65218_regulators { DCDC1, DCDC2, DCDC3, DCDC4,
-                          DCDC5, DCDC6, LDO1, LS3 };
-
 #define TPS65218_REGULATOR(_name, _of, _id, _type, _ops, _n, _vr, _vm, _er, \
                           _em, _cr, _cm, _lr, _nlr, _delay, _fuv, _sr, _sm) \
        {                                                       \
@@ -329,6 +326,8 @@ static int tps65218_regulator_probe(struct platform_device *pdev)
        /* Allocate memory for strobes */
        tps->strobes = devm_kzalloc(&pdev->dev, sizeof(u8) *
                                    TPS65218_NUM_REGULATOR, GFP_KERNEL);
+       if (!tps->strobes)
+               return -ENOMEM;
 
        for (i = 0; i < ARRAY_SIZE(regulators); i++) {
                rdev = devm_regulator_register(&pdev->dev, &regulators[i],
index 94417b4226bd88a42d5d53c845266ee97ec30879..4c00486b7a78002dbf8bb6035f06b8377999017b 100644 (file)
@@ -214,6 +214,8 @@ struct regulator_ops {
        /* set regulator suspend operating mode (defined in consumer.h) */
        int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode);
 
+       int (*resume_early)(struct regulator_dev *rdev);
+
        int (*set_pull_down) (struct regulator_dev *);
 };
 
index 9cd4fef37203cbdc8fc76a07a1d81334acc789d3..93a04893c739417150fcb7afb0a5a0021c27ab0b 100644 (file)
@@ -42,6 +42,16 @@ struct regulator;
 #define REGULATOR_CHANGE_DRMS          0x10
 #define REGULATOR_CHANGE_BYPASS                0x20
 
+/*
+ * operations in suspend mode
+ * DO_NOTHING_IN_SUSPEND - the default value
+ * DISABLE_IN_SUSPEND  - turn off regulator in suspend states
+ * ENABLE_IN_SUSPEND   - keep regulator on in suspend states
+ */
+#define DO_NOTHING_IN_SUSPEND  (-1)
+#define DISABLE_IN_SUSPEND     0
+#define ENABLE_IN_SUSPEND      1
+
 /* Regulator active discharge flags */
 enum regulator_active_discharge {
        REGULATOR_ACTIVE_DISCHARGE_DEFAULT,
@@ -56,16 +66,24 @@ enum regulator_active_discharge {
  * state.  One of enabled or disabled must be set for the
  * configuration to be applied.
  *
- * @uV: Operating voltage during suspend.
+ * @uV: Default operating voltage during suspend, it can be adjusted
+ *     among <min_uV, max_uV>.
+ * @min_uV: Minimum suspend voltage may be set.
+ * @max_uV: Maximum suspend voltage may be set.
  * @mode: Operating mode during suspend.
- * @enabled: Enabled during suspend.
- * @disabled: Disabled during suspend.
+ * @enabled: operations during suspend.
+ *          - DO_NOTHING_IN_SUSPEND
+ *          - DISABLE_IN_SUSPEND
+ *          - ENABLE_IN_SUSPEND
+ * @changeable: Is this state can be switched between enabled/disabled,
  */
 struct regulator_state {
-       int uV; /* suspend voltage */
-       unsigned int mode; /* suspend regulator operating mode */
-       int enabled; /* is regulator enabled in this suspend state */
-       int disabled; /* is the regulator disabled in this suspend state */
+       int uV;
+       int min_uV;
+       int max_uV;
+       unsigned int mode;
+       int enabled;
+       bool changeable;
 };
 
 /**
@@ -225,12 +243,12 @@ struct regulator_init_data {
 
 #ifdef CONFIG_REGULATOR
 void regulator_has_full_constraints(void);
-int regulator_suspend_prepare(suspend_state_t state);
-int regulator_suspend_finish(void);
 #else
 static inline void regulator_has_full_constraints(void)
 {
 }
+#endif
+
 static inline int regulator_suspend_prepare(suspend_state_t state)
 {
        return 0;
@@ -239,6 +257,5 @@ static inline int regulator_suspend_finish(void)
 {
        return 0;
 }
-#endif
 
 #endif